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
8 package org.opendaylight.controller.cluster.datastore.utils;
10 import static org.hamcrest.MatcherAssert.assertThat;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.times;
16 import static org.mockito.Mockito.verify;
18 import com.codahale.metrics.Snapshot;
19 import com.codahale.metrics.Timer;
20 import java.util.concurrent.TimeUnit;
21 import org.apache.commons.lang3.time.StopWatch;
22 import org.hamcrest.BaseMatcher;
23 import org.hamcrest.Description;
24 import org.hamcrest.Matcher;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.mockito.Mock;
28 import org.mockito.MockitoAnnotations;
29 import org.opendaylight.controller.cluster.datastore.DatastoreContext;
31 public class TransactionRateLimiterTest {
34 public ActorUtils actorUtils;
37 public DatastoreContext datastoreContext;
40 public Timer commitTimer;
43 private Timer.Context commitTimerContext;
46 private Snapshot commitSnapshot;
50 MockitoAnnotations.initMocks(this);
51 doReturn(datastoreContext).when(actorUtils).getDatastoreContext();
52 doReturn(30).when(datastoreContext).getShardTransactionCommitTimeoutInSeconds();
53 doReturn(100L).when(datastoreContext).getTransactionCreationInitialRateLimit();
54 doReturn(commitTimer).when(actorUtils).getOperationTimer("commit");
55 doReturn(commitTimerContext).when(commitTimer).time();
56 doReturn(commitSnapshot).when(commitTimer).getSnapshot();
60 public void testAcquireRateLimitChanged() {
61 for (int i = 1; i < 11; i++) {
62 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
63 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
64 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
67 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
69 rateLimiter.acquire();
71 assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
73 assertEquals(147, rateLimiter.getPollOnCount());
78 public void testAcquirePercentileValueZero() {
80 for (int i = 1; i < 11; i++) {
81 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
82 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
83 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
86 doReturn(TimeUnit.MILLISECONDS.toNanos(0) * 1D).when(commitSnapshot).getValue(0.1);
88 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
90 rateLimiter.acquire();
92 assertThat(rateLimiter.getTxCreationLimit(), approximately(192));
94 assertEquals(97, rateLimiter.getPollOnCount());
98 public void testAcquireOnePercentileValueVeryHigh() {
100 for (int i = 1; i < 11; i++) {
101 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
102 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
103 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
107 doReturn(TimeUnit.MILLISECONDS.toNanos(10000) * 1D).when(commitSnapshot).getValue(1.0);
109 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
111 rateLimiter.acquire();
113 assertThat(rateLimiter.getTxCreationLimit(), approximately(282));
115 assertEquals(142, rateLimiter.getPollOnCount());
119 public void testAcquireWithAllPercentileValueVeryHigh() {
121 for (int i = 1; i < 11; i++) {
122 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
123 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
124 doReturn(TimeUnit.MILLISECONDS.toNanos(10000) * 1D).when(commitSnapshot).getValue(i * 0.1);
127 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
129 rateLimiter.acquire();
131 // The initial rate limit will be retained here because the calculated rate limit was too small
132 assertThat(rateLimiter.getTxCreationLimit(), approximately(100));
134 assertEquals(1, rateLimiter.getPollOnCount());
138 public void testAcquireWithRealPercentileValues() {
140 for (int i = 1; i < 11; i++) {
141 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
142 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
143 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
146 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
147 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
148 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
150 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
152 rateLimiter.acquire();
154 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
156 assertEquals(51, rateLimiter.getPollOnCount());
160 public void testAcquireGetRateLimitFromOtherDataStores() {
161 for (int i = 1; i < 11; i++) {
162 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
163 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
164 doReturn(0.0D).when(commitSnapshot).getValue(i * 0.1);
167 Timer operationalCommitTimer = mock(Timer.class);
168 Timer.Context operationalCommitTimerContext = mock(Timer.Context.class);
169 Snapshot operationalCommitSnapshot = mock(Snapshot.class);
171 doReturn(operationalCommitTimer).when(actorUtils).getOperationTimer("operational", "commit");
172 doReturn(operationalCommitTimerContext).when(operationalCommitTimer).time();
173 doReturn(operationalCommitSnapshot).when(operationalCommitTimer).getSnapshot();
175 for (int i = 1; i < 11; i++) {
176 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
177 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
178 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(operationalCommitSnapshot).getValue(i * 0.1);
182 DatastoreContext.getGlobalDatastoreNames().add("config");
183 DatastoreContext.getGlobalDatastoreNames().add("operational");
185 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
187 rateLimiter.acquire();
189 assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
191 assertEquals(147, rateLimiter.getPollOnCount());
195 public void testRateLimiting() {
197 for (int i = 1; i < 11; i++) {
198 doReturn(TimeUnit.SECONDS.toNanos(1) * 1D).when(commitSnapshot).getValue(i * 0.1);
201 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
203 StopWatch watch = new StopWatch();
207 rateLimiter.acquire();
208 rateLimiter.acquire();
209 rateLimiter.acquire();
213 assertTrue("did not take as much time as expected rate limit : " + rateLimiter.getTxCreationLimit(),
214 watch.getTime() > 1000);
218 public void testRateLimitNotCalculatedUntilPollCountReached() {
220 for (int i = 1; i < 11; i++) {
221 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
222 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
223 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
226 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
227 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
228 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
230 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
232 rateLimiter.acquire();
234 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
236 assertEquals(51, rateLimiter.getPollOnCount());
238 for (int i = 0; i < 49; i++) {
239 rateLimiter.acquire();
242 verify(commitTimer, times(1)).getSnapshot();
244 // Acquiring one more time will cause the re-calculation of the rate limit
245 rateLimiter.acquire();
247 verify(commitTimer, times(2)).getSnapshot();
251 public void testAcquireNegativeAcquireAndPollOnCount() {
253 for (int i = 1; i < 11; i++) {
254 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
255 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
256 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
259 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
260 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
261 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
263 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
264 rateLimiter.setAcquireCount(Long.MAX_VALUE - 1);
265 rateLimiter.setPollOnCount(Long.MAX_VALUE);
267 rateLimiter.acquire();
269 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
271 assertEquals(-9223372036854775759L, rateLimiter.getPollOnCount());
273 for (int i = 0; i < 50; i++) {
274 rateLimiter.acquire();
277 verify(commitTimer, times(2)).getSnapshot();
281 public Matcher<Double> approximately(final double val) {
282 return new BaseMatcher<>() {
284 public boolean matches(final Object obj) {
285 Double value = (Double) obj;
286 return value >= val && value <= val + 1;
290 public void describeTo(final Description description) {
291 description.appendText("> " + val + " < " + (val + 1));