1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master.procedure;
20
21 import java.io.IOException;
22 import java.util.concurrent.atomic.AtomicInteger;
23 import java.util.List;
24 import java.util.TreeSet;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.fs.FileSystem;
29 import org.apache.hadoop.fs.Path;
30 import org.apache.hadoop.hbase.HColumnDescriptor;
31 import org.apache.hadoop.hbase.HRegionInfo;
32 import org.apache.hadoop.hbase.HRegionLocation;
33 import org.apache.hadoop.hbase.HTableDescriptor;
34 import org.apache.hadoop.hbase.MetaTableAccessor;
35 import org.apache.hadoop.hbase.RegionLocations;
36 import org.apache.hadoop.hbase.ServerName;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.TableStateManager;
39 import org.apache.hadoop.hbase.client.BufferedMutator;
40 import org.apache.hadoop.hbase.client.Connection;
41 import org.apache.hadoop.hbase.client.Durability;
42 import org.apache.hadoop.hbase.client.Put;
43 import org.apache.hadoop.hbase.client.MetaScanner;
44 import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
45 import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
46 import org.apache.hadoop.hbase.client.Result;
47 import org.apache.hadoop.hbase.master.HMaster;
48 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
49 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
50 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
51 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
52 import org.apache.hadoop.hbase.util.Bytes;
53 import org.apache.hadoop.hbase.util.FSUtils;
54 import org.apache.hadoop.hbase.util.MD5Hash;
55
56 import static org.junit.Assert.assertEquals;
57 import static org.junit.Assert.assertFalse;
58 import static org.junit.Assert.assertTrue;
59
60 public class MasterProcedureTestingUtility {
61 private static final Log LOG = LogFactory.getLog(MasterProcedureTestingUtility.class);
62
63 private MasterProcedureTestingUtility() {
64 }
65
66 public static HTableDescriptor createHTD(final TableName tableName, final String... family) {
67 HTableDescriptor htd = new HTableDescriptor(tableName);
68 for (int i = 0; i < family.length; ++i) {
69 htd.addFamily(new HColumnDescriptor(family[i]));
70 }
71 return htd;
72 }
73
74 public static HRegionInfo[] createTable(final ProcedureExecutor<MasterProcedureEnv> procExec,
75 final TableName tableName, final byte[][] splitKeys, String... family) throws IOException {
76 HTableDescriptor htd = createHTD(tableName, family);
77 return createTable(procExec, htd, splitKeys);
78 }
79
80 public static HRegionInfo[] createTable(final ProcedureExecutor<MasterProcedureEnv> procExec,
81 HTableDescriptor htd, final byte[][] splitKeys) throws IOException {
82 HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
83 long procId = ProcedureTestingUtility.submitAndWait(procExec,
84 new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
85 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId));
86 return regions;
87 }
88
89 public static void validateTableCreation(final HMaster master, final TableName tableName,
90 final HRegionInfo[] regions, String... family) throws IOException {
91 validateTableCreation(master, tableName, regions, true, family);
92 }
93
94 public static void validateTableCreation(final HMaster master, final TableName tableName,
95 final HRegionInfo[] regions, boolean hasFamilyDirs, String... family) throws IOException {
96
97 final FileSystem fs = master.getMasterFileSystem().getFileSystem();
98 final Path tableDir = FSUtils.getTableDir(master.getMasterFileSystem().getRootDir(), tableName);
99 assertTrue(fs.exists(tableDir));
100 FSUtils.logFileSystemState(fs, tableDir, LOG);
101 List<Path> allRegionDirs = FSUtils.getRegionDirs(fs, tableDir);
102 for (int i = 0; i < regions.length; ++i) {
103 Path regionDir = new Path(tableDir, regions[i].getEncodedName());
104 assertTrue(regions[i] + " region dir does not exist", fs.exists(regionDir));
105 assertTrue(allRegionDirs.remove(regionDir));
106 List<Path> allFamilyDirs = FSUtils.getFamilyDirs(fs, regionDir);
107 for (int j = 0; j < family.length; ++j) {
108 final Path familyDir = new Path(regionDir, family[j]);
109 if (hasFamilyDirs) {
110 assertTrue(family[j] + " family dir does not exist", fs.exists(familyDir));
111 assertTrue(allFamilyDirs.remove(familyDir));
112 } else {
113
114 if (!fs.exists(familyDir)) {
115 LOG.warn(family[j] + " family dir does not exist");
116 }
117 allFamilyDirs.remove(familyDir);
118 }
119 }
120 assertTrue("found extraneous families: " + allFamilyDirs, allFamilyDirs.isEmpty());
121 }
122 assertTrue("found extraneous regions: " + allRegionDirs, allRegionDirs.isEmpty());
123
124
125 assertTrue(MetaTableAccessor.tableExists(master.getConnection(), tableName));
126 assertEquals(regions.length, countMetaRegions(master, tableName));
127
128
129 HTableDescriptor htd = master.getTableDescriptors().get(tableName);
130 assertTrue("table descriptor not found", htd != null);
131 for (int i = 0; i < family.length; ++i) {
132 assertTrue("family not found " + family[i], htd.getFamily(Bytes.toBytes(family[i])) != null);
133 }
134 assertEquals(family.length, htd.getFamilies().size());
135 }
136
137 public static void validateTableDeletion(final HMaster master, final TableName tableName,
138 final HRegionInfo[] regions, String... family) throws IOException {
139
140 final FileSystem fs = master.getMasterFileSystem().getFileSystem();
141 final Path tableDir = FSUtils.getTableDir(master.getMasterFileSystem().getRootDir(), tableName);
142 assertFalse(fs.exists(tableDir));
143
144
145 assertFalse(MetaTableAccessor.tableExists(master.getConnection(), tableName));
146 assertEquals(0, countMetaRegions(master, tableName));
147
148
149 assertTrue("found htd of deleted table",
150 master.getTableDescriptors().get(tableName) == null);
151 }
152
153 private static int countMetaRegions(final HMaster master, final TableName tableName)
154 throws IOException {
155 final AtomicInteger actualRegCount = new AtomicInteger(0);
156 final MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
157 @Override
158 public boolean processRow(Result rowResult) throws IOException {
159 RegionLocations list = MetaTableAccessor.getRegionLocations(rowResult);
160 if (list == null) {
161 LOG.warn("No serialized HRegionInfo in " + rowResult);
162 return true;
163 }
164 HRegionLocation l = list.getRegionLocation();
165 if (l == null) {
166 return true;
167 }
168 if (!l.getRegionInfo().getTable().equals(tableName)) {
169 return false;
170 }
171 if (l.getRegionInfo().isOffline() || l.getRegionInfo().isSplit()) return true;
172 HRegionLocation[] locations = list.getRegionLocations();
173 for (HRegionLocation location : locations) {
174 if (location == null) continue;
175 ServerName serverName = location.getServerName();
176
177 if (serverName != null && serverName.getHostAndPort() != null) {
178 actualRegCount.incrementAndGet();
179 }
180 }
181 return true;
182 }
183 };
184 MetaScanner.metaScan(master.getConnection(), visitor, tableName);
185 return actualRegCount.get();
186 }
187
188 public static void validateTableIsEnabled(final HMaster master, final TableName tableName)
189 throws IOException {
190 TableStateManager tsm = master.getAssignmentManager().getTableStateManager();
191 assertTrue(tsm.isTableState(tableName, ZooKeeperProtos.Table.State.ENABLED));
192 }
193
194 public static void validateTableIsDisabled(final HMaster master, final TableName tableName)
195 throws IOException {
196 TableStateManager tsm = master.getAssignmentManager().getTableStateManager();
197 assertTrue(tsm.isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED));
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public static <TState> void testRecoveryAndDoubleExecution(
216 final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId,
217 final int numSteps, final TState[] states) throws Exception {
218 ProcedureTestingUtility.waitProcedure(procExec, procId);
219 assertEquals(false, procExec.isRunning());
220 for (int i = 0; i < numSteps; ++i) {
221 LOG.info("Restart "+ i +" exec state: " + states[i]);
222 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
223 ProcedureTestingUtility.restart(procExec);
224 ProcedureTestingUtility.waitProcedure(procExec, procId);
225 }
226 assertEquals(true, procExec.isRunning());
227 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 public static <TState> void testRecoveryAndDoubleExecution(
247 final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId)
248 throws Exception {
249 ProcedureTestingUtility.waitProcedure(procExec, procId);
250 assertEquals(false, procExec.isRunning());
251 while (!procExec.isFinished(procId)) {
252 ProcedureTestingUtility.restart(procExec);
253 ProcedureTestingUtility.waitProcedure(procExec, procId);
254 }
255 assertEquals(true, procExec.isRunning());
256 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
257 }
258
259 public static <TState> void testRollbackAndDoubleExecution(
260 final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId,
261 final int lastStep, final TState[] states) throws Exception {
262 ProcedureTestingUtility.waitProcedure(procExec, procId);
263
264
265
266
267
268 for (int i = 0; i < lastStep; ++i) {
269 LOG.info("Restart "+ i +" exec state: " + states[i]);
270 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
271 ProcedureTestingUtility.restart(procExec);
272 ProcedureTestingUtility.waitProcedure(procExec, procId);
273 }
274
275
276
277
278
279 InjectAbortOnLoadListener abortListener = new InjectAbortOnLoadListener(procExec);
280 procExec.registerListener(abortListener);
281 try {
282 for (int i = lastStep + 1; i >= 0; --i) {
283 LOG.info("Restart " + i +" rollback state: "+ states[i]);
284 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
285 ProcedureTestingUtility.restart(procExec);
286 ProcedureTestingUtility.waitProcedure(procExec, procId);
287 }
288 } finally {
289 assertTrue(procExec.unregisterListener(abortListener));
290 }
291
292 ProcedureTestingUtility.assertIsAbortException(procExec.getResult(procId));
293 }
294
295 public static <TState> void testRollbackAndDoubleExecutionAfterPONR(
296 final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId,
297 final int lastStep, final TState[] states) throws Exception {
298 ProcedureTestingUtility.waitProcedure(procExec, procId);
299
300
301
302
303
304 for (int i = 0; i < lastStep; ++i) {
305 LOG.info("Restart "+ i +" exec state: " + states[i]);
306 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
307 ProcedureTestingUtility.restart(procExec);
308 ProcedureTestingUtility.waitProcedure(procExec, procId);
309 }
310
311
312 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
313 InjectAbortOnLoadListener abortListener = new InjectAbortOnLoadListener(procExec);
314 procExec.registerListener(abortListener);
315 try {
316 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
317 ProcedureTestingUtility.restart(procExec);
318 LOG.info("Restart and execute");
319 ProcedureTestingUtility.waitProcedure(procExec, procId);
320 } finally {
321 assertTrue(procExec.unregisterListener(abortListener));
322 }
323
324 assertEquals(true, procExec.isRunning());
325 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
326 }
327
328 public static <TState> void testRollbackRetriableFailure(
329 final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId,
330 final int lastStep, final TState[] states) throws Exception {
331 ProcedureTestingUtility.waitProcedure(procExec, procId);
332
333
334
335
336
337 for (int i = 0; i < lastStep; ++i) {
338 LOG.info("Restart "+ i +" exec state: " + states[i]);
339 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
340 ProcedureTestingUtility.restart(procExec);
341 ProcedureTestingUtility.waitProcedure(procExec, procId);
342 }
343
344
345 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
346 InjectAbortOnLoadListener abortListener = new InjectAbortOnLoadListener(procExec);
347 procExec.registerListener(abortListener);
348 try {
349 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId);
350 ProcedureTestingUtility.restart(procExec);
351 LOG.info("Restart and rollback");
352 ProcedureTestingUtility.waitProcedure(procExec, procId);
353 } finally {
354 assertTrue(procExec.unregisterListener(abortListener));
355 }
356
357 ProcedureTestingUtility.assertIsAbortException(procExec.getResult(procId));
358 }
359
360 public static void testRestartWithAbort(ProcedureExecutor<MasterProcedureEnv> procExec,
361 long procId) throws Exception {
362 InjectAbortOnLoadListener abortListener = new InjectAbortOnLoadListener(procExec);
363 abortListener.addProcId(procId);
364 procExec.registerListener(abortListener);
365 try {
366 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
367 ProcedureTestingUtility.restart(procExec);
368 ProcedureTestingUtility.waitProcedure(procExec, procId);
369 } finally {
370 assertTrue(procExec.unregisterListener(abortListener));
371 }
372 }
373
374 public static void validateColumnFamilyAddition(final HMaster master, final TableName tableName,
375 final String family) throws IOException {
376 HTableDescriptor htd = master.getTableDescriptors().get(tableName);
377 assertTrue(htd != null);
378 assertTrue(htd.hasFamily(family.getBytes()));
379 }
380
381 public static void validateColumnFamilyDeletion(final HMaster master, final TableName tableName,
382 final String family) throws IOException {
383
384 HTableDescriptor htd = master.getTableDescriptors().get(tableName);
385 assertTrue(htd != null);
386 assertFalse(htd.hasFamily(family.getBytes()));
387
388
389 final FileSystem fs = master.getMasterFileSystem().getFileSystem();
390 final Path tableDir = FSUtils.getTableDir(master.getMasterFileSystem().getRootDir(), tableName);
391 for (Path regionDir: FSUtils.getRegionDirs(fs, tableDir)) {
392 final Path familyDir = new Path(regionDir, family);
393 assertFalse(family + " family dir should not exist", fs.exists(familyDir));
394 }
395 }
396
397 public static void validateColumnFamilyModification(final HMaster master,
398 final TableName tableName, final String family, HColumnDescriptor columnDescriptor)
399 throws IOException {
400 HTableDescriptor htd = master.getTableDescriptors().get(tableName);
401 assertTrue(htd != null);
402
403 HColumnDescriptor hcfd = htd.getFamily(family.getBytes());
404 assertTrue(hcfd.equals(columnDescriptor));
405 }
406
407 public static void loadData(final Connection connection, final TableName tableName,
408 int rows, final byte[][] splitKeys, final String... sfamilies) throws IOException {
409 byte[][] families = new byte[sfamilies.length][];
410 for (int i = 0; i < families.length; ++i) {
411 families[i] = Bytes.toBytes(sfamilies[i]);
412 }
413
414 BufferedMutator mutator = connection.getBufferedMutator(tableName);
415
416
417 assertTrue(rows >= splitKeys.length);
418 for (byte[] k: splitKeys) {
419 byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), k);
420 byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value)));
421 mutator.mutate(createPut(families, key, value));
422 rows--;
423 }
424
425
426 while (rows-- > 0) {
427 byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), Bytes.toBytes(rows));
428 byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value));
429 mutator.mutate(createPut(families, key, value));
430 }
431 mutator.flush();
432 }
433
434 private static Put createPut(final byte[][] families, final byte[] key, final byte[] value) {
435 byte[] q = Bytes.toBytes("q");
436 Put put = new Put(key);
437 put.setDurability(Durability.SKIP_WAL);
438 for (byte[] family: families) {
439 put.add(family, q, value);
440 }
441 return put;
442 }
443
444 public static long generateNonceGroup(final HMaster master) {
445 return master.getConnection().getNonceGenerator().getNonceGroup();
446 }
447
448 public static long generateNonce(final HMaster master) {
449 return master.getConnection().getNonceGenerator().newNonce();
450 }
451
452 public static class InjectAbortOnLoadListener
453 implements ProcedureExecutor.ProcedureExecutorListener {
454 private final ProcedureExecutor<MasterProcedureEnv> procExec;
455 private TreeSet<Long> procsToAbort = null;
456
457 public InjectAbortOnLoadListener(final ProcedureExecutor<MasterProcedureEnv> procExec) {
458 this.procExec = procExec;
459 }
460
461 public void addProcId(long procId) {
462 if (procsToAbort == null) {
463 procsToAbort = new TreeSet<Long>();
464 }
465 procsToAbort.add(procId);
466 }
467
468 @Override
469 public void procedureLoaded(long procId) {
470 if (procsToAbort != null && !procsToAbort.contains(procId)) {
471 return;
472 }
473 procExec.abort(procId);
474 }
475
476 @Override
477 public void procedureAdded(long procId) {
478
479 @Override
480 public void procedureFinished(long procId) {
481 }
482 }