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.utils;
11 import com.codahale.metrics.Snapshot;
12 import com.codahale.metrics.Timer;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.util.concurrent.RateLimiter;
15 import java.util.concurrent.TimeUnit;
16 import java.util.concurrent.atomic.AtomicLong;
17 import org.opendaylight.controller.cluster.datastore.DatastoreContext;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
21 public class TransactionRateLimiter {
22 private static final Logger LOG = LoggerFactory.getLogger(TransactionRateLimiter.class);
24 private final ActorContext actorContext;
25 private final long commitTimeoutInSeconds;
26 private final String dataStoreName;
27 private final RateLimiter txRateLimiter;
28 private final AtomicLong acquireCount = new AtomicLong();
30 private volatile long pollOnCount = 1;
32 public TransactionRateLimiter(ActorContext actorContext) {
33 this.actorContext = actorContext;
34 this.commitTimeoutInSeconds = actorContext.getDatastoreContext().getShardTransactionCommitTimeoutInSeconds();
35 this.dataStoreName = actorContext.getDataStoreName();
36 this.txRateLimiter = RateLimiter.create(actorContext.getDatastoreContext()
37 .getTransactionCreationInitialRateLimit());
40 public void acquire() {
42 txRateLimiter.acquire();
45 private void adjustRateLimit() {
46 final long count = acquireCount.incrementAndGet();
47 if (count >= pollOnCount) {
48 final Timer commitTimer = actorContext.getOperationTimer(ActorContext.COMMIT);
49 double newRateLimit = calculateNewRateLimit(commitTimer, commitTimeoutInSeconds);
51 if (newRateLimit < 1.0) {
52 newRateLimit = getRateLimitFromOtherDataStores();
55 if (newRateLimit >= 1.0) {
56 txRateLimiter.setRate(newRateLimit);
57 pollOnCount = count + (long) newRateLimit / 2;
62 public double getTxCreationLimit() {
63 return txRateLimiter.getRate();
66 private double getRateLimitFromOtherDataStores() {
67 // Since we have no rate data for unused Tx's data store, adjust to the rate from another
68 // data store that does have rate data.
69 for (String name: DatastoreContext.getGlobalDatastoreNames()) {
70 if (name.equals(this.dataStoreName)) {
74 double newRateLimit = calculateNewRateLimit(actorContext.getOperationTimer(name, ActorContext.COMMIT),
75 this.commitTimeoutInSeconds);
76 if (newRateLimit > 0.0) {
77 LOG.debug("On unused Tx - data Store {} commit rateLimit adjusted to {}",
78 this.dataStoreName, newRateLimit);
87 private static double calculateNewRateLimit(Timer commitTimer, long commitTimeoutInSeconds) {
88 if (commitTimer == null) {
89 // This can happen in unit tests.
93 Snapshot timerSnapshot = commitTimer.getSnapshot();
94 double newRateLimit = 0;
96 long commitTimeoutInNanos = TimeUnit.SECONDS.toNanos(commitTimeoutInSeconds);
98 // Find the time that it takes for transactions to get executed in every 10th percentile
99 // Compute the rate limit for that percentile and sum it up
100 for (int i = 1; i <= 10; i++) {
101 // Get the amount of time transactions take in the i*10th percentile
102 double percentileTimeInNanos = timerSnapshot.getValue(i * 0.1D);
104 if (percentileTimeInNanos > 0) {
105 // Figure out the rate limit for the i*10th percentile in nanos
106 double percentileRateLimit = commitTimeoutInNanos / percentileTimeInNanos;
108 // Add the percentileRateLimit to the total rate limit
109 newRateLimit += percentileRateLimit;
113 // Compute the rate limit per second
114 return newRateLimit / (commitTimeoutInSeconds * 10);
118 long getPollOnCount() {
123 void setPollOnCount(long value) {
128 void setAcquireCount(long value) {
129 acquireCount.set(value);