1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.snapshot;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.HashSet;
31 import java.util.TreeSet;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.fs.FileStatus;
36 import org.apache.hadoop.fs.FileSystem;
37 import org.apache.hadoop.fs.Path;
38 import org.apache.hadoop.fs.PathFilter;
39 import org.apache.hadoop.hbase.HBaseTestingUtility;
40 import org.apache.hadoop.hbase.HColumnDescriptor;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.HRegionInfo;
43 import org.apache.hadoop.hbase.HTableDescriptor;
44 import org.apache.hadoop.hbase.TableName;
45 import org.apache.hadoop.hbase.TableNotEnabledException;
46 import org.apache.hadoop.hbase.client.Durability;
47 import org.apache.hadoop.hbase.client.HBaseAdmin;
48 import org.apache.hadoop.hbase.client.HTable;
49 import org.apache.hadoop.hbase.client.Put;
50 import org.apache.hadoop.hbase.io.HFileLink;
51 import org.apache.hadoop.hbase.master.HMaster;
52 import org.apache.hadoop.hbase.master.MasterFileSystem;
53 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
54 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
55 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
56 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
57 import org.apache.hadoop.hbase.regionserver.HRegion;
58 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
59 import org.apache.hadoop.hbase.regionserver.HRegionServer;
60 import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil;
61 import org.apache.hadoop.hbase.util.Bytes;
62 import org.apache.hadoop.hbase.util.FSTableDescriptors;
63 import org.apache.hadoop.hbase.util.FSUtils;
64 import org.apache.hadoop.hbase.util.FSVisitor;
65 import org.apache.hadoop.hbase.util.MD5Hash;
66 import org.junit.Assert;
67
68 import com.google.protobuf.ServiceException;
69
70
71
72
73 public class SnapshotTestingUtils {
74
75 private static final Log LOG = LogFactory.getLog(SnapshotTestingUtils.class);
76 private static byte[] KEYS = Bytes.toBytes("0123456789");
77
78
79
80
81
82
83
84 public static void assertNoSnapshots(HBaseAdmin admin) throws IOException {
85 assertEquals("Have some previous snapshots", 0, admin.listSnapshots()
86 .size());
87 }
88
89
90
91
92
93 public static List<SnapshotDescription> assertExistsMatchingSnapshot(
94 HBaseAdmin admin, String snapshotName, TableName tableName)
95 throws IOException {
96
97 List<SnapshotDescription> snapshots = admin.listSnapshots();
98
99 List<SnapshotDescription> returnedSnapshots = new ArrayList<SnapshotDescription>();
100 for (SnapshotDescription sd : snapshots) {
101 if (snapshotName.equals(sd.getName()) &&
102 tableName.equals(TableName.valueOf(sd.getTable()))) {
103 returnedSnapshots.add(sd);
104 }
105 }
106
107 Assert.assertTrue("No matching snapshots found.", returnedSnapshots.size()>0);
108 return returnedSnapshots;
109 }
110
111
112
113
114 public static void assertOneSnapshotThatMatches(HBaseAdmin admin,
115 SnapshotDescription snapshot) throws IOException {
116 assertOneSnapshotThatMatches(admin, snapshot.getName(),
117 TableName.valueOf(snapshot.getTable()));
118 }
119
120
121
122
123
124 public static List<SnapshotDescription> assertOneSnapshotThatMatches(
125 HBaseAdmin admin, String snapshotName, TableName tableName)
126 throws IOException {
127
128 List<SnapshotDescription> snapshots = admin.listSnapshots();
129
130 assertEquals("Should only have 1 snapshot", 1, snapshots.size());
131 assertEquals(snapshotName, snapshots.get(0).getName());
132 assertEquals(tableName, TableName.valueOf(snapshots.get(0).getTable()));
133
134 return snapshots;
135 }
136
137
138
139
140
141 public static List<SnapshotDescription> assertOneSnapshotThatMatches(
142 HBaseAdmin admin, byte[] snapshot, TableName tableName) throws IOException {
143 return assertOneSnapshotThatMatches(admin, Bytes.toString(snapshot),
144 tableName);
145 }
146
147
148
149
150
151 public static void confirmSnapshotValid(
152 SnapshotDescription snapshotDescriptor, TableName tableName,
153 byte[] testFamily, Path rootDir, HBaseAdmin admin, FileSystem fs,
154 boolean requireLogs, Path logsDir, Set<String> snapshotServers)
155 throws IOException {
156 ArrayList nonEmptyTestFamilies = new ArrayList(1);
157 nonEmptyTestFamilies.add(testFamily);
158 confirmSnapshotValid(snapshotDescriptor, tableName,
159 nonEmptyTestFamilies, null, rootDir, admin, fs, requireLogs,
160 logsDir, snapshotServers);
161 }
162
163
164
165
166 public static void confirmEmptySnapshotValid(
167 SnapshotDescription snapshotDescriptor, TableName tableName,
168 byte[] testFamily, Path rootDir, HBaseAdmin admin, FileSystem fs,
169 boolean requireLogs, Path logsDir, Set<String> snapshotServers)
170 throws IOException {
171 ArrayList emptyTestFamilies = new ArrayList(1);
172 emptyTestFamilies.add(testFamily);
173 confirmSnapshotValid(snapshotDescriptor, tableName,
174 null, emptyTestFamilies, rootDir, admin, fs, requireLogs,
175 logsDir, snapshotServers);
176 }
177
178
179
180
181
182
183
184 public static void confirmSnapshotValid(
185 SnapshotDescription snapshotDescriptor, TableName tableName,
186 List<byte[]> nonEmptyTestFamilies, List<byte[]> emptyTestFamilies,
187 Path rootDir, HBaseAdmin admin, FileSystem fs, boolean requireLogs,
188 Path logsDir, Set<String> snapshotServers) throws IOException {
189
190 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(
191 snapshotDescriptor, rootDir);
192 assertTrue(fs.exists(snapshotDir));
193
194
195 Path snapshotinfo = new Path(snapshotDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
196 assertTrue(fs.exists(snapshotinfo));
197
198
199 if (requireLogs) {
200 TakeSnapshotUtils.verifyAllLogsGotReferenced(fs, logsDir,
201 snapshotServers, snapshotDescriptor, new Path(snapshotDir,
202 HConstants.HREGION_LOGDIR_NAME));
203 }
204
205
206 HTableDescriptor desc = FSTableDescriptors.getTableDescriptorFromFs(fs, rootDir, tableName);
207 HTableDescriptor snapshotDesc = FSTableDescriptors.getTableDescriptorFromFs(fs, snapshotDir);
208 assertEquals(desc, snapshotDesc);
209
210
211 final Set<String> snapshotRegions = new HashSet<String>();
212 final Set<byte[]> snapshotFamilies = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
213 FSVisitor.visitTableStoreFiles(fs, snapshotDir, new FSVisitor.StoreFileVisitor() {
214 public void storeFile(final String region, final String family, final String hfileName)
215 throws IOException {
216 snapshotRegions.add(region);
217 snapshotFamilies.add(Bytes.toBytes(family));
218 }
219 });
220
221
222 if (nonEmptyTestFamilies != null) {
223 for (final byte[] familyName: nonEmptyTestFamilies) {
224 assertTrue(snapshotFamilies.contains(familyName));
225 }
226 }
227
228
229 if (emptyTestFamilies != null) {
230 for (final byte[] familyName: emptyTestFamilies) {
231 assertFalse(snapshotFamilies.contains(familyName));
232 }
233 }
234
235
236 if ((nonEmptyTestFamilies == null || nonEmptyTestFamilies.size() == 0) &&
237 (emptyTestFamilies != null && emptyTestFamilies.size() > 0)) {
238 assertEquals(0, snapshotRegions.size());
239 return;
240 }
241
242
243 List<HRegionInfo> regions = admin.getTableRegions(tableName);
244 assertEquals(regions.size(), snapshotRegions.size());
245
246
247 for (HRegionInfo info : regions) {
248 String regionName = info.getEncodedName();
249 assertTrue(snapshotRegions.contains(regionName));
250
251 Path regionDir = new Path(snapshotDir, regionName);
252 HRegionInfo snapshotRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
253 assertEquals(info, snapshotRegionInfo);
254 }
255 }
256
257
258
259
260
261
262
263
264
265
266 public static void waitForSnapshotToComplete(HMaster master,
267 SnapshotDescription snapshot, long sleep) throws ServiceException {
268 final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder()
269 .setSnapshot(snapshot).build();
270 IsSnapshotDoneResponse done = IsSnapshotDoneResponse.newBuilder()
271 .buildPartial();
272 while (!done.getDone()) {
273 done = master.isSnapshotDone(null, request);
274 try {
275 Thread.sleep(sleep);
276 } catch (InterruptedException e) {
277 throw new ServiceException(e);
278 }
279 }
280 }
281
282 public static void cleanupSnapshot(HBaseAdmin admin, byte[] tableName)
283 throws IOException {
284 SnapshotTestingUtils.cleanupSnapshot(admin, Bytes.toString(tableName));
285 }
286
287 public static void cleanupSnapshot(HBaseAdmin admin, String snapshotName)
288 throws IOException {
289
290 admin.deleteSnapshot(snapshotName);
291 assertNoSnapshots(admin);
292 }
293
294
295
296
297
298
299
300
301
302 public static void expectSnapshotDoneException(HMaster master,
303 IsSnapshotDoneRequest snapshot,
304 Class<? extends HBaseSnapshotException> clazz) {
305 try {
306 master.isSnapshotDone(null, snapshot);
307 Assert.fail("didn't fail to lookup a snapshot");
308 } catch (ServiceException se) {
309 try {
310 throw ProtobufUtil.getRemoteException(se);
311 } catch (HBaseSnapshotException e) {
312 assertEquals("Threw wrong snapshot exception!", clazz, e.getClass());
313 } catch (Throwable t) {
314 Assert.fail("Threw an unexpected exception:" + t);
315 }
316 }
317 }
318
319
320
321
322
323
324
325
326
327 public static Path[] listHFiles(final FileSystem fs, final Path tableDir)
328 throws IOException {
329 final ArrayList<Path> hfiles = new ArrayList<Path>();
330 FSVisitor.visitTableStoreFiles(fs, tableDir, new FSVisitor.StoreFileVisitor() {
331 public void storeFile(final String region, final String family, final String hfileName)
332 throws IOException {
333 hfiles.add(new Path(tableDir, new Path(region, new Path(family, hfileName))));
334 }
335 });
336 return hfiles.toArray(new Path[hfiles.size()]);
337 }
338
339
340
341
342
343
344 public static void createSnapshotAndValidate(HBaseAdmin admin,
345 TableName tableName, String familyName, String snapshotNameString,
346 Path rootDir, FileSystem fs, boolean onlineSnapshot)
347 throws Exception {
348 ArrayList<byte[]> nonEmptyFamilyNames = new ArrayList<byte[]>(1);
349 nonEmptyFamilyNames.add(Bytes.toBytes(familyName));
350 createSnapshotAndValidate(admin, tableName, nonEmptyFamilyNames,
351 snapshotNameString, rootDir, fs, onlineSnapshot);
352 }
353
354
355
356
357
358 public static void createSnapshotAndValidate(HBaseAdmin admin,
359 TableName tableName, List<byte[]> nonEmptyFamilyNames, List<byte[]> emptyFamilyNames,
360 String snapshotNameString, Path rootDir, FileSystem fs, boolean onlineSnapshot)
361 throws Exception {
362 if (!onlineSnapshot) {
363 try {
364 admin.disableTable(tableName);
365 } catch (TableNotEnabledException tne) {
366 LOG.info("In attempting to disable " + tableName + " it turns out that the this table is " +
367 "already disabled.");
368 }
369 }
370 admin.snapshot(snapshotNameString, tableName);
371
372 List<SnapshotDescription> snapshots = SnapshotTestingUtils.assertExistsMatchingSnapshot(admin,
373 snapshotNameString, tableName);
374 if (snapshots == null || snapshots.size() != 1) {
375 Assert.fail("Incorrect number of snapshots for table " + tableName);
376 }
377
378 SnapshotTestingUtils.confirmSnapshotValid(snapshots.get(0), tableName, nonEmptyFamilyNames,
379 emptyFamilyNames, rootDir, admin, fs, false,
380 new Path(rootDir, HConstants.HREGION_LOGDIR_NAME), null);
381 }
382
383
384
385
386
387
388
389
390
391 public static ArrayList corruptSnapshot(final HBaseTestingUtility util, final String snapshotName)
392 throws IOException {
393 final MasterFileSystem mfs = util.getHBaseCluster().getMaster().getMasterFileSystem();
394 final FileSystem fs = mfs.getFileSystem();
395
396 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName,
397 mfs.getRootDir());
398 SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
399 final TableName table = TableName.valueOf(snapshotDesc.getTable());
400
401 final ArrayList corruptedFiles = new ArrayList();
402 SnapshotReferenceUtil.visitTableStoreFiles(fs, snapshotDir, new FSVisitor.StoreFileVisitor() {
403 public void storeFile (final String region, final String family, final String hfile)
404 throws IOException {
405 HFileLink link = HFileLink.create(util.getConfiguration(), table, region, family, hfile);
406 if (corruptedFiles.size() % 2 == 0) {
407 fs.delete(link.getAvailablePath(fs));
408 corruptedFiles.add(hfile);
409 }
410 }
411 });
412
413 assertTrue(corruptedFiles.size() > 0);
414 return corruptedFiles;
415 }
416
417
418
419
420 public static void waitForTableToBeOnline(final HBaseTestingUtility util,
421 final TableName tableName)
422 throws IOException, InterruptedException {
423 HRegionServer rs = util.getRSForFirstRegionInTable(tableName);
424 List<HRegion> onlineRegions = rs.getOnlineRegions(tableName);
425 for (HRegion region : onlineRegions) {
426 region.waitForFlushesAndCompactions();
427 }
428 util.getHBaseAdmin().isTableAvailable(tableName);
429 }
430
431 public static void createTable(final HBaseTestingUtility util, final TableName tableName,
432 final byte[]... families) throws IOException, InterruptedException {
433 HTableDescriptor htd = new HTableDescriptor(tableName);
434 for (byte[] family: families) {
435 HColumnDescriptor hcd = new HColumnDescriptor(family);
436 htd.addFamily(hcd);
437 }
438 byte[][] splitKeys = new byte[KEYS.length-2][];
439 for (int i = 0; i < splitKeys.length; ++i) {
440 splitKeys[i] = new byte[] { KEYS[i+1] };
441 }
442 util.getHBaseAdmin().createTable(htd, splitKeys);
443 waitForTableToBeOnline(util, tableName);
444 assertEquals(KEYS.length-1, util.getHBaseAdmin().getTableRegions(tableName).size());
445 }
446
447 public static void loadData(final HBaseTestingUtility util, final TableName tableName, int rows,
448 byte[]... families) throws IOException, InterruptedException {
449 loadData(util, new HTable(util.getConfiguration(), tableName), rows, families);
450 }
451
452 public static void loadData(final HBaseTestingUtility util, final HTable table, int rows,
453 byte[]... families) throws IOException, InterruptedException {
454 table.setAutoFlush(false, true);
455
456
457 assertTrue(rows >= KEYS.length);
458 for (byte k0: KEYS) {
459 byte[] k = new byte[] { k0 };
460 byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), k);
461 byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value)));
462 putData(table, families, key, value);
463 rows--;
464 }
465
466
467 while (rows-- > 0) {
468 byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), Bytes.toBytes(rows));
469 byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value));
470 putData(table, families, key, value);
471 }
472 table.flushCommits();
473
474 waitForTableToBeOnline(util, table.getName());
475 }
476
477 private static void putData(final HTable table, final byte[][] families,
478 final byte[] key, final byte[] value) throws IOException {
479 byte[] q = Bytes.toBytes("q");
480 Put put = new Put(key);
481 put.setDurability(Durability.SKIP_WAL);
482 for (byte[] family: families) {
483 put.add(family, q, value);
484 }
485 table.put(put);
486 }
487
488 public static void deleteAllSnapshots(final HBaseAdmin admin)
489 throws IOException {
490
491 for (SnapshotDescription snapshot: admin.listSnapshots()) {
492 admin.deleteSnapshot(snapshot.getName());
493 }
494 SnapshotTestingUtils.assertNoSnapshots(admin);
495 }
496
497 public static void deleteArchiveDirectory(final HBaseTestingUtility util)
498 throws IOException {
499
500 MasterFileSystem mfs = util.getMiniHBaseCluster().getMaster().getMasterFileSystem();
501 Path archiveDir = new Path(mfs.getRootDir(), HConstants.HFILE_ARCHIVE_DIRECTORY);
502 mfs.getFileSystem().delete(archiveDir, true);
503 }
504
505 public static void verifyRowCount(final HBaseTestingUtility util, final TableName tableName,
506 long expectedRows) throws IOException {
507 HTable table = new HTable(util.getConfiguration(), tableName);
508 try {
509 assertEquals(expectedRows, util.countRows(table));
510 } finally {
511 table.close();
512 }
513 }
514 }