HBASE-10349 Table became unusable when master balanced its region after table was dropped
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1559311 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
257440a279
commit
fa298a9573
|
@ -3144,9 +3144,6 @@ public class AssignmentManager extends ZooKeeperListener {
|
||||||
* @param plan Plan to execute.
|
* @param plan Plan to execute.
|
||||||
*/
|
*/
|
||||||
public void balance(final RegionPlan plan) {
|
public void balance(final RegionPlan plan) {
|
||||||
synchronized (this.regionPlans) {
|
|
||||||
this.regionPlans.put(plan.getRegionName(), plan);
|
|
||||||
}
|
|
||||||
HRegionInfo hri = plan.getRegionInfo();
|
HRegionInfo hri = plan.getRegionInfo();
|
||||||
TableName tableName = hri.getTable();
|
TableName tableName = hri.getTable();
|
||||||
if (zkTable.isDisablingOrDisabledTable(tableName)) {
|
if (zkTable.isDisablingOrDisabledTable(tableName)) {
|
||||||
|
@ -3154,7 +3151,24 @@ public class AssignmentManager extends ZooKeeperListener {
|
||||||
+ tableName);
|
+ tableName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move the region only if it's assigned
|
||||||
|
String encodedName = hri.getEncodedName();
|
||||||
|
ReentrantLock lock = locker.acquireLock(encodedName);
|
||||||
|
try {
|
||||||
|
if (!regionStates.isRegionOnline(hri)) {
|
||||||
|
RegionState state = regionStates.getRegionState(encodedName);
|
||||||
|
LOG.info("Ignored moving region not assigned: " + hri + ", "
|
||||||
|
+ (state == null ? "not in region states" : state));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (this.regionPlans) {
|
||||||
|
this.regionPlans.put(plan.getRegionName(), plan);
|
||||||
|
}
|
||||||
unassign(hri, false, plan.getDestination());
|
unassign(hri, false, plan.getDestination());
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
|
|
@ -576,6 +576,23 @@ public class RegionStates {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table is deleted. Remove its regions from all internal maps.
|
||||||
|
* We loop through all regions assuming we don't delete tables too much.
|
||||||
|
*/
|
||||||
|
public synchronized void tableDeleted(final TableName tableName) {
|
||||||
|
Set<HRegionInfo> regionsToDelete = new HashSet<HRegionInfo>();
|
||||||
|
for (RegionState state: regionStates.values()) {
|
||||||
|
HRegionInfo region = state.getRegion();
|
||||||
|
if (region.getTable().equals(tableName)) {
|
||||||
|
regionsToDelete.add(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (HRegionInfo region: regionsToDelete) {
|
||||||
|
deleteRegion(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checking if a region was assigned to a server which is not online now.
|
* Checking if a region was assigned to a server which is not online now.
|
||||||
* If so, we should hold re-assign this region till SSH has split its hlogs.
|
* If so, we should hold re-assign this region till SSH has split its hlogs.
|
||||||
|
@ -746,4 +763,19 @@ public class RegionStates {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a region from all state maps.
|
||||||
|
*/
|
||||||
|
private void deleteRegion(final HRegionInfo hri) {
|
||||||
|
String encodedName = hri.getEncodedName();
|
||||||
|
regionsInTransition.remove(encodedName);
|
||||||
|
regionStates.remove(encodedName);
|
||||||
|
lastAssignments.remove(encodedName);
|
||||||
|
ServerName sn = regionAssignments.remove(hri);
|
||||||
|
if (sn != null) {
|
||||||
|
Set<HRegionInfo> regions = serverHoldings.get(sn);
|
||||||
|
regions.remove(hri);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,11 @@ public class DeleteTableHandler extends TableEventHandler {
|
||||||
LOG.debug("Removing '" + tableName + "' descriptor.");
|
LOG.debug("Removing '" + tableName + "' descriptor.");
|
||||||
this.masterServices.getTableDescriptors().remove(tableName);
|
this.masterServices.getTableDescriptors().remove(tableName);
|
||||||
|
|
||||||
// 7. If entry for this table in zk, and up in AssignmentManager, remove it.
|
// 7. Clean up regions of the table in RegionStates.
|
||||||
|
LOG.debug("Removing '" + tableName + "' from region states.");
|
||||||
|
states.tableDeleted(tableName);
|
||||||
|
|
||||||
|
// 8. If entry for this table in zk, and up in AssignmentManager, remove it.
|
||||||
LOG.debug("Marking '" + tableName + "' as deleted.");
|
LOG.debug("Marking '" + tableName + "' as deleted.");
|
||||||
am.getZKTable().setDeletedTable(tableName);
|
am.getZKTable().setDeletedTable(tableName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1339,4 +1339,26 @@ public class TestAssignmentManager {
|
||||||
am.shutdown();
|
am.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a table is deleted, we should not be able to balance it anymore.
|
||||||
|
* Otherwise, the region will be brought back.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBalanceRegionOfDeletedTable() throws Exception {
|
||||||
|
CatalogTracker ct = Mockito.mock(CatalogTracker.class);
|
||||||
|
AssignmentManager am = new AssignmentManager(this.server, this.serverManager,
|
||||||
|
ct, balancer, null, null, master.getTableLockManager());
|
||||||
|
RegionStates regionStates = am.getRegionStates();
|
||||||
|
HRegionInfo hri = REGIONINFO;
|
||||||
|
regionStates.createRegionState(hri);
|
||||||
|
assertFalse(regionStates.isRegionInTransition(hri));
|
||||||
|
RegionPlan plan = new RegionPlan(hri, SERVERNAME_A, SERVERNAME_B);
|
||||||
|
// Fake table is deleted
|
||||||
|
regionStates.tableDeleted(hri.getTable());
|
||||||
|
am.balance(plan);
|
||||||
|
assertFalse("The region should not in transition",
|
||||||
|
regionStates.isRegionInTransition(hri));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.MiniHBaseCluster;
|
||||||
import org.apache.hadoop.hbase.ServerLoad;
|
import org.apache.hadoop.hbase.ServerLoad;
|
||||||
import org.apache.hadoop.hbase.ServerName;
|
import org.apache.hadoop.hbase.ServerName;
|
||||||
import org.apache.hadoop.hbase.TableName;
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.UnknownRegionException;
|
||||||
import org.apache.hadoop.hbase.catalog.MetaEditor;
|
import org.apache.hadoop.hbase.catalog.MetaEditor;
|
||||||
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
||||||
import org.apache.hadoop.hbase.client.HTable;
|
import org.apache.hadoop.hbase.client.HTable;
|
||||||
|
@ -272,6 +273,54 @@ public class TestAssignmentManagerOnCluster {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a table is deleted, we should not be able to move it anymore.
|
||||||
|
* Otherwise, the region will be brought back.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test (timeout=50000)
|
||||||
|
public void testMoveRegionOfDeletedTable() throws Exception {
|
||||||
|
TableName table =
|
||||||
|
TableName.valueOf("testMoveRegionOfDeletedTable");
|
||||||
|
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
|
||||||
|
try {
|
||||||
|
HRegionInfo hri = createTableAndGetOneRegion(table);
|
||||||
|
|
||||||
|
HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
|
||||||
|
AssignmentManager am = master.getAssignmentManager();
|
||||||
|
RegionStates regionStates = am.getRegionStates();
|
||||||
|
ServerName serverName = regionStates.getRegionServerOfRegion(hri);
|
||||||
|
ServerName destServerName = null;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
HRegionServer destServer = TEST_UTIL.getHBaseCluster().getRegionServer(i);
|
||||||
|
if (!destServer.getServerName().equals(serverName)) {
|
||||||
|
destServerName = destServer.getServerName();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(destServerName != null
|
||||||
|
&& !destServerName.equals(serverName));
|
||||||
|
|
||||||
|
TEST_UTIL.deleteTable(table);
|
||||||
|
|
||||||
|
try {
|
||||||
|
admin.move(hri.getEncodedNameAsBytes(),
|
||||||
|
Bytes.toBytes(destServerName.getServerName()));
|
||||||
|
fail("We should not find the region");
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
assertTrue(ioe instanceof UnknownRegionException);
|
||||||
|
}
|
||||||
|
|
||||||
|
am.balance(new RegionPlan(hri, serverName, destServerName));
|
||||||
|
assertFalse("The region should not be in transition",
|
||||||
|
regionStates.isRegionInTransition(hri));
|
||||||
|
} finally {
|
||||||
|
if (admin.tableExists(table)) {
|
||||||
|
TEST_UTIL.deleteTable(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HRegionInfo createTableAndGetOneRegion(
|
HRegionInfo createTableAndGetOneRegion(
|
||||||
final TableName tableName) throws IOException, InterruptedException {
|
final TableName tableName) throws IOException, InterruptedException {
|
||||||
HTableDescriptor desc = new HTableDescriptor(tableName);
|
HTableDescriptor desc = new HTableDescriptor(tableName);
|
||||||
|
|
Loading…
Reference in New Issue