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