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.HBaseTestingUtil; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HRegionLocation; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.StartTestingClusterOption; 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 HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 058 059 @BeforeClass 060 public static void setUp() throws Exception { 061 StartTestingClusterOption.Builder builder = StartTestingClusterOption.builder(); 062 builder.numMasters(3).numRegionServers(3); 063 TEST_UTIL.startMiniCluster(builder.build()); 064 HBaseTestingUtil.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 = activeMaster.getMetaLocations().size(); 128 for (int numHedgedReqs = 1; numHedgedReqs <= size; numHedgedReqs++) { 129 conf.setInt(MasterRegistry.MASTER_REGISTRY_HEDGED_REQS_FANOUT_KEY, numHedgedReqs); 130 try (MasterRegistry registry = new MasterRegistry(conf, User.getCurrent())) { 131 // Add wait on all replicas being assigned before proceeding w/ test. Failed on occasion 132 // because not all replicas had made it up before test started. 133 RegionReplicaTestHelper.waitUntilAllMetaReplicasAreReady(TEST_UTIL, registry); 134 assertEquals(registry.getClusterId().get(), activeMaster.getClusterId()); 135 assertEquals(registry.getActiveMaster().get(), activeMaster.getServerName()); 136 List<HRegionLocation> metaLocations = 137 Arrays.asList(registry.getMetaRegionLocations().get().getRegionLocations()); 138 List<HRegionLocation> actualMetaLocations = activeMaster.getMetaLocations(); 139 Collections.sort(metaLocations); 140 Collections.sort(actualMetaLocations); 141 assertEquals(actualMetaLocations, metaLocations); 142 } 143 } 144 } 145 146 /** 147 * Tests that the list of masters configured in the MasterRegistry is dynamically refreshed in the 148 * event of errors. 149 */ 150 @Test 151 public void testDynamicMasterConfigurationRefresh() throws Exception { 152 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 153 String currentMasterAddrs = Preconditions.checkNotNull(conf.get(HConstants.MASTER_ADDRS_KEY)); 154 HMaster activeMaster = TEST_UTIL.getHBaseCluster().getMaster(); 155 String clusterId = activeMaster.getClusterId(); 156 // Add a non-working master 157 ServerName badServer = ServerName.valueOf("localhost", 1234, -1); 158 conf.set(HConstants.MASTER_ADDRS_KEY, badServer.toShortString() + "," + currentMasterAddrs); 159 // Set the hedging fan out so that all masters are queried. 160 conf.setInt(MasterRegistry.MASTER_REGISTRY_HEDGED_REQS_FANOUT_KEY, 4); 161 // Do not limit the number of refreshes during the test run. 162 conf.setLong(MasterRegistry.MASTER_REGISTRY_MIN_SECS_BETWEEN_REFRESHES, 0); 163 try (MasterRegistry registry = new MasterRegistry(conf, User.getCurrent())) { 164 final Set<ServerName> masters = registry.getParsedServers(); 165 assertTrue(masters.contains(badServer)); 166 // Make a registry RPC, this should trigger a refresh since one of the hedged RPC fails. 167 assertEquals(registry.getClusterId().get(), clusterId); 168 // Wait for new set of masters to be populated. 169 TEST_UTIL.waitFor(5000, 170 (Waiter.Predicate<Exception>) () -> !registry.getParsedServers().equals(masters)); 171 // new set of masters should not include the bad server 172 final Set<ServerName> newMasters = registry.getParsedServers(); 173 // Bad one should be out. 174 assertEquals(3, newMasters.size()); 175 assertFalse(newMasters.contains(badServer)); 176 // Kill the active master 177 activeMaster.stopMaster(); 178 TEST_UTIL.waitFor(10000, 179 () -> TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().size() == 2); 180 TEST_UTIL.getMiniHBaseCluster().waitForActiveAndReadyMaster(10000); 181 // Wait until the killed master de-registered. This should also trigger another refresh. 182 TEST_UTIL.waitFor(10000, () -> registry.getMasters().get().size() == 2); 183 TEST_UTIL.waitFor(20000, () -> registry.getParsedServers().size() == 2); 184 final Set<ServerName> newMasters2 = registry.getParsedServers(); 185 assertEquals(2, newMasters2.size()); 186 assertFalse(newMasters2.contains(activeMaster.getServerName())); 187 } finally { 188 // Reset the state, add a killed master. 189 TEST_UTIL.getMiniHBaseCluster().startMaster(); 190 } 191 } 192}