1   /**
2    * Copyright 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.master;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  
25  import java.io.IOException;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.HBaseTestingUtility;
31  import org.apache.hadoop.hbase.HColumnDescriptor;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.HRegionInfo;
34  import org.apache.hadoop.hbase.HTableDescriptor;
35  import org.apache.hadoop.hbase.MediumTests;
36  import org.apache.hadoop.hbase.MiniHBaseCluster;
37  import org.apache.hadoop.hbase.ServerName;
38  import org.apache.hadoop.hbase.client.HBaseAdmin;
39  import org.apache.hadoop.hbase.client.HTable;
40  import org.apache.hadoop.hbase.client.Put;
41  import org.apache.hadoop.hbase.client.Result;
42  import org.apache.hadoop.hbase.client.ResultScanner;
43  import org.apache.hadoop.hbase.client.Scan;
44  import org.apache.hadoop.hbase.util.Bytes;
45  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
46  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
47  import org.apache.zookeeper.KeeperException;
48  import org.junit.After;
49  import org.junit.Before;
50  import org.junit.Test;
51  import org.junit.experimental.categories.Category;
52  
53  /**
54   * Test cases for master to recover from ZK session expiry.
55   */
56  @Category(MediumTests.class)
57  public class TestMasterZKSessionRecovery {
58    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
59  
60    /**
61     * The default timeout is 5 minutes.
62     * Shorten it so that the test won't wait for too long.
63     */
64    static {
65      Configuration conf = TEST_UTIL.getConfiguration();
66      conf.setLong("hbase.master.zksession.recover.timeout", 50000);
67      conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
68          MockLoadBalancer.class, LoadBalancer.class);
69    }
70  
71    @Before
72    public void setUp() throws Exception {
73      // Start a cluster of one regionserver.
74      TEST_UTIL.startMiniCluster(1);
75    }
76  
77    @After
78    public void tearDown() throws Exception {
79      TEST_UTIL.shutdownMiniCluster();
80    }
81  
82    /**
83     * Tests that the master does not call retainAssignment after recovery from
84     * expired zookeeper session. Without the HBASE-6046 fix master always tries
85     * to assign all the user regions by calling retainAssignment.
86     */
87    @Test
88    public void testRegionAssignmentAfterMasterRecoveryDueToZKExpiry() throws Exception {
89      MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
90      cluster.startRegionServer();
91      HMaster m = cluster.getMaster();
92      // now the cluster is up. So assign some regions.
93      HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
94      byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"),
95          Bytes.toBytes("c"), Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
96          Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i"), Bytes.toBytes("j") };
97  
98      String tableName = "testRegionAssignmentAfterMasterRecoveryDueToZKExpiry";
99      admin.createTable(new HTableDescriptor(tableName), SPLIT_KEYS);
100     ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
101     ZKAssign.blockUntilNoRIT(zooKeeperWatcher);
102     m.getZooKeeperWatcher().close();
103     MockLoadBalancer.retainAssignCalled = false;
104     m.abort("Test recovery from zk session expired", new KeeperException.SessionExpiredException());
105     assertFalse(m.isStopped());
106     // The recovered master should not call retainAssignment, as it is not a
107     // clean startup.
108     assertFalse("Retain assignment should not be called", MockLoadBalancer.retainAssignCalled);
109   }
110 
111   static class MockLoadBalancer extends DefaultLoadBalancer {
112     static boolean retainAssignCalled = false;
113 
114     @Override
115     public Map<ServerName, List<HRegionInfo>> retainAssignment(
116         Map<HRegionInfo, ServerName> regions, List<ServerName> servers) {
117       retainAssignCalled = true;
118       return super.retainAssignment(regions, servers);
119     }
120   }
121 
122   /**
123    * Tests whether the logs are split when master recovers from a expired
124    * zookeeper session and an RS goes down.
125    */
126   @Test(timeout = 60000)
127   public void testLogSplittingAfterMasterRecoveryDueToZKExpiry() throws IOException,
128       KeeperException, InterruptedException {
129     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
130     cluster.startRegionServer();
131     HMaster m = cluster.getMaster();
132     // now the cluster is up. So assign some regions.
133     HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
134     byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("1"), Bytes.toBytes("2"),
135         Bytes.toBytes("3"), Bytes.toBytes("4"), Bytes.toBytes("5") };
136 
137     String tableName = "testLogSplittingAfterMasterRecoveryDueToZKExpiry";
138     HTableDescriptor htd = new HTableDescriptor(tableName);
139     HColumnDescriptor hcd = new HColumnDescriptor("col");
140     htd.addFamily(hcd);
141     admin.createTable(htd, SPLIT_KEYS);
142     ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
143     ZKAssign.blockUntilNoRIT(zooKeeperWatcher);
144     HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
145 
146     Put p = null;
147     int numberOfPuts = 0;
148     for (numberOfPuts = 0; numberOfPuts < 6; numberOfPuts++) {
149       p = new Put(Bytes.toBytes(numberOfPuts));
150       p.add(Bytes.toBytes("col"), Bytes.toBytes("ql"), Bytes.toBytes("value" + numberOfPuts));
151       table.put(p);
152     }
153     m.getZooKeeperWatcher().close();
154     m.abort("Test recovery from zk session expired", new KeeperException.SessionExpiredException());
155     assertFalse(m.isStopped());
156     cluster.getRegionServer(0).abort("Aborting");
157     // Without patch for HBASE-6046 this test case will always timeout
158     // with patch the test case should pass.
159     Scan scan = new Scan();
160     int numberOfRows = 0;
161     ResultScanner scanner = table.getScanner(scan);
162     Result[] result = scanner.next(1);
163     while (result != null && result.length > 0) {
164       numberOfRows++;
165       result = scanner.next(1);
166     }
167     assertEquals("Number of rows should be equal to number of puts.", numberOfPuts, numberOfRows);
168   }
169 }
170