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.regionserver; 019 020import static org.hamcrest.CoreMatchers.hasItems; 021import static org.hamcrest.MatcherAssert.assertThat; 022import static org.junit.Assert.assertEquals; 023import static org.mockito.ArgumentMatchers.any; 024import static org.mockito.ArgumentMatchers.anyInt; 025import static org.mockito.Mockito.atLeast; 026import static org.mockito.Mockito.atLeastOnce; 027import static org.mockito.Mockito.doAnswer; 028import static org.mockito.Mockito.mock; 029import static org.mockito.Mockito.never; 030import static org.mockito.Mockito.times; 031import static org.mockito.Mockito.verify; 032import static org.mockito.Mockito.when; 033 034import java.io.IOException; 035import java.util.Arrays; 036import java.util.List; 037import org.apache.hadoop.conf.Configuration; 038import org.apache.hadoop.hbase.HBaseClassTestRule; 039import org.apache.hadoop.hbase.HBaseConfiguration; 040import org.apache.hadoop.hbase.ServerName; 041import org.apache.hadoop.hbase.Waiter; 042import org.apache.hadoop.hbase.client.ClusterConnection; 043import org.apache.hadoop.hbase.testclassification.RegionServerTests; 044import org.apache.hadoop.hbase.testclassification.SmallTests; 045import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 046import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker; 047import org.junit.After; 048import org.junit.Before; 049import org.junit.ClassRule; 050import org.junit.Test; 051import org.junit.experimental.categories.Category; 052 053@Category({ RegionServerTests.class, SmallTests.class }) 054public class TestBootstrapNodeManager { 055 056 @ClassRule 057 public static final HBaseClassTestRule CLASS_RULE = 058 HBaseClassTestRule.forClass(TestBootstrapNodeManager.class); 059 060 private Configuration conf; 061 062 private ClusterConnection conn; 063 064 private MasterAddressTracker tracker; 065 066 private BootstrapNodeManager manager; 067 068 @Before 069 public void setUp() { 070 conf = HBaseConfiguration.create(); 071 conf.setLong(BootstrapNodeManager.REQUEST_MASTER_INTERVAL_SECS, 5); 072 conf.setLong(BootstrapNodeManager.REQUEST_MASTER_MIN_INTERVAL_SECS, 1); 073 conf.setLong(BootstrapNodeManager.REQUEST_REGIONSERVER_INTERVAL_SECS, 1); 074 conf.setInt(RSRpcServices.CLIENT_BOOTSTRAP_NODE_LIMIT, 2); 075 conn = mock(ClusterConnection.class); 076 when(conn.getConfiguration()).thenReturn(conf); 077 tracker = mock(MasterAddressTracker.class); 078 } 079 080 @After 081 public void tearDown() { 082 if (manager != null) { 083 manager.stop(); 084 } 085 } 086 087 private void assertListEquals(List<ServerName> expected, List<ServerName> actual) { 088 assertEquals(expected.size(), expected.size()); 089 assertThat(actual, hasItems(expected.toArray(new ServerName[0]))); 090 } 091 092 @Test 093 public void testNormal() throws Exception { 094 List<ServerName> regionServers = 095 Arrays.asList(ServerName.valueOf("server1", 12345, EnvironmentEdgeManager.currentTime()), 096 ServerName.valueOf("server2", 12345, EnvironmentEdgeManager.currentTime()), 097 ServerName.valueOf("server3", 12345, EnvironmentEdgeManager.currentTime()), 098 ServerName.valueOf("server4", 12345, EnvironmentEdgeManager.currentTime())); 099 when(conn.getLiveRegionServers(any(), anyInt())).thenReturn(regionServers); 100 when(conn.getAllBootstrapNodes(any())).thenReturn(regionServers); 101 manager = new BootstrapNodeManager(conn, tracker); 102 Thread.sleep(3000); 103 verify(conn, times(1)).getLiveRegionServers(any(), anyInt()); 104 verify(conn, atLeastOnce()).getAllBootstrapNodes(any()); 105 assertListEquals(regionServers, manager.getBootstrapNodes()); 106 } 107 108 // if we do not return enough region servers, we will always get from master 109 @Test 110 public void testOnlyMaster() throws Exception { 111 List<ServerName> regionServers = 112 Arrays.asList(ServerName.valueOf("server1", 12345, EnvironmentEdgeManager.currentTime())); 113 when(conn.getLiveRegionServers(any(), anyInt())).thenReturn(regionServers); 114 when(conn.getAllBootstrapNodes(any())).thenReturn(regionServers); 115 manager = new BootstrapNodeManager(conn, tracker); 116 Thread.sleep(3000); 117 verify(conn, atLeast(2)).getLiveRegionServers(any(), anyInt()); 118 verify(conn, never()).getAllBootstrapNodes(any()); 119 assertListEquals(regionServers, manager.getBootstrapNodes()); 120 } 121 122 @Test 123 public void testRegionServerError() throws Exception { 124 List<ServerName> regionServers = 125 Arrays.asList(ServerName.valueOf("server1", 12345, EnvironmentEdgeManager.currentTime()), 126 ServerName.valueOf("server2", 12345, EnvironmentEdgeManager.currentTime()), 127 ServerName.valueOf("server3", 12345, EnvironmentEdgeManager.currentTime()), 128 ServerName.valueOf("server4", 12345, EnvironmentEdgeManager.currentTime())); 129 List<ServerName> newRegionServers = 130 Arrays.asList(ServerName.valueOf("server5", 12345, EnvironmentEdgeManager.currentTime()), 131 ServerName.valueOf("server6", 12345, EnvironmentEdgeManager.currentTime())); 132 when(conn.getLiveRegionServers(any(), anyInt())).thenReturn(regionServers); 133 when(conn.getAllBootstrapNodes(any())).thenAnswer(invocation -> { 134 if (invocation.getArgument(0, ServerName.class).getHostname().equals("server4")) { 135 throw new IOException("Inject error"); 136 } else { 137 return regionServers.subList(0, 3); 138 } 139 }); 140 manager = new BootstrapNodeManager(conn, tracker); 141 // we should remove server4 from the list 142 Waiter.waitFor(conf, 30000, () -> manager.getBootstrapNodes().size() == 3); 143 assertListEquals(regionServers.subList(0, 3), manager.getBootstrapNodes()); 144 when(conn.getLiveRegionServers(any(), anyInt())).thenReturn(newRegionServers); 145 doAnswer(invocation -> { 146 String hostname = invocation.getArgument(0, ServerName.class).getHostname(); 147 switch (hostname) { 148 case "server1": 149 return regionServers.subList(0, 1); 150 case "server2": 151 case "server3": 152 throw new IOException("Inject error"); 153 default: 154 return newRegionServers; 155 } 156 }).when(conn).getAllBootstrapNodes(any()); 157 // we should remove server2, server3 from the list and then get the new list from master again 158 Waiter.waitFor(conf, 30000, () -> { 159 List<ServerName> bootstrapNodes = manager.getBootstrapNodes(); 160 if (bootstrapNodes.size() != 2) { 161 return false; 162 } 163 String hostname = bootstrapNodes.get(0).getHostname(); 164 return hostname.equals("server5") || hostname.equals("server6"); 165 }); 166 assertListEquals(newRegionServers, manager.getBootstrapNodes()); 167 } 168}