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