1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.apache.hadoop.hbase.HConstants.NO_NONCE;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.atomic.AtomicInteger;
29
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.HBaseConfiguration;
32 import org.apache.hadoop.hbase.ScheduledChore;
33 import org.apache.hadoop.hbase.Stoppable;
34 import org.apache.hadoop.hbase.testclassification.SmallTests;
35 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36 import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
37 import org.apache.hadoop.hbase.util.Threads;
38 import org.junit.Test;
39 import org.junit.experimental.categories.Category;
40 import org.mockito.Mockito;
41 import org.mockito.invocation.InvocationOnMock;
42 import org.mockito.stubbing.Answer;
43
44 @Category(SmallTests.class)
45 public class TestServerNonceManager {
46
47 @Test
48 public void testMvcc() throws Exception {
49 ServerNonceManager nm = createManager();
50 final long group = 100;
51 final long nonce = 1;
52 final long initMvcc = 999;
53 assertTrue(nm.startOperation(group, nonce, createStoppable()));
54 nm.addMvccToOperationContext(group, nonce, initMvcc);
55 nm.endOperation(group, nonce, true);
56 assertEquals(initMvcc, nm.getMvccFromOperationContext(group, nonce));
57 long newMvcc = initMvcc + 1;
58 for (long newNonce = nonce + 1; newNonce != (nonce + 5); ++newNonce) {
59 assertTrue(nm.startOperation(group, newNonce, createStoppable()));
60 nm.addMvccToOperationContext(group, newNonce, newMvcc);
61 nm.endOperation(group, newNonce, true);
62 assertEquals(newMvcc, nm.getMvccFromOperationContext(group, newNonce));
63 ++newMvcc;
64 }
65 assertEquals(initMvcc, nm.getMvccFromOperationContext(group, nonce));
66 }
67
68 @Test
69 public void testNormalStartEnd() throws Exception {
70 final long[] numbers = new long[] { NO_NONCE, 1, 2, Long.MAX_VALUE, Long.MIN_VALUE };
71 ServerNonceManager nm = createManager();
72 for (int i = 0; i < numbers.length; ++i) {
73 for (int j = 0; j < numbers.length; ++j) {
74 assertTrue(nm.startOperation(numbers[i], numbers[j], createStoppable()));
75 }
76 }
77
78 for (int i = 0; i < numbers.length; ++i) {
79 assertTrue(nm.startOperation(numbers[i], NO_NONCE, createStoppable()));
80 }
81
82 for (int i = 0; i < numbers.length; ++i) {
83 for (int j = 0; j < numbers.length; ++j) {
84 nm.endOperation(numbers[i], numbers[j], false);
85 assertTrue(nm.startOperation(numbers[i], numbers[j], createStoppable()));
86 }
87 }
88
89 for (int i = 0; i < numbers.length; ++i) {
90 for (int j = 0; j < numbers.length; ++j) {
91 nm.endOperation(numbers[i], numbers[j], true);
92 assertEquals(numbers[j] == NO_NONCE,
93 nm.startOperation(numbers[i], numbers[j], createStoppable()));
94 }
95 }
96 }
97
98 @Test
99 public void testNoEndWithoutStart() {
100 ServerNonceManager nm = createManager();
101 try {
102 nm.endOperation(NO_NONCE, 1, true);
103 throw new Error("Should have thrown");
104 } catch (AssertionError err) {}
105 }
106
107 @Test
108 public void testCleanup() throws Exception {
109 ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
110 EnvironmentEdgeManager.injectEdge(edge);
111 try {
112 ServerNonceManager nm = createManager(6);
113 ScheduledChore cleanup = nm.createCleanupScheduledChore(Mockito.mock(Stoppable.class));
114 edge.setValue(1);
115 assertTrue(nm.startOperation(NO_NONCE, 1, createStoppable()));
116 assertTrue(nm.startOperation(NO_NONCE, 2, createStoppable()));
117 assertTrue(nm.startOperation(NO_NONCE, 3, createStoppable()));
118 edge.setValue(2);
119 nm.endOperation(NO_NONCE, 1, true);
120 edge.setValue(4);
121 nm.endOperation(NO_NONCE, 2, true);
122 edge.setValue(9);
123 cleanup.choreForTesting();
124
125 assertTrue(nm.startOperation(NO_NONCE, 1, createStoppable()));
126
127 assertFalse(nm.startOperation(NO_NONCE, 2, createStoppable()));
128
129 nm.endOperation(NO_NONCE, 3, false);
130 assertTrue(nm.startOperation(NO_NONCE, 3, createStoppable()));
131 edge.setValue(11);
132 cleanup.choreForTesting();
133
134 assertTrue(nm.startOperation(NO_NONCE, 2, createStoppable()));
135 } finally {
136 EnvironmentEdgeManager.reset();
137 }
138 }
139
140 @Test
141 public void testWalNonces() throws Exception {
142 ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
143 EnvironmentEdgeManager.injectEdge(edge);
144 try {
145 ServerNonceManager nm = createManager(6);
146 ScheduledChore cleanup = nm.createCleanupScheduledChore(Mockito.mock(Stoppable.class));
147
148 edge.setValue(12);
149 nm.reportOperationFromWal(NO_NONCE, 1, 8);
150 nm.reportOperationFromWal(NO_NONCE, 2, 2);
151 nm.reportOperationFromWal(NO_NONCE, 3, 5);
152 nm.reportOperationFromWal(NO_NONCE, 3, 6);
153
154 assertFalse(nm.startOperation(NO_NONCE, 1, createStoppable()));
155
156 assertTrue(nm.startOperation(NO_NONCE, 2, createStoppable()));
157 assertFalse(nm.startOperation(NO_NONCE, 3, createStoppable()));
158
159 edge.setValue(17);
160 cleanup.choreForTesting();
161 assertFalse(nm.startOperation(NO_NONCE, 1, createStoppable()));
162 assertFalse(nm.startOperation(NO_NONCE, 3, createStoppable()));
163 edge.setValue(19);
164 cleanup.choreForTesting();
165 assertTrue(nm.startOperation(NO_NONCE, 1, createStoppable()));
166 assertTrue(nm.startOperation(NO_NONCE, 3, createStoppable()));
167 } finally {
168 EnvironmentEdgeManager.reset();
169 }
170 }
171
172 @Test
173 public void testConcurrentAttempts() throws Exception {
174 final ServerNonceManager nm = createManager();
175
176 nm.startOperation(NO_NONCE, 1, createStoppable());
177 TestRunnable tr = new TestRunnable(nm, 1, false, createStoppable());
178 Thread t = tr.start();
179 waitForThreadToBlockOrExit(t);
180 nm.endOperation(NO_NONCE, 1, true);
181 t.join();
182 tr.propagateError();
183
184 nm.startOperation(NO_NONCE, 2, createStoppable());
185 tr = new TestRunnable(nm, 2, true, createStoppable());
186 t = tr.start();
187 waitForThreadToBlockOrExit(t);
188 nm.endOperation(NO_NONCE, 2, false);
189 t.join();
190 tr.propagateError();
191 nm.endOperation(NO_NONCE, 2, true);
192
193 nm.startOperation(NO_NONCE, 3, createStoppable());
194 tr = new TestRunnable(nm, 4, true, createStoppable());
195 tr.start().join();
196 tr.propagateError();
197 }
198
199 @Test
200 public void testStopWaiting() throws Exception {
201 final ServerNonceManager nm = createManager();
202 nm.setConflictWaitIterationMs(1);
203 Stoppable stoppingStoppable = createStoppable();
204 Mockito.when(stoppingStoppable.isStopped()).thenAnswer(new Answer<Boolean>() {
205 AtomicInteger answer = new AtomicInteger(3);
206 @Override
207 public Boolean answer(InvocationOnMock invocation) throws Throwable {
208 return 0 < answer.decrementAndGet();
209 }
210 });
211
212 nm.startOperation(NO_NONCE, 1, createStoppable());
213 TestRunnable tr = new TestRunnable(nm, 1, null, stoppingStoppable);
214 Thread t = tr.start();
215 waitForThreadToBlockOrExit(t);
216
217 t.join();
218 tr.propagateError();
219 }
220
221 private void waitForThreadToBlockOrExit(Thread t) throws InterruptedException {
222 for (int i = 9; i >= 0; --i) {
223 if (t.getState() == Thread.State.TIMED_WAITING || t.getState() == Thread.State.WAITING
224 || t.getState() == Thread.State.BLOCKED || t.getState() == Thread.State.TERMINATED) {
225 return;
226 }
227 if (i > 0) Thread.sleep(300);
228 }
229
230
231 }
232
233 private static class TestRunnable implements Runnable {
234 public final CountDownLatch startedLatch = new CountDownLatch(1);
235
236 private final ServerNonceManager nm;
237 private final long nonce;
238 private final Boolean expected;
239 private final Stoppable stoppable;
240
241 private Throwable throwable = null;
242
243 public TestRunnable(ServerNonceManager nm, long nonce, Boolean expected, Stoppable stoppable) {
244 this.nm = nm;
245 this.nonce = nonce;
246 this.expected = expected;
247 this.stoppable = stoppable;
248 }
249
250 public void propagateError() throws Exception {
251 if (throwable == null) return;
252 throw new Exception(throwable);
253 }
254
255 public Thread start() {
256 Thread t = new Thread(this);
257 t = Threads.setDaemonThreadRunning(t);
258 try {
259 startedLatch.await();
260 } catch (InterruptedException e) {
261 fail("Unexpected");
262 }
263 return t;
264 }
265
266 @Override
267 public void run() {
268 startedLatch.countDown();
269 boolean shouldThrow = expected == null;
270 boolean hasThrown = true;
271 try {
272 boolean result = nm.startOperation(NO_NONCE, nonce, stoppable);
273 hasThrown = false;
274 if (!shouldThrow) {
275 assertEquals(expected.booleanValue(), result);
276 }
277 } catch (Throwable t) {
278 if (!shouldThrow) {
279 throwable = t;
280 }
281 }
282 if (shouldThrow && !hasThrown) {
283 throwable = new AssertionError("Should have thrown");
284 }
285 }
286 }
287
288 private Stoppable createStoppable() {
289 Stoppable s = Mockito.mock(Stoppable.class);
290 Mockito.when(s.isStopped()).thenReturn(false);
291 return s;
292 }
293
294 private ServerNonceManager createManager() {
295 return createManager(null);
296 }
297
298 private ServerNonceManager createManager(Integer gracePeriod) {
299 Configuration conf = HBaseConfiguration.create();
300 if (gracePeriod != null) {
301 conf.setInt(ServerNonceManager.HASH_NONCE_GRACE_PERIOD_KEY, gracePeriod.intValue());
302 }
303 return new ServerNonceManager(conf);
304 }
305 }