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