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