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}