View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import static org.apache.hadoop.hbase.HConstants.META_REPLICAS_NUM;
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  
25  import com.google.common.base.Joiner;
26  import com.google.common.base.Preconditions;
27  import com.google.common.collect.ImmutableSet;
28  import com.google.protobuf.RpcController;
29  import java.io.IOException;
30  import java.net.SocketTimeoutException;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.Comparator;
35  import java.util.List;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.hbase.HBaseTestingUtility;
38  import org.apache.hadoop.hbase.HConstants;
39  import org.apache.hadoop.hbase.HRegionLocation;
40  import org.apache.hadoop.hbase.ServerName;
41  import org.apache.hadoop.hbase.TableName;
42  import org.apache.hadoop.hbase.Waiter;
43  import org.apache.hadoop.hbase.exceptions.MasterRegistryFetchException;
44  import org.apache.hadoop.hbase.master.HMaster;
45  import org.apache.hadoop.hbase.testclassification.ClientTests;
46  import org.apache.hadoop.hbase.testclassification.MediumTests;
47  import org.junit.AfterClass;
48  import org.junit.BeforeClass;
49  import org.junit.Test;
50  import org.junit.experimental.categories.Category;
51  
52  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ClientMetaService;
53  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterIdRequest;
54  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterIdResponse;
55  
56  @Category({ MediumTests.class, ClientTests.class })
57  public class TestMasterRegistry {
58  
59    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
60  
61    private static final int META_REPLICA_COUNT = 3;
62  
63    @BeforeClass
64    public static void setUp() throws Exception {
65      TEST_UTIL.getConfiguration().setInt(META_REPLICAS_NUM, META_REPLICA_COUNT);
66      TEST_UTIL.startMiniCluster(3, 3);
67    }
68  
69    @AfterClass
70    public static void tearDown() throws Exception {
71      TEST_UTIL.shutdownMiniCluster();
72    }
73  
74    private static class ExceptionInjectorRegistry extends MasterRegistry {
75      @Override
76      public String getClusterId() throws IOException {
77        GetClusterIdResponse resp = doCall(new Callable<GetClusterIdResponse>() {
78          @Override
79          public GetClusterIdResponse call(ClientMetaService.Interface stub, RpcController controller)
80              throws IOException {
81            throw new SocketTimeoutException("Injected exception.");
82          }
83        });
84        return resp.getClusterId();
85      }
86    }
87  
88    /**
89     * Generates a string of dummy master addresses in host:port format. Every other hostname won't
90     * have a port number.
91     */
92    private static String generateDummyMastersList(int size) {
93      List<String> masters = new ArrayList<>();
94      for (int i = 0; i < size; i++) {
95        masters.add(" localhost" + (i % 2 == 0 ? ":" + (1000 + i) : ""));
96      }
97      return Joiner.on(",").join(masters);
98    }
99  
100   /**
101    * Makes sure the master registry parses the master end points in the configuration correctly.
102    */
103   @Test
104   public void testMasterAddressParsing() throws IOException {
105     Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
106     int numMasters = 10;
107     conf.set(HConstants.MASTER_ADDRS_KEY, generateDummyMastersList(numMasters));
108     List<ServerName> parsedMasters = new ArrayList<>(MasterRegistry.parseMasterAddrs(conf));
109     // Half of them would be without a port, duplicates are removed.
110     assertEquals(numMasters / 2 + 1, parsedMasters.size());
111     // Sort in the increasing order of port numbers.
112     Collections.sort(parsedMasters, new Comparator<ServerName>() {
113       @Override
114       public int compare(ServerName sn1, ServerName sn2) {
115         return sn1.getPort() - sn2.getPort();
116       }
117     });
118     for (int i = 0; i < parsedMasters.size(); i++) {
119       ServerName sn = parsedMasters.get(i);
120       assertEquals("localhost", sn.getHostname());
121       if (i == parsedMasters.size() - 1) {
122         // Last entry should be the one with default port.
123         assertEquals(HConstants.DEFAULT_MASTER_PORT, sn.getPort());
124       } else {
125         assertEquals(1000 + (2 * i), sn.getPort());
126       }
127     }
128   }
129 
130   @Test
131   public void testRegistryRPCs() throws Exception {
132     HMaster activeMaster = TEST_UTIL.getHBaseCluster().getMaster();
133     final MasterRegistry registry = new MasterRegistry();
134     try {
135       registry.init(TEST_UTIL.getConnection());
136       // Add wait on all replicas being assigned before proceeding w/ test. Failed on occasion
137       // because not all replicas had made it up before test started.
138       TEST_UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
139         @Override
140         public boolean evaluate() throws Exception {
141           return registry.getMetaRegionLocations().size() == META_REPLICA_COUNT;
142         }
143       });
144       assertEquals(registry.getClusterId(), activeMaster.getClusterId());
145       assertEquals(registry.getActiveMaster(), activeMaster.getServerName());
146       assertTrue(registry.isTableOnlineState(TableName.META_TABLE_NAME, true));
147       assertFalse(registry.isTableOnlineState(TableName.META_TABLE_NAME, false));
148       List<HRegionLocation> metaLocations =
149           Arrays.asList(registry.getMetaRegionLocations().getRegionLocations());
150       List<HRegionLocation> actualMetaLocations =
151           activeMaster.getMetaRegionLocationCache().getMetaRegionLocations();
152       Collections.sort(metaLocations);
153       Collections.sort(actualMetaLocations);
154       assertEquals(actualMetaLocations, metaLocations);
155       int numRs = registry.getCurrentNrHRS();
156       assertEquals(TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size(), numRs);
157     } finally {
158       registry.close();
159     }
160   }
161 
162   /**
163    * Tests that the list of masters configured in the MasterRegistry is dynamically refreshed in the
164    * event of errors.
165    */
166   @Test
167   public void testDynamicMasterConfigurationRefresh() throws Exception {
168     Configuration conf = TEST_UTIL.getConnection().getConfiguration();
169     String currentMasterAddrs = Preconditions.checkNotNull(conf.get(HConstants.MASTER_ADDRS_KEY));
170     HMaster activeMaster = TEST_UTIL.getHBaseCluster().getMaster();
171     // Add a non-working master
172     ServerName badServer = ServerName.valueOf("localhost", 1234, -1);
173     conf.set(HConstants.MASTER_ADDRS_KEY, badServer.toShortString() + "," + currentMasterAddrs);
174     // Do not limit the number of refreshes during the test run.
175     conf.setLong(MasterAddressRefresher.MIN_SECS_BETWEEN_REFRESHES, 0);
176     final ExceptionInjectorRegistry registry = new ExceptionInjectorRegistry();
177     try {
178       registry.init(TEST_UTIL.getConnection());
179       final ImmutableSet<String> masters = registry.getParsedMasterServers();
180       assertTrue(masters.contains(badServer.toString()));
181       // Make a registry RPC, this should trigger a refresh since one of the RPC fails.
182       try {
183         registry.getClusterId();
184       } catch (MasterRegistryFetchException e) {
185         // Expected.
186       }
187 
188       // Wait for new set of masters to be populated.
189       TEST_UTIL.waitFor(5000,
190           new Waiter.Predicate<Exception>() {
191             @Override
192             public boolean evaluate() throws Exception {
193               return !registry.getParsedMasterServers().equals(masters);
194             }
195           });
196       // new set of masters should not include the bad server
197       final ImmutableSet<String> newMasters = registry.getParsedMasterServers();
198       // Bad one should be out.
199       assertEquals(3, newMasters.size());
200       assertFalse(newMasters.contains(badServer.toString()));
201       // Kill the active master
202       activeMaster.stopMaster();
203       TEST_UTIL.waitFor(10000,
204           new Waiter.Predicate<Exception>() {
205             @Override
206             public boolean evaluate() {
207               return TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().size() == 2;
208             }
209           });
210       TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(10000);
211       // Make a registry RPC, this should trigger a refresh since one of the RPC fails.
212       try {
213         registry.getClusterId();
214       } catch (MasterRegistryFetchException e) {
215         // Expected.
216       }
217       // Wait until the killed master de-registered.
218       TEST_UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
219         @Override
220         public boolean evaluate() throws Exception {
221           return registry.getMasters().size() == 2;
222         }
223       });
224       TEST_UTIL.waitFor(20000, new Waiter.Predicate<Exception>() {
225         @Override
226         public boolean evaluate() throws Exception {
227           return registry.getParsedMasterServers().size() == 2;
228         }
229       });
230       final ImmutableSet<String> newMasters2 = registry.getParsedMasterServers();
231       assertEquals(2, newMasters2.size());
232       assertFalse(newMasters2.contains(activeMaster.getServerName().toString()));
233     } finally {
234       registry.close();
235       // Reset the state, add a killed master.
236       TEST_UTIL.getMiniHBaseCluster().startMaster();
237     }
238   }
239 }