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