2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 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.
48 public interface ControlLoopMetrics {
51 * Gets all transaction identifiers being monitored.
53 * @return transaction id list
55 List<UUID> getTransactionIds();
58 * Gets all detailed transactions.
60 * @return list of transactions
62 List<VirtualControlLoopNotification> getTransactions();
65 * Track controller's notification events.
67 * @param controller policy controller sending out notification
68 * @param notification notification
70 void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification);
73 * Gets an in-progress transaction.
75 * @param requestId request ID
76 * @return in progress notification
78 VirtualControlLoopNotification getTransaction(UUID requestId);
81 * Removes an in-progress transaction.
83 * @param requestId request ID
85 void removeTransaction(UUID requestId);
99 long getCacheOccupancy();
104 * @param cacheSize cache size
106 void setMaxCacheSize(long cacheSize);
109 * Cached transaction expiration timeout in seconds.
111 * @return transaction timeout in seconds
113 long getTransactionTimeout();
116 * Sets transaction timeout in seconds.
118 * @param transactionTimeout transaction timeout in seconds
120 void setTransactionTimeout(long transactionTimeout);
125 * @param cacheSize new cache size
126 * @param transactionTimeout new transaction timeout in seconds
128 void resetCache(long cacheSize, long transactionTimeout);
131 * Refresh underlying transaction management.
136 * Singleton manager object.
138 ControlLoopMetrics manager = new CacheBasedControlLoopMetricsManager();
143 * Control Loop Metrics Tracker Implementation.
145 class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
147 private static final Logger logger = LoggerFactory.getLogger(CacheBasedControlLoopMetricsManager.class);
149 private LoadingCache<UUID, VirtualControlLoopNotification> cache;
150 private long cacheSize = ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT;
152 private long transactionTimeout = ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT;
154 public CacheBasedControlLoopMetricsManager() {
156 Properties properties =
157 SystemPersistence.manager.getProperties(ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME);
163 Long.parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY,
164 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_DEFAULT));
165 } catch (Exception e) {
166 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
167 ControlLoopMetricsFeature.CL_CACHE_TRANS_SIZE_PROPERTY, e);
170 /* transaction timeout */
173 this.transactionTimeout = Long
174 .parseLong(properties.getProperty(ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY,
175 "" + ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_DEFAULT));
176 } catch (Exception e) {
177 logger.warn("{}:{} property cannot be accessed", ControlLoopMetricsFeature.CONFIGURATION_PROPERTIES_NAME,
178 ControlLoopMetricsFeature.CL_CACHE_TRANS_TIMEOUT_SECONDS_PROPERTY, e);
181 resetCache(this.cacheSize, this.transactionTimeout);
185 public void resetCache(long cacheSize, long transactionTimeout) {
186 this.cacheSize = cacheSize;
187 this.transactionTimeout = transactionTimeout;
189 CacheLoader<UUID, VirtualControlLoopNotification> loader =
190 new CacheLoader<UUID, VirtualControlLoopNotification>() {
193 public VirtualControlLoopNotification load(UUID key) throws Exception {
198 RemovalListener<UUID, VirtualControlLoopNotification> listener =
199 new RemovalListener<UUID, VirtualControlLoopNotification>() {
201 public void onRemoval(RemovalNotification<UUID, VirtualControlLoopNotification> notification) {
202 if (notification.wasEvicted()) {
203 evicted(notification.getValue());
205 logger.info("REMOVAL: {} because of {}", notification.getValue().getRequestId(),
206 notification.getCause().name());
211 synchronized (this) {
212 if (this.cache != null) {
213 this.cache.cleanUp();
214 this.cache.invalidateAll();
217 this.cache = CacheBuilder.newBuilder().maximumSize(this.cacheSize)
218 .expireAfterWrite(transactionTimeout, TimeUnit.SECONDS).removalListener(listener).build(loader);
223 public void refresh() {
224 this.cache.cleanUp();
228 public List<UUID> getTransactionIds() {
229 return new ArrayList<>(this.cache.asMap().keySet());
233 public List<VirtualControlLoopNotification> getTransactions() {
234 return new ArrayList<>(this.cache.asMap().values());
238 public void transactionEvent(PolicyController controller, VirtualControlLoopNotification notification) {
239 if (notification == null || notification.getRequestId() == null || notification.getNotification() == null) {
240 logger.warn("Invalid notification: {}", notification);
244 if (notification.getNotificationTime() == null) {
245 notification.setNotificationTime(ZonedDateTime.now());
248 notification.setFrom(notification.getFrom() + ":" + controller.getName());
250 switch (notification.getNotification()) {
255 endTransaction(notification);
259 case OPERATION_SUCCESS:
260 case OPERATION_FAILURE:
261 /* any other value is an in progress transaction */
262 inProgressTransaction(notification);
266 logger.warn("unexpected notification type {} in notification {}",
267 notification.getNotification().toString(), notification);
273 public VirtualControlLoopNotification getTransaction(UUID requestId) {
274 return cache.getIfPresent(requestId);
278 public void removeTransaction(UUID requestId) {
279 cache.invalidate(requestId);
283 * Tracks an in progress control loop transaction.
285 * @param notification control loop notification
287 protected void inProgressTransaction(VirtualControlLoopNotification notification) {
288 if (cache.getIfPresent(notification.getRequestId()) == null) {
289 cache.put(notification.getRequestId(), notification);
292 this.metric(notification);
296 * End of a control loop transaction.
298 * @param notification control loop notification
300 protected void endTransaction(VirtualControlLoopNotification notification) {
301 ZonedDateTime startTime;
302 VirtualControlLoopNotification startNotification = cache.getIfPresent(notification.getRequestId());
303 if (startNotification != null) {
304 startTime = startNotification.getNotificationTime();
306 startTime = notification.getNotificationTime();
309 this.transaction(notification, startTime);
310 if (startNotification != null) {
311 cache.invalidate(startNotification);
315 protected void evicted(VirtualControlLoopNotification notification) {
316 // transaction(notification, ZonedDateTime.now());
317 MDCTransaction trans = MDCTransaction
318 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
319 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
320 .setStartTime(notification.getNotificationTime().toInstant()).setEndTime(Instant.now())
321 .setResponseDescription("EVICTED").setStatusCode(false).metric().resetTransaction();
325 public long getCacheSize() {
326 return this.cacheSize;
330 public void setMaxCacheSize(long cacheSize) {
331 this.cacheSize = cacheSize;
335 public long getTransactionTimeout() {
336 return this.transactionTimeout;
340 public void setTransactionTimeout(long transactionTimeout) {
341 this.transactionTimeout = transactionTimeout;
345 public long getCacheOccupancy() {
346 return this.cache.size();
349 protected void metric(VirtualControlLoopNotification notification) {
350 MDCTransaction trans = MDCTransaction
351 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
352 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
354 List<ControlLoopOperation> operations = notification.getHistory();
355 switch (notification.getNotification()) {
357 trans.setStatusCode(true);
358 trans.metric().resetTransaction();
361 trans.setStatusCode(true);
362 if (!operations.isEmpty()) {
363 ControlLoopOperation operation = operations.get(operations.size() - 1);
364 trans.setTargetEntity(operation.getTarget());
365 trans.setTargetServiceName(operation.getActor());
367 trans.metric().resetTransaction();
369 case OPERATION_SUCCESS:
370 trans.setStatusCode(true);
371 operation(trans, operations);
372 trans.transaction().resetTransaction();
374 case OPERATION_FAILURE:
375 trans.setStatusCode(false);
376 operation(trans, operations);
377 trans.transaction().resetTransaction();
381 logger.warn("unexpected notification type {} in notification {}",
382 notification.getNotification().toString(), notification);
387 protected void operation(MDCTransaction trans, List<ControlLoopOperation> operations) {
388 if (!operations.isEmpty()) {
389 ControlLoopOperation operation = operations.get(operations.size() - 1);
391 if (operation.getTarget() != null) {
392 trans.setTargetEntity(operation.getTarget());
395 if (operation.getActor() != null) {
396 trans.setTargetServiceName(operation.getActor());
399 if (operation.getMessage() != null) {
400 trans.setResponseDescription(operation.getMessage());
403 trans.setInvocationId(operation.getSubRequestId());
405 if (operation.getOutcome() != null) {
406 trans.setResponseCode(operation.getOutcome());
409 if (operation.getStart() != null) {
410 trans.setStartTime(operation.getStart());
413 if (operation.getEnd() != null) {
414 trans.setEndTime(operation.getEnd());
419 protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
420 MDCTransaction trans = MDCTransaction
421 .newTransaction(notification.getRequestId().toString(), notification.getFrom())
422 .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
423 .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
424 .setResponseDescription(notification.getMessage());
426 switch (notification.getNotification()) {
428 trans.setStatusCode(true);
431 trans.setStatusCode(true);
434 trans.setStatusCode(false);
437 trans.setStatusCode(false);
441 logger.warn("unexpected notification type {} in notification {}",
442 notification.getNotification().toString(), notification);
446 trans.transaction().resetTransaction();
450 public String toString() {
451 final StringBuffer sb = new StringBuffer("CacheBasedControlLoopMetricsManager{");
452 sb.append("cacheSize=").append(cacheSize);
453 sb.append(", transactionTimeout=").append(transactionTimeout);
455 return sb.toString();