2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2020 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.apache.commons.collections4.CollectionUtils;
35 import org.onap.policy.controlloop.ControlLoopOperation;
36 import org.onap.policy.controlloop.VirtualControlLoopNotification;
37 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
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;
44 * Control Loop Metrics Tracker Implementation.
46 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
48 private static final String UNEXPECTED_NOTIFICATION_TYPE = "unexpected notification type {} in notification {}";
50 private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
52 private LoadingCache<UUID, VirtualControlLoopNotification> cache;
53 private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
55 private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
57 public CacheBasedControlLoopMetricsManager() {
59 Properties properties = SystemPersistenceConstants.getManager()
60 .getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
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);
73 /* transaction timeout */
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);
84 resetCache(this.cacheSize, this.transactionTimeout);
88 public void resetCache(long cacheSize, long transactionTimeout) {
89 this.cacheSize = cacheSize;
90 this.transactionTimeout = transactionTimeout;
92 CacheLoader<UUID, VirtualControlLoopNotification> loader = new CacheLoader<>() {
95 public VirtualControlLoopNotification load(UUID key) {
100 RemovalListener<UUID, VirtualControlLoopNotification> listener = notification -> {
101 if (notification.wasEvicted()) {
102 evicted(notification.getValue());
103 } else if (logger.isInfoEnabled()) {
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()
180 + ":" + controller.getDrools().getCanonicalSessionNames());
184 public VirtualControlLoopNotification getTransaction(UUID requestId) {
185 return cache.getIfPresent(requestId);
189 public void removeTransaction(UUID requestId) {
190 cache.invalidate(requestId);
194 * Tracks an in progress control loop transaction.
196 * @param notification control loop notification
198 protected void inProgressTransaction(VirtualControlLoopNotification notification) {
199 if (cache.getIfPresent(notification.getRequestId()) == null) {
200 cache.put(notification.getRequestId(), notification);
203 this.metric(notification);
207 * End of a control loop transaction.
209 * @param notification control loop notification
211 protected void endTransaction(VirtualControlLoopNotification notification) {
212 ZonedDateTime startTime;
213 VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
214 if (startNotification != null) {
215 startTime = startNotification.getNotificationTime();
217 startTime = notification.getNotificationTime();
220 this.transaction(notification, startTime);
221 if (startNotification != null) {
222 removeTransaction(startNotification.getRequestId());
226 protected void evicted(VirtualControlLoopNotification notification) {
228 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
229 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
230 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
231 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
235 public long getCacheSize() {
236 return this.cacheSize;
240 public void setMaxCacheSize(long cacheSize) {
241 this.cacheSize = cacheSize;
245 public long getTransactionTimeout() {
246 return this.transactionTimeout;
250 public void setTransactionTimeout(long transactionTimeout) {
251 this.transactionTimeout = transactionTimeout;
255 public long getCacheOccupancy() {
256 return this.cache.size();
259 protected void metric(VirtualControlLoopNotification notification) {
260 MdcTransaction trans = getMdcTransaction(notification);
261 List<ControlLoopOperation> operations = notification.getHistory();
262 switch (notification.getNotification()) {
264 trans.setStatusCode(true).metric().resetTransaction();
267 operation(trans.setStatusCode(true), operations).metric().resetTransaction();
269 case OPERATION_SUCCESS:
270 operation(trans.setStatusCode(true), operations).metric().transaction().resetTransaction();
272 case OPERATION_FAILURE:
273 operation(trans.setStatusCode(false), operations).metric().transaction().resetTransaction();
277 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
278 notification.getNotification(), notification);
283 private MdcTransaction getMdcTransaction(VirtualControlLoopNotification notification) {
284 return MdcTransaction
285 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
286 .setServiceName(notification.getClosedLoopControlName())
287 .setServiceInstanceId(notification.getPolicyScope()
288 + ":" + notification.getPolicyName() + ":" + notification.getPolicyVersion())
289 .setProcessKey("" + notification.getAai())
290 .setTargetEntity(notification.getTargetType() + "." + notification.getTarget())
291 .setResponseCode((notification.getNotification() != null) ? notification.getNotification().name() : "-")
292 .setResponseDescription(notification.getMessage())
293 .setClientIpAddress(notification.getClosedLoopEventClient());
296 protected MdcTransaction operation(MdcTransaction trans, List<ControlLoopOperation> operations) {
297 if (CollectionUtils.isEmpty(operations)) {
301 ControlLoopOperation operation = operations.get(operations.size() - 1);
303 if (operation.getActor() != null) {
304 trans.setTargetServiceName(operation.getActor() + "." + operation.getOperation());
307 if (operation.getTarget() != null) {
308 trans.setTargetVirtualEntity(operation.getTarget());
311 if (operation.getSubRequestId() != null) {
312 trans.setInvocationId(operation.getSubRequestId());
315 if (operation.getOutcome() != null) {
316 trans.setResponseDescription(operation.getOutcome() + ":" + operation.getMessage());
319 if (operation.getStart() != null) {
320 trans.setStartTime(operation.getStart());
323 if (operation.getEnd() != null) {
324 trans.setEndTime(operation.getEnd());
330 protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
331 MdcTransaction trans = getMdcTransaction(notification)
332 .setStartTime(startTime.toInstant())
333 .setEndTime(notification.getNotificationTime().toInstant());
335 switch (notification.getNotification()) {
339 trans.setStatusCode(true);
344 trans.setStatusCode(false);
348 logger.warn(UNEXPECTED_NOTIFICATION_TYPE,
349 notification.getNotification(), notification);
353 trans.transaction().resetTransaction();
357 public String toString() {
358 return "CacheBasedControlLoopMetricsManager{" + "cacheSize=" + cacheSize
359 + ",transactionTimeout="
362 + getCacheOccupancy()