diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index fc80d9c..db748ff 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; @@ -170,6 +171,9 @@ public class AssignmentManager extends ZooKeeperListener { private final Map> mergingRegions = new HashMap>(); + private final Map> splitRegions + = new HashMap>(); + /** * The sleep time for which the assignment will wait before retrying in case of hbase:meta assignment * failure due to lack of availability of region plan or bad region plan @@ -1308,14 +1312,26 @@ public class AssignmentManager extends ZooKeeperListener { boolean disabled = getZKTable().isDisablingOrDisabledTable(regionInfo.getTable()); ServerName serverName = rs.getServerName(); if (serverManager.isServerOnline(serverName)) { - if (rs.isOnServer(serverName) - && (rs.isOpened() || rs.isSplitting())) { - regionOnline(regionInfo, serverName); - if (disabled) { - // if server is offline, no hurt to unassign again - LOG.info("Opened " + regionNameStr - + "but this table is disabled, triggering close of region"); - unassign(regionInfo); + if (rs.isOnServer(serverName) && (rs.isOpened() || rs.isSplitting())) { + synchronized (regionStates) { + regionOnline(regionInfo, serverName); + // Check if the daugter regions are still there, if they are present, offline + // as its the case of a rollback. + HRegionInfo hri_a = splitRegions.get(regionInfo).getFirst(); + HRegionInfo hri_b = splitRegions.get(regionInfo).getSecond(); + if (regionStates.isRegionInTransition(hri_a.getEncodedName())) { + regionOffline(hri_a); + } + if (regionStates.isRegionInTransition(hri_b.getEncodedName())) { + regionOffline(hri_b); + } + splitRegions.remove(regionInfo); + if (disabled) { + // if server is offline, no hurt to unassign again + LOG.info("Opened " + regionNameStr + + "but this table is disabled, triggering close of region"); + unassign(regionInfo); + } } } else if (rs.isMergingNew()) { synchronized (regionStates) { @@ -3980,6 +3996,7 @@ public class AssignmentManager extends ZooKeeperListener { } synchronized (regionStates) { + splitRegions.put(p, new PairOfSameType(hri_a, hri_b)); regionStates.updateRegionState(hri_a, State.SPLITTING_NEW, sn); regionStates.updateRegionState(hri_b, State.SPLITTING_NEW, sn); regionStates.updateRegionState(rt, State.SPLITTING); @@ -3995,6 +4012,7 @@ public class AssignmentManager extends ZooKeeperListener { regionOffline(p, State.SPLIT); regionOnline(hri_a, sn); regionOnline(hri_b, sn); + splitRegions.remove(p); } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/SplitFailObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/SplitFailObserver.java new file mode 100644 index 0000000..9d666e7 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/SplitFailObserver.java @@ -0,0 +1,37 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.regionserver; + +import java.io.IOException; +import java.util.List; + +import org.apache.hadoop.hbase.client.Mutation; +import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; + +public class SplitFailObserver extends BaseRegionObserver { + + @Override + public void preSplitBeforePONR(ObserverContext ctx, + byte[] splitKey, List metaEntries) throws IOException { + throw new IOException("Region Split must fail for test."); + } + +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFailedSplitTransaction.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFailedSplitTransaction.java new file mode 100644 index 0000000..104f649 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFailedSplitTransaction.java @@ -0,0 +1,68 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.regionserver; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class TestFailedSplitTransaction { + private final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + + @Before + public void setup() throws Exception { + TEST_UTIL.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, + SplitFailObserver.class.getName()); + TEST_UTIL.startMiniCluster(3); + } + + @After + public void teardown() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + @Test + public void testFailedSplit() throws Exception { + String name = "testFailedSplit"; + TableName tableName = TableName.valueOf(name); + byte[] colFamily = Bytes.toBytes("info"); + TEST_UTIL.createTable(tableName, colFamily); + HTable table = new HTable(TEST_UTIL.getConfiguration(), name); + TEST_UTIL.loadTable(table, colFamily); + List regions = TEST_UTIL.getHBaseAdmin().getTableRegions(tableName); + assertTrue(regions.size() == 1); + + // The following split would fail. + TEST_UTIL.getHBaseAdmin().split(name); + TEST_UTIL.waitFor(15 * 1000,TEST_UTIL.predicateNoRegionsInTransition()); + regions = TEST_UTIL.getHBaseAdmin().getTableRegions(tableName); + assertTrue(regions.size() == 1); + TEST_UTIL.deleteTable(tableName); + } +}