2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.drools.apps.controlloop.feature.trans;
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;
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;
36 import org.onap.policy.controlloop.ControlLoopOperation;
37 import org.onap.policy.controlloop.VirtualControlLoopNotification;
38 import org.onap.policy.drools.persistence.SystemPersistence;
39 import org.onap.policy.drools.system.PolicyController;
40 import org.onap.policy.drools.utils.logging.MdcTransaction;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * Control Loop Metrics Tracker Implementation.
47 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
49 private static final String UNEXPECTED_NOTIFICATION_TYPE = "unexpected notification type {} in notification {}";
51 private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
53 private LoadingCache<UUID, VirtualControlLoopNotification> cache;
54 private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
56 private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
58 public CacheBasedControlLoopMetricsManager() {
60 Properties properties =
61 SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
67 Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
68 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
69 } catch (Exception e) {
70 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
71 ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
74 /* transaction timeout */
77 this.transactionTimeout = Long
78 .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
79 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
80 } catch (Exception e) {
81 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
82 ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
85 resetCache(this.cacheSize, this.transactionTimeout);
89 public void resetCache(long cacheSize, long transactionTimeout) {
90 this.cacheSize = cacheSize;
91 this.transactionTimeout = transactionTimeout;
93 CacheLoader<UUID, VirtualControlLoopNotification> loader =
94 new CacheLoader<UUID, VirtualControlLoopNotification>() {
97 public VirtualControlLoopNotification load(UUID key) throws Exception {
102 RemovalListener<UUID, VirtualControlLoopNotification> listener = notification -> {
103 if (notification.wasEvicted()) {
104 evicted(notification.getValue());
106 logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
107 notification.getCause().name());
111 synchronized (this) {
112 if (this.cache != null) {
113 this.cache.cleanUp();
114 this.cache.invalidateAll();
117 this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
118 .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
123 public void refresh() {
124 this.cache.cleanUp();
128 public List<UUID> getTransactionIds() {
129 return new ArrayList<>(this.cache.asMap().keySet());
133 public List<VirtualControlLoopNotification> getTransactions() {
134 return new ArrayList<>(this.cache.asMap().values());
138 public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
139 if (!isNotificationValid(notification)) {
143 setNotificationValues(controller, notification);
145 switch (notification.getNotification()) {
150 endTransaction(notification);
154 case OPERATION_SUCCESS:
155 case OPERATION_FAILURE:
156 /* any other value is an in progress transaction */
157 inProgressTransaction(notification);
161 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
162 notification.getNotification(), notification);
167 private boolean isNotificationValid(VirtualControlLoopNotification notification) {
168 if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
169 logger.warn("Invalid notification: {}", notification);
176 private void setNotificationValues(PolicyController controller, VirtualControlLoopNotification notification) {
177 if (notification.getNotificationTime() == null) {
178 notification.setNotificationTime(ZonedDateTime.now());
181 notification.setFrom(notification.getFrom() + ":" + controller.getName());
185 public VirtualControlLoopNotification getTransaction(UUID requestId) {
186 return cache.getIfPresent(requestId);
190 public void removeTransaction(UUID requestId) {
191 cache.invalidate(requestId);
195 * Tracks an in progress control loop transaction.
197 * @param notification control loop notification
199 protected void inProgressTransaction(VirtualControlLoopNotification notification) {
200 if (cache.getIfPresent(notification.getRequestId()) == null) {
201 cache.put(notification.getRequestId(), notification);
204 this.metric(notification);
208 * End of a control loop transaction.
210 * @param notification control loop notification
212 protected void endTransaction(VirtualControlLoopNotification notification) {
213 ZonedDateTime startTime;
214 VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
215 if (startNotification != null) {
216 startTime = startNotification.getNotificationTime();
218 startTime = notification.getNotificationTime();
221 this.transaction(notification, startTime);
222 if (startNotification != null) {
223 cache.invalidate(startNotification);
227 protected void evicted(VirtualControlLoopNotification notification) {
229 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
230 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
231 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
232 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
236 public long getCacheSize() {
237 return this.cacheSize;
241 public void setMaxCacheSize(long cacheSize) {
242 this.cacheSize = cacheSize;
246 public long getTransactionTimeout() {
247 return this.transactionTimeout;
251 public void setTransactionTimeout(long transactionTimeout) {
252 this.transactionTimeout = transactionTimeout;
256 public long getCacheOccupancy() {
257 return this.cache.size();
260 protected void metric(VirtualControlLoopNotification notification) {
261 MdcTransaction trans = MdcTransaction
262 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
263 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
265 List<ControlLoopOperation> operations = notification.getHistory();
266 switch (notification.getNotification()) {
268 trans.setStatusCode(true);
269 trans.metric().resetTransaction();
272 metricOperation(trans, operations);
274 case OPERATION_SUCCESS:
275 trans.setStatusCode(true);
276 operation(trans, operations);
277 trans.transaction().resetTransaction();
279 case OPERATION_FAILURE:
280 trans.setStatusCode(false);
281 operation(trans, operations);
282 trans.transaction().resetTransaction();
286 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
287 notification.getNotification(), notification);
292 private void metricOperation(MdcTransaction trans, List<ControlLoopOperation> operations) {
293 trans.setStatusCode(true);
294 if (!operations.isEmpty()) {
295 ControlLoopOperation operation = operations.get(operations.size() - 1);
296 trans.setTargetEntity(operation.getTarget());
297 trans.setTargetServiceName(operation.getActor());
299 trans.metric().resetTransaction();
302 protected void operation(MdcTransaction trans, List<ControlLoopOperation> operations) {
303 if (!operations.isEmpty()) {
304 ControlLoopOperation operation = operations.get(operations.size() - 1);
306 if (operation.getTarget() != null) {
307 trans.setTargetEntity(operation.getTarget());
310 if (operation.getActor() != null) {
311 trans.setTargetServiceName(operation.getActor());
314 if (operation.getMessage() != null) {
315 trans.setResponseDescription(operation.getMessage());
318 trans.setInvocationId(operation.getSubRequestId());
320 if (operation.getOutcome() != null) {
321 trans.setResponseCode(operation.getOutcome());
324 if (operation.getStart() != null) {
325 trans.setStartTime(operation.getStart());
328 if (operation.getEnd() != null) {
329 trans.setEndTime(operation.getEnd());
334 protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
335 MdcTransaction trans = MdcTransaction
336 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
337 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
338 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
339 .setResponseDescription(notification.getMessage());
341 switch (notification.getNotification()) {
343 trans.setStatusCode(true);
346 trans.setStatusCode(true);
349 trans.setStatusCode(false);
352 trans.setStatusCode(false);
356 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
357 notification.getNotification(), notification);
361 trans.transaction().resetTransaction();
365 public String toString() {
366 final StringBuilder sb = new StringBuilder();
367 sb.append("CacheBasedControlLoopMetricsManager{");
368 sb.append("cacheSize=").append(cacheSize);
369 sb.append(", transactionTimeout=").append(transactionTimeout);
371 return sb.toString();