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.quotas; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertTrue; 024 025import java.io.IOException; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.HashSet; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Set; 032import java.util.concurrent.atomic.AtomicLong; 033import java.util.concurrent.atomic.AtomicReference; 034 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.fs.FileSystem; 037import org.apache.hadoop.fs.Path; 038import org.apache.hadoop.hbase.Cell; 039import org.apache.hadoop.hbase.HBaseClassTestRule; 040import org.apache.hadoop.hbase.HBaseTestingUtility; 041import org.apache.hadoop.hbase.HConstants; 042import org.apache.hadoop.hbase.NamespaceDescriptor; 043import org.apache.hadoop.hbase.TableName; 044import org.apache.hadoop.hbase.Waiter.Predicate; 045import org.apache.hadoop.hbase.client.Admin; 046import org.apache.hadoop.hbase.client.Connection; 047import org.apache.hadoop.hbase.client.Get; 048import org.apache.hadoop.hbase.client.RegionInfo; 049import org.apache.hadoop.hbase.client.Result; 050import org.apache.hadoop.hbase.client.SnapshotDescription; 051import org.apache.hadoop.hbase.client.SnapshotType; 052import org.apache.hadoop.hbase.client.Table; 053import org.apache.hadoop.hbase.master.HMaster; 054import org.apache.hadoop.hbase.quotas.SpaceQuotaHelperForTests.NoFilesToDischarge; 055import org.apache.hadoop.hbase.quotas.SpaceQuotaHelperForTests.SpaceQuotaSnapshotPredicate; 056import org.apache.hadoop.hbase.regionserver.HStore; 057import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil; 058import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil.SnapshotVisitor; 059import org.apache.hadoop.hbase.testclassification.MediumTests; 060import org.junit.AfterClass; 061import org.junit.Before; 062import org.junit.BeforeClass; 063import org.junit.ClassRule; 064import org.junit.Rule; 065import org.junit.Test; 066import org.junit.experimental.categories.Category; 067import org.junit.rules.TestName; 068import org.slf4j.Logger; 069import org.slf4j.LoggerFactory; 070 071import org.apache.hbase.thirdparty.com.google.common.collect.HashMultimap; 072import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap; 073import org.apache.hbase.thirdparty.com.google.common.collect.Multimap; 074 075import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest.StoreFile; 076 077/** 078 * Test class for the {@link SnapshotQuotaObserverChore}. 079 */ 080@Category(MediumTests.class) 081public class TestSnapshotQuotaObserverChore { 082 083 @ClassRule 084 public static final HBaseClassTestRule CLASS_RULE = 085 HBaseClassTestRule.forClass(TestSnapshotQuotaObserverChore.class); 086 087 private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotQuotaObserverChore.class); 088 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 089 private static final AtomicLong COUNTER = new AtomicLong(); 090 091 @Rule 092 public TestName testName = new TestName(); 093 094 private Connection conn; 095 private Admin admin; 096 private SpaceQuotaHelperForTests helper; 097 private HMaster master; 098 private SnapshotQuotaObserverChore testChore; 099 100 @BeforeClass 101 public static void setUp() throws Exception { 102 Configuration conf = TEST_UTIL.getConfiguration(); 103 SpaceQuotaHelperForTests.updateConfigForQuotas(conf); 104 // Clean up the compacted files faster than normal (15s instead of 2mins) 105 conf.setInt("hbase.hfile.compaction.discharger.interval", 15 * 1000); 106 TEST_UTIL.startMiniCluster(1); 107 } 108 109 @AfterClass 110 public static void tearDown() throws Exception { 111 TEST_UTIL.shutdownMiniCluster(); 112 } 113 114 @Before 115 public void setup() throws Exception { 116 conn = TEST_UTIL.getConnection(); 117 admin = TEST_UTIL.getAdmin(); 118 helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, COUNTER); 119 master = TEST_UTIL.getHBaseCluster().getMaster(); 120 helper.removeAllQuotas(conn); 121 testChore = new SnapshotQuotaObserverChore( 122 TEST_UTIL.getConnection(), TEST_UTIL.getConfiguration(), master.getFileSystem(), master, 123 null); 124 } 125 126 @Test 127 public void testSnapshotsFromTables() throws Exception { 128 TableName tn1 = helper.createTableWithRegions(1); 129 TableName tn2 = helper.createTableWithRegions(1); 130 TableName tn3 = helper.createTableWithRegions(1); 131 132 // Set a space quota on table 1 and 2 (but not 3) 133 admin.setQuota(QuotaSettingsFactory.limitTableSpace( 134 tn1, SpaceQuotaHelperForTests.ONE_GIGABYTE, SpaceViolationPolicy.NO_INSERTS)); 135 admin.setQuota(QuotaSettingsFactory.limitTableSpace( 136 tn2, SpaceQuotaHelperForTests.ONE_GIGABYTE, SpaceViolationPolicy.NO_INSERTS)); 137 138 // Create snapshots on each table (we didn't write any data, so just skipflush) 139 admin.snapshot(new SnapshotDescription(tn1 + "snapshot", tn1, SnapshotType.SKIPFLUSH)); 140 admin.snapshot(new SnapshotDescription(tn2 + "snapshot", tn2, SnapshotType.SKIPFLUSH)); 141 admin.snapshot(new SnapshotDescription(tn3 + "snapshot", tn3, SnapshotType.SKIPFLUSH)); 142 143 Multimap<TableName,String> mapping = testChore.getSnapshotsToComputeSize(); 144 assertEquals(2, mapping.size()); 145 assertEquals(1, mapping.get(tn1).size()); 146 assertEquals(tn1 + "snapshot", mapping.get(tn1).iterator().next()); 147 assertEquals(1, mapping.get(tn2).size()); 148 assertEquals(tn2 + "snapshot", mapping.get(tn2).iterator().next()); 149 150 admin.snapshot(new SnapshotDescription(tn2 + "snapshot1", tn2, SnapshotType.SKIPFLUSH)); 151 admin.snapshot(new SnapshotDescription(tn3 + "snapshot1", tn3, SnapshotType.SKIPFLUSH)); 152 153 mapping = testChore.getSnapshotsToComputeSize(); 154 assertEquals(3, mapping.size()); 155 assertEquals(1, mapping.get(tn1).size()); 156 assertEquals(tn1 + "snapshot", mapping.get(tn1).iterator().next()); 157 assertEquals(2, mapping.get(tn2).size()); 158 assertEquals( 159 new HashSet<String>(Arrays.asList(tn2 + "snapshot", tn2 + "snapshot1")), mapping.get(tn2)); 160 } 161 162 @Test 163 public void testSnapshotsFromNamespaces() throws Exception { 164 NamespaceDescriptor ns = NamespaceDescriptor.create("snapshots_from_namespaces").build(); 165 admin.createNamespace(ns); 166 167 TableName tn1 = helper.createTableWithRegions(ns.getName(), 1); 168 TableName tn2 = helper.createTableWithRegions(ns.getName(), 1); 169 TableName tn3 = helper.createTableWithRegions(1); 170 171 // Set a space quota on the namespace 172 admin.setQuota(QuotaSettingsFactory.limitNamespaceSpace( 173 ns.getName(), SpaceQuotaHelperForTests.ONE_GIGABYTE, SpaceViolationPolicy.NO_INSERTS)); 174 175 // Create snapshots on each table (we didn't write any data, so just skipflush) 176 admin.snapshot(new SnapshotDescription( 177 tn1.getQualifierAsString() + "snapshot", tn1, SnapshotType.SKIPFLUSH)); 178 admin.snapshot(new SnapshotDescription( 179 tn2.getQualifierAsString() + "snapshot", tn2, SnapshotType.SKIPFLUSH)); 180 admin.snapshot(new SnapshotDescription( 181 tn3.getQualifierAsString() + "snapshot", tn3, SnapshotType.SKIPFLUSH)); 182 183 Multimap<TableName,String> mapping = testChore.getSnapshotsToComputeSize(); 184 assertEquals(2, mapping.size()); 185 assertEquals(1, mapping.get(tn1).size()); 186 assertEquals(tn1.getQualifierAsString() + "snapshot", mapping.get(tn1).iterator().next()); 187 assertEquals(1, mapping.get(tn2).size()); 188 assertEquals(tn2.getQualifierAsString() + "snapshot", mapping.get(tn2).iterator().next()); 189 190 admin.snapshot(new SnapshotDescription( 191 tn2.getQualifierAsString() + "snapshot1", tn2, SnapshotType.SKIPFLUSH)); 192 admin.snapshot(new SnapshotDescription( 193 tn3.getQualifierAsString() + "snapshot2", tn3, SnapshotType.SKIPFLUSH)); 194 195 mapping = testChore.getSnapshotsToComputeSize(); 196 assertEquals(3, mapping.size()); 197 assertEquals(1, mapping.get(tn1).size()); 198 assertEquals(tn1.getQualifierAsString() + "snapshot", mapping.get(tn1).iterator().next()); 199 assertEquals(2, mapping.get(tn2).size()); 200 assertEquals( 201 new HashSet<String>(Arrays.asList(tn2.getQualifierAsString() + "snapshot", 202 tn2.getQualifierAsString() + "snapshot1")), mapping.get(tn2)); 203 } 204 205 @Test 206 public void testSnapshotSize() throws Exception { 207 // Create a table and set a quota 208 TableName tn1 = helper.createTableWithRegions(5); 209 admin.setQuota(QuotaSettingsFactory.limitTableSpace( 210 tn1, SpaceQuotaHelperForTests.ONE_GIGABYTE, SpaceViolationPolicy.NO_INSERTS)); 211 212 // Write some data and flush it 213 helper.writeData(tn1, 256L * SpaceQuotaHelperForTests.ONE_KILOBYTE); 214 admin.flush(tn1); 215 216 final long snapshotSize = TEST_UTIL.getMiniHBaseCluster().getRegions(tn1).stream() 217 .flatMap(r -> r.getStores().stream()).mapToLong(HStore::getHFilesSize).sum(); 218 219 // Wait for the Master chore to run to see the usage (with a fudge factor) 220 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 221 @Override 222 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 223 return snapshot.getUsage() == snapshotSize; 224 } 225 }); 226 227 // Create a snapshot on the table 228 final String snapshotName = tn1 + "snapshot"; 229 admin.snapshot(new SnapshotDescription(snapshotName, tn1, SnapshotType.SKIPFLUSH)); 230 231 // Get the snapshots 232 Multimap<TableName,String> snapshotsToCompute = testChore.getSnapshotsToComputeSize(); 233 assertEquals( 234 "Expected to see the single snapshot: " + snapshotsToCompute, 1, snapshotsToCompute.size()); 235 236 // Get the size of our snapshot 237 Map<String,Long> namespaceSnapshotSizes = testChore.computeSnapshotSizes( 238 snapshotsToCompute); 239 assertEquals(1, namespaceSnapshotSizes.size()); 240 Long size = namespaceSnapshotSizes.get(tn1.getNamespaceAsString()); 241 assertNotNull(size); 242 // The snapshot should take up no space since the table refers to it completely 243 assertEquals(0, size.longValue()); 244 245 // Write some more data, flush it, and then major_compact the table 246 helper.writeData(tn1, 256L * SpaceQuotaHelperForTests.ONE_KILOBYTE); 247 admin.flush(tn1); 248 TEST_UTIL.compact(tn1, true); 249 250 // Test table should reflect it's original size since ingest was deterministic 251 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 252 private final long regionSize = TEST_UTIL.getMiniHBaseCluster().getRegions(tn1).stream() 253 .flatMap(r -> r.getStores().stream()).mapToLong(HStore::getHFilesSize).sum(); 254 255 @Override 256 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 257 LOG.debug("Current usage=" + snapshot.getUsage() + " snapshotSize=" + snapshotSize); 258 // The usage of table space consists of region size and snapshot size 259 return closeInSize(snapshot.getUsage(), snapshotSize + regionSize, 260 SpaceQuotaHelperForTests.ONE_KILOBYTE); 261 } 262 }); 263 264 // Wait for no compacted files on the regions of our table 265 TEST_UTIL.waitFor(30_000, new NoFilesToDischarge(TEST_UTIL.getMiniHBaseCluster(), tn1)); 266 267 // Still should see only one snapshot 268 snapshotsToCompute = testChore.getSnapshotsToComputeSize(); 269 assertEquals( 270 "Expected to see the single snapshot: " + snapshotsToCompute, 1, snapshotsToCompute.size()); 271 namespaceSnapshotSizes = testChore.computeSnapshotSizes( 272 snapshotsToCompute); 273 assertEquals(1, namespaceSnapshotSizes.size()); 274 size = namespaceSnapshotSizes.get(tn1.getNamespaceAsString()); 275 assertNotNull(size); 276 // The snapshot should take up the size the table originally took up 277 assertEquals(snapshotSize, size.longValue()); 278 } 279 280 @Test 281 public void testPersistingSnapshotsForNamespaces() throws Exception { 282 TableName tn1 = TableName.valueOf("ns1:tn1"); 283 TableName tn2 = TableName.valueOf("ns1:tn2"); 284 TableName tn3 = TableName.valueOf("ns2:tn1"); 285 TableName tn4 = TableName.valueOf("ns2:tn2"); 286 TableName tn5 = TableName.valueOf("tn1"); 287 // Shim in a custom factory to avoid computing snapshot sizes. 288 FileArchiverNotifierFactory test = new FileArchiverNotifierFactory() { 289 Map<TableName,Long> tableToSize = ImmutableMap.of( 290 tn1, 1024L, tn2, 1024L, tn3, 512L, tn4, 1024L, tn5, 3072L); 291 @Override 292 public FileArchiverNotifier get( 293 Connection conn, Configuration conf, FileSystem fs, TableName tn) { 294 return new FileArchiverNotifier() { 295 @Override public void addArchivedFiles(Set<Entry<String,Long>> fileSizes) 296 throws IOException {} 297 298 @Override 299 public long computeAndStoreSnapshotSizes(Collection<String> currentSnapshots) 300 throws IOException { 301 return tableToSize.get(tn); 302 } 303 }; 304 } 305 }; 306 try { 307 FileArchiverNotifierFactoryImpl.setInstance(test); 308 309 Multimap<TableName,String> snapshotsToCompute = HashMultimap.create(); 310 snapshotsToCompute.put(tn1, ""); 311 snapshotsToCompute.put(tn2, ""); 312 snapshotsToCompute.put(tn3, ""); 313 snapshotsToCompute.put(tn4, ""); 314 snapshotsToCompute.put(tn5, ""); 315 Map<String,Long> nsSizes = testChore.computeSnapshotSizes(snapshotsToCompute); 316 assertEquals(3, nsSizes.size()); 317 assertEquals(2048L, (long) nsSizes.get("ns1")); 318 assertEquals(1536L, (long) nsSizes.get("ns2")); 319 assertEquals(3072L, (long) nsSizes.get(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR)); 320 } finally { 321 FileArchiverNotifierFactoryImpl.reset(); 322 } 323 } 324 325 @Test 326 public void testRemovedSnapshots() throws Exception { 327 // Create a table and set a quota 328 TableName tn1 = helper.createTableWithRegions(1); 329 admin.setQuota(QuotaSettingsFactory.limitTableSpace(tn1, SpaceQuotaHelperForTests.ONE_GIGABYTE, 330 SpaceViolationPolicy.NO_INSERTS)); 331 332 // Write some data and flush it 333 helper.writeData(tn1, 256L * SpaceQuotaHelperForTests.ONE_KILOBYTE); // 256 KB 334 335 final AtomicReference<Long> lastSeenSize = new AtomicReference<>(); 336 // Wait for the Master chore to run to see the usage (with a fudge factor) 337 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 338 @Override 339 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 340 lastSeenSize.set(snapshot.getUsage()); 341 return snapshot.getUsage() > 230L * SpaceQuotaHelperForTests.ONE_KILOBYTE; 342 } 343 }); 344 345 // Create a snapshot on the table 346 final String snapshotName1 = tn1 + "snapshot1"; 347 admin.snapshot(new SnapshotDescription(snapshotName1, tn1, SnapshotType.SKIPFLUSH)); 348 349 // Snapshot size has to be 0 as the snapshot shares the data with the table 350 final Table quotaTable = conn.getTable(QuotaUtil.QUOTA_TABLE_NAME); 351 TEST_UTIL.waitFor(30_000, new Predicate<Exception>() { 352 @Override 353 public boolean evaluate() throws Exception { 354 Get g = QuotaTableUtil.makeGetForSnapshotSize(tn1, snapshotName1); 355 Result r = quotaTable.get(g); 356 if (r == null || r.isEmpty()) { 357 return false; 358 } 359 r.advance(); 360 Cell c = r.current(); 361 return QuotaTableUtil.parseSnapshotSize(c) == 0; 362 } 363 }); 364 // Total usage has to remain same as what we saw before taking a snapshot 365 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 366 @Override 367 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 368 return snapshot.getUsage() == lastSeenSize.get(); 369 } 370 }); 371 372 // Major compact the table to force a rewrite 373 TEST_UTIL.compact(tn1, true); 374 // Now the snapshot size has to prev total size 375 TEST_UTIL.waitFor(30_000, new Predicate<Exception>() { 376 @Override 377 public boolean evaluate() throws Exception { 378 Get g = QuotaTableUtil.makeGetForSnapshotSize(tn1, snapshotName1); 379 Result r = quotaTable.get(g); 380 if (r == null || r.isEmpty()) { 381 return false; 382 } 383 r.advance(); 384 Cell c = r.current(); 385 // The compaction result file has an additional compaction event tracker 386 return lastSeenSize.get() == QuotaTableUtil.parseSnapshotSize(c); 387 } 388 }); 389 // The total size now has to be equal/more than double of prev total size 390 // as double the number of store files exist now. 391 final AtomicReference<Long> sizeAfterCompaction = new AtomicReference<>(); 392 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 393 @Override 394 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 395 sizeAfterCompaction.set(snapshot.getUsage()); 396 return snapshot.getUsage() >= 2 * lastSeenSize.get(); 397 } 398 }); 399 400 // Delete the snapshot 401 admin.deleteSnapshot(snapshotName1); 402 // Total size has to come down to prev totalsize - snapshot size(which was removed) 403 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 404 @Override 405 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 406 return snapshot.getUsage() == (sizeAfterCompaction.get() - lastSeenSize.get()); 407 } 408 }); 409 } 410 411 @Test 412 public void testBucketingFilesToSnapshots() throws Exception { 413 // Create a table and set a quota 414 TableName tn1 = helper.createTableWithRegions(1); 415 admin.setQuota(QuotaSettingsFactory.limitTableSpace( 416 tn1, SpaceQuotaHelperForTests.ONE_GIGABYTE, SpaceViolationPolicy.NO_INSERTS)); 417 418 // Write some data and flush it 419 helper.writeData(tn1, 256L * SpaceQuotaHelperForTests.ONE_KILOBYTE); 420 admin.flush(tn1); 421 422 final AtomicReference<Long> lastSeenSize = new AtomicReference<>(); 423 // Wait for the Master chore to run to see the usage (with a fudge factor) 424 TEST_UTIL.waitFor(30_000, new SpaceQuotaSnapshotPredicate(conn, tn1) { 425 @Override 426 boolean evaluate(SpaceQuotaSnapshot snapshot) throws Exception { 427 lastSeenSize.set(snapshot.getUsage()); 428 return snapshot.getUsage() > 230L * SpaceQuotaHelperForTests.ONE_KILOBYTE; 429 } 430 }); 431 432 // Create a snapshot on the table 433 final String snapshotName1 = tn1 + "snapshot1"; 434 admin.snapshot(new SnapshotDescription(snapshotName1, tn1, SnapshotType.SKIPFLUSH)); 435 // Major compact the table to force a rewrite 436 TEST_UTIL.compact(tn1, true); 437 438 // Make sure that the snapshot owns the size 439 final Table quotaTable = conn.getTable(QuotaUtil.QUOTA_TABLE_NAME); 440 TEST_UTIL.waitFor(30_000, new Predicate<Exception>() { 441 @Override 442 public boolean evaluate() throws Exception { 443 LOG.info("Waiting to see quota snapshot1 size"); 444 debugFilesForSnapshot(tn1, snapshotName1); 445 Get g = QuotaTableUtil.makeGetForSnapshotSize(tn1, snapshotName1); 446 Result r = quotaTable.get(g); 447 if (r == null || r.isEmpty()) { 448 return false; 449 } 450 r.advance(); 451 Cell c = r.current(); 452 // The compaction result file has an additional compaction event tracker 453 return lastSeenSize.get() == QuotaTableUtil.parseSnapshotSize(c); 454 } 455 }); 456 457 LOG.info("Snapshotting table again"); 458 // Create another snapshot on the table 459 final String snapshotName2 = tn1 + "snapshot2"; 460 admin.snapshot(new SnapshotDescription(snapshotName2, tn1, SnapshotType.SKIPFLUSH)); 461 LOG.info("Compacting table"); 462 // Major compact the table to force a rewrite 463 TEST_UTIL.compact(tn1, true); 464 465 // Make sure that the snapshot owns the size 466 TEST_UTIL.waitFor(30_000, new Predicate<Exception>() { 467 @Override 468 public boolean evaluate() throws Exception { 469 LOG.info("Waiting to see quota snapshot2 size"); 470 debugFilesForSnapshot(tn1, snapshotName2); 471 Get g = QuotaTableUtil.makeGetForSnapshotSize(tn1, snapshotName2); 472 Result r = quotaTable.get(g); 473 if (r == null || r.isEmpty()) { 474 return false; 475 } 476 r.advance(); 477 Cell c = r.current(); 478 return closeInSize(lastSeenSize.get(), 479 QuotaTableUtil.parseSnapshotSize(c), SpaceQuotaHelperForTests.ONE_KILOBYTE); 480 } 481 }); 482 483 Get g = QuotaTableUtil.createGetNamespaceSnapshotSize(tn1.getNamespaceAsString()); 484 Result r = quotaTable.get(g); 485 assertNotNull(r); 486 assertFalse(r.isEmpty()); 487 r.advance(); 488 long size = QuotaTableUtil.parseSnapshotSize(r.current()); 489 // Two snapshots of equal size. 490 assertTrue(closeInSize(lastSeenSize.get() * 2, size, SpaceQuotaHelperForTests.ONE_KILOBYTE)); 491 } 492 493 /** 494 * Prints details about every file referenced by the snapshot with the given name. 495 */ 496 void debugFilesForSnapshot(TableName table, String snapshot) throws IOException { 497 final Configuration conf = TEST_UTIL.getConfiguration(); 498 final FileSystem fs = TEST_UTIL.getTestFileSystem(); 499 final Path snapshotDir = new Path(conf.get("hbase.rootdir"), HConstants.SNAPSHOT_DIR_NAME); 500 SnapshotReferenceUtil.visitReferencedFiles(conf, fs, new Path(snapshotDir, snapshot), 501 new SnapshotVisitor() { 502 @Override 503 public void storeFile( 504 RegionInfo regionInfo, String familyName, StoreFile storeFile) throws IOException { 505 LOG.info("Snapshot={} references file={}, size={}", snapshot, storeFile.getName(), 506 storeFile.getFileSize()); 507 } 508 } 509 ); 510 } 511 512 /** 513 * Computes if {@code size2} is within {@code delta} of {@code size1}, inclusive. 514 * 515 * The size of our store files will change after the first major compaction as the last 516 * compaction gets serialized into the store file (see the fields referenced by 517 * COMPACTION_EVENT_KEY in HFilePrettyPrinter). 518 */ 519 boolean closeInSize(long size1, long size2, long delta) { 520 long lower = size1 - delta; 521 long upper = size1 + delta; 522 return lower <= size2 && size2 <= upper; 523 } 524}