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