2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.cluster.datastore;
11 import com.codahale.metrics.Snapshot;
12 import com.codahale.metrics.Timer;
13 import com.google.common.base.Preconditions;
14 import java.util.concurrent.TimeUnit;
15 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
20 * TransactionRateLimitingCallback computes the new transaction rate limit on the successful completion of a
23 public class TransactionRateLimitingCallback implements OperationCallback{
25 private static final Logger LOG = LoggerFactory.getLogger(TransactionRateLimitingCallback.class);
26 private static final String COMMIT = "commit";
28 private final Timer commitTimer;
29 private final ActorContext actorContext;
30 private Timer.Context timerContext;
32 TransactionRateLimitingCallback(ActorContext actorContext){
33 this.actorContext = actorContext;
34 commitTimer = actorContext.getOperationTimer(COMMIT);
39 timerContext = commitTimer.time();
43 public void success() {
44 Preconditions.checkState(timerContext != null, "Call run before success");
47 double newRateLimit = calculateNewRateLimit(commitTimer, actorContext.getDatastoreContext());
49 LOG.debug("Data Store {} commit rateLimit adjusted to {}", actorContext.getDataStoreType(), newRateLimit);
51 actorContext.setTxCreationLimit(newRateLimit);
55 public void failure() {
56 // This would mean we couldn't get a transaction completed in 30 seconds which is
57 // the default transaction commit timeout. Using the timeout information to figure out the rate limit is
58 // not going to be useful - so we leave it as it is
61 private static double calculateNewRateLimit(Timer commitTimer, DatastoreContext context) {
62 if(commitTimer == null) {
63 // This can happen in unit tests.
67 Snapshot timerSnapshot = commitTimer.getSnapshot();
68 double newRateLimit = 0;
70 long commitTimeoutInSeconds = context.getShardTransactionCommitTimeoutInSeconds();
71 long commitTimeoutInNanos = TimeUnit.SECONDS.toNanos(commitTimeoutInSeconds);
73 // Find the time that it takes for transactions to get executed in every 10th percentile
74 // Compute the rate limit for that percentile and sum it up
75 for(int i=1;i<=10;i++){
76 // Get the amount of time transactions take in the i*10th percentile
77 double percentileTimeInNanos = timerSnapshot.getValue(i * 0.1D);
79 if(percentileTimeInNanos > 0) {
80 // Figure out the rate limit for the i*10th percentile in nanos
81 double percentileRateLimit = (commitTimeoutInNanos / percentileTimeInNanos);
83 // Add the percentileRateLimit to the total rate limit
84 newRateLimit += percentileRateLimit;
88 // Compute the rate limit per second
89 return newRateLimit/(commitTimeoutInSeconds*10);
92 public static void adjustRateLimitForUnusedTransaction(ActorContext actorContext) {
93 // Unused transactions in one data store can artificially limit the rate for other data stores
94 // if the first data store's rate is still at a lower initial rate since the front-end creates
95 // transactions in each data store up-front even though the client may not actually submit changes.
96 // So we may have to adjust the rate for data stores with unused transactions.
98 // First calculate the current rate for the data store. If it's 0 then there have been no
99 // actual transactions committed to the data store.
101 double newRateLimit = calculateNewRateLimit(actorContext.getOperationTimer(COMMIT),
102 actorContext.getDatastoreContext());
103 if(newRateLimit == 0.0) {
104 // Since we have no rate data for unused Tx's data store, adjust to the rate from another
105 // data store that does have rate data.
106 for(String datastoreType: DatastoreContext.getGlobalDatastoreTypes()) {
107 if(datastoreType.equals(actorContext.getDataStoreType())) {
111 newRateLimit = calculateNewRateLimit(actorContext.getOperationTimer(datastoreType, COMMIT),
112 actorContext.getDatastoreContext());
113 if(newRateLimit > 0.0) {
114 LOG.debug("On unused Tx - data Store {} commit rateLimit adjusted to {}",
115 actorContext.getDataStoreType(), newRateLimit);
117 actorContext.setTxCreationLimit(newRateLimit);