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; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Optional; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseTestingUtility; 029import org.apache.hadoop.hbase.HConstants; 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(HBaseTestingUtility util, 047 ConnectionRegistry registry) { 048 Configuration conf = util.getConfiguration(); 049 int regionReplicaCount = util.getConfiguration().getInt(HConstants.META_REPLICAS_NUM, 050 HConstants.DEFAULT_META_REPLICA_NUM); 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(HBaseTestingUtility 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(HBaseTestingUtility util, HRegionLocation currentLoc) 096 throws Exception { 097 ServerName serverName = currentLoc.getServerName(); 098 RegionInfo regionInfo = currentLoc.getRegion(); 099 TableName tableName = regionInfo.getTable(); 100 int replicaId = regionInfo.getReplicaId(); 101 ServerName newServerName = util.getHBaseCluster().getRegionServerThreads().stream() 102 .map(t -> t.getRegionServer().getServerName()).filter(sn -> !sn.equals(serverName)).findAny() 103 .get(); 104 util.getAdmin().move(regionInfo.getEncodedNameAsBytes(), newServerName); 105 util.waitFor(30000, new ExplainingPredicate<Exception>() { 106 107 @Override 108 public boolean evaluate() throws Exception { 109 Optional<ServerName> newServerName = getRSCarryingReplica(util, tableName, replicaId); 110 return newServerName.isPresent() && !newServerName.get().equals(serverName); 111 } 112 113 @Override 114 public String explainFailure() throws Exception { 115 return regionInfo.getRegionNameAsString() + " is still on " + serverName; 116 } 117 }); 118 return newServerName; 119 } 120 121 interface Locator { 122 RegionLocations getRegionLocations(TableName tableName, int replicaId, boolean reload) 123 throws Exception; 124 125 void updateCachedLocationOnError(HRegionLocation loc, Throwable error) throws Exception; 126 } 127 128 static void testLocator(HBaseTestingUtility util, TableName tableName, Locator locator) 129 throws Exception { 130 RegionLocations locs = 131 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false); 132 assertEquals(3, locs.size()); 133 for (int i = 0; i < 3; i++) { 134 HRegionLocation loc = locs.getRegionLocation(i); 135 assertNotNull(loc); 136 ServerName serverName = getRSCarryingReplica(util, tableName, i).get(); 137 assertEquals(serverName, loc.getServerName()); 138 } 139 ServerName newServerName = moveRegion(util, locs.getDefaultRegionLocation()); 140 // The cached location should not be changed 141 assertEquals(locs.getDefaultRegionLocation().getServerName(), 142 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 143 .getDefaultRegionLocation().getServerName()); 144 // should get the new location when reload = true 145 assertEquals(newServerName, 146 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, true) 147 .getDefaultRegionLocation().getServerName()); 148 // the cached location should be replaced 149 assertEquals(newServerName, 150 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 151 .getDefaultRegionLocation().getServerName()); 152 153 ServerName newServerName1 = moveRegion(util, locs.getRegionLocation(1)); 154 ServerName newServerName2 = moveRegion(util, locs.getRegionLocation(2)); 155 156 // The cached location should not be change 157 assertEquals(locs.getRegionLocation(1).getServerName(), 158 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 159 // clear the cached location for replica 1 160 locator.updateCachedLocationOnError(locs.getRegionLocation(1), new NotServingRegionException()); 161 // the cached location for replica 2 should not be changed 162 assertEquals(locs.getRegionLocation(2).getServerName(), 163 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 164 // should get the new location as we have cleared the old location 165 assertEquals(newServerName1, 166 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 167 // as we will get the new location for replica 2 at once, we should also get the new location 168 // for replica 2 169 assertEquals(newServerName2, 170 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 171 } 172 173 public static void assertReplicaDistributed(HBaseTestingUtility util, Table t) 174 throws IOException { 175 if (t.getDescriptor().getRegionReplication() <= 1) { 176 return; 177 } 178 List<RegionInfo> regionInfos = new ArrayList<>(); 179 for (JVMClusterUtil.RegionServerThread rs : util.getMiniHBaseCluster() 180 .getRegionServerThreads()) { 181 regionInfos.clear(); 182 for (Region r : rs.getRegionServer().getRegions(t.getName())) { 183 if (contains(regionInfos, r.getRegionInfo())) { 184 fail("Replica regions should be assigned to different region servers"); 185 } else { 186 regionInfos.add(r.getRegionInfo()); 187 } 188 } 189 } 190 } 191 192 private static boolean contains(List<RegionInfo> regionInfos, RegionInfo regionInfo) { 193 for (RegionInfo info : regionInfos) { 194 if (RegionReplicaUtil.isReplicasForSameRegion(info, regionInfo)) { 195 return true; 196 } 197 } 198 return false; 199 } 200}