Rename ActorContext to ActorUtils
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / utils / TransactionRateLimiterTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.cluster.datastore.utils;
10
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;
18
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;
31
32 public class TransactionRateLimiterTest {
33
34     @Mock
35     public ActorUtils actorUtils;
36
37     @Mock
38     public DatastoreContext datastoreContext;
39
40     @Mock
41     public Timer commitTimer;
42
43     @Mock
44     private Timer.Context commitTimerContext;
45
46     @Mock
47     private Snapshot commitSnapshot;
48
49     @Before
50     public void setUp() {
51         MockitoAnnotations.initMocks(this);
52         doReturn(datastoreContext).when(actorUtils).getDatastoreContext();
53         doReturn(30).when(datastoreContext).getShardTransactionCommitTimeoutInSeconds();
54         doReturn(100L).when(datastoreContext).getTransactionCreationInitialRateLimit();
55         doReturn(commitTimer).when(actorUtils).getOperationTimer("commit");
56         doReturn(commitTimerContext).when(commitTimer).time();
57         doReturn(commitSnapshot).when(commitTimer).getSnapshot();
58     }
59
60     @Test
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);
66         }
67
68         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
69
70         rateLimiter.acquire();
71
72         assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
73
74         assertEquals(147, rateLimiter.getPollOnCount());
75     }
76
77
78     @Test
79     public void testAcquirePercentileValueZero() {
80
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);
85         }
86
87         doReturn(TimeUnit.MILLISECONDS.toNanos(0) * 1D).when(commitSnapshot).getValue(0.1);
88
89         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
90
91         rateLimiter.acquire();
92
93         assertThat(rateLimiter.getTxCreationLimit(), approximately(192));
94
95         assertEquals(97, rateLimiter.getPollOnCount());
96     }
97
98     @Test
99     public void testAcquireOnePercentileValueVeryHigh() {
100
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);
105         }
106
107         // ten seconds
108         doReturn(TimeUnit.MILLISECONDS.toNanos(10000) * 1D).when(commitSnapshot).getValue(1.0);
109
110         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
111
112         rateLimiter.acquire();
113
114         assertThat(rateLimiter.getTxCreationLimit(), approximately(282));
115
116         assertEquals(142, rateLimiter.getPollOnCount());
117     }
118
119     @Test
120     public void testAcquireWithAllPercentileValueVeryHigh() {
121
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);
126         }
127
128         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
129
130         rateLimiter.acquire();
131
132         // The initial rate limit will be retained here because the calculated rate limit was too small
133         assertThat(rateLimiter.getTxCreationLimit(), approximately(100));
134
135         assertEquals(1, rateLimiter.getPollOnCount());
136     }
137
138     @Test
139     public void testAcquireWithRealPercentileValues() {
140
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);
145         }
146
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);
150
151         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
152
153         rateLimiter.acquire();
154
155         assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
156
157         assertEquals(51, rateLimiter.getPollOnCount());
158     }
159
160     @Test
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);
166         }
167
168         Timer operationalCommitTimer = mock(Timer.class);
169         Timer.Context operationalCommitTimerContext = mock(Timer.Context.class);
170         Snapshot operationalCommitSnapshot = mock(Snapshot.class);
171
172         doReturn(operationalCommitTimer).when(actorUtils).getOperationTimer("operational", "commit");
173         doReturn(operationalCommitTimerContext).when(operationalCommitTimer).time();
174         doReturn(operationalCommitSnapshot).when(operationalCommitTimer).getSnapshot();
175
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);
180         }
181
182
183         DatastoreContext.getGlobalDatastoreNames().add("config");
184         DatastoreContext.getGlobalDatastoreNames().add("operational");
185
186         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
187
188         rateLimiter.acquire();
189
190         assertThat(rateLimiter.getTxCreationLimit(), approximately(292));
191
192         assertEquals(147, rateLimiter.getPollOnCount());
193     }
194
195     @Test
196     public void testRateLimiting() {
197
198         for (int i = 1; i < 11; i++) {
199             doReturn(TimeUnit.SECONDS.toNanos(1) * 1D).when(commitSnapshot).getValue(i * 0.1);
200         }
201
202         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
203
204         StopWatch watch = new StopWatch();
205
206         watch.start();
207
208         rateLimiter.acquire();
209         rateLimiter.acquire();
210         rateLimiter.acquire();
211
212         watch.stop();
213
214         assertTrue("did not take as much time as expected rate limit : " + rateLimiter.getTxCreationLimit(),
215                 watch.getTime() > 1000);
216     }
217
218     @Test
219     public void testRateLimitNotCalculatedUntilPollCountReached() {
220
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);
225         }
226
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);
230
231         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
232
233         rateLimiter.acquire();
234
235         assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
236
237         assertEquals(51, rateLimiter.getPollOnCount());
238
239         for (int i = 0; i < 49; i++) {
240             rateLimiter.acquire();
241         }
242
243         verify(commitTimer, times(1)).getSnapshot();
244
245         // Acquiring one more time will cause the re-calculation of the rate limit
246         rateLimiter.acquire();
247
248         verify(commitTimer, times(2)).getSnapshot();
249     }
250
251     @Test
252     public void testAcquireNegativeAcquireAndPollOnCount() {
253
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);
258         }
259
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);
263
264         TransactionRateLimiter rateLimiter = new TransactionRateLimiter(actorUtils);
265         rateLimiter.setAcquireCount(Long.MAX_VALUE - 1);
266         rateLimiter.setPollOnCount(Long.MAX_VALUE);
267
268         rateLimiter.acquire();
269
270         assertThat(rateLimiter.getTxCreationLimit(), approximately(101));
271
272         assertEquals(-9223372036854775759L, rateLimiter.getPollOnCount());
273
274         for (int i = 0; i < 50; i++) {
275             rateLimiter.acquire();
276         }
277
278         verify(commitTimer, times(2)).getSnapshot();
279
280     }
281
282     public Matcher<Double> approximately(final double val) {
283         return new BaseMatcher<Double>() {
284             @Override
285             public boolean matches(Object obj) {
286                 Double value = (Double) obj;
287                 return value >= val && value <= val + 1;
288             }
289
290             @Override
291             public void describeTo(Description description) {
292                 description.appendText("> " + val + " < " + (val + 1));
293             }
294         };
295     }
296
297
298 }