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