View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import static org.apache.hadoop.hbase.util.HFileArchiveTestingUtil.assertArchiveEqualToOriginal;
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertTrue;
25  import static org.mockito.Mockito.doReturn;
26  import static org.mockito.Mockito.spy;
27  
28  import java.io.IOException;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.SortedMap;
32  import java.util.TreeMap;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.fs.FSDataOutputStream;
38  import org.apache.hadoop.fs.FileStatus;
39  import org.apache.hadoop.fs.FileSystem;
40  import org.apache.hadoop.fs.Path;
41  import org.apache.hadoop.hbase.ChoreService;
42  import org.apache.hadoop.hbase.CoordinatedStateManager;
43  import org.apache.hadoop.hbase.HBaseTestingUtility;
44  import org.apache.hadoop.hbase.HColumnDescriptor;
45  import org.apache.hadoop.hbase.HConstants;
46  import org.apache.hadoop.hbase.HRegionInfo;
47  import org.apache.hadoop.hbase.HTableDescriptor;
48  import org.apache.hadoop.hbase.MetaMockingUtil;
49  import org.apache.hadoop.hbase.NamespaceDescriptor;
50  import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
51  import org.apache.hadoop.hbase.ProcedureInfo;
52  import org.apache.hadoop.hbase.Server;
53  import org.apache.hadoop.hbase.ServerName;
54  import org.apache.hadoop.hbase.testclassification.SmallTests;
55  import org.apache.hadoop.hbase.TableDescriptors;
56  import org.apache.hadoop.hbase.TableName;
57  import org.apache.hadoop.hbase.client.ClusterConnection;
58  import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
59  import org.apache.hadoop.hbase.client.Result;
60  import org.apache.hadoop.hbase.coordination.BaseCoordinatedStateManager;
61  import org.apache.hadoop.hbase.coordination.SplitLogManagerCoordination;
62  import org.apache.hadoop.hbase.coordination.SplitLogManagerCoordination.SplitLogManagerDetails;
63  import org.apache.hadoop.hbase.executor.ExecutorService;
64  import org.apache.hadoop.hbase.io.Reference;
65  import org.apache.hadoop.hbase.master.CatalogJanitor.SplitParentFirstComparator;
66  import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
67  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
68  import org.apache.hadoop.hbase.procedure.MasterProcedureManagerHost;
69  import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
70  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
71  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
72  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
73  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
74  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiResponse;
75  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateRequest;
76  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateResponse;
77  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
78  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
79  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ResultOrException;
80  import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
81  import org.apache.hadoop.hbase.regionserver.HStore;
82  import org.apache.hadoop.hbase.security.User;
83  import org.apache.hadoop.hbase.util.Bytes;
84  import org.apache.hadoop.hbase.util.FSUtils;
85  import org.apache.hadoop.hbase.util.HFileArchiveUtil;
86  import org.apache.hadoop.hbase.util.Triple;
87  import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
88  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
89  import org.junit.Test;
90  import org.junit.experimental.categories.Category;
91  import org.mockito.Mockito;
92  import org.mockito.invocation.InvocationOnMock;
93  import org.mockito.stubbing.Answer;
94  
95  import com.google.protobuf.RpcController;
96  import com.google.protobuf.Service;
97  import com.google.protobuf.ServiceException;
98  
99  @Category(SmallTests.class)
100 public class TestCatalogJanitor {
101   private static final Log LOG = LogFactory.getLog(TestCatalogJanitor.class);
102 
103   /**
104    * Pseudo server for below tests.
105    * Be sure to call stop on the way out else could leave some mess around.
106    */
107   class MockServer implements Server {
108     private final ClusterConnection connection;
109     private final Configuration c;
110 
111     MockServer(final HBaseTestingUtility htu)
112     throws NotAllMetaRegionsOnlineException, IOException, InterruptedException {
113       this.c = htu.getConfiguration();
114       ClientProtos.ClientService.BlockingInterface ri =
115         Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
116       MutateResponse.Builder builder = MutateResponse.newBuilder();
117       builder.setProcessed(true);
118       try {
119         Mockito.when(ri.mutate(
120           (RpcController)Mockito.any(), (MutateRequest)Mockito.any())).
121             thenReturn(builder.build());
122       } catch (ServiceException se) {
123         throw ProtobufUtil.getRemoteException(se);
124       }
125       try {
126         Mockito.when(ri.multi(
127           (RpcController)Mockito.any(), (MultiRequest)Mockito.any())).
128             thenAnswer(new Answer<MultiResponse>() {
129               @Override
130               public MultiResponse answer(InvocationOnMock invocation) throws Throwable {
131                 return buildMultiResponse( (MultiRequest)invocation.getArguments()[1]);
132               }
133             });
134       } catch (ServiceException se) {
135         throw ProtobufUtil.getRemoteException(se);
136       }
137       // Mock an HConnection and a AdminProtocol implementation.  Have the
138       // HConnection return the HRI.  Have the HRI return a few mocked up responses
139       // to make our test work.
140       this.connection =
141         HConnectionTestingUtility.getMockedConnectionAndDecorate(this.c,
142           Mockito.mock(AdminProtos.AdminService.BlockingInterface.class), ri,
143             ServerName.valueOf("example.org,12345,6789"),
144           HRegionInfo.FIRST_META_REGIONINFO);
145       // Set hbase.rootdir into test dir.
146       FileSystem fs = FileSystem.get(this.c);
147       Path rootdir = FSUtils.getRootDir(this.c);
148       FSUtils.setRootDir(this.c, rootdir);
149       AdminProtos.AdminService.BlockingInterface hri =
150         Mockito.mock(AdminProtos.AdminService.BlockingInterface.class);
151     }
152 
153     @Override
154     public ClusterConnection getConnection() {
155       return this.connection;
156     }
157 
158     @Override
159     public MetaTableLocator getMetaTableLocator() {
160       return null;
161     }
162 
163     @Override
164     public Configuration getConfiguration() {
165       return this.c;
166     }
167 
168     @Override
169     public ServerName getServerName() {
170       return ServerName.valueOf("mockserver.example.org", 1234, -1L);
171     }
172 
173     @Override
174     public ZooKeeperWatcher getZooKeeper() {
175       return null;
176     }
177 
178     @Override
179     public CoordinatedStateManager getCoordinatedStateManager() {
180       BaseCoordinatedStateManager m = Mockito.mock(BaseCoordinatedStateManager.class);
181       SplitLogManagerCoordination c = Mockito.mock(SplitLogManagerCoordination.class);
182       Mockito.when(m.getSplitLogManagerCoordination()).thenReturn(c);
183       SplitLogManagerDetails d = Mockito.mock(SplitLogManagerDetails.class);
184       Mockito.when(c.getDetails()).thenReturn(d);
185       return m;
186     }
187 
188     @Override
189     public void abort(String why, Throwable e) {
190       //no-op
191     }
192 
193     @Override
194     public boolean isAborted() {
195       return false;
196     }
197 
198     @Override
199     public boolean isStopped() {
200       return false;
201     }
202 
203     @Override
204     public void stop(String why) {
205     }
206 
207     @Override
208     public ChoreService getChoreService() {
209       return null;
210     }
211   }
212 
213   /**
214    * Mock MasterServices for tests below.
215    */
216   class MockMasterServices implements MasterServices {
217     private final MasterFileSystem mfs;
218     private final AssignmentManager asm;
219     private final ServerManager sm;
220 
221     MockMasterServices(final Server server) throws IOException {
222       this.mfs = new MasterFileSystem(server, this);
223       this.asm = Mockito.mock(AssignmentManager.class);
224       this.sm = Mockito.mock(ServerManager.class);
225     }
226 
227     @Override
228     public void checkTableModifiable(TableName tableName) throws IOException {
229       //no-op
230     }
231 
232     @Override
233     public long createTable(
234         final HTableDescriptor desc,
235         final byte[][] splitKeys,
236         final long nonceGroup,
237         final long nonce) throws IOException {
238       // no-op
239       return -1;
240     }
241 
242     @Override
243     public long createSystemTable(final HTableDescriptor hTableDescriptor) throws IOException {
244       return -1;
245     }
246 
247     @Override
248     public SnapshotManager getSnapshotManager() {
249       return null;
250     }
251 
252     @Override
253     public MasterProcedureManagerHost getMasterProcedureManagerHost() {
254       return null;
255     }
256 
257     @Override
258     public AssignmentManager getAssignmentManager() {
259       return this.asm;
260     }
261 
262     @Override
263     public ExecutorService getExecutorService() {
264       return null;
265     }
266 
267     @Override
268     public ChoreService getChoreService() {
269       return null;
270     }
271 
272     @Override
273     public MasterFileSystem getMasterFileSystem() {
274       return this.mfs;
275     }
276 
277     @Override
278     public MasterCoprocessorHost getMasterCoprocessorHost() {
279       return null;
280     }
281 
282     @Override
283     public MasterQuotaManager getMasterQuotaManager() {
284       return null;
285     }
286 
287     @Override
288     public ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
289       return null;
290     }
291 
292     @Override
293     public ServerManager getServerManager() {
294       return sm;
295     }
296 
297     @Override
298     public ZooKeeperWatcher getZooKeeper() {
299       return null;
300     }
301 
302     @Override
303     public CoordinatedStateManager getCoordinatedStateManager() {
304       return null;
305     }
306 
307     @Override
308     public MetaTableLocator getMetaTableLocator() {
309       return null;
310     }
311 
312     @Override
313     public ClusterConnection getConnection() {
314       return null;
315     }
316 
317     @Override
318     public Configuration getConfiguration() {
319       return mfs.conf;
320     }
321 
322     @Override
323     public ServerName getServerName() {
324       return null;
325     }
326 
327     @Override
328     public void abort(String why, Throwable e) {
329       //no-op
330     }
331 
332     @Override
333     public boolean isAborted() {
334       return false;
335     }
336 
337     private boolean stopped = false;
338 
339     @Override
340     public void stop(String why) {
341       stopped = true;
342     }
343 
344     @Override
345     public boolean isStopped() {
346       return stopped;
347     }
348 
349     @Override
350     public TableDescriptors getTableDescriptors() {
351       return new TableDescriptors() {
352         @Override
353         public HTableDescriptor remove(TableName tablename) throws IOException {
354           // TODO Auto-generated method stub
355           return null;
356         }
357 
358         @Override
359         public Map<String, HTableDescriptor> getAll() throws IOException {
360           // TODO Auto-generated method stub
361           return null;
362         }
363 
364         @Override
365         public HTableDescriptor get(TableName tablename)
366         throws IOException {
367           return createHTableDescriptor();
368         }
369 
370         @Override
371         public Map<String, HTableDescriptor> getByNamespace(String name) throws IOException {
372           return null;
373         }
374 
375         @Override
376         public void add(HTableDescriptor htd) throws IOException {
377           // TODO Auto-generated method stub
378 
379         }
380         @Override
381         public void setCacheOn() throws IOException {
382         }
383 
384         @Override
385         public void setCacheOff() throws IOException {
386         }
387       };
388     }
389 
390     @Override
391     public boolean isServerCrashProcessingEnabled() {
392       return true;
393     }
394 
395     @Override
396     public boolean registerService(Service instance) {
397       return false;
398     }
399 
400     @Override
401     public void createNamespace(
402         final NamespaceDescriptor descriptor,
403         final long nonceGroup,
404         final long nonce) throws IOException {
405       //To change body of implemented methods use File | Settings | File Templates.
406     }
407 
408     @Override
409     public void createNamespaceSync(
410         final NamespaceDescriptor descriptor,
411         final long nonceGroup,
412         final long nonce,
413         final boolean executeCoprocessor) throws IOException {
414       //To change body of implemented methods use File | Settings | File Templates.
415     }
416 
417     @Override
418     public void modifyNamespace(
419         final NamespaceDescriptor descriptor,
420         final long nonceGroup,
421         final long nonce) throws IOException {
422       //To change body of implemented methods use File | Settings | File Templates.
423     }
424 
425     @Override
426     public void deleteNamespace(
427         final String name,
428         final long nonceGroup,
429         final long nonce) throws IOException {
430       //To change body of implemented methods use File | Settings | File Templates.
431     }
432 
433     @Override
434     public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException {
435       return null;  //To change body of implemented methods use File | Settings | File Templates.
436     }
437 
438     @Override
439     public List<NamespaceDescriptor> listNamespaceDescriptors() throws IOException {
440       return null;  //To change body of implemented methods use File | Settings | File Templates.
441     }
442 
443     @Override
444     public boolean abortProcedure(final long procId, final boolean mayInterruptIfRunning)
445         throws IOException {
446       return false;  //To change body of implemented methods use File | Settings | File Templates.
447     }
448 
449     @Override
450     public List<ProcedureInfo> listProcedures() throws IOException {
451       return null;  //To change body of implemented methods use File | Settings | File Templates.
452     }
453 
454     @Override
455     public List<HTableDescriptor> listTableDescriptorsByNamespace(String name) throws IOException {
456       return null;  //To change body of implemented methods use File | Settings | File Templates.
457     }
458 
459     @Override
460     public List<TableName> listTableNamesByNamespace(String name) throws IOException {
461       return null;
462     }
463 
464     @Override
465     public long deleteTable(
466         final TableName tableName,
467         final long nonceGroup,
468         final long nonce) throws IOException {
469       return -1;
470     }
471 
472     public void truncateTable(
473         final TableName tableName,
474         final boolean preserveSplits,
475         final long nonceGroup,
476         final long nonce) throws IOException {
477     }
478 
479     @Override
480     public void modifyTable(
481         final TableName tableName,
482         final HTableDescriptor descriptor,
483         final long nonceGroup,
484         final long nonce) throws IOException {
485     }
486 
487     @Override
488     public long enableTable(
489         final TableName tableName,
490         final long nonceGroup,
491         final long nonce) throws IOException {
492       return -1;
493     }
494     public LoadBalancer getLoadBalancer() {
495       return null;
496     }
497 
498     @Override
499     public long disableTable(
500         TableName tableName,
501         final long nonceGroup,
502         final long nonce) throws IOException {
503       return -1;
504     }
505 
506     @Override
507     public void addColumn(
508         final TableName tableName,
509         final HColumnDescriptor columnDescriptor,
510         final long nonceGroup,
511         final long nonce) throws IOException { }
512 
513     @Override
514     public void modifyColumn(
515         final TableName tableName,
516         final HColumnDescriptor descriptor,
517         final long nonceGroup,
518         final long nonce) throws IOException { }
519 
520     @Override
521     public void deleteColumn(
522         final TableName tableName,
523         final byte[] columnName,
524         final long nonceGroup,
525         final long nonce) throws IOException { }
526 
527     @Override
528     public TableLockManager getTableLockManager() {
529       return null;
530     }
531 
532     @Override
533     public TableNamespaceManager getTableNamespaceManager() {
534       return null;
535     }
536 
537     @Override
538     public void dispatchMergingRegions(HRegionInfo region_a, HRegionInfo region_b,
539         boolean forcible, User user) throws IOException {
540     }
541 
542     @Override
543     public boolean isInitialized() {
544       // Auto-generated method stub
545       return false;
546     }
547 
548     @Override
549     public boolean isInMaintenanceMode() {
550       return false;
551     }
552 
553     @Override
554     public long getLastMajorCompactionTimestamp(TableName table) throws IOException {
555       // Auto-generated method stub
556       return 0;
557     }
558 
559     @Override
560     public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
561       // Auto-generated method stub
562       return 0;
563     }
564 
565     @Override
566     public String getRegionServerVersion(ServerName sn) {
567       return null;
568     }
569 
570     @Override
571     public void checkIfShouldMoveSystemRegionAsync() {
572     }
573   }
574 
575   @Test
576   public void testCleanParent() throws IOException, InterruptedException {
577     HBaseTestingUtility htu = new HBaseTestingUtility();
578     setRootDirAndCleanIt(htu, "testCleanParent");
579     Server server = new MockServer(htu);
580     try {
581       MasterServices services = new MockMasterServices(server);
582       CatalogJanitor janitor = new CatalogJanitor(server, services);
583       // Create regions.
584       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("table"));
585       htd.addFamily(new HColumnDescriptor("f"));
586       HRegionInfo parent =
587         new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
588             Bytes.toBytes("eee"));
589       HRegionInfo splita =
590         new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
591             Bytes.toBytes("ccc"));
592       HRegionInfo splitb =
593         new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
594             Bytes.toBytes("eee"));
595       // Test that when both daughter regions are in place, that we do not
596       // remove the parent.
597       Result r = createResult(parent, splita, splitb);
598       // Add a reference under splitA directory so we don't clear out the parent.
599       Path rootdir = services.getMasterFileSystem().getRootDir();
600       Path tabledir =
601         FSUtils.getTableDir(rootdir, htd.getTableName());
602       Path storedir = HStore.getStoreHomedir(tabledir, splita,
603           htd.getColumnFamilies()[0].getName());
604       Reference ref = Reference.createTopReference(Bytes.toBytes("ccc"));
605       long now = System.currentTimeMillis();
606       // Reference name has this format: StoreFile#REF_NAME_PARSER
607       Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName());
608       FileSystem fs = services.getMasterFileSystem().getFileSystem();
609       Path path = ref.write(fs, p);
610       assertTrue(fs.exists(path));
611       assertFalse(janitor.cleanParent(parent, r));
612       // Remove the reference file and try again.
613       assertTrue(fs.delete(p, true));
614       assertTrue(janitor.cleanParent(parent, r));
615     } finally {
616       server.stop("shutdown");
617     }
618   }
619 
620   /**
621    * Make sure parent gets cleaned up even if daughter is cleaned up before it.
622    * @throws IOException
623    * @throws InterruptedException
624    */
625   @Test
626   public void testParentCleanedEvenIfDaughterGoneFirst()
627   throws IOException, InterruptedException {
628     parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
629       "testParentCleanedEvenIfDaughterGoneFirst", Bytes.toBytes("eee"));
630   }
631 
632   /**
633    * Make sure last parent with empty end key gets cleaned up even if daughter is cleaned up before it.
634    * @throws IOException
635    * @throws InterruptedException
636    */
637   @Test
638   public void testLastParentCleanedEvenIfDaughterGoneFirst()
639   throws IOException, InterruptedException {
640     parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
641       "testLastParentCleanedEvenIfDaughterGoneFirst", new byte[0]);
642   }
643 
644   /**
645    * Make sure parent with specified end key gets cleaned up even if daughter is cleaned up before it.
646    *
647    * @param rootDir the test case name, used as the HBase testing utility root
648    * @param lastEndKey the end key of the split parent
649    * @throws IOException
650    * @throws InterruptedException
651    */
652   private void parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
653   final String rootDir, final byte[] lastEndKey)
654   throws IOException, InterruptedException {
655     HBaseTestingUtility htu = new HBaseTestingUtility();
656     setRootDirAndCleanIt(htu, rootDir);
657     Server server = new MockServer(htu);
658     MasterServices services = new MockMasterServices(server);
659     CatalogJanitor janitor = new CatalogJanitor(server, services);
660     final HTableDescriptor htd = createHTableDescriptor();
661 
662     // Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
663 
664     // Parent
665     HRegionInfo parent = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
666       lastEndKey);
667     // Sleep a second else the encoded name on these regions comes out
668     // same for all with same start key and made in same second.
669     Thread.sleep(1001);
670 
671     // Daughter a
672     HRegionInfo splita = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
673       Bytes.toBytes("ccc"));
674     Thread.sleep(1001);
675     // Make daughters of daughter a; splitaa and splitab.
676     HRegionInfo splitaa = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
677       Bytes.toBytes("bbb"));
678     HRegionInfo splitab = new HRegionInfo(htd.getTableName(), Bytes.toBytes("bbb"),
679       Bytes.toBytes("ccc"));
680 
681     // Daughter b
682     HRegionInfo splitb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
683       lastEndKey);
684     Thread.sleep(1001);
685     // Make Daughters of daughterb; splitba and splitbb.
686     HRegionInfo splitba = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
687       Bytes.toBytes("ddd"));
688     HRegionInfo splitbb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ddd"),
689     lastEndKey);
690 
691     // First test that our Comparator works right up in CatalogJanitor.
692     // Just fo kicks.
693     SortedMap<HRegionInfo, Result> regions =
694       new TreeMap<HRegionInfo, Result>(new CatalogJanitor.SplitParentFirstComparator());
695     // Now make sure that this regions map sorts as we expect it to.
696     regions.put(parent, createResult(parent, splita, splitb));
697     regions.put(splitb, createResult(splitb, splitba, splitbb));
698     regions.put(splita, createResult(splita, splitaa, splitab));
699     // Assert its properly sorted.
700     int index = 0;
701     for (Map.Entry<HRegionInfo, Result> e: regions.entrySet()) {
702       if (index == 0) {
703         assertTrue(e.getKey().getEncodedName().equals(parent.getEncodedName()));
704       } else if (index == 1) {
705         assertTrue(e.getKey().getEncodedName().equals(splita.getEncodedName()));
706       } else if (index == 2) {
707         assertTrue(e.getKey().getEncodedName().equals(splitb.getEncodedName()));
708       }
709       index++;
710     }
711 
712     // Now play around with the cleanParent function.  Create a ref from splita
713     // up to the parent.
714     Path splitaRef =
715       createReferences(services, htd, parent, splita, Bytes.toBytes("ccc"), false);
716     // Make sure actual super parent sticks around because splita has a ref.
717     assertFalse(janitor.cleanParent(parent, regions.get(parent)));
718 
719     //splitba, and split bb, do not have dirs in fs.  That means that if
720     // we test splitb, it should get cleaned up.
721     assertTrue(janitor.cleanParent(splitb, regions.get(splitb)));
722 
723     // Now remove ref from splita to parent... so parent can be let go and so
724     // the daughter splita can be split (can't split if still references).
725     // BUT make the timing such that the daughter gets cleaned up before we
726     // can get a chance to let go of the parent.
727     FileSystem fs = FileSystem.get(htu.getConfiguration());
728     assertTrue(fs.delete(splitaRef, true));
729     // Create the refs from daughters of splita.
730     Path splitaaRef =
731       createReferences(services, htd, splita, splitaa, Bytes.toBytes("bbb"), false);
732     Path splitabRef =
733       createReferences(services, htd, splita, splitab, Bytes.toBytes("bbb"), true);
734 
735     // Test splita.  It should stick around because references from splitab, etc.
736     assertFalse(janitor.cleanParent(splita, regions.get(splita)));
737 
738     // Now clean up parent daughter first.  Remove references from its daughters.
739     assertTrue(fs.delete(splitaaRef, true));
740     assertTrue(fs.delete(splitabRef, true));
741     assertTrue(janitor.cleanParent(splita, regions.get(splita)));
742 
743     // Super parent should get cleaned up now both splita and splitb are gone.
744     assertTrue(janitor.cleanParent(parent, regions.get(parent)));
745 
746     services.stop("test finished");
747     janitor.cancel(true);
748   }
749 
750   /**
751    * CatalogJanitor.scan() should not clean parent regions if their own
752    * parents are still referencing them. This ensures that grandfather regions
753    * do not point to deleted parent regions.
754    */
755   @Test
756   public void testScanDoesNotCleanRegionsWithExistingParents() throws Exception {
757     HBaseTestingUtility htu = new HBaseTestingUtility();
758     setRootDirAndCleanIt(htu, "testScanDoesNotCleanRegionsWithExistingParents");
759     Server server = new MockServer(htu);
760     MasterServices services = new MockMasterServices(server);
761 
762     final HTableDescriptor htd = createHTableDescriptor();
763 
764     // Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
765 
766     // Parent
767     HRegionInfo parent = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
768       new byte[0], true);
769     // Sleep a second else the encoded name on these regions comes out
770     // same for all with same start key and made in same second.
771     Thread.sleep(1001);
772 
773     // Daughter a
774     HRegionInfo splita = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
775       Bytes.toBytes("ccc"), true);
776     Thread.sleep(1001);
777     // Make daughters of daughter a; splitaa and splitab.
778     HRegionInfo splitaa = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
779       Bytes.toBytes("bbb"), false);
780     HRegionInfo splitab = new HRegionInfo(htd.getTableName(), Bytes.toBytes("bbb"),
781       Bytes.toBytes("ccc"), false);
782 
783     // Daughter b
784     HRegionInfo splitb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
785         new byte[0]);
786     Thread.sleep(1001);
787 
788     final Map<HRegionInfo, Result> splitParents =
789         new TreeMap<HRegionInfo, Result>(new SplitParentFirstComparator());
790     splitParents.put(parent, createResult(parent, splita, splitb));
791     splita.setOffline(true); //simulate that splita goes offline when it is split
792     splitParents.put(splita, createResult(splita, splitaa,splitab));
793 
794     final Map<HRegionInfo, Result> mergedRegions = new TreeMap<HRegionInfo, Result>();
795     CatalogJanitor janitor = spy(new CatalogJanitor(server, services));
796     doReturn(new Triple<Integer, Map<HRegionInfo, Result>, Map<HRegionInfo, Result>>(
797             10, mergedRegions, splitParents)).when(janitor)
798         .getMergedRegionsAndSplitParents();
799 
800     //create ref from splita to parent
801     Path splitaRef =
802         createReferences(services, htd, parent, splita, Bytes.toBytes("ccc"), false);
803 
804     //parent and A should not be removed
805     assertEquals(0, janitor.scan());
806 
807     //now delete the ref
808     FileSystem fs = FileSystem.get(htu.getConfiguration());
809     assertTrue(fs.delete(splitaRef, true));
810 
811     //now, both parent, and splita can be deleted
812     assertEquals(2, janitor.scan());
813 
814     services.stop("test finished");
815     janitor.cancel(true);
816   }
817 
818   /**
819    * Test that we correctly archive all the storefiles when a region is deleted
820    * @throws Exception
821    */
822   @Test
823   public void testSplitParentFirstComparator() {
824     SplitParentFirstComparator comp = new SplitParentFirstComparator();
825     final HTableDescriptor htd = createHTableDescriptor();
826 
827     /*  Region splits:
828      *
829      *  rootRegion --- firstRegion --- firstRegiona
830      *              |               |- firstRegionb
831      *              |
832      *              |- lastRegion --- lastRegiona  --- lastRegionaa
833      *                             |                |- lastRegionab
834      *                             |- lastRegionb
835      *
836      *  rootRegion   :   []  - []
837      *  firstRegion  :   []  - bbb
838      *  lastRegion   :   bbb - []
839      *  firstRegiona :   []  - aaa
840      *  firstRegionb :   aaa - bbb
841      *  lastRegiona  :   bbb - ddd
842      *  lastRegionb  :   ddd - []
843      */
844 
845     // root region
846     HRegionInfo rootRegion = new HRegionInfo(htd.getTableName(),
847       HConstants.EMPTY_START_ROW,
848       HConstants.EMPTY_END_ROW, true);
849     HRegionInfo firstRegion = new HRegionInfo(htd.getTableName(),
850       HConstants.EMPTY_START_ROW,
851       Bytes.toBytes("bbb"), true);
852     HRegionInfo lastRegion = new HRegionInfo(htd.getTableName(),
853       Bytes.toBytes("bbb"),
854       HConstants.EMPTY_END_ROW, true);
855 
856     assertTrue(comp.compare(rootRegion, rootRegion) == 0);
857     assertTrue(comp.compare(firstRegion, firstRegion) == 0);
858     assertTrue(comp.compare(lastRegion, lastRegion) == 0);
859     assertTrue(comp.compare(rootRegion, firstRegion) < 0);
860     assertTrue(comp.compare(rootRegion, lastRegion) < 0);
861     assertTrue(comp.compare(firstRegion, lastRegion) < 0);
862 
863     //first region split into a, b
864     HRegionInfo firstRegiona = new HRegionInfo(htd.getTableName(),
865       HConstants.EMPTY_START_ROW,
866       Bytes.toBytes("aaa"), true);
867     HRegionInfo firstRegionb = new HRegionInfo(htd.getTableName(),
868         Bytes.toBytes("aaa"),
869       Bytes.toBytes("bbb"), true);
870     //last region split into a, b
871     HRegionInfo lastRegiona = new HRegionInfo(htd.getTableName(),
872       Bytes.toBytes("bbb"),
873       Bytes.toBytes("ddd"), true);
874     HRegionInfo lastRegionb = new HRegionInfo(htd.getTableName(),
875       Bytes.toBytes("ddd"),
876       HConstants.EMPTY_END_ROW, true);
877 
878     assertTrue(comp.compare(firstRegiona, firstRegiona) == 0);
879     assertTrue(comp.compare(firstRegionb, firstRegionb) == 0);
880     assertTrue(comp.compare(rootRegion, firstRegiona) < 0);
881     assertTrue(comp.compare(rootRegion, firstRegionb) < 0);
882     assertTrue(comp.compare(firstRegion, firstRegiona) < 0);
883     assertTrue(comp.compare(firstRegion, firstRegionb) < 0);
884     assertTrue(comp.compare(firstRegiona, firstRegionb) < 0);
885 
886     assertTrue(comp.compare(lastRegiona, lastRegiona) == 0);
887     assertTrue(comp.compare(lastRegionb, lastRegionb) == 0);
888     assertTrue(comp.compare(rootRegion, lastRegiona) < 0);
889     assertTrue(comp.compare(rootRegion, lastRegionb) < 0);
890     assertTrue(comp.compare(lastRegion, lastRegiona) < 0);
891     assertTrue(comp.compare(lastRegion, lastRegionb) < 0);
892     assertTrue(comp.compare(lastRegiona, lastRegionb) < 0);
893 
894     assertTrue(comp.compare(firstRegiona, lastRegiona) < 0);
895     assertTrue(comp.compare(firstRegiona, lastRegionb) < 0);
896     assertTrue(comp.compare(firstRegionb, lastRegiona) < 0);
897     assertTrue(comp.compare(firstRegionb, lastRegionb) < 0);
898 
899     HRegionInfo lastRegionaa = new HRegionInfo(htd.getTableName(),
900       Bytes.toBytes("bbb"),
901       Bytes.toBytes("ccc"), false);
902     HRegionInfo lastRegionab = new HRegionInfo(htd.getTableName(),
903       Bytes.toBytes("ccc"),
904       Bytes.toBytes("ddd"), false);
905 
906     assertTrue(comp.compare(lastRegiona, lastRegionaa) < 0);
907     assertTrue(comp.compare(lastRegiona, lastRegionab) < 0);
908     assertTrue(comp.compare(lastRegionaa, lastRegionab) < 0);
909 
910   }
911 
912   @Test
913   public void testArchiveOldRegion() throws Exception {
914     String table = "table";
915     HBaseTestingUtility htu = new HBaseTestingUtility();
916     setRootDirAndCleanIt(htu, "testCleanParent");
917     Server server = new MockServer(htu);
918     MasterServices services = new MockMasterServices(server);
919 
920     // create the janitor
921     CatalogJanitor janitor = new CatalogJanitor(server, services);
922 
923     // Create regions.
924     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(table));
925     htd.addFamily(new HColumnDescriptor("f"));
926     HRegionInfo parent = new HRegionInfo(htd.getTableName(),
927         Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
928     HRegionInfo splita = new HRegionInfo(htd.getTableName(),
929         Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
930     HRegionInfo splitb = new HRegionInfo(htd.getTableName(),
931         Bytes.toBytes("ccc"),
932         Bytes.toBytes("eee"));
933 
934     // Test that when both daughter regions are in place, that we do not
935     // remove the parent.
936     Result parentMetaRow = createResult(parent, splita, splitb);
937     FileSystem fs = FileSystem.get(htu.getConfiguration());
938     Path rootdir = services.getMasterFileSystem().getRootDir();
939     // have to set the root directory since we use it in HFileDisposer to figure out to get to the
940     // archive directory. Otherwise, it just seems to pick the first root directory it can find (so
941     // the single test passes, but when the full suite is run, things get borked).
942     FSUtils.setRootDir(fs.getConf(), rootdir);
943     Path tabledir = FSUtils.getTableDir(rootdir, htd.getTableName());
944     Path storedir = HStore.getStoreHomedir(tabledir, parent, htd.getColumnFamilies()[0].getName());
945     Path storeArchive = HFileArchiveUtil.getStoreArchivePath(services.getConfiguration(), parent,
946       tabledir, htd.getColumnFamilies()[0].getName());
947     LOG.debug("Table dir:" + tabledir);
948     LOG.debug("Store dir:" + storedir);
949     LOG.debug("Store archive dir:" + storeArchive);
950 
951     // add a couple of store files that we can check for
952     FileStatus[] mockFiles = addMockStoreFiles(2, services, storedir);
953     // get the current store files for comparison
954     FileStatus[] storeFiles = fs.listStatus(storedir);
955     int index = 0;
956     for (FileStatus file : storeFiles) {
957       LOG.debug("Have store file:" + file.getPath());
958       assertEquals("Got unexpected store file", mockFiles[index].getPath(),
959         storeFiles[index].getPath());
960       index++;
961     }
962 
963     // do the cleaning of the parent
964     assertTrue(janitor.cleanParent(parent, parentMetaRow));
965     LOG.debug("Finished cleanup of parent region");
966 
967     // and now check to make sure that the files have actually been archived
968     FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive);
969     logFiles("archived files", storeFiles);
970     logFiles("archived files", archivedStoreFiles);
971 
972     assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs);
973 
974     // cleanup
975     FSUtils.delete(fs, rootdir, true);
976     services.stop("Test finished");
977     server.stop("Test finished");
978     janitor.cancel(true);
979   }
980 
981   /**
982    * @param description description of the files for logging
983    * @param storeFiles the status of the files to log
984    */
985   private void logFiles(String description, FileStatus[] storeFiles) {
986     LOG.debug("Current " + description + ": ");
987     for (FileStatus file : storeFiles) {
988       LOG.debug(file.getPath());
989     }
990   }
991 
992   /**
993    * Test that if a store file with the same name is present as those already backed up cause the
994    * already archived files to be timestamped backup
995    */
996   @Test
997   public void testDuplicateHFileResolution() throws Exception {
998     String table = "table";
999     HBaseTestingUtility htu = new HBaseTestingUtility();
1000     setRootDirAndCleanIt(htu, "testCleanParent");
1001     Server server = new MockServer(htu);
1002     MasterServices services = new MockMasterServices(server);
1003 
1004     // create the janitor
1005 
1006     CatalogJanitor janitor = new CatalogJanitor(server, services);
1007 
1008     // Create regions.
1009     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(table));
1010     htd.addFamily(new HColumnDescriptor("f"));
1011     HRegionInfo parent = new HRegionInfo(htd.getTableName(),
1012         Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
1013     HRegionInfo splita = new HRegionInfo(htd.getTableName(),
1014         Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
1015     HRegionInfo splitb = new HRegionInfo(htd.getTableName(),
1016         Bytes.toBytes("ccc"), Bytes.toBytes("eee"));
1017     // Test that when both daughter regions are in place, that we do not
1018     // remove the parent.
1019     Result r = createResult(parent, splita, splitb);
1020 
1021     FileSystem fs = FileSystem.get(htu.getConfiguration());
1022 
1023     Path rootdir = services.getMasterFileSystem().getRootDir();
1024     // have to set the root directory since we use it in HFileDisposer to figure out to get to the
1025     // archive directory. Otherwise, it just seems to pick the first root directory it can find (so
1026     // the single test passes, but when the full suite is run, things get borked).
1027     FSUtils.setRootDir(fs.getConf(), rootdir);
1028     Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable());
1029     Path storedir = HStore.getStoreHomedir(tabledir, parent, htd.getColumnFamilies()[0].getName());
1030     System.out.println("Old root:" + rootdir);
1031     System.out.println("Old table:" + tabledir);
1032     System.out.println("Old store:" + storedir);
1033 
1034     Path storeArchive = HFileArchiveUtil.getStoreArchivePath(services.getConfiguration(), parent,
1035       tabledir, htd.getColumnFamilies()[0].getName());
1036     System.out.println("Old archive:" + storeArchive);
1037 
1038     // enable archiving, make sure that files get archived
1039     addMockStoreFiles(2, services, storedir);
1040     // get the current store files for comparison
1041     FileStatus[] storeFiles = fs.listStatus(storedir);
1042     // do the cleaning of the parent
1043     assertTrue(janitor.cleanParent(parent, r));
1044 
1045     // and now check to make sure that the files have actually been archived
1046     FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive);
1047     assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs);
1048 
1049     // now add store files with the same names as before to check backup
1050     // enable archiving, make sure that files get archived
1051     addMockStoreFiles(2, services, storedir);
1052 
1053     // do the cleaning of the parent
1054     assertTrue(janitor.cleanParent(parent, r));
1055 
1056     // and now check to make sure that the files have actually been archived
1057     archivedStoreFiles = fs.listStatus(storeArchive);
1058     assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs, true);
1059 
1060     // cleanup
1061     services.stop("Test finished");
1062     server.stop("shutdown");
1063     janitor.cancel(true);
1064   }
1065 
1066   private FileStatus[] addMockStoreFiles(int count, MasterServices services, Path storedir)
1067       throws IOException {
1068     // get the existing store files
1069     FileSystem fs = services.getMasterFileSystem().getFileSystem();
1070     fs.mkdirs(storedir);
1071     // create the store files in the parent
1072     for (int i = 0; i < count; i++) {
1073       Path storeFile = new Path(storedir, "_store" + i);
1074       FSDataOutputStream dos = fs.create(storeFile, true);
1075       dos.writeBytes("Some data: " + i);
1076       dos.close();
1077     }
1078     LOG.debug("Adding " + count + " store files to the storedir:" + storedir);
1079     // make sure the mock store files are there
1080     FileStatus[] storeFiles = fs.listStatus(storedir);
1081     assertEquals("Didn't have expected store files", count, storeFiles.length);
1082     return storeFiles;
1083   }
1084 
1085   private String setRootDirAndCleanIt(final HBaseTestingUtility htu,
1086       final String subdir)
1087   throws IOException {
1088     Path testdir = htu.getDataTestDir(subdir);
1089     FileSystem fs = FileSystem.get(htu.getConfiguration());
1090     if (fs.exists(testdir)) assertTrue(fs.delete(testdir, true));
1091     FSUtils.setRootDir(htu.getConfiguration(), testdir);
1092     return FSUtils.getRootDir(htu.getConfiguration()).toString();
1093   }
1094 
1095   /**
1096    * @param services Master services instance.
1097    * @param htd
1098    * @param parent
1099    * @param daughter
1100    * @param midkey
1101    * @param top True if we are to write a 'top' reference.
1102    * @return Path to reference we created.
1103    * @throws IOException
1104    */
1105   private Path createReferences(final MasterServices services,
1106       final HTableDescriptor htd, final HRegionInfo parent,
1107       final HRegionInfo daughter, final byte [] midkey, final boolean top)
1108   throws IOException {
1109     Path rootdir = services.getMasterFileSystem().getRootDir();
1110     Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable());
1111     Path storedir = HStore.getStoreHomedir(tabledir, daughter,
1112       htd.getColumnFamilies()[0].getName());
1113     Reference ref =
1114       top? Reference.createTopReference(midkey): Reference.createBottomReference(midkey);
1115     long now = System.currentTimeMillis();
1116     // Reference name has this format: StoreFile#REF_NAME_PARSER
1117     Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName());
1118     FileSystem fs = services.getMasterFileSystem().getFileSystem();
1119     ref.write(fs, p);
1120     return p;
1121   }
1122 
1123   private Result createResult(final HRegionInfo parent, final HRegionInfo a,
1124       final HRegionInfo b)
1125   throws IOException {
1126     return MetaMockingUtil.getMetaTableRowResult(parent, null, a, b);
1127   }
1128 
1129   private HTableDescriptor createHTableDescriptor() {
1130     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("t"));
1131     htd.addFamily(new HColumnDescriptor("f"));
1132     return htd;
1133   }
1134 
1135   private MultiResponse buildMultiResponse(MultiRequest req) {
1136     MultiResponse.Builder builder = MultiResponse.newBuilder();
1137     RegionActionResult.Builder regionActionResultBuilder =
1138         RegionActionResult.newBuilder();
1139     ResultOrException.Builder roeBuilder = ResultOrException.newBuilder();
1140     for (RegionAction regionAction: req.getRegionActionList()) {
1141       regionActionResultBuilder.clear();
1142       for (ClientProtos.Action action: regionAction.getActionList()) {
1143         roeBuilder.clear();
1144         roeBuilder.setResult(ClientProtos.Result.getDefaultInstance());
1145         roeBuilder.setIndex(action.getIndex());
1146         regionActionResultBuilder.addResultOrException(roeBuilder.build());
1147       }
1148       builder.addRegionActionResult(regionActionResultBuilder.build());
1149     }
1150     return builder.build();
1151   }
1152 
1153 }
1154