1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.procedure2;
20
21 import java.io.IOException;
22 import java.util.concurrent.atomic.AtomicReference;
23 import java.util.concurrent.CountDownLatch;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.hadoop.fs.FileSystem;
28 import org.apache.hadoop.fs.Path;
29 import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
30 import org.apache.hadoop.hbase.ProcedureInfo;
31 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore;
32 import org.apache.hadoop.hbase.testclassification.SmallTests;
33 import org.apache.hadoop.hbase.testclassification.MasterTests;
34 import org.apache.hadoop.hbase.security.User;
35 import org.apache.hadoop.hbase.util.Bytes;
36 import org.apache.hadoop.hbase.util.NonceKey;
37 import org.apache.hadoop.hbase.util.Threads;
38
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.experimental.categories.Category;
43
44 import static org.junit.Assert.assertEquals;
45 import static org.junit.Assert.assertFalse;
46 import static org.junit.Assert.assertTrue;
47 import static org.junit.Assert.fail;
48
49 @Category({MasterTests.class, SmallTests.class})
50 public class TestProcedureNonce {
51 private static final Log LOG = LogFactory.getLog(TestProcedureNonce.class);
52
53 private static final int PROCEDURE_EXECUTOR_SLOTS = 2;
54
55 private static TestProcEnv procEnv;
56 private static ProcedureExecutor<TestProcEnv> procExecutor;
57 private static ProcedureStore procStore;
58
59 private HBaseCommonTestingUtility htu;
60 private FileSystem fs;
61 private Path logDir;
62
63 @Before
64 public void setUp() throws IOException {
65 htu = new HBaseCommonTestingUtility();
66 Path testDir = htu.getDataTestDir();
67 fs = testDir.getFileSystem(htu.getConfiguration());
68 assertTrue(testDir.depth() > 1);
69
70 logDir = new Path(testDir, "proc-logs");
71 procEnv = new TestProcEnv();
72 procStore = ProcedureTestingUtility.createStore(htu.getConfiguration(), logDir);
73 procExecutor = new ProcedureExecutor(htu.getConfiguration(), procEnv, procStore);
74 procExecutor.testing = new ProcedureExecutor.Testing();
75 procStore.start(PROCEDURE_EXECUTOR_SLOTS);
76 procExecutor.start(PROCEDURE_EXECUTOR_SLOTS, true);
77 }
78
79 @After
80 public void tearDown() throws IOException {
81 procExecutor.stop();
82 procStore.stop(false);
83 fs.delete(logDir, true);
84 }
85
86 @Test(timeout=30000)
87 public void testCompletedProcWithSameNonce() throws Exception {
88 final long nonceGroup = 123;
89 final long nonce = 2222;
90
91
92 final NonceKey nonceKey = procExecutor.createNonceKey(nonceGroup, nonce);
93 assertFalse(procExecutor.registerNonce(nonceKey) >= 0);
94
95
96 Procedure proc = new TestSingleStepProcedure();
97 long procId = procExecutor.submitProcedure(proc, nonceKey);
98 ProcedureTestingUtility.waitProcedure(procExecutor, procId);
99
100
101 ProcedureTestingUtility.restart(procExecutor);
102 ProcedureTestingUtility.waitProcedure(procExecutor, procId);
103
104
105
106 assertEquals(procId, procExecutor.registerNonce(nonceKey));
107
108 ProcedureInfo result = procExecutor.getResult(procId);
109 ProcedureTestingUtility.assertProcNotFailed(result);
110 }
111
112 @Test(timeout=30000)
113 public void testRunningProcWithSameNonce() throws Exception {
114 final long nonceGroup = 456;
115 final long nonce = 33333;
116
117
118 final NonceKey nonceKey = procExecutor.createNonceKey(nonceGroup, nonce);
119 assertFalse(procExecutor.registerNonce(nonceKey) >= 0);
120
121
122 CountDownLatch latch = new CountDownLatch(1);
123 TestSingleStepProcedure proc = new TestSingleStepProcedure();
124 procEnv.setWaitLatch(latch);
125 long procId = procExecutor.submitProcedure(proc, nonceKey);
126 while (proc.step != 1) Threads.sleep(25);
127
128
129
130 assertEquals(procId, procExecutor.registerNonce(nonceKey));
131
132
133 latch.countDown();
134
135
136 ProcedureTestingUtility.restart(procExecutor);
137 ProcedureTestingUtility.waitProcedure(procExecutor, procId);
138
139
140
141 assertEquals(procId, procExecutor.registerNonce(nonceKey));
142
143 ProcedureInfo result = procExecutor.getResult(procId);
144 ProcedureTestingUtility.assertProcNotFailed(result);
145 }
146
147 @Test
148 public void testSetFailureResultForNonce() throws IOException {
149 final long nonceGroup = 234;
150 final long nonce = 55555;
151
152
153 final NonceKey nonceKey = procExecutor.createNonceKey(nonceGroup, nonce);
154 assertFalse(procExecutor.registerNonce(nonceKey) >= 0);
155
156 procExecutor.setFailureResultForNonce(nonceKey, "testProc", User.getCurrent(),
157 new IOException("test failure"));
158
159 final long procId = procExecutor.registerNonce(nonceKey);
160 ProcedureInfo result = procExecutor.getResult(procId);
161 ProcedureTestingUtility.assertProcFailed(result);
162 }
163
164 @Test(timeout=30000)
165 public void testConcurrentNonceRegistration() throws IOException {
166 testConcurrentNonceRegistration(true, 567, 44444);
167 }
168
169 @Test(timeout=30000)
170 public void testConcurrentNonceRegistrationWithRollback() throws IOException {
171 testConcurrentNonceRegistration(false, 890, 55555);
172 }
173
174 private void testConcurrentNonceRegistration(final boolean submitProcedure,
175 final long nonceGroup, final long nonce) throws IOException {
176
177 final NonceKey nonceKey = procExecutor.createNonceKey(nonceGroup, nonce);
178
179 final AtomicReference<Throwable> t1Exception = new AtomicReference();
180 final AtomicReference<Throwable> t2Exception = new AtomicReference();
181
182 final CountDownLatch t1NonceRegisteredLatch = new CountDownLatch(1);
183 final CountDownLatch t2BeforeNonceRegisteredLatch = new CountDownLatch(1);
184 final Thread[] threads = new Thread[2];
185 threads[0] = new Thread() {
186 @Override
187 public void run() {
188 try {
189
190 assertFalse("unexpected already registered nonce",
191 procExecutor.registerNonce(nonceKey) >= 0);
192 t1NonceRegisteredLatch.countDown();
193
194
195 t2BeforeNonceRegisteredLatch.await();
196 Threads.sleep(1000);
197
198 if (submitProcedure) {
199 CountDownLatch latch = new CountDownLatch(1);
200 TestSingleStepProcedure proc = new TestSingleStepProcedure();
201 procEnv.setWaitLatch(latch);
202
203 procExecutor.submitProcedure(proc, nonceKey);
204 Threads.sleep(100);
205
206
207 latch.countDown();
208 } else {
209 procExecutor.unregisterNonceIfProcedureWasNotSubmitted(nonceKey);
210 }
211 } catch (Throwable e) {
212 t1Exception.set(e);
213 } finally {
214 t1NonceRegisteredLatch.countDown();
215 t2BeforeNonceRegisteredLatch.countDown();
216 }
217 }
218 };
219
220 threads[1] = new Thread() {
221 @Override
222 public void run() {
223 try {
224
225 t1NonceRegisteredLatch.await();
226
227
228 t2BeforeNonceRegisteredLatch.countDown();
229 assertFalse("unexpected non registered nonce",
230 procExecutor.registerNonce(nonceKey) < 0);
231 } catch (Throwable e) {
232 t2Exception.set(e);
233 } finally {
234 t1NonceRegisteredLatch.countDown();
235 t2BeforeNonceRegisteredLatch.countDown();
236 }
237 }
238 };
239
240 for (int i = 0; i < threads.length; ++i) threads[i].start();
241 for (int i = 0; i < threads.length; ++i) Threads.shutdown(threads[i]);
242 ProcedureTestingUtility.waitNoProcedureRunning(procExecutor);
243 assertEquals(null, t1Exception.get());
244 assertEquals(null, t2Exception.get());
245 }
246
247 public static class TestSingleStepProcedure extends SequentialProcedure<TestProcEnv> {
248 private int step = 0;
249
250 public TestSingleStepProcedure() { }
251
252 @Override
253 protected Procedure[] execute(TestProcEnv env) throws InterruptedException {
254 step++;
255 env.waitOnLatch();
256 LOG.debug("execute procedure " + this + " step=" + step);
257 step++;
258 setResult(Bytes.toBytes(step));
259 return null;
260 }
261
262 @Override
263 protected void rollback(TestProcEnv env) { }
264
265 @Override
266 protected boolean abort(TestProcEnv env) { return true; }
267 }
268
269 private static class TestProcEnv {
270 private CountDownLatch latch = null;
271
272
273
274
275 public void setWaitLatch(CountDownLatch latch) {
276 this.latch = latch;
277 }
278
279 public void waitOnLatch() throws InterruptedException {
280 if (latch != null) {
281 latch.await();
282 }
283 }
284 }
285 }