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 com.google.common.cache.RemovalNotification;
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;
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;
46 * Control Loop Metrics Tracker Implementation.
48 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
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 =
60 SystemPersistence.manager.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 =
93 new CacheLoader<UUID, VirtualControlLoopNotification>() {
96 public VirtualControlLoopNotification load(UUID key) throws Exception {
101 RemovalListener<UUID, VirtualControlLoopNotification> listener =
102 new RemovalListener<UUID, VirtualControlLoopNotification>() {
104 public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
105 if (notification.wasEvicted()) {
106 evicted(notification.getValue());
108 logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
109 notification.getCause().name());
114 synchronized (this) {
115 if (this.cache != null) {
116 this.cache.cleanUp();
117 this.cache.invalidateAll();
120 this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
121 .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
126 public void refresh() {
127 this.cache.cleanUp();
131 public List<UUID> getTransactionIds() {
132 return new ArrayList<>(this.cache.asMap().keySet());
136 public List<VirtualControlLoopNotification> getTransactions() {
137 return new ArrayList<>(this.cache.asMap().values());
141 public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
142 if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
143 logger.warn("Invalid notification: {}", notification);
147 if (notification.getNotificationTime() == null) {
148 notification.setNotificationTime(ZonedDateTime.now());
151 notification.setFrom(notification.getFrom() + ":" + controller.getName());
153 switch (notification.getNotification()) {
158 endTransaction(notification);
162 case OPERATION_SUCCESS:
163 case OPERATION_FAILURE:
164 /* any other value is an in progress transaction */
165 inProgressTransaction(notification);
169 logger.warn("unexpected notification type {} in notification {}",
170 notification.getNotification().toString(), notification);
176 public VirtualControlLoopNotification getTransaction(UUID requestId) {
177 return cache.getIfPresent(requestId);
181 public void removeTransaction(UUID requestId) {
182 cache.invalidate(requestId);
186 * Tracks an in progress control loop transaction.
188 * @param notification control loop notification
190 protected void inProgressTransaction(VirtualControlLoopNotification notification) {
191 if (cache.getIfPresent(notification.getRequestId()) == null) {
192 cache.put(notification.getRequestId(), notification);
195 this.metric(notification);
199 * End of a control loop transaction.
201 * @param notification control loop notification
203 protected void endTransaction(VirtualControlLoopNotification notification) {
204 ZonedDateTime startTime;
205 VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
206 if (startNotification != null) {
207 startTime = startNotification.getNotificationTime();
209 startTime = notification.getNotificationTime();
212 this.transaction(notification, startTime);
213 if (startNotification != null) {
214 cache.invalidate(startNotification);
218 protected void evicted(VirtualControlLoopNotification notification) {
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();
227 public long getCacheSize() {
228 return this.cacheSize;
232 public void setMaxCacheSize(long cacheSize) {
233 this.cacheSize = cacheSize;
237 public long getTransactionTimeout() {
238 return this.transactionTimeout;
242 public void setTransactionTimeout(long transactionTimeout) {
243 this.transactionTimeout = transactionTimeout;
247 public long getCacheOccupancy() {
248 return this.cache.size();
251 protected void metric(VirtualControlLoopNotification notification) {
252 MDCTransaction trans = MDCTransaction
253 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
254 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
256 List<ControlLoopOperation> operations = notification.getHistory();
257 switch (notification.getNotification()) {
259 trans.setStatusCode(true);
260 trans.metric().resetTransaction();
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());
269 trans.metric().resetTransaction();
271 case OPERATION_SUCCESS:
272 trans.setStatusCode(true);
273 operation(trans, operations);
274 trans.transaction().resetTransaction();
276 case OPERATION_FAILURE:
277 trans.setStatusCode(false);
278 operation(trans, operations);
279 trans.transaction().resetTransaction();
283 logger.warn("unexpected notification type {} in notification {}",
284 notification.getNotification().toString(), notification);
289 protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
290 if (!operations.isEmpty()) {
291 ControlLoopOperation operation = operations.get(operations.size() - 1);
293 if (operation.getTarget() != null) {
294 trans.setTargetEntity(operation.getTarget());
297 if (operation.getActor() != null) {
298 trans.setTargetServiceName(operation.getActor());
301 if (operation.getMessage() != null) {
302 trans.setResponseDescription(operation.getMessage());
305 trans.setInvocationId(operation.getSubRequestId());
307 if (operation.getOutcome() != null) {
308 trans.setResponseCode(operation.getOutcome());
311 if (operation.getStart() != null) {
312 trans.setStartTime(operation.getStart());
315 if (operation.getEnd() != null) {
316 trans.setEndTime(operation.getEnd());
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());
328 switch (notification.getNotification()) {
330 trans.setStatusCode(true);
333 trans.setStatusCode(true);
336 trans.setStatusCode(false);
339 trans.setStatusCode(false);
343 logger.warn("unexpected notification type {} in notification {}",
344 notification.getNotification(), notification);
348 trans.transaction().resetTransaction();
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);
358 return sb.toString();