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.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.Set;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FileSystem;
31 import org.apache.hadoop.fs.Path;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.ProcedureInfo;
34 import org.apache.hadoop.hbase.exceptions.IllegalArgumentIOException;
35 import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
36 import org.apache.hadoop.hbase.io.util.StreamUtils;
37 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore;
38 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore.ProcedureIterator;
39 import org.apache.hadoop.hbase.procedure2.store.NoopProcedureStore;
40 import org.apache.hadoop.hbase.procedure2.store.wal.WALProcedureStore;
41 import org.apache.hadoop.hbase.protobuf.generated.ErrorHandlingProtos.ForeignExceptionMessage;
42 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos.ProcedureState;
43 import org.apache.hadoop.hbase.util.NonceKey;
44 import org.apache.hadoop.hbase.util.Threads;
45
46 import static org.junit.Assert.assertEquals;
47 import static org.junit.Assert.assertFalse;
48 import static org.junit.Assert.assertTrue;
49
50 public class ProcedureTestingUtility {
51 private static final Log LOG = LogFactory.getLog(ProcedureTestingUtility.class);
52
53 private ProcedureTestingUtility() {
54 }
55
56 public static ProcedureStore createStore(final Configuration conf, final Path dir)
57 throws IOException {
58 return createWalStore(conf, dir);
59 }
60
61 public static WALProcedureStore createWalStore(final Configuration conf, final Path dir)
62 throws IOException {
63 return new WALProcedureStore(conf, dir, new WALProcedureStore.LeaseRecovery() {
64 @Override
65 public void recoverFileLease(FileSystem fs, Path path) throws IOException {
66
67 }
68 });
69 }
70
71 public static <TEnv> void restart(ProcedureExecutor<TEnv> procExecutor)
72 throws Exception {
73 restart(procExecutor, null, true);
74 }
75
76 public static <TEnv> void restart(ProcedureExecutor<TEnv> procExecutor,
77 Runnable beforeStartAction, boolean failOnCorrupted) throws Exception {
78 ProcedureStore procStore = procExecutor.getStore();
79 int storeThreads = procExecutor.getNumThreads();
80 int execThreads = procExecutor.getNumThreads();
81
82 procExecutor.stop();
83 procExecutor.join();
84 procStore.stop(false);
85
86 if (beforeStartAction != null) {
87 beforeStartAction.run();
88 }
89
90 procStore.start(storeThreads);
91 procExecutor.start(execThreads, failOnCorrupted);
92 }
93
94 public static void storeRestart(ProcedureStore procStore, ProcedureStore.ProcedureLoader loader)
95 throws Exception {
96 procStore.stop(false);
97 procStore.start(procStore.getNumThreads());
98 procStore.recoverLease();
99 procStore.load(loader);
100 }
101
102 public static void storeRestartAndAssert(ProcedureStore procStore, long maxProcId,
103 long runnableCount, int completedCount, int corruptedCount) throws Exception {
104 final LoadCounter loader = new LoadCounter();
105 storeRestart(procStore, loader);
106 assertEquals(maxProcId, loader.getMaxProcId());
107 assertEquals(runnableCount, loader.getRunnableCount());
108 assertEquals(completedCount, loader.getCompletedCount());
109 assertEquals(corruptedCount, loader.getCorruptedCount());
110 }
111
112 public static <TEnv> void setKillBeforeStoreUpdate(ProcedureExecutor<TEnv> procExecutor,
113 boolean value) {
114 if (procExecutor.testing == null) {
115 procExecutor.testing = new ProcedureExecutor.Testing();
116 }
117 procExecutor.testing.killBeforeStoreUpdate = value;
118 LOG.warn("Set Kill before store update to: " + procExecutor.testing.killBeforeStoreUpdate);
119 }
120
121 public static <TEnv> void setToggleKillBeforeStoreUpdate(ProcedureExecutor<TEnv> procExecutor,
122 boolean value) {
123 if (procExecutor.testing == null) {
124 procExecutor.testing = new ProcedureExecutor.Testing();
125 }
126 procExecutor.testing.toggleKillBeforeStoreUpdate = value;
127 }
128
129 public static <TEnv> void toggleKillBeforeStoreUpdate(ProcedureExecutor<TEnv> procExecutor) {
130 if (procExecutor.testing == null) {
131 procExecutor.testing = new ProcedureExecutor.Testing();
132 }
133 procExecutor.testing.killBeforeStoreUpdate = !procExecutor.testing.killBeforeStoreUpdate;
134 LOG.warn("Set Kill before store update to: " + procExecutor.testing.killBeforeStoreUpdate);
135 }
136
137 public static <TEnv> void setKillAndToggleBeforeStoreUpdate(ProcedureExecutor<TEnv> procExecutor,
138 boolean value) {
139 ProcedureTestingUtility.setKillBeforeStoreUpdate(procExecutor, value);
140 ProcedureTestingUtility.setToggleKillBeforeStoreUpdate(procExecutor, value);
141 }
142
143 public static <TEnv> long submitAndWait(Configuration conf, TEnv env, Procedure<TEnv> proc)
144 throws IOException {
145 NoopProcedureStore procStore = new NoopProcedureStore();
146 ProcedureExecutor<TEnv> procExecutor = new ProcedureExecutor<TEnv>(conf, env, procStore);
147 procStore.start(1);
148 procExecutor.start(1, false);
149 try {
150 return submitAndWait(procExecutor, proc, HConstants.NO_NONCE, HConstants.NO_NONCE);
151 } finally {
152 procStore.stop(false);
153 procExecutor.stop();
154 }
155 }
156
157 public static <TEnv> long submitAndWait(ProcedureExecutor<TEnv> procExecutor, Procedure proc) {
158 return submitAndWait(procExecutor, proc, HConstants.NO_NONCE, HConstants.NO_NONCE);
159 }
160
161 public static <TEnv> long submitAndWait(ProcedureExecutor<TEnv> procExecutor, Procedure proc,
162 final long nonceGroup, final long nonce) {
163 long procId = submitProcedure(procExecutor, proc, nonceGroup, nonce);
164 waitProcedure(procExecutor, procId);
165 return procId;
166 }
167
168 public static <TEnv> long submitProcedure(ProcedureExecutor<TEnv> procExecutor, Procedure proc,
169 final long nonceGroup, final long nonce) {
170 final NonceKey nonceKey = procExecutor.createNonceKey(nonceGroup, nonce);
171 long procId = procExecutor.registerNonce(nonceKey);
172 assertFalse(procId >= 0);
173 return procExecutor.submitProcedure(proc, nonceKey);
174 }
175
176 public static <TEnv> void waitProcedure(ProcedureExecutor<TEnv> procExecutor, Procedure proc) {
177 while (proc.getState() == ProcedureState.INITIALIZING) {
178 Threads.sleepWithoutInterrupt(250);
179 }
180 waitProcedure(procExecutor, proc.getProcId());
181 }
182
183 public static <TEnv> void waitProcedure(ProcedureExecutor<TEnv> procExecutor, long procId) {
184 while (!procExecutor.isFinished(procId) && procExecutor.isRunning()) {
185 Threads.sleepWithoutInterrupt(250);
186 }
187 }
188
189 public static <TEnv> void waitNoProcedureRunning(ProcedureExecutor<TEnv> procExecutor) {
190 int stableRuns = 0;
191 while (stableRuns < 10) {
192 if (procExecutor.getActiveExecutorCount() > 0 || procExecutor.getRunnableSet().size() > 0) {
193 stableRuns = 0;
194 Threads.sleepWithoutInterrupt(100);
195 } else {
196 stableRuns++;
197 Threads.sleepWithoutInterrupt(25);
198 }
199 }
200 }
201
202 public static <TEnv> void assertProcNotYetCompleted(ProcedureExecutor<TEnv> procExecutor,
203 long procId) {
204 assertFalse("expected a running proc", procExecutor.isFinished(procId));
205 assertEquals(null, procExecutor.getResult(procId));
206 }
207
208 public static <TEnv> void assertProcNotFailed(ProcedureExecutor<TEnv> procExecutor,
209 long procId) {
210 ProcedureInfo result = procExecutor.getResult(procId);
211 assertTrue("expected procedure result", result != null);
212 assertProcNotFailed(result);
213 }
214
215 public static void assertProcNotFailed(final ProcedureInfo result) {
216 ForeignExceptionMessage exception = result.getForeignExceptionMessage();
217 String msg = exception != null ? result.getExceptionFullMessage() : "no exception found";
218 assertFalse(msg, result.isFailed());
219 }
220
221 public static Throwable assertProcFailed(final ProcedureInfo result) {
222 assertEquals(true, result.isFailed());
223 LOG.info("procId=" + result.getProcId() + " exception: " + result.getException().getMessage());
224 return getExceptionCause(result);
225 }
226
227 public static void assertIsAbortException(final ProcedureInfo result) {
228 assertEquals(true, result.isFailed());
229 LOG.info(result.getExceptionFullMessage());
230 Throwable cause = getExceptionCause(result);
231 assertTrue("expected abort exception, got " + cause,
232 cause instanceof ProcedureAbortedException);
233 }
234
235 public static void assertIsTimeoutException(final ProcedureInfo result) {
236 assertEquals(true, result.isFailed());
237 LOG.info(result.getExceptionFullMessage());
238 Throwable cause = getExceptionCause(result);
239 assertTrue("expected TimeoutIOException, got " + cause, cause instanceof TimeoutIOException);
240 }
241
242 public static void assertIsIllegalArgumentException(final ProcedureInfo result) {
243 assertEquals(true, result.isFailed());
244 LOG.info(result.getExceptionFullMessage());
245 Throwable cause = ProcedureTestingUtility.getExceptionCause(result);
246 assertTrue("expected IllegalArgumentIOException, got " + cause,
247 cause instanceof IllegalArgumentIOException);
248 }
249
250 public static Throwable getExceptionCause(final ProcedureInfo procInfo) {
251 assert procInfo.getForeignExceptionMessage() != null;
252 return RemoteProcedureException.fromProto(procInfo.getForeignExceptionMessage()).getCause();
253 }
254
255 public static class TestProcedure extends Procedure<Void> {
256 private byte[] data = null;
257
258 public TestProcedure() {}
259
260 public TestProcedure(long procId) {
261 this(procId, 0);
262 }
263
264 public TestProcedure(long procId, long parentId) {
265 this(procId, parentId, null);
266 }
267
268 public TestProcedure(long procId, long parentId, byte[] data) {
269 setData(data);
270 setProcId(procId);
271 if (parentId > 0) {
272 setParentProcId(parentId);
273 }
274 }
275
276 public void addStackId(final int index) {
277 addStackIndex(index);
278 }
279
280 public void setFinishedState() {
281 setState(ProcedureState.FINISHED);
282 }
283
284 public void setData(final byte[] data) {
285 this.data = data;
286 }
287
288 @Override
289 protected Procedure[] execute(Void env) { return null; }
290
291 @Override
292 protected void rollback(Void env) { }
293
294 @Override
295 protected boolean abort(Void env) { return false; }
296
297 @Override
298 protected void serializeStateData(final OutputStream stream) throws IOException {
299 StreamUtils.writeRawVInt32(stream, data != null ? data.length : 0);
300 if (data != null) stream.write(data);
301 }
302
303 @Override
304 protected void deserializeStateData(final InputStream stream) throws IOException {
305 int len = StreamUtils.readRawVarint32(stream);
306 if (len > 0) {
307 data = new byte[len];
308 stream.read(data);
309 } else {
310 data = null;
311 }
312 }
313 }
314
315 public static class LoadCounter implements ProcedureStore.ProcedureLoader {
316 private final ArrayList<Procedure> corrupted = new ArrayList<Procedure>();
317 private final ArrayList<ProcedureInfo> completed = new ArrayList<ProcedureInfo>();
318 private final ArrayList<Procedure> runnable = new ArrayList<Procedure>();
319
320 private Set<Long> procIds;
321 private long maxProcId = 0;
322
323 public LoadCounter() {
324 this(null);
325 }
326
327 public LoadCounter(final Set<Long> procIds) {
328 this.procIds = procIds;
329 }
330
331 public void reset() {
332 reset(null);
333 }
334
335 public void reset(final Set<Long> procIds) {
336 corrupted.clear();
337 completed.clear();
338 runnable.clear();
339 this.procIds = procIds;
340 this.maxProcId = 0;
341 }
342
343 public long getMaxProcId() {
344 return maxProcId;
345 }
346
347 public ArrayList<Procedure> getRunnables() {
348 return runnable;
349 }
350
351 public int getRunnableCount() {
352 return runnable.size();
353 }
354
355 public ArrayList<ProcedureInfo> getCompleted() {
356 return completed;
357 }
358
359 public int getCompletedCount() {
360 return completed.size();
361 }
362
363 public int getLoadedCount() {
364 return runnable.size() + completed.size();
365 }
366
367 public ArrayList<Procedure> getCorrupted() {
368 return corrupted;
369 }
370
371 public int getCorruptedCount() {
372 return corrupted.size();
373 }
374
375 @Override
376 public void setMaxProcId(long maxProcId) {
377 this.maxProcId = maxProcId;
378 }
379
380 @Override
381 public void load(ProcedureIterator procIter) throws IOException {
382 while (procIter.hasNext()) {
383 long procId;
384 if (procIter.isNextCompleted()) {
385 ProcedureInfo proc = procIter.nextAsProcedureInfo();
386 procId = proc.getProcId();
387 LOG.debug("loading completed procId=" + procId + ": " + proc);
388 completed.add(proc);
389 } else {
390 Procedure proc = procIter.nextAsProcedure();
391 procId = proc.getProcId();
392 LOG.debug("loading runnable procId=" + procId + ": " + proc);
393 runnable.add(proc);
394 }
395 if (procIds != null) {
396 assertTrue("procId=" + procId + " unexpected", procIds.contains(procId));
397 }
398 }
399 }
400
401 @Override
402 public void handleCorrupted(ProcedureIterator procIter) throws IOException {
403 while (procIter.hasNext()) {
404 Procedure proc = procIter.nextAsProcedure();
405 LOG.debug("corrupted procId=" + proc.getProcId() + ": " + proc);
406 corrupted.add(proc);
407 }
408 }
409 }
410 }