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 static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertThat;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.times;
17 import static org.mockito.Mockito.verify;
19 import com.codahale.metrics.Snapshot;
20 import com.codahale.metrics.Timer;
21 import java.util.concurrent.TimeUnit;
22 import org.apache.commons.lang3.time.StopWatch;
23 import org.hamcrest.BaseMatcher;
24 import org.hamcrest.Description;
25 import org.hamcrest.Matcher;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.mockito.Mock;
29 import org.mockito.MockitoAnnotations;
30 import org.opendaylight.controller.cluster.datastore.DatastoreContext;
32 public class TransactionRateLimiterTest {
35 public ActorContext actorContext;
38 public DatastoreContext datastoreContext;
41 public Timer commitTimer;
44 private Timer.Context commitTimerContext;
47 private Snapshot commitSnapshot;
51 MockitoAnnotations.initMocks(this);
52 doReturn(datastoreContext).when(actorContext).getDatastoreContext();
53 doReturn(30).when(datastoreContext).getShardTransactionCommitTimeoutInSeconds();
54 doReturn(100L).when(datastoreContext).getTransactionCreationInitialRateLimit();
55 doReturn(commitTimer).when(actorContext).getOperationTimer("commit");
56 doReturn(commitTimerContext).when(commitTimer).time();
57 doReturn(commitSnapshot).when(commitTimer).getSnapshot();
61 public void testAcquireRateLimitChanged() {
62 for (int i = 1; i < 11; i++) {
63 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
64 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
65 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
68 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
70 rateLimiter.acquire();
72 assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
74 assertEquals(147, rateLimiter.getPollOnCount());
79 public void testAcquirePercentileValueZero() {
81 for (int i = 1; i < 11; i++) {
82 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
83 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
84 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
87 doReturn(TimeUnit.MILLISECONDS.toNanos(0) * 1D).when(commitSnapshot).getValue(0.1);
89 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
91 rateLimiter.acquire();
93 assertThat(rateLimiter.getTxCreationLimit(), approximately(192));
95 assertEquals(97, rateLimiter.getPollOnCount());
99 public void testAcquireOnePercentileValueVeryHigh() {
101 for (int i = 1; i < 11; i++) {
102 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
103 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
104 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(commitSnapshot).getValue(i * 0.1);
108 doReturn(TimeUnit.MILLISECONDS.toNanos(10000) * 1D).when(commitSnapshot).getValue(1.0);
110 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
112 rateLimiter.acquire();
114 assertThat(rateLimiter.getTxCreationLimit(), approximately(282));
116 assertEquals(142, rateLimiter.getPollOnCount());
120 public void testAcquireWithAllPercentileValueVeryHigh() {
122 for (int i = 1; i < 11; i++) {
123 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
124 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
125 doReturn(TimeUnit.MILLISECONDS.toNanos(10000) * 1D).when(commitSnapshot).getValue(i * 0.1);
128 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
130 rateLimiter.acquire();
132 // The initial rate limit will be retained here because the calculated rate limit was too small
133 assertThat(rateLimiter.getTxCreationLimit(), approximately(100));
135 assertEquals(1, rateLimiter.getPollOnCount());
139 public void testAcquireWithRealPercentileValues() {
141 for (int i = 1; i < 11; i++) {
142 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
143 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
144 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
147 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
148 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
149 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
151 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
153 rateLimiter.acquire();
155 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
157 assertEquals(51, rateLimiter.getPollOnCount());
161 public void testAcquireGetRateLimitFromOtherDataStores() {
162 for (int i = 1; i < 11; i++) {
163 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
164 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
165 doReturn(0.0D).when(commitSnapshot).getValue(i * 0.1);
168 Timer operationalCommitTimer = mock(Timer.class);
169 Timer.Context operationalCommitTimerContext = mock(Timer.Context.class);
170 Snapshot operationalCommitSnapshot = mock(Snapshot.class);
172 doReturn(operationalCommitTimer).when(actorContext).getOperationTimer("operational", "commit");
173 doReturn(operationalCommitTimerContext).when(operationalCommitTimer).time();
174 doReturn(operationalCommitSnapshot).when(operationalCommitTimer).getSnapshot();
176 for (int i = 1; i < 11; i++) {
177 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
178 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
179 doReturn(TimeUnit.MILLISECONDS.toNanos(i) * 1D).when(operationalCommitSnapshot).getValue(i * 0.1);
183 DatastoreContext.getGlobalDatastoreNames().add("config");
184 DatastoreContext.getGlobalDatastoreNames().add("operational");
186 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
188 rateLimiter.acquire();
190 assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
192 assertEquals(147, rateLimiter.getPollOnCount());
196 public void testRateLimiting() {
198 for (int i = 1; i < 11; i++) {
199 doReturn(TimeUnit.SECONDS.toNanos(1) * 1D).when(commitSnapshot).getValue(i * 0.1);
202 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
204 StopWatch watch = new StopWatch();
208 rateLimiter.acquire();
209 rateLimiter.acquire();
210 rateLimiter.acquire();
214 assertTrue("did not take as much time as expected rate limit : " + rateLimiter.getTxCreationLimit(),
215 watch.getTime() > 1000);
219 public void testRateLimitNotCalculatedUntilPollCountReached() {
221 for (int i = 1; i < 11; i++) {
222 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
223 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
224 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
227 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
228 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
229 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
231 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
233 rateLimiter.acquire();
235 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
237 assertEquals(51, rateLimiter.getPollOnCount());
239 for (int i = 0; i < 49; i++) {
240 rateLimiter.acquire();
243 verify(commitTimer, times(1)).getSnapshot();
245 // Acquiring one more time will cause the re-calculation of the rate limit
246 rateLimiter.acquire();
248 verify(commitTimer, times(2)).getSnapshot();
252 public void testAcquireNegativeAcquireAndPollOnCount() {
254 for (int i = 1; i < 11; i++) {
255 // Keep on increasing the amount of time it takes to complete transaction for each tenth of a
256 // percentile. Essentially this would be 1ms for the 10th percentile, 2ms for 20th percentile and so on.
257 doReturn(TimeUnit.MILLISECONDS.toNanos(8) * 1D).when(commitSnapshot).getValue(i * 0.1);
260 doReturn(TimeUnit.MILLISECONDS.toNanos(20) * 1D).when(commitSnapshot).getValue(0.7);
261 doReturn(TimeUnit.MILLISECONDS.toNanos(100) * 1D).when(commitSnapshot).getValue(0.9);
262 doReturn(TimeUnit.MILLISECONDS.toNanos(200) * 1D).when(commitSnapshot).getValue(1.0);
264 TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorContext);
265 rateLimiter.setAcquireCount(Long.MAX_VALUE - 1);
266 rateLimiter.setPollOnCount(Long.MAX_VALUE);
268 rateLimiter.acquire();
270 assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
272 assertEquals(-9223372036854775759L, rateLimiter.getPollOnCount());
274 for (int i = 0; i < 50; i++) {
275 rateLimiter.acquire();
278 verify(commitTimer, times(2)).getSnapshot();
282 public Matcher<Double> approximately(final double val) {
283 return new BaseMatcher<Double>() {
285 public boolean matches(Object obj) {
286 Double value = (Double) obj;
287 return value >= val && value <= val + 1;
291 public void describeTo(Description description) {
292 description.appendText("> " + val + " < " + (val + 1));