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