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.assertNotNull; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import java.util.Optional; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HBaseTestingUtil; 030import org.apache.hadoop.hbase.HRegionLocation; 031import org.apache.hadoop.hbase.NotServingRegionException; 032import org.apache.hadoop.hbase.RegionLocations; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.Waiter; 036import org.apache.hadoop.hbase.Waiter.ExplainingPredicate; 037import org.apache.hadoop.hbase.regionserver.Region; 038import org.apache.hadoop.hbase.util.JVMClusterUtil; 039 040public final class RegionReplicaTestHelper { 041 042 private RegionReplicaTestHelper() { 043 } 044 045 // waits for all replicas to have region location 046 static void waitUntilAllMetaReplicasAreReady(HBaseTestingUtil util, ConnectionRegistry registry) 047 throws IOException { 048 Configuration conf = util.getConfiguration(); 049 int regionReplicaCount = 050 util.getAdmin().getDescriptor(TableName.META_TABLE_NAME).getRegionReplication(); 051 Waiter.waitFor(conf, conf.getLong("hbase.client.sync.wait.timeout.msec", 60000), 200, true, 052 new ExplainingPredicate<IOException>() { 053 @Override 054 public String explainFailure() { 055 return "Not all meta replicas get assigned"; 056 } 057 058 @Override 059 public boolean evaluate() { 060 try { 061 RegionLocations locs = registry.getMetaRegionLocations().get(); 062 if (locs.size() < regionReplicaCount) { 063 return false; 064 } 065 for (int i = 0; i < regionReplicaCount; i++) { 066 HRegionLocation loc = locs.getRegionLocation(i); 067 // Wait until the replica is served by a region server. There could be delay between 068 // the replica being available to the connection and region server opening it. 069 Optional<ServerName> rsCarryingReplica = 070 getRSCarryingReplica(util, loc.getRegion().getTable(), i); 071 if (!rsCarryingReplica.isPresent()) { 072 return false; 073 } 074 } 075 return true; 076 } catch (Exception e) { 077 TestZKConnectionRegistry.LOG.warn("Failed to get meta region locations", e); 078 return false; 079 } 080 } 081 }); 082 } 083 084 static Optional<ServerName> getRSCarryingReplica(HBaseTestingUtil util, TableName tableName, 085 int replicaId) { 086 return util.getHBaseCluster().getRegionServerThreads().stream().map(t -> t.getRegionServer()) 087 .filter(rs -> rs.getRegions(tableName).stream() 088 .anyMatch(r -> r.getRegionInfo().getReplicaId() == replicaId)) 089 .findAny().map(rs -> rs.getServerName()); 090 } 091 092 /** 093 * Return the new location. 094 */ 095 static ServerName moveRegion(HBaseTestingUtil util, HRegionLocation currentLoc) throws Exception { 096 ServerName serverName = currentLoc.getServerName(); 097 RegionInfo regionInfo = currentLoc.getRegion(); 098 TableName tableName = regionInfo.getTable(); 099 int replicaId = regionInfo.getReplicaId(); 100 ServerName newServerName = util.getHBaseCluster().getRegionServerThreads().stream() 101 .map(t -> t.getRegionServer().getServerName()).filter(sn -> !sn.equals(serverName)).findAny() 102 .get(); 103 util.getAdmin().move(regionInfo.getEncodedNameAsBytes(), newServerName); 104 util.waitFor(30000, new ExplainingPredicate<Exception>() { 105 106 @Override 107 public boolean evaluate() throws Exception { 108 Optional<ServerName> newServerName = getRSCarryingReplica(util, tableName, replicaId); 109 return newServerName.isPresent() && !newServerName.get().equals(serverName); 110 } 111 112 @Override 113 public String explainFailure() throws Exception { 114 return regionInfo.getRegionNameAsString() + " is still on " + serverName; 115 } 116 }); 117 return newServerName; 118 } 119 120 interface Locator { 121 RegionLocations getRegionLocations(TableName tableName, int replicaId, boolean reload) 122 throws Exception; 123 124 void updateCachedLocationOnError(HRegionLocation loc, Throwable error) throws Exception; 125 } 126 127 static void testLocator(HBaseTestingUtil util, TableName tableName, Locator locator) 128 throws Exception { 129 RegionLocations locs = 130 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false); 131 assertEquals(3, locs.size()); 132 for (int i = 0; i < 3; i++) { 133 HRegionLocation loc = locs.getRegionLocation(i); 134 assertNotNull(loc); 135 ServerName serverName = getRSCarryingReplica(util, tableName, i).get(); 136 assertEquals(serverName, loc.getServerName()); 137 } 138 ServerName newServerName = moveRegion(util, locs.getDefaultRegionLocation()); 139 // The cached location should not be changed 140 assertEquals(locs.getDefaultRegionLocation().getServerName(), 141 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 142 .getDefaultRegionLocation().getServerName()); 143 // should get the new location when reload = true 144 // when meta replica LoadBalance mode is enabled, it may delay a bit. 145 util.waitFor(3000, new ExplainingPredicate<Exception>() { 146 @Override 147 public boolean evaluate() throws Exception { 148 ServerName sn = 149 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, true) 150 .getDefaultRegionLocation().getServerName(); 151 return newServerName.equals(sn); 152 } 153 154 @Override 155 public String explainFailure() throws Exception { 156 return "New location does not show up in meta (replica) region"; 157 } 158 }); 159 160 assertEquals(newServerName, 161 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, true) 162 .getDefaultRegionLocation().getServerName()); 163 // the cached location should be replaced 164 assertEquals(newServerName, 165 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 166 .getDefaultRegionLocation().getServerName()); 167 168 ServerName newServerName1 = moveRegion(util, locs.getRegionLocation(1)); 169 ServerName newServerName2 = moveRegion(util, locs.getRegionLocation(2)); 170 171 // The cached location should not be change 172 assertEquals(locs.getRegionLocation(1).getServerName(), 173 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 174 // clear the cached location for replica 1 175 locator.updateCachedLocationOnError(locs.getRegionLocation(1), new NotServingRegionException()); 176 // the cached location for replica 2 should not be changed 177 assertEquals(locs.getRegionLocation(2).getServerName(), 178 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 179 // should get the new location as we have cleared the old location 180 assertEquals(newServerName1, 181 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 182 // as we will get the new location for replica 2 at once, we should also get the new location 183 // for replica 2 184 assertEquals(newServerName2, 185 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 186 } 187 188 public static void assertReplicaDistributed(HBaseTestingUtil util, Table t) throws IOException { 189 if (t.getDescriptor().getRegionReplication() <= 1) { 190 return; 191 } 192 List<RegionInfo> regionInfos = new ArrayList<>(); 193 for (JVMClusterUtil.RegionServerThread rs : util.getMiniHBaseCluster() 194 .getRegionServerThreads()) { 195 regionInfos.clear(); 196 for (Region r : rs.getRegionServer().getRegions(t.getName())) { 197 if (contains(regionInfos, r.getRegionInfo())) { 198 fail("Replica regions should be assigned to different region servers"); 199 } else { 200 regionInfos.add(r.getRegionInfo()); 201 } 202 } 203 } 204 } 205 206 private static boolean contains(List<RegionInfo> regionInfos, RegionInfo regionInfo) { 207 for (RegionInfo info : regionInfos) { 208 if (RegionReplicaUtil.isReplicasForSameRegion(info, regionInfo)) { 209 return true; 210 } 211 } 212 return false; 213 } 214}