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      ZooKeeperWatcher zkw = m.getZooKeeperWatcher();
93      int expectedNumOfListeners = zkw.getNumberOfListeners();
94      // now the cluster is up. So assign some regions.
95      HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
96      byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"),
97          Bytes.toBytes("c"), Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
98          Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i"), Bytes.toBytes("j") };
99  
100     String tableName = "testRegionAssignmentAfterMasterRecoveryDueToZKExpiry";
101     admin.createTable(new HTableDescriptor(tableName), SPLIT_KEYS);
102     ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
103     ZKAssign.blockUntilNoRIT(zooKeeperWatcher);
104     m.getZooKeeperWatcher().close();
105     MockLoadBalancer.retainAssignCalled = false;
106     m.abort("Test recovery from zk session expired", new KeeperException.SessionExpiredException());
107     assertFalse(m.isStopped());
108     // The recovered master should not call retainAssignment, as it is not a
109     // clean startup.
110     assertFalse("Retain assignment should not be called", MockLoadBalancer.retainAssignCalled);
111     // number of listeners should be same as the value before master aborted
112     assertEquals(expectedNumOfListeners, zkw.getNumberOfListeners());
113   }
114 
115   static class MockLoadBalancer extends DefaultLoadBalancer {
116     static boolean retainAssignCalled = false;
117 
118     @Override
119     public Map<ServerName, List<HRegionInfo>> retainAssignment(
120         Map<HRegionInfo, ServerName> regions, List<ServerName> servers) {
121       retainAssignCalled = true;
122       return super.retainAssignment(regions, servers);
123     }
124   }
125 
126   /**
127    * Tests whether the logs are split when master recovers from a expired
128    * zookeeper session and an RS goes down.
129    */
130   @Test(timeout = 60000)
131   public void testLogSplittingAfterMasterRecoveryDueToZKExpiry() throws IOException,
132       KeeperException, InterruptedException {
133     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
134     cluster.startRegionServer();
135     HMaster m = cluster.getMaster();
136     // now the cluster is up. So assign some regions.
137     HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
138     byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("1"), Bytes.toBytes("2"),
139         Bytes.toBytes("3"), Bytes.toBytes("4"), Bytes.toBytes("5") };
140 
141     String tableName = "testLogSplittingAfterMasterRecoveryDueToZKExpiry";
142     HTableDescriptor htd = new HTableDescriptor(tableName);
143     HColumnDescriptor hcd = new HColumnDescriptor("col");
144     htd.addFamily(hcd);
145     admin.createTable(htd, SPLIT_KEYS);
146     ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
147     ZKAssign.blockUntilNoRIT(zooKeeperWatcher);
148     HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
149 
150     Put p = null;
151     int numberOfPuts = 0;
152     for (numberOfPuts = 0; numberOfPuts < 6; numberOfPuts++) {
153       p = new Put(Bytes.toBytes(numberOfPuts));
154       p.add(Bytes.toBytes("col"), Bytes.toBytes("ql"), Bytes.toBytes("value" + numberOfPuts));
155       table.put(p);
156     }
157     m.getZooKeeperWatcher().close();
158     m.abort("Test recovery from zk session expired", new KeeperException.SessionExpiredException());
159     assertFalse(m.isStopped());
160     cluster.getRegionServer(0).abort("Aborting");
161     // Without patch for HBASE-6046 this test case will always timeout
162     // with patch the test case should pass.
163     Scan scan = new Scan();
164     int numberOfRows = 0;
165     ResultScanner scanner = table.getScanner(scan);
166     Result[] result = scanner.next(1);
167     while (result != null && result.length > 0) {
168       numberOfRows++;
169       result = scanner.next(1);
170     }
171     assertEquals("Number of rows should be equal to number of puts.", numberOfPuts, numberOfRows);
172   }
173 }
174