1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.master.cleaner;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import com.google.common.util.concurrent.Uninterruptibles;
26 import java.io.IOException;
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.concurrent.TimeUnit;
31 import java.util.regex.Pattern;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.conf.Configuration;
36 import org.apache.hadoop.fs.FileSystem;
37 import org.apache.hadoop.fs.Path;
38 import org.apache.hadoop.hbase.HBaseTestingUtility;
39 import org.apache.hadoop.hbase.HConstants;
40 import org.apache.hadoop.hbase.HTableDescriptor;
41 import org.apache.hadoop.hbase.Waiter.Predicate;
42 import org.apache.hadoop.hbase.client.Put;
43 import org.apache.hadoop.hbase.client.Table;
44 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
45 import org.apache.hadoop.hbase.testclassification.MediumTests;
46 import org.apache.hadoop.hbase.TableName;
47 import org.apache.hadoop.hbase.client.Admin;
48 import org.apache.hadoop.hbase.master.HMaster;
49 import org.apache.hadoop.hbase.master.snapshot.DisabledTableSnapshotHandler;
50 import org.apache.hadoop.hbase.master.snapshot.SnapshotHFileCleaner;
51 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
52 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteSnapshotRequest;
53 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest;
54 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsResponse;
55 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotCleanupEnabledRequest;
56 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotCleanupEnabledResponse;
57 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
58 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
59 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetSnapshotCleanupRequest;
60 import org.apache.hadoop.hbase.regionserver.CompactedHFilesDischarger;
61 import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy;
62 import org.apache.hadoop.hbase.regionserver.HRegion;
63 import org.apache.hadoop.hbase.regionserver.HRegionServer;
64 import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
65 import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil;
66 import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
67 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
68 import org.apache.hadoop.hbase.util.Bytes;
69 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
70 import org.apache.hadoop.hbase.util.FSUtils;
71 import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
72 import org.junit.After;
73 import org.junit.AfterClass;
74 import org.junit.Assert;
75 import org.junit.Before;
76 import org.junit.BeforeClass;
77 import org.junit.Test;
78 import org.junit.experimental.categories.Category;
79 import org.mockito.Mockito;
80
81 import com.google.common.collect.Lists;
82 import com.google.protobuf.ServiceException;
83
84
85
86
87 @Category(MediumTests.class)
88 public class TestSnapshotFromMaster {
89
90 private static final Log LOG = LogFactory.getLog(TestSnapshotFromMaster.class);
91 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
92 private static final int NUM_RS = 2;
93 private static Path rootDir;
94 private static FileSystem fs;
95 private static HMaster master;
96
97
98 private static Path archiveDir;
99 private static final byte[] TEST_FAM = Bytes.toBytes("fam");
100 private static final TableName TABLE_NAME =
101 TableName.valueOf("test");
102
103 private static final long cacheRefreshPeriod = 500;
104 private static final int blockingStoreFiles = 12;
105
106
107
108
109 @BeforeClass
110 public static void setupCluster() throws Exception {
111 setupConf(UTIL.getConfiguration());
112 UTIL.startMiniCluster(NUM_RS);
113 fs = UTIL.getDFSCluster().getFileSystem();
114 master = UTIL.getMiniHBaseCluster().getMaster();
115 rootDir = master.getMasterFileSystem().getRootDir();
116 archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
117 }
118
119 private static void setupConf(Configuration conf) {
120
121 conf.setInt("hbase.regionsever.info.port", -1);
122
123 conf.setInt("hbase.hregion.memstore.flush.size", 25000);
124
125
126 conf.setInt("hbase.hstore.compaction.min", 2);
127 conf.setInt("hbase.hstore.compactionThreshold", 5);
128
129 conf.setInt("hbase.hstore.blockingStoreFiles", blockingStoreFiles);
130
131 conf.set(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, "");
132 conf.set(HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS, "");
133
134 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
135 conf.setLong(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS, 3 * 1000L);
136 conf.setLong(SnapshotHFileCleaner.HFILE_CACHE_REFRESH_PERIOD_CONF_KEY, cacheRefreshPeriod);
137 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
138 ConstantSizeRegionSplitPolicy.class.getName());
139 conf.setInt("hbase.hfile.compactions.cleaner.interval", 20 * 1000);
140 conf.setInt("hbase.master.cleaner.snapshot.interval", 500);
141 }
142
143 @Before
144 public void setup() throws Exception {
145 UTIL.createTable(TABLE_NAME, TEST_FAM);
146 master.getSnapshotManager().setSnapshotHandlerForTesting(TABLE_NAME, null);
147 }
148
149 @After
150 public void tearDown() throws Exception {
151 UTIL.deleteTable(TABLE_NAME);
152 SnapshotTestingUtils.deleteAllSnapshots(UTIL.getHBaseAdmin());
153 SnapshotTestingUtils.deleteArchiveDirectory(UTIL);
154 }
155
156 @AfterClass
157 public static void cleanupTest() throws Exception {
158 try {
159 UTIL.shutdownMiniCluster();
160 } catch (Exception e) {
161
162 }
163 }
164
165
166
167
168
169
170
171
172
173
174 @Test(timeout = 300000)
175 public void testIsDoneContract() throws Exception {
176
177 IsSnapshotDoneRequest.Builder builder = IsSnapshotDoneRequest.newBuilder();
178
179 String snapshotName = "asyncExpectedFailureTest";
180
181
182 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(),
183 UnknownSnapshotException.class);
184
185
186 SnapshotDescription desc = SnapshotDescription.newBuilder()
187 .setName(snapshotName).setTable(TABLE_NAME.getNameAsString()).build();
188 builder.setSnapshot(desc);
189 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(),
190 UnknownSnapshotException.class);
191
192
193 DisabledTableSnapshotHandler mockHandler = Mockito.mock(DisabledTableSnapshotHandler.class);
194 Mockito.when(mockHandler.getException()).thenReturn(null);
195 Mockito.when(mockHandler.getSnapshot()).thenReturn(desc);
196 Mockito.when(mockHandler.isFinished()).thenReturn(new Boolean(true));
197 Mockito.when(mockHandler.getCompletionTimestamp())
198 .thenReturn(EnvironmentEdgeManager.currentTime());
199
200 master.getSnapshotManager()
201 .setSnapshotHandlerForTesting(TABLE_NAME, mockHandler);
202
203
204 builder = IsSnapshotDoneRequest.newBuilder();
205 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(),
206 UnknownSnapshotException.class);
207
208
209 builder.setSnapshot(desc);
210 IsSnapshotDoneResponse response =
211 master.getMasterRpcServices().isSnapshotDone(null, builder.build());
212 assertTrue("Snapshot didn't complete when it should have.", response.getDone());
213
214
215 builder.setSnapshot(SnapshotDescription.newBuilder().setName("Not A Snapshot").build());
216 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(),
217 UnknownSnapshotException.class);
218
219
220 snapshotName = "completed";
221 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
222 desc = desc.toBuilder().setName(snapshotName).build();
223 SnapshotDescriptionUtils.writeSnapshotInfo(desc, snapshotDir, fs);
224
225 builder.setSnapshot(desc);
226 response = master.getMasterRpcServices().isSnapshotDone(null, builder.build());
227 assertTrue("Completed, on-disk snapshot not found", response.getDone());
228 }
229
230 @Test(timeout = 300000)
231 public void testGetCompletedSnapshots() throws Exception {
232
233 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build();
234 GetCompletedSnapshotsResponse response =
235 master.getMasterRpcServices().getCompletedSnapshots(null, request);
236 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount());
237
238
239 String snapshotName = "completed";
240 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
241 SnapshotDescription snapshot = SnapshotDescription.newBuilder().setName(snapshotName).build();
242 SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, snapshotDir, fs);
243
244
245 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
246 assertEquals("Found unexpected number of snapshots", 1, response.getSnapshotsCount());
247 List<SnapshotDescription> snapshots = response.getSnapshotsList();
248 List<SnapshotDescription> expected = Lists.newArrayList(snapshot);
249 assertEquals("Returned snapshots don't match created snapshots", expected, snapshots);
250
251
252 snapshotName = "completed_two";
253 snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
254 snapshot = SnapshotDescription.newBuilder().setName(snapshotName).build();
255 SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, snapshotDir, fs);
256 expected.add(snapshot);
257
258
259 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
260 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount());
261 snapshots = response.getSnapshotsList();
262 assertEquals("Returned snapshots don't match created snapshots", expected, snapshots);
263 }
264
265 @Test(timeout = 300000)
266 public void testDeleteSnapshot() throws Exception {
267
268 String snapshotName = "completed";
269 SnapshotDescription snapshot = SnapshotDescription.newBuilder().setName(snapshotName).build();
270
271 DeleteSnapshotRequest request = DeleteSnapshotRequest.newBuilder().setSnapshot(snapshot)
272 .build();
273 try {
274 master.getMasterRpcServices().deleteSnapshot(null, request);
275 fail("Master didn't throw exception when attempting to delete snapshot that doesn't exist");
276 } catch (ServiceException e) {
277 LOG.debug("Correctly failed delete of non-existant snapshot:" + e.getMessage());
278 }
279
280
281 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
282 SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, snapshotDir, fs);
283
284
285 master.getMasterRpcServices().deleteSnapshot(null, request);
286 }
287
288 @Test
289 public void testGetCompletedSnapshotsWithCleanup() throws Exception {
290
291 SetSnapshotCleanupRequest setSnapshotCleanupRequest =
292 SetSnapshotCleanupRequest.newBuilder().setEnabled(true).build();
293 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest);
294
295
296 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build();
297 GetCompletedSnapshotsResponse response =
298 master.getMasterRpcServices().getCompletedSnapshots(null, request);
299 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount());
300
301
302 createSnapshotWithTtl("snapshot_01", 1L);
303 createSnapshotWithTtl("snapshot_02", 10L);
304
305
306 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
307 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount());
308
309
310 Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
311 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
312 assertEquals("Found unexpected number of snapshots", 1, response.getSnapshotsCount());
313 }
314
315 @Test
316 public void testGetCompletedSnapshotsWithoutCleanup() throws Exception {
317
318 SetSnapshotCleanupRequest setSnapshotCleanupRequest =
319 SetSnapshotCleanupRequest.newBuilder().setEnabled(false).build();
320 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest);
321
322
323 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build();
324 GetCompletedSnapshotsResponse response =
325 master.getMasterRpcServices().getCompletedSnapshots(null, request);
326 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount());
327
328
329 createSnapshotWithTtl("snapshot_02", 1L);
330 createSnapshotWithTtl("snapshot_03", 1L);
331
332
333 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
334 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount());
335
336
337 Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
338 response = master.getMasterRpcServices().getCompletedSnapshots(null, request);
339 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount());
340 }
341
342 @Test
343 public void testSnapshotCleanupStatus() throws Exception {
344
345 SetSnapshotCleanupRequest setSnapshotCleanupRequest =
346 SetSnapshotCleanupRequest.newBuilder().setEnabled(true).build();
347 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest);
348
349
350 IsSnapshotCleanupEnabledRequest isSnapshotCleanupEnabledRequest =
351 IsSnapshotCleanupEnabledRequest.newBuilder().build();
352 IsSnapshotCleanupEnabledResponse isSnapshotCleanupEnabledResponse =
353 master.getMasterRpcServices().isSnapshotCleanupEnabled(null,
354 isSnapshotCleanupEnabledRequest);
355 Assert.assertTrue(isSnapshotCleanupEnabledResponse.getEnabled());
356
357
358 setSnapshotCleanupRequest = SetSnapshotCleanupRequest.newBuilder()
359 .setEnabled(false).build();
360 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest);
361
362
363 isSnapshotCleanupEnabledRequest = IsSnapshotCleanupEnabledRequest
364 .newBuilder().build();
365 isSnapshotCleanupEnabledResponse =
366 master.getMasterRpcServices().isSnapshotCleanupEnabled(null,
367 isSnapshotCleanupEnabledRequest);
368 assertFalse(isSnapshotCleanupEnabledResponse.getEnabled());
369 }
370
371
372
373
374
375
376 @Test(timeout = 300000)
377 public void testSnapshotHFileArchiving() throws Exception {
378 Admin admin = UTIL.getHBaseAdmin();
379
380 SnapshotTestingUtils.assertNoSnapshots(admin);
381
382
383
384 UTIL.deleteTable(TABLE_NAME);
385 HTableDescriptor htd = new HTableDescriptor(TABLE_NAME);
386 htd.setCompactionEnabled(false);
387
388 UTIL.createTable(htd, new byte[][] { TEST_FAM }, null);
389
390
391 for (int i = 0; i < blockingStoreFiles / 2; i ++) {
392 UTIL.loadTable(UTIL.getConnection().getTable(TABLE_NAME), TEST_FAM);
393 UTIL.flush(TABLE_NAME);
394 }
395
396
397 admin.disableTable(TABLE_NAME);
398 htd.setCompactionEnabled(true);
399
400
401 String snapshotName = "snapshot";
402 byte[] snapshotNameBytes = Bytes.toBytes(snapshotName);
403 admin.snapshot(snapshotNameBytes, TABLE_NAME);
404
405 LOG.info("After snapshot File-System state");
406 FSUtils.logFileSystemState(fs, rootDir, LOG);
407
408
409 SnapshotTestingUtils.assertOneSnapshotThatMatches(admin, snapshotNameBytes, TABLE_NAME);
410
411
412 admin.modifyTable(TABLE_NAME, htd);
413
414
415 admin.enableTable(TABLE_NAME);
416
417
418 List<HRegion> regions = UTIL.getHBaseCluster().getRegions(TABLE_NAME);
419 for (HRegion region : regions) {
420 region.waitForFlushesAndCompactions();
421 region.compactStores();
422 }
423 List<RegionServerThread> regionServerThreads = UTIL.getMiniHBaseCluster()
424 .getRegionServerThreads();
425 HRegionServer hrs = null;
426 for (RegionServerThread rs : regionServerThreads) {
427 if (!rs.getRegionServer().getOnlineRegions(TABLE_NAME).isEmpty()) {
428 hrs = rs.getRegionServer();
429 break;
430 }
431 }
432 CompactedHFilesDischarger cleaner = new CompactedHFilesDischarger(100, null, hrs, false);
433 cleaner.chore();
434 LOG.info("After compaction File-System state");
435 FSUtils.logFileSystemState(fs, rootDir, LOG);
436
437
438 LOG.debug("Running hfile cleaners");
439 ensureHFileCleanersRun();
440 LOG.info("After cleaners File-System state: " + rootDir);
441 FSUtils.logFileSystemState(fs, rootDir, LOG);
442
443
444 Path snapshotTable = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
445 Set<String> snapshotHFiles = SnapshotReferenceUtil.getHFileNames(
446 UTIL.getConfiguration(), fs, snapshotTable);
447
448 LOG.debug("Have snapshot hfiles:");
449 for (String fileName : snapshotHFiles) {
450 LOG.debug(fileName);
451 }
452
453 Collection<String> archives = getHFiles(archiveDir, fs, TABLE_NAME);
454
455
456 Collection<String> hfiles = getHFiles(rootDir, fs, TABLE_NAME);
457
458
459 for (String fileName : snapshotHFiles) {
460 boolean exist = archives.contains(fileName) || hfiles.contains(fileName);
461 assertTrue("Archived hfiles " + archives
462 + " and table hfiles " + hfiles + " is missing snapshot file:" + fileName, exist);
463 }
464
465
466 admin.deleteSnapshot(snapshotNameBytes);
467 SnapshotTestingUtils.assertNoSnapshots(admin);
468
469
470
471 List<BaseHFileCleanerDelegate> delegates = UTIL.getMiniHBaseCluster().getMaster()
472 .getHFileCleaner().cleanersChain;
473 for (BaseHFileCleanerDelegate delegate: delegates) {
474 if (delegate instanceof SnapshotHFileCleaner) {
475 ((SnapshotHFileCleaner)delegate).getFileCacheForTesting().triggerCacheRefreshForTesting();
476 }
477 }
478
479 LOG.debug("Running hfile cleaners");
480 ensureHFileCleanersRun();
481 LOG.info("After delete snapshot cleaners run File-System state");
482 FSUtils.logFileSystemState(fs, rootDir, LOG);
483
484 archives = getHFiles(archiveDir, fs, TABLE_NAME);
485 assertEquals("Still have some hfiles in the archive, when their snapshot has been deleted.", 0,
486 archives.size());
487 }
488
489
490
491
492
493 private final Collection<String> getHFiles(Path dir, FileSystem fs, TableName tableName) throws IOException {
494 Path tableDir = FSUtils.getTableDir(dir, tableName);
495 return SnapshotTestingUtils.listHFileNames(fs, tableDir);
496 }
497
498
499
500
501 private static void ensureHFileCleanersRun() {
502 UTIL.getHBaseCluster().getMaster().getHFileCleaner().chore();
503 }
504
505 private SnapshotDescription createSnapshotWithTtl(final String snapshotName, final long ttl)
506 throws IOException {
507 SnapshotTestingUtils.SnapshotMock snapshotMock =
508 new SnapshotTestingUtils.SnapshotMock(UTIL.getConfiguration(), fs, rootDir);
509 SnapshotTestingUtils.SnapshotMock.SnapshotBuilder builder =
510 snapshotMock.createSnapshotV2(snapshotName, "test", 0, ttl);
511 builder.commit();
512 return builder.getSnapshotDescription();
513 }
514
515 @Test
516 public void testAsyncSnapshotWillNotBlockSnapshotHFileCleaner() throws Exception {
517
518 Table table = UTIL.getConnection().getTable(TABLE_NAME);
519 for (int i = 0; i < 10; i++) {
520 Put put = new Put(Bytes.toBytes(i)).addColumn(TEST_FAM, Bytes.toBytes("q"), Bytes.toBytes(i));
521 table.put(put);
522 }
523 final String snapshotName = "testAsyncSnapshotWillNotBlockSnapshotHFileCleaner01";
524 SnapshotDescription snapshot = SnapshotDescription.newBuilder().setName(snapshotName)
525 .setTable(TABLE_NAME.getNameAsString()).setType(SnapshotDescription.Type.FLUSH).build();
526 UTIL.getHBaseAdmin().takeSnapshotAsync(snapshot);
527 UTIL.waitFor(10 * 1000L, 200L, new Predicate<Exception>() {
528 @Override
529 public boolean evaluate() throws Exception {
530 return UTIL.getHBaseAdmin().listSnapshots(Pattern.compile(snapshotName)).size() == 1;
531 }
532 });
533 UTIL.waitFor(30000, new Predicate<Exception>() {
534 @Override
535 public boolean evaluate() throws Exception {
536 return !master.getSnapshotManager().isTakingAnySnapshot();
537 }
538 });
539 }
540 }