790d434fdc960a1c8b1fb9292cddaa6433d88d3c
[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
315     protected void evicted(VirtualControlLoopNotification notification) {
316         // transaction(notification, ZonedDateTime.now());
317         MDCTransaction trans = MDCTransaction
318                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
319                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
320                 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
321                 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
322     }
323
324     @Override
325     public long getCacheSize() {
326         return this.cacheSize;
327     }
328
329     @Override
330     public void setMaxCacheSize(long cacheSize) {
331         this.cacheSize = cacheSize;
332     }
333
334     @Override
335     public long getTransactionTimeout() {
336         return this.transactionTimeout;
337     }
338
339     @Override
340     public void setTransactionTimeout(long transactionTimeout) {
341         this.transactionTimeout = transactionTimeout;
342     }
343
344     @Override
345     public long getCacheOccupancy() {
346         return this.cache.size();
347     }
348
349     protected void metric(VirtualControlLoopNotification notification) {
350         MDCTransaction trans = MDCTransaction
351                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
352                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
353
354         List<ControlLoopOperation> operations = notification.getHistory();
355         switch (notification.getNotification()) {
356             case ACTIVE:
357                 trans.setStatusCode(true);
358                 trans.metric().resetTransaction();
359                 break;
360             case OPERATION:
361                 trans.setStatusCode(true);
362                 if (!operations.isEmpty()) {
363                     ControlLoopOperation operation = operations.get(operations.size() - 1);
364                     trans.setTargetEntity(operation.getTarget());
365                     trans.setTargetServiceName(operation.getActor());
366                 }
367                 trans.metric().resetTransaction();
368                 break;
369             case OPERATION_SUCCESS:
370                 trans.setStatusCode(true);
371                 operation(trans, operations);
372                 trans.transaction().resetTransaction();
373                 break;
374             case OPERATION_FAILURE:
375                 trans.setStatusCode(false);
376                 operation(trans, operations);
377                 trans.transaction().resetTransaction();
378                 break;
379             default:
380                 /* unexpected */
381                 logger.warn("unexpected notification type {} in notification {}",
382                         notification.getNotification().toString(), notification);
383                 break;
384         }
385     }
386
387     protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
388         if (!operations.isEmpty()) {
389             ControlLoopOperation operation = operations.get(operations.size() - 1);
390
391             if (operation.getTarget() != null) {
392                 trans.setTargetEntity(operation.getTarget());
393             }
394
395             if (operation.getActor() != null) {
396                 trans.setTargetServiceName(operation.getActor());
397             }
398
399             if (operation.getMessage() != null) {
400                 trans.setResponseDescription(operation.getMessage());
401             }
402
403             trans.setInvocationId(operation.getSubRequestId());
404
405             if (operation.getOutcome() != null) {
406                 trans.setResponseCode(operation.getOutcome());
407             }
408
409             if (operation.getStart() != null) {
410                 trans.setStartTime(operation.getStart());
411             }
412
413             if (operation.getEnd() != null) {
414                 trans.setEndTime(operation.getEnd());
415             }
416         }
417     }
418
419     protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
420         MDCTransaction trans = MDCTransaction
421                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
422                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
423                 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
424                 .setResponseDescription(notification.getMessage());
425
426         switch (notification.getNotification()) {
427             case FINAL_OPENLOOP:
428                 trans.setStatusCode(true);
429                 break;
430             case FINAL_SUCCESS:
431                 trans.setStatusCode(true);
432                 break;
433             case FINAL_FAILURE:
434                 trans.setStatusCode(false);
435                 break;
436             case REJECTED:
437                 trans.setStatusCode(false);
438                 break;
439             default:
440                 /* unexpected */
441                 logger.warn("unexpected notification type {} in notification {}",
442                         notification.getNotification().toString(), notification);
443                 break;
444         }
445
446         trans.transaction().resetTransaction();
447     }
448
449     @Override
450     public String toString() {
451         final StringBuffer sb = new StringBuffer("CacheBasedControlLoopMetricsManager{");
452         sb.append("cacheSize=").append(cacheSize);
453         sb.append(", transactionTimeout=").append(transactionTimeout);
454         sb.append('}');
455         return sb.toString();
456     }
457 }