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;
27 import java.time.Instant;
28 import java.time.ZonedDateTime;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Properties;
32 import java.util.UUID;
33 import java.util.concurrent.TimeUnit;
34 import org.onap.policy.controlloop.ControlLoopOperation;
35 import org.onap.policy.controlloop.VirtualControlLoopNotification;
36 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
37 import org.onap.policy.drools.system.PolicyController;
38 import org.onap.policy.drools.utils.logging.MdcTransaction;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * Control Loop Metrics Tracker Implementation.
45 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
47 private static final String UNEXPECTED_NOTIFICATION_TYPE = "unexpected notification type {} in notification {}";
49 private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
51 private LoadingCache<UUID, VirtualControlLoopNotification> cache;
52 private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
54 private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
56 public CacheBasedControlLoopMetricsManager() {
58 Properties properties = SystemPersistenceConstants.getManager()
59 .getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
65 Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
66 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
67 } catch (Exception e) {
68 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
69 ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
72 /* transaction timeout */
75 this.transactionTimeout = Long
76 .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
77 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
78 } catch (Exception e) {
79 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
80 ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
83 resetCache(this.cacheSize, this.transactionTimeout);
87 public void resetCache(long cacheSize, long transactionTimeout) {
88 this.cacheSize = cacheSize;
89 this.transactionTimeout = transactionTimeout;
91 CacheLoader<UUID, VirtualControlLoopNotification> loader =
92 new CacheLoader<UUID, VirtualControlLoopNotification>() {
95 public VirtualControlLoopNotification load(UUID key) throws Exception {
100 RemovalListener<UUID, VirtualControlLoopNotification> listener = notification -> {
101 if (notification.wasEvicted()) {
102 evicted(notification.getValue());
104 logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
105 notification.getCause().name());
109 synchronized (this) {
110 if (this.cache != null) {
111 this.cache.cleanUp();
112 this.cache.invalidateAll();
115 this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
116 .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
121 public void refresh() {
122 this.cache.cleanUp();
126 public List<UUID> getTransactionIds() {
127 return new ArrayList<>(this.cache.asMap().keySet());
131 public List<VirtualControlLoopNotification> getTransactions() {
132 return new ArrayList<>(this.cache.asMap().values());
136 public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
137 if (!isNotificationValid(notification)) {
141 setNotificationValues(controller, notification);
143 switch (notification.getNotification()) {
148 endTransaction(notification);
152 case OPERATION_SUCCESS:
153 case OPERATION_FAILURE:
154 /* any other value is an in progress transaction */
155 inProgressTransaction(notification);
159 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
160 notification.getNotification(), notification);
165 private boolean isNotificationValid(VirtualControlLoopNotification notification) {
166 if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
167 logger.warn("Invalid notification: {}", notification);
174 private void setNotificationValues(PolicyController controller, VirtualControlLoopNotification notification) {
175 if (notification.getNotificationTime() == null) {
176 notification.setNotificationTime(ZonedDateTime.now());
179 notification.setFrom(notification.getFrom() + ":" + controller.getName());
183 public VirtualControlLoopNotification getTransaction(UUID requestId) {
184 return cache.getIfPresent(requestId);
188 public void removeTransaction(UUID requestId) {
189 cache.invalidate(requestId);
193 * Tracks an in progress control loop transaction.
195 * @param notification control loop notification
197 protected void inProgressTransaction(VirtualControlLoopNotification notification) {
198 if (cache.getIfPresent(notification.getRequestId()) == null) {
199 cache.put(notification.getRequestId(), notification);
202 this.metric(notification);
206 * End of a control loop transaction.
208 * @param notification control loop notification
210 protected void endTransaction(VirtualControlLoopNotification notification) {
211 ZonedDateTime startTime;
212 VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
213 if (startNotification != null) {
214 startTime = startNotification.getNotificationTime();
216 startTime = notification.getNotificationTime();
219 this.transaction(notification, startTime);
220 if (startNotification != null) {
221 cache.invalidate(startNotification);
225 protected void evicted(VirtualControlLoopNotification notification) {
227 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
228 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
229 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
230 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
234 public long getCacheSize() {
235 return this.cacheSize;
239 public void setMaxCacheSize(long cacheSize) {
240 this.cacheSize = cacheSize;
244 public long getTransactionTimeout() {
245 return this.transactionTimeout;
249 public void setTransactionTimeout(long transactionTimeout) {
250 this.transactionTimeout = transactionTimeout;
254 public long getCacheOccupancy() {
255 return this.cache.size();
258 protected void metric(VirtualControlLoopNotification notification) {
259 MdcTransaction trans = MdcTransaction
260 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
261 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
263 List<ControlLoopOperation> operations = notification.getHistory();
264 switch (notification.getNotification()) {
266 trans.setStatusCode(true);
267 trans.metric().resetTransaction();
270 metricOperation(trans, operations);
272 case OPERATION_SUCCESS:
273 trans.setStatusCode(true);
274 operation(trans, operations);
275 trans.transaction().resetTransaction();
277 case OPERATION_FAILURE:
278 trans.setStatusCode(false);
279 operation(trans, operations);
280 trans.transaction().resetTransaction();
284 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
285 notification.getNotification(), notification);
290 private void metricOperation(MdcTransaction trans, List<ControlLoopOperation> operations) {
291 trans.setStatusCode(true);
292 if (!operations.isEmpty()) {
293 ControlLoopOperation operation = operations.get(operations.size() - 1);
294 trans.setTargetEntity(operation.getTarget());
295 trans.setTargetServiceName(operation.getActor());
297 trans.metric().resetTransaction();
300 protected void operation(MdcTransaction trans, List<ControlLoopOperation> operations) {
301 if (!operations.isEmpty()) {
302 ControlLoopOperation operation = operations.get(operations.size() - 1);
304 if (operation.getTarget() != null) {
305 trans.setTargetEntity(operation.getTarget());
308 if (operation.getActor() != null) {
309 trans.setTargetServiceName(operation.getActor());
312 if (operation.getMessage() != null) {
313 trans.setResponseDescription(operation.getMessage());
316 trans.setInvocationId(operation.getSubRequestId());
318 if (operation.getOutcome() != null) {
319 trans.setResponseCode(operation.getOutcome());
322 if (operation.getStart() != null) {
323 trans.setStartTime(operation.getStart());
326 if (operation.getEnd() != null) {
327 trans.setEndTime(operation.getEnd());
332 protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
333 MdcTransaction trans = MdcTransaction
334 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
335 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
336 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
337 .setResponseDescription(notification.getMessage());
339 switch (notification.getNotification()) {
341 trans.setStatusCode(true);
344 trans.setStatusCode(true);
347 trans.setStatusCode(false);
350 trans.setStatusCode(false);
354 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
355 notification.getNotification(), notification);
359 trans.transaction().resetTransaction();
363 public String toString() {
364 final StringBuilder sb = new StringBuilder();
365 sb.append("CacheBasedControlLoopMetricsManager{");
366 sb.append("cacheSize=").append(cacheSize);
367 sb.append(", transactionTimeout=").append(transactionTimeout);
369 return sb.toString();