3c818b8b326f180d3a36436892d8e25550f16c0a
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.apps.controlloop.feature.trans;
22
23 import com.google.common.cache.CacheBuilder;
24 import com.google.common.cache.CacheLoader;
25 import com.google.common.cache.LoadingCache;
26 import com.google.common.cache.RemovalListener;
27 import com.google.common.cache.RemovalNotification;
28
29 import java.time.Instant;
30 import java.time.ZonedDateTime;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Properties;
34 import java.util.UUID;
35 import java.util.concurrent.TimeUnit;
36
37 import org.onap.policy.controlloop.ControlLoopOperation;
38 import org.onap.policy.controlloop.VirtualControlLoopNotification;
39 import org.onap.policy.drools.persistence.SystemPersistence;
40 import org.onap.policy.drools.system.PolicyController;
41 import org.onap.policy.drools.utils.logging.MDCTransaction;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * Control Loop Metrics Tracker
47  */
48 public interface ControlLoopMetrics {
49
50     /**
51      * gets all transaction identifiers being monitored
52      *
53      * @return transaction id list
54      */
55     List<UUID> getTransactionIds();
56
57     /**
58      * gets all detailed transactions
59      *
60      * @return list of transactions
61      */
62     List<VirtualControlLoopNotification> getTransactions();
63
64     /**
65      * track controller's notification events
66      *
67      * @param controller policy controller sending out notification
68      * @param notification notification
69      */
70     void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification);
71
72     /**
73      * gets an in-progress transaction
74      *
75      * @param requestId request ID
76      * @return in progress notification
77      */
78     VirtualControlLoopNotification getTransaction(UUID requestId);
79
80     /**
81      * removes an in-progress transaction
82      *
83      * @param requestId request ID
84      */
85     void removeTransaction(UUID requestId);
86
87     /**
88      * get cache size
89      *
90      * @return cache size
91      */
92     long getCacheSize();
93
94     /**
95      * get cache size
96      *
97      * @return cache size
98      */
99     long getCacheOccupancy();
100
101     /**
102      * sets cache size
103      *
104      * @param cacheSize cache size
105      */
106     void setMaxCacheSize(long cacheSize);
107
108     /**
109      * cached transaction expiration timeout in seconds
110      *
111      * @return transaction timeout in seconds
112      */
113     long getTransactionTimeout();
114
115     /**
116      * sets transaction timeout in seconds
117      *
118      * @param transactionTimeout transaction timeout in seconds
119      */
120     void setTransactionTimeout(long transactionTimeout);
121
122     /**
123      * reset cache
124      *
125      * @param cacheSize new cache size
126      * @param transactionTimeout new transaction timeout in seconds
127      */
128     void resetCache(long cacheSize, long transactionTimeout);
129
130     /**
131      * refresh underlying transaction management
132      */
133     void refresh();
134
135     /**
136      * singleton manager object
137      */
138     ControlLoopMetrics manager = new CacheBasedControlLoopMetricsManager();
139 }
140
141
142 /**
143  * Control Loop Metrics Tracker Implementation
144  */
145 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
146
147     private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
148
149     private LoadingCache<UUID, VirtualControlLoopNotification> cache;
150     private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
151
152     private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
153
154     public CacheBasedControlLoopMetricsManager() {
155
156         Properties properties =
157                 SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
158
159         /* cache size */
160
161         try {
162             this.cacheSize =
163                     Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
164                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
165         } catch (Exception e) {
166             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
167                     ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
168         }
169
170         /* transaction timeout */
171
172         try {
173             this.transactionTimeout = Long
174                     .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
175                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
176         } catch (Exception e) {
177             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
178                     ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
179         }
180
181         resetCache(this.cacheSize, this.transactionTimeout);
182     }
183
184     @Override
185     public void resetCache(long cacheSize, long transactionTimeout) {
186         this.cacheSize = cacheSize;
187         this.transactionTimeout = transactionTimeout;
188
189         CacheLoader<UUID, VirtualControlLoopNotification> loader =
190                 new CacheLoader<UUID, VirtualControlLoopNotification>() {
191
192                     @Override
193                     public VirtualControlLoopNotification load(UUID key) throws Exception {
194                         return null;
195                     }
196                 };
197
198         RemovalListener<UUID, VirtualControlLoopNotification> listener =
199                 new RemovalListener<UUID, VirtualControlLoopNotification>() {
200                     @Override
201                     public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
202                         if (notification.wasEvicted()) {
203                             evicted(notification.getValue());
204                         } else {
205                             logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
206                                     notification.getCause().name());
207                         }
208                     }
209                 };
210
211         synchronized (this) {
212             if (this.cache != null) {
213                 this.cache.cleanUp();
214                 this.cache.invalidateAll();
215             }
216
217             this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
218                     .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
219         }
220     }
221
222     @Override
223     public void refresh() {
224         this.cache.cleanUp();
225     }
226
227     @Override
228     public List<UUID> getTransactionIds() {
229         return new ArrayList<>(this.cache.asMap().keySet());
230     }
231
232     @Override
233     public List<VirtualControlLoopNotification> getTransactions() {
234         return new ArrayList<>(this.cache.asMap().values());
235     }
236
237     @Override
238     public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
239         if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
240             logger.warn("Invalid notification: {}", notification);
241             return;
242         }
243
244         if (notification.getNotificationTime() == null) {
245             notification.setNotificationTime(ZonedDateTime.now());
246         }
247
248         notification.setFrom(notification.getFrom() + ":" + controller.getName());
249
250         switch (notification.getNotification()) {
251             case REJECTED:
252             case FINAL_FAILURE:
253             case FINAL_SUCCESS:
254             case FINAL_OPENLOOP:
255                 endTransaction(notification);
256                 break;
257             case ACTIVE:
258             case OPERATION:
259             case OPERATION_SUCCESS:
260             case OPERATION_FAILURE:
261                 /* any other value is an in progress transaction */
262                 inProgressTransaction(notification);
263                 break;
264             default:
265                 /* unexpected */
266                 logger.warn("unexpected notification type {} in notification {}",
267                         notification.getNotification().toString(), notification);
268                 break;
269         }
270     }
271
272     @Override
273     public VirtualControlLoopNotification getTransaction(UUID requestId) {
274         return cache.getIfPresent(requestId);
275     }
276
277     @Override
278     public void removeTransaction(UUID requestId) {
279         cache.invalidate(requestId);
280     }
281
282     /**
283      * tracks an in progress control loop transaction
284      *
285      * @param notification control loop notification
286      */
287     protected void inProgressTransaction(VirtualControlLoopNotification notification) {
288         if (cache.getIfPresent(notification.getRequestId()) == null) {
289             cache.put(notification.getRequestId(), notification);
290         }
291
292         this.metric(notification);
293     }
294
295     /**
296      * end of a control loop transaction
297      *
298      * @param notification control loop notification
299      */
300     protected void endTransaction(VirtualControlLoopNotification notification) {
301         ZonedDateTime startTime;
302         VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
303         if (startNotification != null) {
304             startTime = startNotification.getNotificationTime();
305         } else {
306             startTime = notification.getNotificationTime();
307         }
308
309         this.transaction(notification, startTime);
310         if (startNotification != null)
311             cache.invalidate(startNotification);
312     }
313
314     protected void evicted(VirtualControlLoopNotification notification) {
315         // transaction(notification, ZonedDateTime.now());
316         MDCTransaction trans = MDCTransaction
317                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
318                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
319                 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
320                 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
321     }
322
323     @Override
324     public long getCacheSize() {
325         return this.cacheSize;
326     }
327
328     @Override
329     public void setMaxCacheSize(long cacheSize) {
330         this.cacheSize = cacheSize;
331     }
332
333     @Override
334     public long getTransactionTimeout() {
335         return this.transactionTimeout;
336     }
337
338     @Override
339     public void setTransactionTimeout(long transactionTimeout) {
340         this.transactionTimeout = transactionTimeout;
341     }
342
343     @Override
344     public long getCacheOccupancy() {
345         return this.cache.size();
346     }
347
348     protected void metric(VirtualControlLoopNotification notification) {
349         MDCTransaction trans = MDCTransaction
350                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
351                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
352
353         List<ControlLoopOperation> operations = notification.getHistory();
354         switch (notification.getNotification()) {
355             case ACTIVE:
356                 trans.setStatusCode(true);
357                 trans.metric().resetTransaction();
358                 break;
359             case OPERATION:
360                 trans.setStatusCode(true);
361                 if (!operations.isEmpty()) {
362                     ControlLoopOperation operation = operations.get(operations.size() - 1);
363                     trans.setTargetEntity(operation.getTarget());
364                     trans.setTargetServiceName(operation.getActor());
365                 }
366                 trans.metric().resetTransaction();
367                 break;
368             case OPERATION_SUCCESS:
369                 trans.setStatusCode(true);
370                 operation(trans, operations);
371                 trans.transaction().resetTransaction();
372                 break;
373             case OPERATION_FAILURE:
374                 trans.setStatusCode(false);
375                 operation(trans, operations);
376                 trans.transaction().resetTransaction();
377                 break;
378             default:
379                 /* unexpected */
380                 logger.warn("unexpected notification type {} in notification {}",
381                         notification.getNotification().toString(), notification);
382                 break;
383         }
384     }
385
386     protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
387         if (!operations.isEmpty()) {
388             ControlLoopOperation operation = operations.get(operations.size() - 1);
389
390             if (operation.getTarget() != null) {
391                 trans.setTargetEntity(operation.getTarget());
392             }
393
394             if (operation.getActor() != null) {
395                 trans.setTargetServiceName(operation.getActor());
396             }
397
398             if (operation.getMessage() != null) {
399                 trans.setResponseDescription(operation.getMessage());
400             }
401
402             trans.setInvocationId(operation.getSubRequestId());
403
404             if (operation.getOutcome() != null) {
405                 trans.setResponseCode(operation.getOutcome());
406             }
407
408             if (operation.getStart() != null) {
409                 trans.setStartTime(operation.getStart());
410             }
411
412             if (operation.getEnd() != null) {
413                 trans.setEndTime(operation.getEnd());
414             }
415         }
416     }
417
418     protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
419         MDCTransaction trans = MDCTransaction
420                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
421                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
422                 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
423                 .setResponseDescription(notification.getMessage());
424
425         switch (notification.getNotification()) {
426             case FINAL_OPENLOOP:
427                 trans.setStatusCode(true);
428                 break;
429             case FINAL_SUCCESS:
430                 trans.setStatusCode(true);
431                 break;
432             case FINAL_FAILURE:
433                 trans.setStatusCode(false);
434                 break;
435             case REJECTED:
436                 trans.setStatusCode(false);
437                 break;
438             default:
439                 /* unexpected */
440                 logger.warn("unexpected notification type {} in notification {}",
441                         notification.getNotification().toString(), notification);
442                 break;
443         }
444
445         trans.transaction().resetTransaction();
446     }
447
448     @Override
449     public String toString() {
450         final StringBuffer sb = new StringBuffer("CacheBasedControlLoopMetricsManager{");
451         sb.append("cacheSize=").append(cacheSize);
452         sb.append(", transactionTimeout=").append(transactionTimeout);
453         sb.append('}');
454         return sb.toString();
455     }
456 }