001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.Comparator; 028import java.util.List; 029import java.util.Set; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HRegionLocation; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.StartMiniClusterOption; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.Waiter; 039import org.apache.hadoop.hbase.master.HMaster; 040import org.apache.hadoop.hbase.security.User; 041import org.apache.hadoop.hbase.testclassification.ClientTests; 042import org.apache.hadoop.hbase.testclassification.MediumTests; 043import org.junit.AfterClass; 044import org.junit.BeforeClass; 045import org.junit.ClassRule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048 049import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 050 051@Category({ MediumTests.class, ClientTests.class }) 052public class TestMasterRegistry { 053 054 @ClassRule 055 public static final HBaseClassTestRule CLASS_RULE = 056 HBaseClassTestRule.forClass(TestMasterRegistry.class); 057 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 058 059 @BeforeClass 060 public static void setUp() throws Exception { 061 StartMiniClusterOption.Builder builder = StartMiniClusterOption.builder(); 062 builder.numMasters(3).numRegionServers(3); 063 TEST_UTIL.startMiniCluster(builder.build()); 064 HBaseTestingUtility.setReplicas(TEST_UTIL.getAdmin(), TableName.META_TABLE_NAME, 3); 065 } 066 067 @AfterClass 068 public static void tearDown() throws Exception { 069 TEST_UTIL.shutdownMiniCluster(); 070 } 071 072 /** 073 * Generates a string of dummy master addresses in host:port format. Every other hostname won't 074 * have a port number. 075 */ 076 private static String generateDummyMastersList(int size) { 077 List<String> masters = new ArrayList<>(); 078 for (int i = 0; i < size; i++) { 079 masters.add(" localhost" + (i % 2 == 0 ? ":" + (1000 + i) : "")); 080 } 081 return String.join(",", masters); 082 } 083 084 /** 085 * Makes sure the master registry parses the master end points in the configuration correctly. 086 */ 087 @Test 088 public void testMasterAddressParsing() throws Exception { 089 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 090 int numMasters = 10; 091 conf.set(HConstants.MASTER_ADDRS_KEY, generateDummyMastersList(numMasters)); 092 List<ServerName> parsedMasters = new ArrayList<>(MasterRegistry.parseMasterAddrs(conf)); 093 // Half of them would be without a port, duplicates are removed. 094 assertEquals(numMasters / 2 + 1, parsedMasters.size()); 095 // Sort in the increasing order of port numbers. 096 Collections.sort(parsedMasters, Comparator.comparingInt(ServerName::getPort)); 097 for (int i = 0; i < parsedMasters.size(); i++) { 098 ServerName sn = parsedMasters.get(i); 099 assertEquals("localhost", sn.getHostname()); 100 if (i == parsedMasters.size() - 1) { 101 // Last entry should be the one with default port. 102 assertEquals(HConstants.DEFAULT_MASTER_PORT, sn.getPort()); 103 } else { 104 assertEquals(1000 + (2 * i), sn.getPort()); 105 } 106 } 107 } 108 109 @Test 110 public void testMasterPortDefaults() throws Exception { 111 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 112 conf.set(HConstants.MASTER_ADDRS_KEY, "localhost"); 113 List<ServerName> parsedMasters = new ArrayList<>(MasterRegistry.parseMasterAddrs(conf)); 114 ServerName sn = parsedMasters.get(0); 115 assertEquals(HConstants.DEFAULT_MASTER_PORT, sn.getPort()); 116 final int CUSTOM_MASTER_PORT = 9999; 117 conf.setInt(HConstants.MASTER_PORT, CUSTOM_MASTER_PORT); 118 parsedMasters = new ArrayList<>(MasterRegistry.parseMasterAddrs(conf)); 119 sn = parsedMasters.get(0); 120 assertEquals(CUSTOM_MASTER_PORT, sn.getPort()); 121 } 122 123 @Test 124 public void testRegistryRPCs() throws Exception { 125 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 126 HMaster activeMaster = TEST_UTIL.getHBaseCluster().getMaster(); 127 final int size = 128 activeMaster.getMetaRegionLocationCache().getMetaRegionLocations().get().size(); 129 for (int numHedgedReqs = 1; numHedgedReqs <= size; numHedgedReqs++) { 130 conf.setInt(MasterRegistry.MASTER_REGISTRY_HEDGED_REQS_FANOUT_KEY, numHedgedReqs); 131 try (MasterRegistry registry = new MasterRegistry(conf, User.getCurrent())) { 132 // Add wait on all replicas being assigned before proceeding w/ test. Failed on occasion 133 // because not all replicas had made it up before test started. 134 RegionReplicaTestHelper.waitUntilAllMetaReplicasAreReady(TEST_UTIL, registry); 135 assertEquals(registry.getClusterId().get(), activeMaster.getClusterId()); 136 assertEquals(registry.getActiveMaster().get(), activeMaster.getServerName()); 137 List<HRegionLocation> metaLocations = 138 Arrays.asList(registry.getMetaRegionLocations().get().getRegionLocations()); 139 List<HRegionLocation> actualMetaLocations = 140 activeMaster.getMetaRegionLocationCache().getMetaRegionLocations().get(); 141 Collections.sort(metaLocations); 142 Collections.sort(actualMetaLocations); 143 assertEquals(actualMetaLocations, metaLocations); 144 } 145 } 146 } 147 148 /** 149 * Tests that the list of masters configured in the MasterRegistry is dynamically refreshed in the 150 * event of errors. 151 */ 152 @Test 153 public void testDynamicMasterConfigurationRefresh() throws Exception { 154 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 155 String currentMasterAddrs = Preconditions.checkNotNull(conf.get(HConstants.MASTER_ADDRS_KEY)); 156 HMaster activeMaster = TEST_UTIL.getHBaseCluster().getMaster(); 157 String clusterId = activeMaster.getClusterId(); 158 // Add a non-working master 159 ServerName badServer = ServerName.valueOf("localhost", 1234, -1); 160 conf.set(HConstants.MASTER_ADDRS_KEY, badServer.toShortString() + "," + currentMasterAddrs); 161 // Set the hedging fan out so that all masters are queried. 162 conf.setInt(MasterRegistry.MASTER_REGISTRY_HEDGED_REQS_FANOUT_KEY, 4); 163 // Do not limit the number of refreshes during the test run. 164 conf.setLong(MasterRegistry.MASTER_REGISTRY_MIN_SECS_BETWEEN_REFRESHES, 0); 165 try (MasterRegistry registry = new MasterRegistry(conf, User.getCurrent())) { 166 final Set<ServerName> masters = registry.getParsedServers(); 167 assertTrue(masters.contains(badServer)); 168 // Make a registry RPC, this should trigger a refresh since one of the hedged RPC fails. 169 assertEquals(registry.getClusterId().get(), clusterId); 170 // Wait for new set of masters to be populated. 171 TEST_UTIL.waitFor(5000, 172 (Waiter.Predicate<Exception>) () -> !registry.getParsedServers().equals(masters)); 173 // new set of masters should not include the bad server 174 final Set<ServerName> newMasters = registry.getParsedServers(); 175 // Bad one should be out. 176 assertEquals(3, newMasters.size()); 177 assertFalse(newMasters.contains(badServer)); 178 // Kill the active master 179 activeMaster.stopMaster(); 180 TEST_UTIL.waitFor(10000, 181 () -> TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().size() == 2); 182 TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(10000); 183 // Wait until the killed master de-registered. This should also trigger another refresh. 184 TEST_UTIL.waitFor(10000, () -> registry.getMasters().get().size() == 2); 185 TEST_UTIL.waitFor(20000, () -> registry.getParsedServers().size() == 2); 186 final Set<ServerName> newMasters2 = registry.getParsedServers(); 187 assertEquals(2, newMasters2.size()); 188 assertFalse(newMasters2.contains(activeMaster.getServerName())); 189 } finally { 190 // Reset the state, add a killed master. 191 TEST_UTIL.getMiniHBaseCluster().startMaster(); 192 } 193 } 194}