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.client;
20  
21  import javax.annotation.Nullable;
22  
23  import java.io.IOException;
24  import static org.apache.hadoop.hbase.util.hbck.HbckTestingUtil.assertErrors;
25  import static org.apache.hadoop.hbase.util.hbck.HbckTestingUtil.doFsck;
26  import static org.junit.Assert.*;
27  
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.List;
31  import java.util.concurrent.ExecutorService;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.hbase.Abortable;
37  import org.apache.hadoop.hbase.HBaseTestingUtility;
38  import org.apache.hadoop.hbase.HConstants;
39  import org.apache.hadoop.hbase.HRegionInfo;
40  import org.apache.hadoop.hbase.HRegionLocation;
41  import org.apache.hadoop.hbase.MetaTableAccessor;
42  import org.apache.hadoop.hbase.RegionLocations;
43  import org.apache.hadoop.hbase.ServerName;
44  import org.apache.hadoop.hbase.TableName;
45  import org.apache.hadoop.hbase.TableNotFoundException;
46  import org.apache.hadoop.hbase.Waiter;
47  import org.apache.hadoop.hbase.client.ConnectionManager.HConnectionImplementation;
48  import org.apache.hadoop.hbase.regionserver.StorefileRefresherChore;
49  import org.apache.hadoop.hbase.testclassification.MediumTests;
50  import org.apache.hadoop.hbase.util.Bytes;
51  import org.apache.hadoop.hbase.util.HBaseFsck;
52  import org.apache.hadoop.hbase.util.HBaseFsckRepair;
53  import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter.ERROR_CODE;
54  import org.apache.hadoop.hbase.util.hbck.HbckTestingUtil;
55  import org.apache.hadoop.hbase.zookeeper.LoadBalancerTracker;
56  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
57  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
58  import org.junit.After;
59  import org.junit.Before;
60  import org.junit.Test;
61  import org.junit.experimental.categories.Category;
62  
63  /**
64   * Tests the scenarios where replicas are enabled for the meta table
65   */
66  @Category(MediumTests.class)
67  public class TestMetaWithReplicas {
68    static final Log LOG = LogFactory.getLog(TestMetaWithReplicas.class);
69    private final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
70  
71    @Before
72    public void setup() throws Exception {
73      TEST_UTIL.getConfiguration().setInt("zookeeper.session.timeout", 30000);
74      TEST_UTIL.getConfiguration().setInt(HConstants.META_REPLICAS_NUM, 3);
75      TEST_UTIL.getConfiguration().setInt(
76          StorefileRefresherChore.REGIONSERVER_STOREFILE_REFRESH_PERIOD, 1000);
77      TEST_UTIL.startMiniCluster(3);
78      // disable the balancer
79      LoadBalancerTracker l = new LoadBalancerTracker(TEST_UTIL.getZooKeeperWatcher(),
80          new Abortable() {
81        boolean aborted = false;
82        @Override
83        public boolean isAborted() {
84          return aborted;
85        }
86        @Override
87        public void abort(String why, Throwable e) {
88          aborted = true;
89        }
90      });
91      l.setBalancerOn(false);
92      for (int replicaId = 1; replicaId < 3; replicaId ++) {
93        HRegionInfo h = RegionReplicaUtil.getRegionInfoForReplica(HRegionInfo.FIRST_META_REGIONINFO,
94          replicaId);
95        TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager().waitForAssignment(h);
96      }
97      LOG.debug("All meta replicas assigned");
98    }
99  
100   @After
101   public void tearDown() throws Exception {
102     TEST_UTIL.shutdownMiniCluster();
103   }
104 
105   @Test
106   public void testMetaHTDReplicaCount() throws Exception {
107     assertTrue(TEST_UTIL.getHBaseAdmin().getTableDescriptor(TableName.META_TABLE_NAME)
108         .getRegionReplication() == 3);
109   }
110 
111   @Test
112   public void testZookeeperNodesForReplicas() throws Exception {
113     // Checks all the znodes exist when meta's replicas are enabled
114     ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
115     Configuration conf = TEST_UTIL.getConfiguration();
116     String baseZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
117         HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
118     String primaryMetaZnode = ZKUtil.joinZNode(baseZNode,
119         conf.get("zookeeper.znode.metaserver", "meta-region-server"));
120     // check that the data in the znode is parseable (this would also mean the znode exists)
121     byte[] data = ZKUtil.getData(zkw, primaryMetaZnode);
122     ServerName.parseFrom(data);
123     for (int i = 1; i < 3; i++) {
124       String secZnode = ZKUtil.joinZNode(baseZNode,
125           conf.get("zookeeper.znode.metaserver", "meta-region-server") + "-" + i);
126       String str = zkw.getZNodeForReplica(i);
127       assertTrue(str.equals(secZnode));
128       // check that the data in the znode is parseable (this would also mean the znode exists)
129       data = ZKUtil.getData(zkw, secZnode);
130       ServerName.parseFrom(data);
131     }
132   }
133 
134   @Test
135   public void testShutdownHandling() throws Exception {
136     // This test creates a table, flushes the meta (with 3 replicas), kills the
137     // server holding the primary meta replica. Then it does a put/get into/from
138     // the test table. The put/get operations would use the replicas to locate the
139     // location of the test table's region
140     shutdownMetaAndDoValidations(TEST_UTIL);
141   }
142 
143   public static void shutdownMetaAndDoValidations(HBaseTestingUtility util) throws Exception {
144     // This test creates a table, flushes the meta (with 3 replicas), kills the
145     // server holding the primary meta replica. Then it does a put/get into/from
146     // the test table. The put/get operations would use the replicas to locate the
147     // location of the test table's region
148     ZooKeeperWatcher zkw = util.getZooKeeperWatcher();
149     Configuration conf = util.getConfiguration();
150     conf.setBoolean(HConstants.USE_META_REPLICAS, true);
151 
152     String baseZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
153         HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
154     String primaryMetaZnode = ZKUtil.joinZNode(baseZNode,
155         conf.get("zookeeper.znode.metaserver", "meta-region-server"));
156     byte[] data = ZKUtil.getData(zkw, primaryMetaZnode);
157     ServerName primary = ServerName.parseFrom(data);
158 
159     byte[] TABLE = Bytes.toBytes("testShutdownHandling");
160     byte[][] FAMILIES = new byte[][] { Bytes.toBytes("foo") };
161     if (util.getHBaseAdmin().tableExists(TABLE)) {
162       util.getHBaseAdmin().disableTable(TABLE);
163       util.getHBaseAdmin().deleteTable(TABLE);
164     }
165     Table htable = util.createTable(TABLE, FAMILIES, conf);
166 
167     util.getHBaseAdmin().flush(TableName.META_TABLE_NAME);
168     Thread.sleep(conf.getInt(StorefileRefresherChore.REGIONSERVER_STOREFILE_REFRESH_PERIOD,
169        30000) * 6);
170     Connection c = ConnectionFactory.createConnection(util.getConfiguration());
171     List<HRegionInfo> regions = MetaTableAccessor.getTableRegions(zkw, c,
172         TableName.valueOf(TABLE));
173     HRegionLocation hrl = MetaTableAccessor.getRegionLocation(c, regions.get(0));
174     // Ensure that the primary server for test table is not the same one as the primary
175     // of the meta region since we will be killing the srv holding the meta's primary...
176     // We want to be able to write to the test table even when the meta is not present ..
177     // If the servers are the same, then move the test table's region out of the server
178     // to another random server
179     if (hrl.getServerName().equals(primary)) {
180       util.getHBaseAdmin().move(hrl.getRegionInfo().getEncodedNameAsBytes(), null);
181       // wait for the move to complete
182       do {
183         Thread.sleep(10);
184         hrl = MetaTableAccessor.getRegionLocation(c, regions.get(0));
185       } while (primary.equals(hrl.getServerName()));
186       util.getHBaseAdmin().flush(TableName.META_TABLE_NAME);
187       Thread.sleep(conf.getInt(StorefileRefresherChore.REGIONSERVER_STOREFILE_REFRESH_PERIOD,
188          30000) * 3);
189     }
190     ServerName master = util.getHBaseClusterInterface().getClusterStatus().getMaster();
191     // kill the master so that regionserver recovery is not triggered at all
192     // for the meta server
193     util.getHBaseClusterInterface().stopMaster(master);
194     util.getHBaseClusterInterface().waitForMasterToStop(master, 60000);
195     if (!master.equals(primary)) {
196       util.getHBaseClusterInterface().killRegionServer(primary);
197       util.getHBaseClusterInterface().waitForRegionServerToStop(primary, 60000);
198     }
199     ((ClusterConnection)c).clearRegionCache();
200     htable.close();
201     htable = c.getTable(TableName.valueOf(TABLE));
202     byte[] row = "test".getBytes();
203     Put put = new Put(row);
204     put.add("foo".getBytes(), row, row);
205     BufferedMutator m = c.getBufferedMutator(TableName.valueOf(TABLE));
206     m.mutate(put);
207     m.flush();
208     // Try to do a get of the row that was just put
209     Get get = new Get(row);
210     Result r = htable.get(get);
211     assertTrue(Arrays.equals(r.getRow(), row));
212     // now start back the killed servers and disable use of replicas. That would mean
213     // calls go to the primary
214     util.getHBaseClusterInterface().startMaster(master.getHostname(), 0);
215     util.getHBaseClusterInterface().startRegionServer(primary.getHostname(), 0);
216     util.getHBaseClusterInterface().waitForActiveAndReadyMaster();
217     ((ClusterConnection)c).clearRegionCache();
218     htable.close();
219     conf.setBoolean(HConstants.USE_META_REPLICAS, false);
220     htable = c.getTable(TableName.valueOf(TABLE));
221     r = htable.get(get);
222     assertTrue(Arrays.equals(r.getRow(), row));
223   }
224 
225   @Test
226   public void testMetaLookupThreadPoolCreated() throws Exception {
227     byte[] TABLE = Bytes.toBytes("testMetaLookupThreadPoolCreated");
228     byte[][] FAMILIES = new byte[][] { Bytes.toBytes("foo") };
229     if (TEST_UTIL.getHBaseAdmin().tableExists(TABLE)) {
230       TEST_UTIL.getHBaseAdmin().disableTable(TABLE);
231       TEST_UTIL.getHBaseAdmin().deleteTable(TABLE);
232     }
233     Table htable = TEST_UTIL.createTable(TABLE, FAMILIES, TEST_UTIL.getConfiguration());
234     byte[] row = "test".getBytes();
235     HConnectionImplementation c = ((HConnectionImplementation)((HTable)htable).connection);
236     // check that metalookup pool would get created
237     c.relocateRegion(TABLE, row);
238     ExecutorService ex = c.getCurrentMetaLookupPool();
239     assert(ex != null);
240   }
241 
242   @Test
243   public void testChangingReplicaCount() throws Exception {
244     // tests changing the replica count across master restarts
245     // reduce the replica count from 3 to 2
246     stopMasterAndValidateReplicaCount(3, 2);
247     // increase the replica count from 2 to 3
248     stopMasterAndValidateReplicaCount(2, 3);
249   }
250 
251   private void stopMasterAndValidateReplicaCount(int originalReplicaCount, int newReplicaCount)
252       throws Exception {
253     ServerName sn = TEST_UTIL.getHBaseClusterInterface().getClusterStatus().getMaster();
254     TEST_UTIL.getHBaseClusterInterface().stopMaster(sn);
255     TEST_UTIL.getHBaseClusterInterface().waitForMasterToStop(sn, 60000);
256     List<String> metaZnodes = TEST_UTIL.getZooKeeperWatcher().getMetaReplicaNodes();
257     assert(metaZnodes.size() == originalReplicaCount); //we should have what was configured before
258     TEST_UTIL.getHBaseClusterInterface().getConf().setInt(HConstants.META_REPLICAS_NUM,
259         newReplicaCount);
260     TEST_UTIL.getHBaseClusterInterface().startMaster(sn.getHostname(), 0);
261     TEST_UTIL.getHBaseClusterInterface().waitForActiveAndReadyMaster();
262     int count = 0;
263     do {
264       metaZnodes = TEST_UTIL.getZooKeeperWatcher().getMetaReplicaNodes();
265       Thread.sleep(10);
266       count++;
267       // wait for the count to be different from the originalReplicaCount. When the
268       // replica count is reduced, that will happen when the master unassigns excess
269       // replica, and deletes the excess znodes
270     } while (metaZnodes.size() == originalReplicaCount && count < 1000);
271     assert(metaZnodes.size() == newReplicaCount);
272     // also check if hbck returns without errors
273     TEST_UTIL.getConfiguration().setInt(HConstants.META_REPLICAS_NUM,
274         newReplicaCount);
275     HBaseFsck hbck = HbckTestingUtil.doFsck(TEST_UTIL.getConfiguration(), false);
276     HbckTestingUtil.assertNoErrors(hbck);
277   }
278 
279   @Test
280   public void testHBaseFsckWithMetaReplicas() throws Exception {
281     HBaseFsck hbck = HbckTestingUtil.doFsck(TEST_UTIL.getConfiguration(), false);
282     HbckTestingUtil.assertNoErrors(hbck);
283   }
284 
285   @Test
286   public void testHBaseFsckWithFewerMetaReplicas() throws Exception {
287     ClusterConnection c = (ClusterConnection)ConnectionFactory.createConnection(
288         TEST_UTIL.getConfiguration());
289     RegionLocations rl = c.locateRegion(TableName.META_TABLE_NAME, HConstants.EMPTY_START_ROW,
290         false, false);
291     HBaseFsckRepair.closeRegionSilentlyAndWait(c,
292         rl.getRegionLocation(1).getServerName(), rl.getRegionLocation(1).getRegionInfo());
293     // check that problem exists
294     HBaseFsck hbck = doFsck(TEST_UTIL.getConfiguration(), false);
295     assertErrors(hbck, new ERROR_CODE[]{ERROR_CODE.UNKNOWN,ERROR_CODE.NO_META_REGION});
296     // fix the problem
297     hbck = doFsck(TEST_UTIL.getConfiguration(), true);
298     // run hbck again to make sure we don't see any errors
299     hbck = doFsck(TEST_UTIL.getConfiguration(), false);
300     assertErrors(hbck, new ERROR_CODE[]{});
301   }
302 
303   @Test
304   public void testHBaseFsckWithFewerMetaReplicaZnodes() throws Exception {
305     ClusterConnection c = (ClusterConnection)ConnectionFactory.createConnection(
306         TEST_UTIL.getConfiguration());
307     RegionLocations rl = c.locateRegion(TableName.META_TABLE_NAME, HConstants.EMPTY_START_ROW,
308         false, false);
309     HBaseFsckRepair.closeRegionSilentlyAndWait(c,
310         rl.getRegionLocation(2).getServerName(), rl.getRegionLocation(2).getRegionInfo());
311     ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
312     ZKUtil.deleteNode(zkw, zkw.getZNodeForReplica(2));
313     // check that problem exists
314     HBaseFsck hbck = doFsck(TEST_UTIL.getConfiguration(), false);
315     assertErrors(hbck, new ERROR_CODE[]{ERROR_CODE.UNKNOWN,ERROR_CODE.NO_META_REGION});
316     // fix the problem
317     hbck = doFsck(TEST_UTIL.getConfiguration(), true);
318     // run hbck again to make sure we don't see any errors
319     hbck = doFsck(TEST_UTIL.getConfiguration(), false);
320     assertErrors(hbck, new ERROR_CODE[]{});
321   }
322 
323   @Test
324   public void testAccessingUnknownTables() throws Exception {
325     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
326     conf.setBoolean(HConstants.USE_META_REPLICAS, true);
327     Table table = TEST_UTIL.getConnection().getTable(TableName.valueOf("RandomTable"));
328     Get get = new Get(Bytes.toBytes("foo"));
329     try {
330       table.get(get);
331     } catch (TableNotFoundException t) {
332       return;
333     }
334     fail("Expected TableNotFoundException");
335   }
336 
337   @Test
338   public void testMetaAddressChange() throws Exception {
339     // checks that even when the meta's location changes, the various
340     // caches update themselves. Uses the master operations to test
341     // this
342     Configuration conf = TEST_UTIL.getConfiguration();
343     ZooKeeperWatcher zkw = TEST_UTIL.getZooKeeperWatcher();
344     String baseZNode = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
345         HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
346     String primaryMetaZnode = ZKUtil.joinZNode(baseZNode,
347         conf.get("zookeeper.znode.metaserver", "meta-region-server"));
348     // check that the data in the znode is parseable (this would also mean the znode exists)
349     byte[] data = ZKUtil.getData(zkw, primaryMetaZnode);
350     ServerName currentServer = ServerName.parseFrom(data);
351     Collection<ServerName> liveServers = TEST_UTIL.getHBaseAdmin().getClusterStatus().getServers();
352     ServerName moveToServer = null;
353     for (ServerName s : liveServers) {
354       if (!currentServer.equals(s)) {
355         moveToServer = s;
356       }
357     }
358     assert(moveToServer != null);
359     String tableName = "randomTable5678";
360     TEST_UTIL.createTable(TableName.valueOf(tableName), "f");
361     assertTrue(TEST_UTIL.getHBaseAdmin().tableExists(tableName));
362     TEST_UTIL.getHBaseAdmin().move(HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes(),
363         Bytes.toBytes(moveToServer.getServerName()));
364     int i = 0;
365     do {
366       Thread.sleep(10);
367       data = ZKUtil.getData(zkw, primaryMetaZnode);
368       currentServer = ServerName.parseFrom(data);
369       i++;
370     } while (!moveToServer.equals(currentServer) && i < 1000); //wait for 10 seconds overall
371     assert(i != 1000);
372     TEST_UTIL.getHBaseAdmin().disableTable("randomTable5678");
373     assertTrue(TEST_UTIL.getHBaseAdmin().isTableDisabled("randomTable5678"));
374   }
375 
376   @Test
377   public void testShutdownOfReplicaHolder() throws Exception {
378     // checks that the when the server holding meta replica is shut down, the meta replica
379     // can be recovered
380     RegionLocations rl = ConnectionManager.getConnectionInternal(TEST_UTIL.getConfiguration()).
381         locateRegion(TableName.META_TABLE_NAME, Bytes.toBytes(""), false, true);
382     HRegionLocation hrl = rl.getRegionLocation(1);
383     ServerName oldServer = hrl.getServerName();
384     TEST_UTIL.getHBaseClusterInterface().killRegionServer(oldServer);
385     int i = 0;
386     do {
387       LOG.debug("Waiting for the replica " + hrl.getRegionInfo() + " to come up");
388       Thread.sleep(30000); //wait for the detection/recovery
389       rl = ConnectionManager.getConnectionInternal(TEST_UTIL.getConfiguration()).
390           locateRegion(TableName.META_TABLE_NAME, Bytes.toBytes(""), false, true);
391       hrl = rl.getRegionLocation(1);
392       i++;
393     } while ((hrl == null || hrl.getServerName().equals(oldServer)) && i < 3);
394     assertTrue(i != 3);
395   }
396 
397   @Test
398   public void testHBaseFsckWithExcessMetaReplicas() throws Exception {
399     HBaseFsck hbck = new HBaseFsck(TEST_UTIL.getConfiguration());
400     // Create a meta replica (this will be the 4th one) and assign it
401     HRegionInfo h = RegionReplicaUtil.getRegionInfoForReplica(
402         HRegionInfo.FIRST_META_REGIONINFO, 3);
403     // create in-memory state otherwise master won't assign
404     TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager()
405              .getRegionStates().createRegionState(h);
406     TEST_UTIL.getMiniHBaseCluster().getMaster().assignRegion(h);
407     HBaseFsckRepair.waitUntilAssigned(TEST_UTIL.getHBaseAdmin(), h);
408     // check that problem exists
409     hbck = doFsck(TEST_UTIL.getConfiguration(), false);
410     assertErrors(hbck, new ERROR_CODE[]{ERROR_CODE.UNKNOWN, ERROR_CODE.SHOULD_NOT_BE_DEPLOYED});
411     // fix the problem
412     hbck = doFsck(TEST_UTIL.getConfiguration(), true);
413     // run hbck again to make sure we don't see any errors
414     hbck = doFsck(TEST_UTIL.getConfiguration(), false);
415     assertErrors(hbck, new ERROR_CODE[]{});
416   }
417 }