001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.util; 021 022import org.apache.hadoop.hbase.HRegionLocation; 023import org.apache.hadoop.hbase.ServerName; 024import org.apache.hadoop.hbase.client.Admin; 025import org.apache.hadoop.hbase.client.Connection; 026import org.apache.hadoop.hbase.client.RegionInfo; 027import org.apache.hadoop.hbase.client.ResultScanner; 028import org.apache.hadoop.hbase.client.Scan; 029import org.apache.hadoop.hbase.client.Table; 030import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import java.io.IOException; 036import java.util.List; 037import java.util.concurrent.Callable; 038 039/** 040 * Move Regions and make sure that they are up on the target server.If a region movement fails we 041 * exit as failure 042 */ 043@InterfaceAudience.Private 044class MoveWithAck implements Callable<Boolean> { 045 046 private static final Logger LOG = LoggerFactory.getLogger(MoveWithAck.class); 047 048 private final RegionInfo region; 049 private final ServerName targetServer; 050 private final List<RegionInfo> movedRegions; 051 private final ServerName sourceServer; 052 private final Connection conn; 053 private final Admin admin; 054 055 MoveWithAck(Connection conn, RegionInfo regionInfo, ServerName sourceServer, 056 ServerName targetServer, List<RegionInfo> movedRegions) throws IOException { 057 this.conn = conn; 058 this.region = regionInfo; 059 this.targetServer = targetServer; 060 this.movedRegions = movedRegions; 061 this.sourceServer = sourceServer; 062 this.admin = conn.getAdmin(); 063 } 064 065 @Override 066 public Boolean call() throws IOException, InterruptedException { 067 boolean moved = false; 068 int count = 0; 069 int retries = admin.getConfiguration() 070 .getInt(RegionMover.MOVE_RETRIES_MAX_KEY, RegionMover.DEFAULT_MOVE_RETRIES_MAX); 071 int maxWaitInSeconds = admin.getConfiguration() 072 .getInt(RegionMover.MOVE_WAIT_MAX_KEY, RegionMover.DEFAULT_MOVE_WAIT_MAX); 073 long startTime = EnvironmentEdgeManager.currentTime(); 074 boolean sameServer = true; 075 // Assert we can scan the region in its current location 076 isSuccessfulScan(region); 077 LOG.info("Moving region: {} from {} to {}", region.getRegionNameAsString(), sourceServer, 078 targetServer); 079 while (count < retries && sameServer) { 080 if (count > 0) { 081 LOG.debug("Retry {} of maximum {} for region: {}", count, retries, 082 region.getRegionNameAsString()); 083 } 084 count = count + 1; 085 admin.move(region.getEncodedNameAsBytes(), targetServer); 086 long maxWait = startTime + (maxWaitInSeconds * 1000); 087 while (EnvironmentEdgeManager.currentTime() < maxWait) { 088 sameServer = isSameServer(region, sourceServer); 089 if (!sameServer) { 090 break; 091 } 092 Thread.sleep(1000); 093 } 094 } 095 if (sameServer) { 096 LOG.error("Region: {} stuck on {} for {} sec , newServer={}", region.getRegionNameAsString(), 097 this.sourceServer, getTimeDiffInSec(startTime), this.targetServer); 098 } else { 099 isSuccessfulScan(region); 100 LOG.info("Moved Region {} , cost (sec): {}", region.getRegionNameAsString(), 101 getTimeDiffInSec(startTime)); 102 moved = true; 103 movedRegions.add(region); 104 } 105 return moved; 106 } 107 108 private static String getTimeDiffInSec(long startTime) { 109 return String.format("%.3f", (float) (EnvironmentEdgeManager.currentTime() - startTime) / 1000); 110 } 111 112 /** 113 * Tries to scan a row from passed region 114 */ 115 private void isSuccessfulScan(RegionInfo region) throws IOException { 116 Scan scan = new Scan().withStartRow(region.getStartKey()).setRaw(true).setOneRowLimit() 117 .setMaxResultSize(1L).setCaching(1).setFilter(new FirstKeyOnlyFilter()) 118 .setCacheBlocks(false); 119 try (Table table = conn.getTable(region.getTable()); 120 ResultScanner scanner = table.getScanner(scan)) { 121 scanner.next(); 122 } catch (IOException e) { 123 LOG.error("Could not scan region: {}", region.getEncodedName(), e); 124 throw e; 125 } 126 } 127 128 /** 129 * Returns true if passed region is still on serverName when we look at hbase:meta. 130 * @return true if region is hosted on serverName otherwise false 131 */ 132 private boolean isSameServer(RegionInfo region, ServerName serverName) 133 throws IOException { 134 ServerName serverForRegion = getServerNameForRegion(region, admin, conn); 135 return serverForRegion != null && serverForRegion.equals(serverName); 136 } 137 138 /** 139 * Get servername that is up in hbase:meta hosting the given region. this is hostname + port + 140 * startcode comma-delimited. Can return null 141 * @return regionServer hosting the given region 142 */ 143 static ServerName getServerNameForRegion(RegionInfo region, Admin admin, Connection conn) 144 throws IOException { 145 if (!admin.isTableEnabled(region.getTable())) { 146 return null; 147 } 148 HRegionLocation loc; 149 try { 150 loc = conn.getRegionLocator(region.getTable()) 151 .getRegionLocation(region.getStartKey(), region.getReplicaId(), true); 152 } catch (IOException e) { 153 if (e.getMessage() != null && e.getMessage().startsWith("Unable to find region for")) { 154 return null; 155 } 156 throw e; 157 } 158 if (loc != null) { 159 return loc.getServerName(); 160 } else { 161 return null; 162 } 163 } 164 165}