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; 022 023import java.io.IOException; 024import java.util.Optional; 025import org.apache.hadoop.hbase.HBaseTestingUtility; 026import org.apache.hadoop.hbase.HRegionLocation; 027import org.apache.hadoop.hbase.NotServingRegionException; 028import org.apache.hadoop.hbase.RegionLocations; 029import org.apache.hadoop.hbase.ServerName; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.Waiter.ExplainingPredicate; 032import org.apache.hadoop.hbase.util.Bytes; 033 034final class RegionReplicaTestHelper { 035 036 private RegionReplicaTestHelper() { 037 } 038 039 // waits for all replicas to have region location 040 static void waitUntilAllMetaReplicasHavingRegionLocation(AsyncRegistry registry, 041 int regionReplication) throws IOException { 042 TestZKAsyncRegistry.TEST_UTIL.waitFor( 043 TestZKAsyncRegistry.TEST_UTIL.getConfiguration() 044 .getLong("hbase.client.sync.wait.timeout.msec", 60000), 045 200, true, new ExplainingPredicate<IOException>() { 046 @Override 047 public String explainFailure() throws IOException { 048 return "Not all meta replicas get assigned"; 049 } 050 051 @Override 052 public boolean evaluate() throws IOException { 053 try { 054 RegionLocations locs = registry.getMetaRegionLocation().get(); 055 if (locs.size() < regionReplication) { 056 return false; 057 } 058 for (int i = 0; i < regionReplication; i++) { 059 if (locs.getRegionLocation(i) == null) { 060 return false; 061 } 062 } 063 return true; 064 } catch (Exception e) { 065 TestZKAsyncRegistry.LOG.warn("Failed to get meta region locations", e); 066 return false; 067 } 068 } 069 }); 070 } 071 072 static Optional<ServerName> getRSCarryingReplica(HBaseTestingUtility util, TableName tableName, 073 int replicaId) { 074 return util.getHBaseCluster().getRegionServerThreads().stream().map(t -> t.getRegionServer()) 075 .filter(rs -> rs.getRegions(tableName).stream() 076 .anyMatch(r -> r.getRegionInfo().getReplicaId() == replicaId)) 077 .findAny().map(rs -> rs.getServerName()); 078 } 079 080 /** 081 * Return the new location. 082 */ 083 static ServerName moveRegion(HBaseTestingUtility util, HRegionLocation currentLoc) 084 throws Exception { 085 ServerName serverName = currentLoc.getServerName(); 086 RegionInfo regionInfo = currentLoc.getRegion(); 087 TableName tableName = regionInfo.getTable(); 088 int replicaId = regionInfo.getReplicaId(); 089 ServerName newServerName = util.getHBaseCluster().getRegionServerThreads().stream() 090 .map(t -> t.getRegionServer().getServerName()).filter(sn -> !sn.equals(serverName)).findAny() 091 .get(); 092 util.getAdmin().move(regionInfo.getEncodedNameAsBytes(), 093 Bytes.toBytes(newServerName.getServerName())); 094 util.waitFor(30000, new ExplainingPredicate<Exception>() { 095 096 @Override 097 public boolean evaluate() throws Exception { 098 Optional<ServerName> newServerName = getRSCarryingReplica(util, tableName, replicaId); 099 return newServerName.isPresent() && !newServerName.get().equals(serverName); 100 } 101 102 @Override 103 public String explainFailure() throws Exception { 104 return regionInfo.getRegionNameAsString() + " is still on " + serverName; 105 } 106 }); 107 return newServerName; 108 } 109 110 interface Locator { 111 RegionLocations getRegionLocations(TableName tableName, int replicaId, boolean reload) 112 throws Exception; 113 114 void updateCachedLocationOnError(HRegionLocation loc, Throwable error) throws Exception; 115 } 116 117 static void testLocator(HBaseTestingUtility util, TableName tableName, Locator locator) 118 throws Exception { 119 RegionLocations locs = 120 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false); 121 assertEquals(3, locs.size()); 122 for (int i = 0; i < 3; i++) { 123 HRegionLocation loc = locs.getRegionLocation(i); 124 assertNotNull(loc); 125 ServerName serverName = getRSCarryingReplica(util, tableName, i).get(); 126 assertEquals(serverName, loc.getServerName()); 127 } 128 ServerName newServerName = moveRegion(util, locs.getDefaultRegionLocation()); 129 // The cached location should not be changed 130 assertEquals(locs.getDefaultRegionLocation().getServerName(), 131 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 132 .getDefaultRegionLocation().getServerName()); 133 // should get the new location when reload = true 134 assertEquals(newServerName, 135 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, true) 136 .getDefaultRegionLocation().getServerName()); 137 // the cached location should be replaced 138 assertEquals(newServerName, 139 locator.getRegionLocations(tableName, RegionReplicaUtil.DEFAULT_REPLICA_ID, false) 140 .getDefaultRegionLocation().getServerName()); 141 142 ServerName newServerName1 = moveRegion(util, locs.getRegionLocation(1)); 143 ServerName newServerName2 = moveRegion(util, locs.getRegionLocation(2)); 144 145 // The cached location should not be change 146 assertEquals(locs.getRegionLocation(1).getServerName(), 147 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 148 // clear the cached location for replica 1 149 locator.updateCachedLocationOnError(locs.getRegionLocation(1), new NotServingRegionException()); 150 // the cached location for replica 2 should not be changed 151 assertEquals(locs.getRegionLocation(2).getServerName(), 152 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 153 // should get the new location as we have cleared the old location 154 assertEquals(newServerName1, 155 locator.getRegionLocations(tableName, 1, false).getRegionLocation(1).getServerName()); 156 // as we will get the new location for replica 2 at once, we should also get the new location 157 // for replica 2 158 assertEquals(newServerName2, 159 locator.getRegionLocations(tableName, 2, false).getRegionLocation(2).getServerName()); 160 } 161}