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