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}