2d7bfa6c09eed0eb9ecae927770249a4d81b3cf4
[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      * Singleton manager object.
52      */
53     ControlLoopMetrics manager = new CacheBasedControlLoopMetricsManager();
54
55     /**
56      * Gets all transaction identifiers being monitored.
57      *
58      * @return transaction id list
59      */
60     List<UUID> getTransactionIds();
61
62     /**
63      * Gets all detailed transactions.
64      *
65      * @return list of transactions
66      */
67     List<VirtualControlLoopNotification> getTransactions();
68
69     /**
70      * Track controller's notification events.
71      *
72      * @param controller policy controller sending out notification
73      * @param notification notification
74      */
75     void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification);
76
77     /**
78      * Gets an in-progress transaction.
79      *
80      * @param requestId request ID
81      * @return in progress notification
82      */
83     VirtualControlLoopNotification getTransaction(UUID requestId);
84
85     /**
86      * Removes an in-progress transaction.
87      *
88      * @param requestId request ID
89      */
90     void removeTransaction(UUID requestId);
91
92     /**
93      * Get cache size.
94      *
95      * @return cache size
96      */
97     long getCacheSize();
98
99     /**
100      * Get cache size.
101      *
102      * @return cache size
103      */
104     long getCacheOccupancy();
105
106     /**
107      * Sets cache size.
108      *
109      * @param cacheSize cache size
110      */
111     void setMaxCacheSize(long cacheSize);
112
113     /**
114      * Cached transaction expiration timeout in seconds.
115      *
116      * @return transaction timeout in seconds
117      */
118     long getTransactionTimeout();
119
120     /**
121      * Sets transaction timeout in seconds.
122      *
123      * @param transactionTimeout transaction timeout in seconds
124      */
125     void setTransactionTimeout(long transactionTimeout);
126
127     /**
128      * Reset cache.
129      *
130      * @param cacheSize new cache size
131      * @param transactionTimeout new transaction timeout in seconds
132      */
133     void resetCache(long cacheSize, long transactionTimeout);
134
135     /**
136      * Refresh underlying transaction management.
137      */
138     void refresh();
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
315     protected void evicted(VirtualControlLoopNotification notification) {
316         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(), notification);
442                 break;
443         }
444
445         trans.transaction().resetTransaction();
446     }
447
448     @Override
449     public String toString() {
450         final StringBuilder sb = new StringBuilder();
451         sb.append("CacheBasedControlLoopMetricsManager{");
452         sb.append("cacheSize=").append(cacheSize);
453         sb.append(", transactionTimeout=").append(transactionTimeout);
454         sb.append('}');
455         return sb.toString();
456     }
457 }