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.HBaseTestingUtility; 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 waitUntilAllMetaReplicasHavingRegionLocation(Configuration conf, 047 AsyncRegistry registry, int regionReplication) throws IOException { 048 Waiter.waitFor(conf, conf.getLong("hbase.client.sync.wait.timeout.msec", 60000), 200, true, 049 new ExplainingPredicate<IOException>() { 050 @Override 051 public String explainFailure() throws IOException { 052 return "Not all meta replicas get assigned"; 053 } 054 055 @Override 056 public boolean evaluate() throws IOException { 057 try { 058 RegionLocations locs = registry.getMetaRegionLocation().get(); 059 if (locs.size() < regionReplication) { 060 return false; 061 } 062 for (int i = 0; i < regionReplication; i++) { 063 if (locs.getRegionLocation(i) == null) { 064 return false; 065 } 066 } 067 return true; 068 } catch (Exception e) { 069 TestZKAsyncRegistry.LOG.warn("Failed to get meta region locations", e); 070 return false; 071 } 072 } 073 }); 074 } 075 076 static Optional<ServerName> getRSCarryingReplica(HBaseTestingUtility util, TableName tableName, 077 int replicaId) { 078 return util.getHBaseCluster().getRegionServerThreads().stream().map(t -> t.getRegionServer()) 079 .filter(rs -> rs.getRegions(tableName).stream() 080 .anyMatch(r -> r.getRegionInfo().getReplicaId() == replicaId)) 081 .findAny().map(rs -> rs.getServerName()); 082 } 083 084 /** 085 * Return the new location. 086 */ 087 static ServerName moveRegion(HBaseTestingUtility util, HRegionLocation currentLoc) 088 throws Exception { 089 ServerName serverName = currentLoc.getServerName(); 090 RegionInfo regionInfo = currentLoc.getRegion(); 091 TableName tableName = regionInfo.getTable(); 092 int replicaId = regionInfo.getReplicaId(); 093 ServerName newServerName = util.getHBaseCluster().getRegionServerThreads().stream() 094 .map(t -> t.getRegionServer().getServerName()).filter(sn -> !sn.equals(serverName)).findAny() 095 .get(); 096 util.getAdmin().move(regionInfo.getEncodedNameAsBytes(), newServerName); 097 util.waitFor(30000, new ExplainingPredicate<Exception>() { 098 099 @Override 100 public boolean evaluate() throws Exception { 101 Optional<ServerName> newServerName = getRSCarryingReplica(util, tableName, replicaId); 102 return newServerName.isPresent() && !newServerName.get().equals(serverName); 103 } 104 105 @Override 106 public String explainFailure() throws Exception { 107 return regionInfo.getRegionNameAsString() + " is still on " + serverName; 108 } 109 }); 110 return newServerName; 111 } 112 113 interface Locator { 114 RegionLocations getRegionLocations(TableName tableName, int replicaId, boolean reload) 115 throws Exception; 116 117 void updateCachedLocationOnError(HRegionLocation loc, Throwable error) throws Exception; 118 } 119 120 static void testLocator(HBaseTestingUtility util, TableName tableName, Locator locator) 121 throws Exception { 122 RegionLocations locs = 123 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false); 124 assertEquals(3, locs.size()); 125 for (int i = 0; i < 3; i++) { 126 HRegionLocation loc = locs.getRegionLocation(i); 127 assertNotNull(loc); 128 ServerName serverName = getRSCarryingReplica(util, tableName, i).get(); 129 assertEquals(serverName, loc.getServerName()); 130 } 131 ServerName newServerName = moveRegion(util, locs.getDefaultRegionLocation()); 132 // The cached location should not be changed 133 assertEquals(locs.getDefaultRegionLocation().getServerName(), 134 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 135 .getDefaultRegionLocation().getServerName()); 136 // should get the new location when reload = true 137 assertEquals(newServerName, 138 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, true) 139 .getDefaultRegionLocation().getServerName()); 140 // the cached location should be replaced 141 assertEquals(newServerName, 142 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 143 .getDefaultRegionLocation().getServerName()); 144 145 ServerName newServerName1 = moveRegion(util, locs.getRegionLocation(1)); 146 ServerName newServerName2 = moveRegion(util, locs.getRegionLocation(2)); 147 148 // The cached location should not be change 149 assertEquals(locs.getRegionLocation(1).getServerName(), 150 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 151 // clear the cached location for replica 1 152 locator.updateCachedLocationOnError(locs.getRegionLocation(1), new NotServingRegionException()); 153 // the cached location for replica 2 should not be changed 154 assertEquals(locs.getRegionLocation(2).getServerName(), 155 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 156 // should get the new location as we have cleared the old location 157 assertEquals(newServerName1, 158 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 159 // as we will get the new location for replica 2 at once, we should also get the new location 160 // for replica 2 161 assertEquals(newServerName2, 162 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 163 } 164 165 public static void assertReplicaDistributed(HBaseTestingUtility util, Table t) 166 throws IOException { 167 if (t.getDescriptor().getRegionReplication() <= 1) { 168 return; 169 } 170 List<RegionInfo> regionInfos = new ArrayList<>(); 171 for (JVMClusterUtil.RegionServerThread rs : util.getMiniHBaseCluster() 172 .getRegionServerThreads()) { 173 regionInfos.clear(); 174 for (Region r : rs.getRegionServer().getRegions(t.getName())) { 175 if (contains(regionInfos, r.getRegionInfo())) { 176 fail("Replica regions should be assigned to different region servers"); 177 } else { 178 regionInfos.add(r.getRegionInfo()); 179 } 180 } 181 } 182 } 183 184 private static boolean contains(List<RegionInfo> regionInfos, RegionInfo regionInfo) { 185 for (RegionInfo info : regionInfos) { 186 if (RegionReplicaUtil.isReplicasForSameRegion(info, regionInfo)) { 187 return true; 188 } 189 } 190 return false; 191 } 192}