c5d6a32acdc3fe7d701dd63c90a012dfae19c7e5
[policy/drools-applications.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019 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 Implementation.
47  */
48 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
49
50     private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
51
52     private LoadingCache<UUID, VirtualControlLoopNotification> cache;
53     private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
54
55     private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
56
57     public CacheBasedControlLoopMetricsManager() {
58
59         Properties properties =
60                 SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
61
62         /* cache size */
63
64         try {
65             this.cacheSize =
66                     Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
67                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
68         } catch (Exception e) {
69             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
70                     ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
71         }
72
73         /* transaction timeout */
74
75         try {
76             this.transactionTimeout = Long
77                     .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
78                             "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
79         } catch (Exception e) {
80             logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
81                     ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
82         }
83
84         resetCache(this.cacheSize, this.transactionTimeout);
85     }
86
87     @Override
88     public void resetCache(long cacheSize, long transactionTimeout) {
89         this.cacheSize = cacheSize;
90         this.transactionTimeout = transactionTimeout;
91
92         CacheLoader<UUID, VirtualControlLoopNotification> loader =
93                 new CacheLoader<UUID, VirtualControlLoopNotification>() {
94
95             @Override
96             public VirtualControlLoopNotification load(UUID key) throws Exception {
97                 return null;
98             }
99         };
100
101         RemovalListener<UUID, VirtualControlLoopNotification> listener =
102                 new RemovalListener<UUID, VirtualControlLoopNotification>() {
103             @Override
104             public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
105                 if (notification.wasEvicted()) {
106                     evicted(notification.getValue());
107                 } else {
108                     logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
109                                     notification.getCause().name());
110                 }
111             }
112         };
113
114         synchronized (this) {
115             if (this.cache != null) {
116                 this.cache.cleanUp();
117                 this.cache.invalidateAll();
118             }
119
120             this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
121                     .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
122         }
123     }
124
125     @Override
126     public void refresh() {
127         this.cache.cleanUp();
128     }
129
130     @Override
131     public List<UUID> getTransactionIds() {
132         return new ArrayList<>(this.cache.asMap().keySet());
133     }
134
135     @Override
136     public List<VirtualControlLoopNotification> getTransactions() {
137         return new ArrayList<>(this.cache.asMap().values());
138     }
139
140     @Override
141     public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
142         if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
143             logger.warn("Invalid notification: {}", notification);
144             return;
145         }
146
147         if (notification.getNotificationTime() == null) {
148             notification.setNotificationTime(ZonedDateTime.now());
149         }
150
151         notification.setFrom(notification.getFrom() + ":" + controller.getName());
152
153         switch (notification.getNotification()) {
154             case REJECTED:
155             case FINAL_FAILURE:
156             case FINAL_SUCCESS:
157             case FINAL_OPENLOOP:
158                 endTransaction(notification);
159                 break;
160             case ACTIVE:
161             case OPERATION:
162             case OPERATION_SUCCESS:
163             case OPERATION_FAILURE:
164                 /* any other value is an in progress transaction */
165                 inProgressTransaction(notification);
166                 break;
167             default:
168                 /* unexpected */
169                 logger.warn("unexpected notification type {} in notification {}",
170                         notification.getNotification().toString(), notification);
171                 break;
172         }
173     }
174
175     @Override
176     public VirtualControlLoopNotification getTransaction(UUID requestId) {
177         return cache.getIfPresent(requestId);
178     }
179
180     @Override
181     public void removeTransaction(UUID requestId) {
182         cache.invalidate(requestId);
183     }
184
185     /**
186      * Tracks an in progress control loop transaction.
187      *
188      * @param notification control loop notification
189      */
190     protected void inProgressTransaction(VirtualControlLoopNotification notification) {
191         if (cache.getIfPresent(notification.getRequestId()) == null) {
192             cache.put(notification.getRequestId(), notification);
193         }
194
195         this.metric(notification);
196     }
197
198     /**
199      * End of a control loop transaction.
200      *
201      * @param notification control loop notification
202      */
203     protected void endTransaction(VirtualControlLoopNotification notification) {
204         ZonedDateTime startTime;
205         VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
206         if (startNotification != null) {
207             startTime = startNotification.getNotificationTime();
208         } else {
209             startTime = notification.getNotificationTime();
210         }
211
212         this.transaction(notification, startTime);
213         if (startNotification != null) {
214             cache.invalidate(startNotification);
215         }
216     }
217
218     protected void evicted(VirtualControlLoopNotification notification) {
219         MDCTransaction
220                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
221                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
222                 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
223                 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
224     }
225
226     @Override
227     public long getCacheSize() {
228         return this.cacheSize;
229     }
230
231     @Override
232     public void setMaxCacheSize(long cacheSize) {
233         this.cacheSize = cacheSize;
234     }
235
236     @Override
237     public long getTransactionTimeout() {
238         return this.transactionTimeout;
239     }
240
241     @Override
242     public void setTransactionTimeout(long transactionTimeout) {
243         this.transactionTimeout = transactionTimeout;
244     }
245
246     @Override
247     public long getCacheOccupancy() {
248         return this.cache.size();
249     }
250
251     protected void metric(VirtualControlLoopNotification notification) {
252         MDCTransaction trans = MDCTransaction
253                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
254                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
255
256         List<ControlLoopOperation> operations = notification.getHistory();
257         switch (notification.getNotification()) {
258             case ACTIVE:
259                 trans.setStatusCode(true);
260                 trans.metric().resetTransaction();
261                 break;
262             case OPERATION:
263                 trans.setStatusCode(true);
264                 if (!operations.isEmpty()) {
265                     ControlLoopOperation operation = operations.get(operations.size() - 1);
266                     trans.setTargetEntity(operation.getTarget());
267                     trans.setTargetServiceName(operation.getActor());
268                 }
269                 trans.metric().resetTransaction();
270                 break;
271             case OPERATION_SUCCESS:
272                 trans.setStatusCode(true);
273                 operation(trans, operations);
274                 trans.transaction().resetTransaction();
275                 break;
276             case OPERATION_FAILURE:
277                 trans.setStatusCode(false);
278                 operation(trans, operations);
279                 trans.transaction().resetTransaction();
280                 break;
281             default:
282                 /* unexpected */
283                 logger.warn("unexpected notification type {} in notification {}",
284                         notification.getNotification().toString(), notification);
285                 break;
286         }
287     }
288
289     protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
290         if (!operations.isEmpty()) {
291             ControlLoopOperation operation = operations.get(operations.size() - 1);
292
293             if (operation.getTarget() != null) {
294                 trans.setTargetEntity(operation.getTarget());
295             }
296
297             if (operation.getActor() != null) {
298                 trans.setTargetServiceName(operation.getActor());
299             }
300
301             if (operation.getMessage() != null) {
302                 trans.setResponseDescription(operation.getMessage());
303             }
304
305             trans.setInvocationId(operation.getSubRequestId());
306
307             if (operation.getOutcome() != null) {
308                 trans.setResponseCode(operation.getOutcome());
309             }
310
311             if (operation.getStart() != null) {
312                 trans.setStartTime(operation.getStart());
313             }
314
315             if (operation.getEnd() != null) {
316                 trans.setEndTime(operation.getEnd());
317             }
318         }
319     }
320
321     protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
322         MDCTransaction trans = MDCTransaction
323                 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
324                 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
325                 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
326                 .setResponseDescription(notification.getMessage());
327
328         switch (notification.getNotification()) {
329             case FINAL_OPENLOOP:
330                 trans.setStatusCode(true);
331                 break;
332             case FINAL_SUCCESS:
333                 trans.setStatusCode(true);
334                 break;
335             case FINAL_FAILURE:
336                 trans.setStatusCode(false);
337                 break;
338             case REJECTED:
339                 trans.setStatusCode(false);
340                 break;
341             default:
342                 /* unexpected */
343                 logger.warn("unexpected notification type {} in notification {}",
344                         notification.getNotification(), notification);
345                 break;
346         }
347
348         trans.transaction().resetTransaction();
349     }
350
351     @Override
352     public String toString() {
353         final StringBuilder sb = new StringBuilder();
354         sb.append("CacheBasedControlLoopMetricsManager{");
355         sb.append("cacheSize=").append(cacheSize);
356         sb.append(", transactionTimeout=").append(transactionTimeout);
357         sb.append('}');
358         return sb.toString();
359     }
360 }