1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.util;
21  
22  import static org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter.ERROR_CODE;
23  import static org.junit.Assert.assertArrayEquals;
24  import static org.junit.Assert.assertEquals;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.fs.FileStatus;
30  import org.apache.hadoop.fs.FileSystem;
31  import org.apache.hadoop.fs.Path;
32  import org.apache.hadoop.hbase.*;
33  import org.apache.hadoop.hbase.client.*;
34  import org.apache.hadoop.hbase.ipc.HRegionInterface;
35  import org.junit.BeforeClass;
36  import org.junit.Test;
37  
38  import java.io.IOException;
39  import java.util.ArrayList;
40  import java.util.Arrays;
41  import java.util.List;
42  import java.util.Random;
43  
44  public class TestHBaseFsck {
45  
46    final Log LOG = LogFactory.getLog(getClass());
47    private final static HBaseTestingUtility TEST_UTIL =
48        new HBaseTestingUtility();
49    private final static Configuration conf = TEST_UTIL.getConfiguration();
50    private final static byte[] TABLE = Bytes.toBytes("table");
51    private final static byte[] FAM = Bytes.toBytes("fam");
52  
53    @BeforeClass
54    public static void setUpBeforeClass() throws Exception {
55      TEST_UTIL.startMiniCluster(3);
56    }
57  
58    private List doFsck(boolean fix) throws Exception {
59      HBaseFsck fsck = new HBaseFsck(conf);
60      fsck.displayFullReport();  // i.e. -details
61      fsck.setTimeLag(0);
62      fsck.setFixErrors(fix);
63      fsck.doWork();
64      return fsck.getErrors().getErrorList();
65    }
66  
67    private void assertNoErrors(List errs) throws Exception {
68      assertEquals(0, errs.size());
69    }
70  
71    private void assertErrors(List errs, ERROR_CODE[] expectedErrors) {
72      assertEquals(Arrays.asList(expectedErrors), errs);
73    }
74  
75    @Test
76    public void testHBaseFsck() throws Exception {
77      assertNoErrors(doFsck(false));
78  
79      TEST_UTIL.createTable(TABLE, FAM);
80  
81      // We created 1 table, should be fine
82      assertNoErrors(doFsck(false));
83  
84      // Now let's mess it up and change the assignment in .META. to
85      // point to a different region server
86      HTable meta = new HTable(conf, HTableDescriptor.META_TABLEDESC.getName());
87      ResultScanner scanner = meta.getScanner(new Scan());
88  
89      resforloop:
90      for (Result res : scanner) {
91        long startCode = Bytes.toLong(res.getValue(HConstants.CATALOG_FAMILY,
92            HConstants.STARTCODE_QUALIFIER));
93  
94        for (JVMClusterUtil.RegionServerThread rs :
95            TEST_UTIL.getHBaseCluster().getRegionServerThreads()) {
96  
97          HServerInfo hsi = rs.getRegionServer().getServerInfo();
98  
99          // When we find a diff RS, change the assignment and break
100         if (startCode != hsi.getStartCode()) {
101           Put put = new Put(res.getRow());
102           put.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
103               Bytes.toBytes(hsi.getHostnamePort()));
104           put.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
105               Bytes.toBytes(hsi.getStartCode()));
106           meta.put(put);
107           break resforloop;
108         }
109       }
110     }
111 
112     // Try to fix the data
113     assertErrors(doFsck(true), new ERROR_CODE[]{
114         ERROR_CODE.SERVER_DOES_NOT_MATCH_META});
115     Thread.sleep(15000);
116 
117     // Should be fixed now
118     assertNoErrors(doFsck(false));
119 
120     // comment needed - what is the purpose of this line
121     new HTable(conf, TABLE).getScanner(new Scan());
122   }
123 
124   private HRegionInfo createRegion(Configuration conf, final HTableDescriptor
125       htd, byte[] startKey, byte[] endKey)
126       throws IOException {
127     HTable meta = new HTable(conf, HConstants.META_TABLE_NAME);
128     HRegionInfo hri = new HRegionInfo(htd, startKey, endKey);
129     Put put = new Put(hri.getRegionName());
130     put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
131         Writables.getBytes(hri));
132     meta.put(put);
133     return hri;
134   }
135 
136   @Test
137   /**
138    * Tests for inconsistencies in the META data (duplicate start keys, or holes)
139    */
140   public void testHBaseFsckMeta() throws Exception {
141     assertNoErrors(doFsck(false));
142 
143     HTable tbl = TEST_UTIL.createTable(Bytes.toBytes("table2"), FAM);
144 
145     HRegionInfo hriOrig = tbl.getRegionsInfo().keySet().iterator().next();
146     HServerAddress rsAddressOrig = tbl.getRegionsInfo().get(hriOrig);
147 
148     byte[][] startKeys = new byte[][]{
149         HConstants.EMPTY_BYTE_ARRAY,
150         Bytes.toBytes("A"),
151         Bytes.toBytes("B"),
152         Bytes.toBytes("C")
153     };
154     TEST_UTIL.createMultiRegions(conf, tbl, FAM, startKeys);
155     Path rootDir = new Path(conf.get(HConstants.HBASE_DIR));
156     FileSystem fs = rootDir.getFileSystem(conf);
157     Path p = new Path(rootDir + "/table2", hriOrig.getEncodedName());
158     fs.delete(p, true);
159 
160     Thread.sleep(1 * 1000);
161     ArrayList servers = new ArrayList();
162     servers.add(rsAddressOrig);
163     HBaseFsckRepair.fixDupeAssignment(conf, hriOrig, servers);
164 
165     // We created 1 table, should be fine
166     assertNoErrors(doFsck(false));
167 
168     // Now let's mess it up, by adding a region with a duplicate startkey
169     HRegionInfo hriDupe = createRegion(conf, tbl.getTableDescriptor(),
170         Bytes.toBytes("A"), Bytes.toBytes("A2"));
171     TEST_UTIL.getHBaseCluster().getMaster().assignRegion(hriDupe);
172     TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
173         .waitForAssignment(hriDupe);
174     assertErrors(doFsck(false), new ERROR_CODE[]{ERROR_CODE.DUPE_STARTKEYS});
175 
176     // Mess it up by creating an overlap in the metadata
177     HRegionInfo hriOverlap = createRegion(conf, tbl.getTableDescriptor(),
178         Bytes.toBytes("A2"), Bytes.toBytes("B2"));
179     TEST_UTIL.getHBaseCluster().getMaster().assignRegion(hriOverlap);
180     TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
181         .waitForAssignment(hriOverlap);
182     assertErrors(doFsck(false), new ERROR_CODE[]{
183         ERROR_CODE.DUPE_STARTKEYS, ERROR_CODE.OVERLAP_IN_REGION_CHAIN,
184         ERROR_CODE.OVERLAP_IN_REGION_CHAIN});
185 
186     // Mess it up by leaving a hole in the meta data
187     HRegionInfo hriHole = createRegion(conf, tbl.getTableDescriptor(),
188         Bytes.toBytes("D"), Bytes.toBytes("E"));
189     TEST_UTIL.getHBaseCluster().getMaster().assignRegion(hriHole);
190     TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
191         .waitForAssignment(hriHole);
192 //    assertError(doFsck(false), ERROR_CODE.OVERLAP_IN_REGION_CHAIN);
193     assertErrors(doFsck(false), new ERROR_CODE[]{ ERROR_CODE.DUPE_STARTKEYS,
194         ERROR_CODE.OVERLAP_IN_REGION_CHAIN, ERROR_CODE.OVERLAP_IN_REGION_CHAIN,
195         ERROR_CODE.HOLE_IN_REGION_CHAIN });
196 
197   }
198 
199 }