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.master; 019 020import static org.apache.hadoop.hbase.util.HFileArchiveTestingUtil.assertArchiveEqualToOriginal; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.mockito.Mockito.doReturn; 025import static org.mockito.Mockito.spy; 026 027import java.io.IOException; 028import java.util.Map; 029import java.util.NavigableMap; 030import java.util.Objects; 031import java.util.SortedMap; 032import java.util.SortedSet; 033import java.util.TreeMap; 034import java.util.concurrent.ConcurrentSkipListMap; 035import org.apache.hadoop.fs.FSDataOutputStream; 036import org.apache.hadoop.fs.FileStatus; 037import org.apache.hadoop.fs.FileSystem; 038import org.apache.hadoop.fs.Path; 039import org.apache.hadoop.hbase.HBaseClassTestRule; 040import org.apache.hadoop.hbase.HBaseTestingUtility; 041import org.apache.hadoop.hbase.HColumnDescriptor; 042import org.apache.hadoop.hbase.HConstants; 043import org.apache.hadoop.hbase.HRegionInfo; 044import org.apache.hadoop.hbase.MetaMockingUtil; 045import org.apache.hadoop.hbase.ServerName; 046import org.apache.hadoop.hbase.TableName; 047import org.apache.hadoop.hbase.client.Result; 048import org.apache.hadoop.hbase.client.TableDescriptor; 049import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 050import org.apache.hadoop.hbase.io.Reference; 051import org.apache.hadoop.hbase.master.CatalogJanitor.SplitParentFirstComparator; 052import org.apache.hadoop.hbase.master.assignment.MockMasterServices; 053import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 054import org.apache.hadoop.hbase.regionserver.ChunkCreator; 055import org.apache.hadoop.hbase.regionserver.HStore; 056import org.apache.hadoop.hbase.regionserver.MemStoreLABImpl; 057import org.apache.hadoop.hbase.testclassification.MasterTests; 058import org.apache.hadoop.hbase.testclassification.SmallTests; 059import org.apache.hadoop.hbase.util.Bytes; 060import org.apache.hadoop.hbase.util.FSUtils; 061import org.apache.hadoop.hbase.util.HFileArchiveUtil; 062import org.apache.zookeeper.KeeperException; 063import org.junit.After; 064import org.junit.Before; 065import org.junit.BeforeClass; 066import org.junit.ClassRule; 067import org.junit.Rule; 068import org.junit.Test; 069import org.junit.experimental.categories.Category; 070import org.junit.rules.TestName; 071import org.slf4j.Logger; 072import org.slf4j.LoggerFactory; 073 074@Category({MasterTests.class, SmallTests.class}) 075public class TestCatalogJanitor { 076 077 @ClassRule 078 public static final HBaseClassTestRule CLASS_RULE = 079 HBaseClassTestRule.forClass(TestCatalogJanitor.class); 080 081 private static final Logger LOG = LoggerFactory.getLogger(TestCatalogJanitor.class); 082 @Rule public final TestName name = new TestName(); 083 private static final HBaseTestingUtility HTU = new HBaseTestingUtility(); 084 private MockMasterServices masterServices; 085 private CatalogJanitor janitor; 086 087 @BeforeClass 088 public static void beforeClass() throws Exception { 089 ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null); 090 } 091 092 @Before 093 public void setup() throws IOException, KeeperException { 094 setRootDirAndCleanIt(HTU, this.name.getMethodName()); 095 NavigableMap<ServerName, SortedSet<byte []>> regionsToRegionServers = 096 new ConcurrentSkipListMap<ServerName, SortedSet<byte []>>(); 097 this.masterServices = 098 new MockMasterServices(HTU.getConfiguration(), regionsToRegionServers); 099 this.masterServices.start(10, null); 100 this.janitor = new CatalogJanitor(masterServices); 101 } 102 103 @After 104 public void teardown() { 105 this.janitor.cancel(true); 106 this.masterServices.stop("DONE"); 107 } 108 109 /** 110 * Test clearing a split parent. 111 */ 112 @Test 113 public void testCleanParent() throws IOException, InterruptedException { 114 TableDescriptor td = createTableDescriptorForCurrentMethod(); 115 // Create regions. 116 HRegionInfo parent = 117 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("eee")); 118 HRegionInfo splita = 119 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc")); 120 HRegionInfo splitb = 121 new HRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("eee")); 122 // Test that when both daughter regions are in place, that we do not remove the parent. 123 Result r = createResult(parent, splita, splitb); 124 // Add a reference under splitA directory so we don't clear out the parent. 125 Path rootdir = this.masterServices.getMasterFileSystem().getRootDir(); 126 Path tabledir = FSUtils.getTableDir(rootdir, td.getTableName()); 127 Path parentdir = new Path(tabledir, parent.getEncodedName()); 128 Path storedir = HStore.getStoreHomedir(tabledir, splita, td.getColumnFamilies()[0].getName()); 129 Reference ref = Reference.createTopReference(Bytes.toBytes("ccc")); 130 long now = System.currentTimeMillis(); 131 // Reference name has this format: StoreFile#REF_NAME_PARSER 132 Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName()); 133 FileSystem fs = this.masterServices.getMasterFileSystem().getFileSystem(); 134 Path path = ref.write(fs, p); 135 assertTrue(fs.exists(path)); 136 LOG.info("Created reference " + path); 137 // Add a parentdir for kicks so can check it gets removed by the catalogjanitor. 138 fs.mkdirs(parentdir); 139 assertFalse(this.janitor.cleanParent(parent, r)); 140 ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor()); 141 assertTrue(fs.exists(parentdir)); 142 // Remove the reference file and try again. 143 assertTrue(fs.delete(p, true)); 144 assertTrue(this.janitor.cleanParent(parent, r)); 145 // Parent cleanup is run async as a procedure. Make sure parentdir is removed. 146 ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor()); 147 assertTrue(!fs.exists(parentdir)); 148 } 149 150 /** 151 * Make sure parent gets cleaned up even if daughter is cleaned up before it. 152 */ 153 @Test 154 public void testParentCleanedEvenIfDaughterGoneFirst() 155 throws IOException, InterruptedException { 156 parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(this.name.getMethodName(), 157 Bytes.toBytes("eee")); 158 } 159 160 /** 161 * Make sure last parent with empty end key gets cleaned up even if daughter is cleaned up before it. 162 */ 163 @Test 164 public void testLastParentCleanedEvenIfDaughterGoneFirst() 165 throws IOException, InterruptedException { 166 parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(this.name.getMethodName(), 167 new byte[0]); 168 } 169 170 /** 171 * @return A TableDescriptor with a tableName of current method name and a column 172 * family that is MockMasterServices.DEFAULT_COLUMN_FAMILY_NAME) 173 */ 174 private TableDescriptor createTableDescriptorForCurrentMethod() { 175 return TableDescriptorBuilder.newBuilder(TableName.valueOf(this.name.getMethodName())). 176 setColumnFamily(new HColumnDescriptor(MockMasterServices.DEFAULT_COLUMN_FAMILY_NAME)). 177 build(); 178 } 179 180 /** 181 * Make sure parent with specified end key gets cleaned up even if daughter is cleaned up before it. 182 * 183 * @param rootDir the test case name, used as the HBase testing utility root 184 * @param lastEndKey the end key of the split parent 185 */ 186 private void parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst( 187 final String rootDir, final byte[] lastEndKey) 188 throws IOException, InterruptedException { 189 TableDescriptor td = createTableDescriptorForCurrentMethod(); 190 // Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc. 191 HRegionInfo parent = new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), lastEndKey); 192 // Sleep a second else the encoded name on these regions comes out 193 // same for all with same start key and made in same second. 194 Thread.sleep(1001); 195 196 // Daughter a 197 HRegionInfo splita = 198 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc")); 199 Thread.sleep(1001); 200 // Make daughters of daughter a; splitaa and splitab. 201 HRegionInfo splitaa = 202 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb")); 203 HRegionInfo splitab = 204 new HRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc")); 205 206 // Daughter b 207 HRegionInfo splitb = 208 new HRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), lastEndKey); 209 Thread.sleep(1001); 210 // Make Daughters of daughterb; splitba and splitbb. 211 HRegionInfo splitba = 212 new HRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("ddd")); 213 HRegionInfo splitbb = 214 new HRegionInfo(td.getTableName(), Bytes.toBytes("ddd"), lastEndKey); 215 216 // First test that our Comparator works right up in CatalogJanitor. 217 SortedMap<HRegionInfo, Result> regions = 218 new TreeMap<>(new CatalogJanitor.SplitParentFirstComparator()); 219 // Now make sure that this regions map sorts as we expect it to. 220 regions.put(parent, createResult(parent, splita, splitb)); 221 regions.put(splitb, createResult(splitb, splitba, splitbb)); 222 regions.put(splita, createResult(splita, splitaa, splitab)); 223 // Assert its properly sorted. 224 int index = 0; 225 for (Map.Entry<HRegionInfo, Result> e: regions.entrySet()) { 226 if (index == 0) { 227 assertTrue(e.getKey().getEncodedName().equals(parent.getEncodedName())); 228 } else if (index == 1) { 229 assertTrue(e.getKey().getEncodedName().equals(splita.getEncodedName())); 230 } else if (index == 2) { 231 assertTrue(e.getKey().getEncodedName().equals(splitb.getEncodedName())); 232 } 233 index++; 234 } 235 236 // Now play around with the cleanParent function. Create a ref from splita up to the parent. 237 Path splitaRef = 238 createReferences(this.masterServices, td, parent, splita, Bytes.toBytes("ccc"), false); 239 // Make sure actual super parent sticks around because splita has a ref. 240 assertFalse(janitor.cleanParent(parent, regions.get(parent))); 241 242 //splitba, and split bb, do not have dirs in fs. That means that if 243 // we test splitb, it should get cleaned up. 244 assertTrue(janitor.cleanParent(splitb, regions.get(splitb))); 245 246 // Now remove ref from splita to parent... so parent can be let go and so 247 // the daughter splita can be split (can't split if still references). 248 // BUT make the timing such that the daughter gets cleaned up before we 249 // can get a chance to let go of the parent. 250 FileSystem fs = FileSystem.get(HTU.getConfiguration()); 251 assertTrue(fs.delete(splitaRef, true)); 252 // Create the refs from daughters of splita. 253 Path splitaaRef = 254 createReferences(this.masterServices, td, splita, splitaa, Bytes.toBytes("bbb"), false); 255 Path splitabRef = 256 createReferences(this.masterServices, td, splita, splitab, Bytes.toBytes("bbb"), true); 257 258 // Test splita. It should stick around because references from splitab, etc. 259 assertFalse(janitor.cleanParent(splita, regions.get(splita))); 260 261 // Now clean up parent daughter first. Remove references from its daughters. 262 assertTrue(fs.delete(splitaaRef, true)); 263 assertTrue(fs.delete(splitabRef, true)); 264 assertTrue(janitor.cleanParent(splita, regions.get(splita))); 265 266 // Super parent should get cleaned up now both splita and splitb are gone. 267 assertTrue(janitor.cleanParent(parent, regions.get(parent))); 268 } 269 270 /** 271 * CatalogJanitor.scan() should not clean parent regions if their own 272 * parents are still referencing them. This ensures that grandparent regions 273 * do not point to deleted parent regions. 274 */ 275 @Test 276 public void testScanDoesNotCleanRegionsWithExistingParents() throws Exception { 277 TableDescriptor td = createTableDescriptorForCurrentMethod(); 278 // Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc. 279 280 // Parent 281 HRegionInfo parent = new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), 282 HConstants.EMPTY_BYTE_ARRAY, true); 283 // Sleep a second else the encoded name on these regions comes out 284 // same for all with same start key and made in same second. 285 Thread.sleep(1001); 286 287 // Daughter a 288 HRegionInfo splita = 289 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"), true); 290 Thread.sleep(1001); 291 292 // Make daughters of daughter a; splitaa and splitab. 293 HRegionInfo splitaa = 294 new HRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb"), false); 295 HRegionInfo splitab = 296 new HRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc"), false); 297 298 // Daughter b 299 HRegionInfo splitb = 300 new HRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), HConstants.EMPTY_BYTE_ARRAY); 301 Thread.sleep(1001); 302 303 // Parent has daughters splita and splitb. Splita has daughters splitaa and splitab. 304 final Map<HRegionInfo, Result> splitParents = new TreeMap<>(new SplitParentFirstComparator()); 305 splitParents.put(parent, createResult(parent, splita, splitb)); 306 splita.setOffline(true); //simulate that splita goes offline when it is split 307 splitParents.put(splita, createResult(splita, splitaa, splitab)); 308 309 final Map<HRegionInfo, Result> mergedRegions = new TreeMap<>(); 310 CatalogJanitor spy = spy(this.janitor); 311 312 CatalogJanitor.Report report = new CatalogJanitor.Report(); 313 report.count = 10; 314 report.mergedRegions.putAll(mergedRegions); 315 report.splitParents.putAll(splitParents); 316 317 doReturn(report).when(spy).scanForReport(); 318 319 // Create ref from splita to parent 320 LOG.info("parent=" + parent.getShortNameToLog() + ", splita=" + splita.getShortNameToLog()); 321 Path splitaRef = 322 createReferences(this.masterServices, td, parent, splita, Bytes.toBytes("ccc"), false); 323 LOG.info("Created reference " + splitaRef); 324 325 // Parent and splita should not be removed because a reference from splita to parent. 326 int gcs = spy.scan(); 327 assertEquals(0, gcs); 328 329 // Now delete the ref 330 FileSystem fs = FileSystem.get(HTU.getConfiguration()); 331 assertTrue(fs.delete(splitaRef, true)); 332 333 //now, both parent, and splita can be deleted 334 gcs = spy.scan(); 335 assertEquals(2, gcs); 336 } 337 338 /** 339 * Test that we correctly archive all the storefiles when a region is deleted 340 * @throws Exception 341 */ 342 @Test 343 public void testSplitParentFirstComparator() { 344 SplitParentFirstComparator comp = new SplitParentFirstComparator(); 345 TableDescriptor td = createTableDescriptorForCurrentMethod(); 346 347 /* Region splits: 348 * 349 * rootRegion --- firstRegion --- firstRegiona 350 * | |- firstRegionb 351 * | 352 * |- lastRegion --- lastRegiona --- lastRegionaa 353 * | |- lastRegionab 354 * |- lastRegionb 355 * 356 * rootRegion : [] - [] 357 * firstRegion : [] - bbb 358 * lastRegion : bbb - [] 359 * firstRegiona : [] - aaa 360 * firstRegionb : aaa - bbb 361 * lastRegiona : bbb - ddd 362 * lastRegionb : ddd - [] 363 */ 364 365 // root region 366 HRegionInfo rootRegion = new HRegionInfo(td.getTableName(), 367 HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, true); 368 HRegionInfo firstRegion = new HRegionInfo(td.getTableName(), 369 HConstants.EMPTY_START_ROW, Bytes.toBytes("bbb"), true); 370 HRegionInfo lastRegion = new HRegionInfo(td.getTableName(), 371 Bytes.toBytes("bbb"), HConstants.EMPTY_END_ROW, true); 372 373 assertTrue(comp.compare(rootRegion, rootRegion) == 0); 374 assertTrue(comp.compare(firstRegion, firstRegion) == 0); 375 assertTrue(comp.compare(lastRegion, lastRegion) == 0); 376 assertTrue(comp.compare(rootRegion, firstRegion) < 0); 377 assertTrue(comp.compare(rootRegion, lastRegion) < 0); 378 assertTrue(comp.compare(firstRegion, lastRegion) < 0); 379 380 //first region split into a, b 381 HRegionInfo firstRegiona = new HRegionInfo(td.getTableName(), 382 HConstants.EMPTY_START_ROW, Bytes.toBytes("aaa"), true); 383 HRegionInfo firstRegionb = new HRegionInfo(td.getTableName(), 384 Bytes.toBytes("aaa"), Bytes.toBytes("bbb"), true); 385 //last region split into a, b 386 HRegionInfo lastRegiona = new HRegionInfo(td.getTableName(), 387 Bytes.toBytes("bbb"), Bytes.toBytes("ddd"), true); 388 HRegionInfo lastRegionb = new HRegionInfo(td.getTableName(), 389 Bytes.toBytes("ddd"), HConstants.EMPTY_END_ROW, true); 390 391 assertTrue(comp.compare(firstRegiona, firstRegiona) == 0); 392 assertTrue(comp.compare(firstRegionb, firstRegionb) == 0); 393 assertTrue(comp.compare(rootRegion, firstRegiona) < 0); 394 assertTrue(comp.compare(rootRegion, firstRegionb) < 0); 395 assertTrue(comp.compare(firstRegion, firstRegiona) < 0); 396 assertTrue(comp.compare(firstRegion, firstRegionb) < 0); 397 assertTrue(comp.compare(firstRegiona, firstRegionb) < 0); 398 399 assertTrue(comp.compare(lastRegiona, lastRegiona) == 0); 400 assertTrue(comp.compare(lastRegionb, lastRegionb) == 0); 401 assertTrue(comp.compare(rootRegion, lastRegiona) < 0); 402 assertTrue(comp.compare(rootRegion, lastRegionb) < 0); 403 assertTrue(comp.compare(lastRegion, lastRegiona) < 0); 404 assertTrue(comp.compare(lastRegion, lastRegionb) < 0); 405 assertTrue(comp.compare(lastRegiona, lastRegionb) < 0); 406 407 assertTrue(comp.compare(firstRegiona, lastRegiona) < 0); 408 assertTrue(comp.compare(firstRegiona, lastRegionb) < 0); 409 assertTrue(comp.compare(firstRegionb, lastRegiona) < 0); 410 assertTrue(comp.compare(firstRegionb, lastRegionb) < 0); 411 412 HRegionInfo lastRegionaa = new HRegionInfo(td.getTableName(), 413 Bytes.toBytes("bbb"), Bytes.toBytes("ccc"), false); 414 HRegionInfo lastRegionab = new HRegionInfo(td.getTableName(), 415 Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), false); 416 417 assertTrue(comp.compare(lastRegiona, lastRegionaa) < 0); 418 assertTrue(comp.compare(lastRegiona, lastRegionab) < 0); 419 assertTrue(comp.compare(lastRegionaa, lastRegionab) < 0); 420 } 421 422 @Test 423 public void testArchiveOldRegion() throws Exception { 424 // Create regions. 425 TableDescriptor td = createTableDescriptorForCurrentMethod(); 426 HRegionInfo parent = new HRegionInfo(td.getTableName(), 427 Bytes.toBytes("aaa"), Bytes.toBytes("eee")); 428 HRegionInfo splita = new HRegionInfo(td.getTableName(), 429 Bytes.toBytes("aaa"), Bytes.toBytes("ccc")); 430 HRegionInfo splitb = new HRegionInfo(td.getTableName(), 431 Bytes.toBytes("ccc"), Bytes.toBytes("eee")); 432 433 // Test that when both daughter regions are in place, that we do not 434 // remove the parent. 435 Result parentMetaRow = createResult(parent, splita, splitb); 436 FileSystem fs = FileSystem.get(HTU.getConfiguration()); 437 Path rootdir = this.masterServices.getMasterFileSystem().getRootDir(); 438 // have to set the root directory since we use it in HFileDisposer to figure out to get to the 439 // archive directory. Otherwise, it just seems to pick the first root directory it can find (so 440 // the single test passes, but when the full suite is run, things get borked). 441 FSUtils.setRootDir(fs.getConf(), rootdir); 442 Path tabledir = FSUtils.getTableDir(rootdir, td.getTableName()); 443 Path storedir = HStore.getStoreHomedir(tabledir, parent, td.getColumnFamilies()[0].getName()); 444 Path storeArchive = 445 HFileArchiveUtil.getStoreArchivePath(this.masterServices.getConfiguration(), parent, 446 tabledir, td.getColumnFamilies()[0].getName()); 447 LOG.debug("Table dir:" + tabledir); 448 LOG.debug("Store dir:" + storedir); 449 LOG.debug("Store archive dir:" + storeArchive); 450 451 // add a couple of store files that we can check for 452 FileStatus[] mockFiles = addMockStoreFiles(2, this.masterServices, storedir); 453 // get the current store files for comparison 454 FileStatus[] storeFiles = fs.listStatus(storedir); 455 int index = 0; 456 for (FileStatus file : storeFiles) { 457 LOG.debug("Have store file:" + file.getPath()); 458 assertEquals("Got unexpected store file", mockFiles[index].getPath(), 459 storeFiles[index].getPath()); 460 index++; 461 } 462 463 // do the cleaning of the parent 464 assertTrue(janitor.cleanParent(parent, parentMetaRow)); 465 Path parentDir = new Path(tabledir, parent.getEncodedName()); 466 // Cleanup procedure runs async. Wait till it done. 467 ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor()); 468 assertTrue(!fs.exists(parentDir)); 469 LOG.debug("Finished cleanup of parent region"); 470 471 // and now check to make sure that the files have actually been archived 472 FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive); 473 logFiles("archived files", storeFiles); 474 logFiles("archived files", archivedStoreFiles); 475 476 assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs); 477 478 // cleanup 479 FSUtils.delete(fs, rootdir, true); 480 } 481 482 /** 483 * @param description description of the files for logging 484 * @param storeFiles the status of the files to log 485 */ 486 private void logFiles(String description, FileStatus[] storeFiles) { 487 LOG.debug("Current " + description + ": "); 488 for (FileStatus file : storeFiles) { 489 LOG.debug(Objects.toString(file.getPath())); 490 } 491 } 492 493 /** 494 * Test that if a store file with the same name is present as those already backed up cause the 495 * already archived files to be timestamped backup 496 */ 497 @Test 498 public void testDuplicateHFileResolution() throws Exception { 499 TableDescriptor td = createTableDescriptorForCurrentMethod(); 500 501 // Create regions. 502 HRegionInfo parent = new HRegionInfo(td.getTableName(), 503 Bytes.toBytes("aaa"), Bytes.toBytes("eee")); 504 HRegionInfo splita = new HRegionInfo(td.getTableName(), 505 Bytes.toBytes("aaa"), Bytes.toBytes("ccc")); 506 HRegionInfo splitb = new HRegionInfo(td.getTableName(), 507 Bytes.toBytes("ccc"), Bytes.toBytes("eee")); 508 // Test that when both daughter regions are in place, that we do not 509 // remove the parent. 510 Result r = createResult(parent, splita, splitb); 511 FileSystem fs = FileSystem.get(HTU.getConfiguration()); 512 Path rootdir = this.masterServices.getMasterFileSystem().getRootDir(); 513 // Have to set the root directory since we use it in HFileDisposer to figure out to get to the 514 // archive directory. Otherwise, it just seems to pick the first root directory it can find (so 515 // the single test passes, but when the full suite is run, things get borked). 516 FSUtils.setRootDir(fs.getConf(), rootdir); 517 Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable()); 518 Path storedir = HStore.getStoreHomedir(tabledir, parent, td.getColumnFamilies()[0].getName()); 519 System.out.println("Old root:" + rootdir); 520 System.out.println("Old table:" + tabledir); 521 System.out.println("Old store:" + storedir); 522 523 Path storeArchive = 524 HFileArchiveUtil.getStoreArchivePath(this.masterServices.getConfiguration(), parent, 525 tabledir, td.getColumnFamilies()[0].getName()); 526 System.out.println("Old archive:" + storeArchive); 527 528 // enable archiving, make sure that files get archived 529 addMockStoreFiles(2, this.masterServices, storedir); 530 // get the current store files for comparison 531 FileStatus[] storeFiles = fs.listStatus(storedir); 532 // Do the cleaning of the parent 533 assertTrue(janitor.cleanParent(parent, r)); 534 Path parentDir = new Path(tabledir, parent.getEncodedName()); 535 ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor()); 536 assertTrue(!fs.exists(parentDir)); 537 538 // And now check to make sure that the files have actually been archived 539 FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive); 540 assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs); 541 542 // now add store files with the same names as before to check backup 543 // enable archiving, make sure that files get archived 544 addMockStoreFiles(2, this.masterServices, storedir); 545 546 // Do the cleaning of the parent 547 assertTrue(janitor.cleanParent(parent, r)); 548 // Cleanup procedure runs async. Wait till it done. 549 ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor()); 550 assertTrue(!fs.exists(parentDir)); 551 552 // and now check to make sure that the files have actually been archived 553 archivedStoreFiles = fs.listStatus(storeArchive); 554 assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs, true); 555 } 556 557 private FileStatus[] addMockStoreFiles(int count, MasterServices services, Path storedir) 558 throws IOException { 559 // get the existing store files 560 FileSystem fs = services.getMasterFileSystem().getFileSystem(); 561 fs.mkdirs(storedir); 562 // create the store files in the parent 563 for (int i = 0; i < count; i++) { 564 Path storeFile = new Path(storedir, "_store" + i); 565 FSDataOutputStream dos = fs.create(storeFile, true); 566 dos.writeBytes("Some data: " + i); 567 dos.close(); 568 } 569 LOG.debug("Adding " + count + " store files to the storedir:" + storedir); 570 // make sure the mock store files are there 571 FileStatus[] storeFiles = fs.listStatus(storedir); 572 assertEquals("Didn't have expected store files", count, storeFiles.length); 573 return storeFiles; 574 } 575 576 private String setRootDirAndCleanIt(final HBaseTestingUtility htu, final String subdir) 577 throws IOException { 578 Path testdir = htu.getDataTestDir(subdir); 579 FileSystem fs = FileSystem.get(htu.getConfiguration()); 580 if (fs.exists(testdir)) assertTrue(fs.delete(testdir, true)); 581 FSUtils.setRootDir(htu.getConfiguration(), testdir); 582 return FSUtils.getRootDir(htu.getConfiguration()).toString(); 583 } 584 585 private Path createReferences(final MasterServices services, 586 final TableDescriptor td, final HRegionInfo parent, 587 final HRegionInfo daughter, final byte [] midkey, final boolean top) 588 throws IOException { 589 Path rootdir = services.getMasterFileSystem().getRootDir(); 590 Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable()); 591 Path storedir = HStore.getStoreHomedir(tabledir, daughter, 592 td.getColumnFamilies()[0].getName()); 593 Reference ref = 594 top? Reference.createTopReference(midkey): Reference.createBottomReference(midkey); 595 long now = System.currentTimeMillis(); 596 // Reference name has this format: StoreFile#REF_NAME_PARSER 597 Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName()); 598 FileSystem fs = services.getMasterFileSystem().getFileSystem(); 599 ref.write(fs, p); 600 return p; 601 } 602 603 private Result createResult(final HRegionInfo parent, final HRegionInfo a, 604 final HRegionInfo b) 605 throws IOException { 606 return MetaMockingUtil.getMetaTableRowResult(parent, null, a, b); 607 } 608}