001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.junit.Assert.fail; 024 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028import java.util.regex.Pattern; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.fs.FileSystem; 031import org.apache.hadoop.fs.Path; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtil; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.TableNameTestRule; 037import org.apache.hadoop.hbase.TableNotFoundException; 038import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; 039import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy; 040import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 041import org.apache.hadoop.hbase.snapshot.SnapshotCreationException; 042import org.apache.hadoop.hbase.snapshot.SnapshotDoesNotExistException; 043import org.apache.hadoop.hbase.snapshot.SnapshotManifestV1; 044import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 045import org.apache.hadoop.hbase.testclassification.ClientTests; 046import org.apache.hadoop.hbase.testclassification.LargeTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.apache.hadoop.hbase.util.CommonFSUtils; 049import org.junit.After; 050import org.junit.AfterClass; 051import org.junit.Before; 052import org.junit.BeforeClass; 053import org.junit.ClassRule; 054import org.junit.Rule; 055import org.junit.Test; 056import org.junit.experimental.categories.Category; 057import org.junit.runner.RunWith; 058import org.junit.runners.Parameterized; 059import org.junit.runners.Parameterized.Parameter; 060import org.junit.runners.Parameterized.Parameters; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 065 066import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 067 068/** 069 * Test create/using/deleting snapshots from the client 070 * <p> 071 * This is an end-to-end test for the snapshot utility 072 */ 073@RunWith(Parameterized.class) 074@Category({ LargeTests.class, ClientTests.class }) 075public class TestSnapshotFromClient { 076 077 @ClassRule 078 public static final HBaseClassTestRule CLASS_RULE = 079 HBaseClassTestRule.forClass(TestSnapshotFromClient.class); 080 081 private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotFromClient.class); 082 083 protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 084 protected static final int NUM_RS = 2; 085 protected static final String STRING_TABLE_NAME = "test"; 086 protected static final byte[] TEST_FAM = Bytes.toBytes("fam"); 087 protected static final TableName TABLE_NAME = TableName.valueOf(STRING_TABLE_NAME); 088 private static final Pattern MATCH_ALL = Pattern.compile(".*"); 089 090 @Rule 091 public TableNameTestRule name = new TableNameTestRule(); 092 093 @Parameter 094 public StoreFileTrackerFactory.Trackers trackerImpl; 095 096 @Parameters(name = "{index}: tracker={0}") 097 public static List<Object[]> params() { 098 return Arrays.asList(new Object[] { StoreFileTrackerFactory.Trackers.DEFAULT }, 099 new Object[] { StoreFileTrackerFactory.Trackers.FILE }); 100 } 101 102 /** 103 * Setup the config for the cluster 104 * @throws Exception on failure 105 */ 106 @BeforeClass 107 public static void setupCluster() throws Exception { 108 setupConf(UTIL.getConfiguration()); 109 UTIL.startMiniCluster(NUM_RS); 110 } 111 112 protected static void setupConf(Configuration conf) { 113 // disable the ui 114 conf.setInt("hbase.regionsever.info.port", -1); 115 // change the flush size to a small amount, regulating number of store files 116 conf.setInt("hbase.hregion.memstore.flush.size", 25000); 117 // so make sure we get a compaction when doing a load, but keep around some 118 // files in the store 119 conf.setInt("hbase.hstore.compaction.min", 10); 120 conf.setInt("hbase.hstore.compactionThreshold", 10); 121 // block writes if we get to 12 store files 122 conf.setInt("hbase.hstore.blockingStoreFiles", 12); 123 // Enable snapshot 124 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 125 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 126 ConstantSizeRegionSplitPolicy.class.getName()); 127 } 128 129 @Before 130 public void setup() throws Exception { 131 createTable(); 132 } 133 134 protected void createTable() throws Exception { 135 TableDescriptor htd = 136 TableDescriptorBuilder.newBuilder(TABLE_NAME).setRegionReplication(getNumReplicas()) 137 .setValue(StoreFileTrackerFactory.TRACKER_IMPL, trackerImpl.name()).build(); 138 UTIL.createTable(htd, new byte[][] { TEST_FAM }, null); 139 } 140 141 protected int getNumReplicas() { 142 return 1; 143 } 144 145 @After 146 public void tearDown() throws Exception { 147 UTIL.deleteTable(TABLE_NAME); 148 SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin()); 149 SnapshotTestingUtils.deleteArchiveDirectory(UTIL); 150 } 151 152 @AfterClass 153 public static void cleanupTest() throws Exception { 154 try { 155 UTIL.shutdownMiniCluster(); 156 } catch (Exception e) { 157 LOG.warn("failure shutting down cluster", e); 158 } 159 } 160 161 /** 162 * Test snapshotting not allowed hbase:meta and -ROOT- 163 */ 164 @Test(expected = IllegalArgumentException.class) 165 public void testMetaTablesSnapshot() throws Exception { 166 UTIL.getAdmin().snapshot("metaSnapshot", TableName.META_TABLE_NAME); 167 } 168 169 /** 170 * Test HBaseAdmin#deleteSnapshots(String) which deletes snapshots whose names match the parameter 171 */ 172 @Test 173 public void testSnapshotDeletionWithRegex() throws Exception { 174 Admin admin = UTIL.getAdmin(); 175 // make sure we don't fail on listing snapshots 176 SnapshotTestingUtils.assertNoSnapshots(admin); 177 178 // put some stuff in the table 179 Table table = UTIL.getConnection().getTable(TABLE_NAME); 180 UTIL.loadTable(table, TEST_FAM); 181 table.close(); 182 183 String snapshot1 = "TableSnapshot1"; 184 admin.snapshot(snapshot1, TABLE_NAME); 185 LOG.debug("Snapshot1 completed."); 186 187 String snapshot2 = "TableSnapshot2"; 188 admin.snapshot(snapshot2, TABLE_NAME); 189 LOG.debug("Snapshot2 completed."); 190 191 String snapshot3 = "3rdTableSnapshot"; 192 admin.snapshot(snapshot3, TABLE_NAME); 193 LOG.debug(snapshot3 + " completed."); 194 195 // delete the first two snapshots 196 admin.deleteSnapshots(Pattern.compile("TableSnapshot.*")); 197 List<SnapshotDescription> snapshots = admin.listSnapshots(); 198 assertEquals(1, snapshots.size()); 199 assertEquals(snapshot3, snapshots.get(0).getName()); 200 201 admin.deleteSnapshot(snapshot3); 202 admin.close(); 203 } 204 205 /** 206 * Test snapshotting a table that is offline 207 */ 208 @Test 209 public void testOfflineTableSnapshot() throws Exception { 210 Admin admin = UTIL.getAdmin(); 211 // make sure we don't fail on listing snapshots 212 SnapshotTestingUtils.assertNoSnapshots(admin); 213 214 // put some stuff in the table 215 Table table = UTIL.getConnection().getTable(TABLE_NAME); 216 UTIL.loadTable(table, TEST_FAM, false); 217 218 LOG.debug("FS state before disable:"); 219 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 220 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 221 // XXX if this is flakey, might want to consider using the async version and looping as 222 // disableTable can succeed and still timeout. 223 admin.disableTable(TABLE_NAME); 224 225 LOG.debug("FS state before snapshot:"); 226 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 227 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 228 229 // take a snapshot of the disabled table 230 final String SNAPSHOT_NAME = "offlineTableSnapshot"; 231 String snapshot = SNAPSHOT_NAME; 232 233 admin.snapshot(new SnapshotDescription(SNAPSHOT_NAME, TABLE_NAME, SnapshotType.DISABLED, null, 234 -1, SnapshotManifestV1.DESCRIPTOR_VERSION, null)); 235 LOG.debug("Snapshot completed."); 236 237 // make sure we have the snapshot 238 List<SnapshotDescription> snapshots = 239 SnapshotTestingUtils.assertOneSnapshotThatMatches(admin, snapshot, TABLE_NAME); 240 241 // make sure its a valid snapshot 242 FileSystem fs = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem(); 243 Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 244 LOG.debug("FS state after snapshot:"); 245 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 246 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 247 SnapshotTestingUtils.confirmSnapshotValid( 248 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshots.get(0)), TABLE_NAME, TEST_FAM, rootDir, 249 admin, fs); 250 251 admin.deleteSnapshot(snapshot); 252 snapshots = admin.listSnapshots(); 253 SnapshotTestingUtils.assertNoSnapshots(admin); 254 } 255 256 @Test 257 public void testSnapshotFailsOnNonExistantTable() throws Exception { 258 Admin admin = UTIL.getAdmin(); 259 // make sure we don't fail on listing snapshots 260 SnapshotTestingUtils.assertNoSnapshots(admin); 261 String tableName = "_not_a_table"; 262 263 // make sure the table doesn't exist 264 boolean fail = false; 265 do { 266 try { 267 admin.getDescriptor(TableName.valueOf(tableName)); 268 fail = true; 269 LOG.error("Table:" + tableName + " already exists, checking a new name"); 270 tableName = tableName + "!"; 271 } catch (TableNotFoundException e) { 272 fail = false; 273 } 274 } while (fail); 275 276 // snapshot the non-existant table 277 try { 278 admin.snapshot("fail", TableName.valueOf(tableName)); 279 fail("Snapshot succeeded even though there is not table."); 280 } catch (SnapshotCreationException e) { 281 LOG.info("Correctly failed to snapshot a non-existant table:" + e.getMessage()); 282 } 283 } 284 285 @Test 286 public void testOfflineTableSnapshotWithEmptyRegions() throws Exception { 287 // test with an empty table with one region 288 289 Admin admin = UTIL.getAdmin(); 290 // make sure we don't fail on listing snapshots 291 SnapshotTestingUtils.assertNoSnapshots(admin); 292 293 LOG.debug("FS state before disable:"); 294 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 295 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 296 admin.disableTable(TABLE_NAME); 297 298 LOG.debug("FS state before snapshot:"); 299 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 300 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 301 302 // take a snapshot of the disabled table 303 String snapshot = "testOfflineTableSnapshotWithEmptyRegions"; 304 admin.snapshot(snapshot, TABLE_NAME); 305 LOG.debug("Snapshot completed."); 306 307 // make sure we have the snapshot 308 List<SnapshotDescription> snapshots = 309 SnapshotTestingUtils.assertOneSnapshotThatMatches(admin, snapshot, TABLE_NAME); 310 311 // make sure its a valid snapshot 312 FileSystem fs = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem(); 313 Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 314 LOG.debug("FS state after snapshot:"); 315 CommonFSUtils.logFileSystemState(UTIL.getTestFileSystem(), 316 CommonFSUtils.getRootDir(UTIL.getConfiguration()), LOG); 317 318 List<byte[]> emptyCfs = Lists.newArrayList(TEST_FAM); // no file in the region 319 List<byte[]> nonEmptyCfs = Lists.newArrayList(); 320 SnapshotTestingUtils.confirmSnapshotValid( 321 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshots.get(0)), TABLE_NAME, nonEmptyCfs, 322 emptyCfs, rootDir, admin, fs); 323 324 admin.deleteSnapshot(snapshot); 325 snapshots = admin.listSnapshots(); 326 SnapshotTestingUtils.assertNoSnapshots(admin); 327 } 328 329 @Test 330 public void testListTableSnapshots() throws Exception { 331 Admin admin = null; 332 final TableName tableName = name.getTableName(); 333 try { 334 admin = UTIL.getAdmin(); 335 336 TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName).build(); 337 UTIL.createTable(htd, new byte[][] { TEST_FAM }, UTIL.getConfiguration()); 338 339 String table1Snapshot1 = "Table1Snapshot1"; 340 admin.snapshot(table1Snapshot1, TABLE_NAME); 341 LOG.debug("Snapshot1 completed."); 342 343 String table1Snapshot2 = "Table1Snapshot2"; 344 admin.snapshot(table1Snapshot2, TABLE_NAME); 345 LOG.debug("Snapshot2 completed."); 346 347 String table2Snapshot1 = "Table2Snapshot1"; 348 admin.snapshot(table2Snapshot1, tableName); 349 LOG.debug(table2Snapshot1 + " completed."); 350 351 List<SnapshotDescription> listTableSnapshots = 352 admin.listTableSnapshots(Pattern.compile("test.*"), MATCH_ALL); 353 List<String> listTableSnapshotNames = new ArrayList<>(); 354 assertEquals(3, listTableSnapshots.size()); 355 for (SnapshotDescription s : listTableSnapshots) { 356 listTableSnapshotNames.add(s.getName()); 357 } 358 assertTrue(listTableSnapshotNames.contains(table1Snapshot1)); 359 assertTrue(listTableSnapshotNames.contains(table1Snapshot2)); 360 assertTrue(listTableSnapshotNames.contains(table2Snapshot1)); 361 } finally { 362 if (admin != null) { 363 try { 364 admin.deleteSnapshots(Pattern.compile("Table.*")); 365 } catch (SnapshotDoesNotExistException ignore) { 366 } 367 if (admin.tableExists(tableName)) { 368 UTIL.deleteTable(tableName); 369 } 370 admin.close(); 371 } 372 } 373 } 374 375 @Test 376 public void testListTableSnapshotsWithRegex() throws Exception { 377 Admin admin = null; 378 try { 379 admin = UTIL.getAdmin(); 380 381 String table1Snapshot1 = "Table1Snapshot1"; 382 admin.snapshot(table1Snapshot1, TABLE_NAME); 383 LOG.debug("Snapshot1 completed."); 384 385 String table1Snapshot2 = "Table1Snapshot2"; 386 admin.snapshot(table1Snapshot2, TABLE_NAME); 387 LOG.debug("Snapshot2 completed."); 388 389 String table2Snapshot1 = "Table2Snapshot1"; 390 admin.snapshot(table2Snapshot1, TABLE_NAME); 391 LOG.debug(table2Snapshot1 + " completed."); 392 393 List<SnapshotDescription> listTableSnapshots = 394 admin.listTableSnapshots(Pattern.compile("test.*"), Pattern.compile("Table1.*")); 395 List<String> listTableSnapshotNames = new ArrayList<>(); 396 assertEquals(2, listTableSnapshots.size()); 397 for (SnapshotDescription s : listTableSnapshots) { 398 listTableSnapshotNames.add(s.getName()); 399 } 400 assertTrue(listTableSnapshotNames.contains(table1Snapshot1)); 401 assertTrue(listTableSnapshotNames.contains(table1Snapshot2)); 402 assertFalse(listTableSnapshotNames.contains(table2Snapshot1)); 403 } finally { 404 if (admin != null) { 405 try { 406 admin.deleteSnapshots(Pattern.compile("Table.*")); 407 } catch (SnapshotDoesNotExistException ignore) { 408 } 409 admin.close(); 410 } 411 } 412 } 413 414 @Test 415 public void testDeleteTableSnapshots() throws Exception { 416 Admin admin = null; 417 final TableName tableName = name.getTableName(); 418 try { 419 admin = UTIL.getAdmin(); 420 421 TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName).build(); 422 UTIL.createTable(htd, new byte[][] { TEST_FAM }, UTIL.getConfiguration()); 423 424 String table1Snapshot1 = "Table1Snapshot1"; 425 admin.snapshot(table1Snapshot1, TABLE_NAME); 426 LOG.debug("Snapshot1 completed."); 427 428 String table1Snapshot2 = "Table1Snapshot2"; 429 admin.snapshot(table1Snapshot2, TABLE_NAME); 430 LOG.debug("Snapshot2 completed."); 431 432 String table2Snapshot1 = "Table2Snapshot1"; 433 admin.snapshot(table2Snapshot1, tableName); 434 LOG.debug(table2Snapshot1 + " completed."); 435 436 Pattern tableNamePattern = Pattern.compile("test.*"); 437 admin.deleteTableSnapshots(tableNamePattern, MATCH_ALL); 438 assertEquals(0, admin.listTableSnapshots(tableNamePattern, MATCH_ALL).size()); 439 } finally { 440 if (admin != null) { 441 if (admin.tableExists(tableName)) { 442 UTIL.deleteTable(tableName); 443 } 444 admin.close(); 445 } 446 } 447 } 448 449 @Test 450 public void testDeleteTableSnapshotsWithRegex() throws Exception { 451 Admin admin = null; 452 Pattern tableNamePattern = Pattern.compile("test.*"); 453 try { 454 admin = UTIL.getAdmin(); 455 456 String table1Snapshot1 = "Table1Snapshot1"; 457 admin.snapshot(table1Snapshot1, TABLE_NAME); 458 LOG.debug("Snapshot1 completed."); 459 460 String table1Snapshot2 = "Table1Snapshot2"; 461 admin.snapshot(table1Snapshot2, TABLE_NAME); 462 LOG.debug("Snapshot2 completed."); 463 464 String table2Snapshot1 = "Table2Snapshot1"; 465 admin.snapshot(table2Snapshot1, TABLE_NAME); 466 LOG.debug(table2Snapshot1 + " completed."); 467 468 admin.deleteTableSnapshots(tableNamePattern, Pattern.compile("Table1.*")); 469 assertEquals(1, admin.listTableSnapshots(tableNamePattern, MATCH_ALL).size()); 470 } finally { 471 if (admin != null) { 472 try { 473 admin.deleteTableSnapshots(tableNamePattern, MATCH_ALL); 474 } catch (SnapshotDoesNotExistException ignore) { 475 } 476 admin.close(); 477 } 478 } 479 } 480}