Author: todd
Date: Wed Sep 14 06:45:34 2011
New Revision: 1170449
URL: http://svn.apache.org/viewvc?rev=1170449&view=rev
Log:
HBASE-4287 If region opening fails, change region in transition into a
FAILED_OPEN state so that it can be retried quickly.
Modified:
hbase/trunk/CHANGES.txt
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/ExecutorService.java
hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
hbase/trunk/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java
Modified: hbase/trunk/CHANGES.txt
URL:
http://svn.apache.org/viewvc/hbase/trunk/CHANGES.txt?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
--- hbase/trunk/CHANGES.txt (original)
+++ hbase/trunk/CHANGES.txt Wed Sep 14 06:45:34 2011
@@ -477,6 +477,8 @@ Release 0.91.0 - Unreleased
(Lars Hofhansl)
HBASE-4359 Show dead RegionServer names in the HMaster info page
(Harsh J)
+ HBASE-4287 If region opening fails, change region in transition into
+ a FAILED_OPEN state so that it can be retried quickly. (todd)
TASKS
HBASE-3559 Move report of split to master OFF the heartbeat channel
Modified:
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
---
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java
(original)
+++
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/EventHandler.java
Wed Sep 14 06:45:34 2011
@@ -109,6 +109,7 @@ public abstract class EventHandler imple
RS_ZK_REGION_OPENED (4), // RS has finished opening a region
RS_ZK_REGION_SPLITTING (5), // RS has started a region split
RS_ZK_REGION_SPLIT (6), // RS split has completed.
+ RS_ZK_REGION_FAILED_OPEN (7), // RS failed to open a region
// Messages originating from Master to RS
M_RS_OPEN_REGION (20), // Master asking RS to open a region
Modified:
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/ExecutorService.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/ExecutorService.java?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
---
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/ExecutorService.java
(original)
+++
hbase/trunk/src/main/java/org/apache/hadoop/hbase/executor/ExecutorService.java
Wed Sep 14 06:45:34 2011
@@ -118,6 +118,7 @@ public class ExecutorService {
// Master executor services
case RS_ZK_REGION_CLOSED:
+ case RS_ZK_REGION_FAILED_OPEN:
return ExecutorType.MASTER_CLOSE_REGION;
case RS_ZK_REGION_OPENED:
Modified:
hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
---
hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
(original)
+++
hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
Wed Sep 14 06:45:34 2011
@@ -465,6 +465,7 @@ public class AssignmentManager extends Z
break;
case RS_ZK_REGION_CLOSED:
+ case RS_ZK_REGION_FAILED_OPEN:
// Region is closed, insert into RIT and handle it
addToRITandCallClose(regionInfo, RegionState.State.CLOSED, data);
break;
@@ -705,6 +706,21 @@ public class AssignmentManager extends Z
this.executorService.submit(new ClosedRegionHandler(master,
this, regionState.getRegion()));
break;
+
+ case RS_ZK_REGION_FAILED_OPEN:
+ if (regionState == null ||
+ (!regionState.isPendingOpen() && !regionState.isOpening())) {
+ LOG.warn("Received FAILED_OPEN for region " +
prettyPrintedRegionName +
+ " from server " + data.getOrigin() + " but region was in " +
+ " the state " + regionState + " and not in PENDING_OPEN or
OPENING");
+ return;
+ }
+ // Handle this the same as if it were opened and then closed.
+ regionState.update(RegionState.State.CLOSED,
+ data.getStamp(), data.getOrigin());
+ this.executorService.submit(new ClosedRegionHandler(master,
+ this, regionState.getRegion()));
+ break;
case RS_ZK_REGION_OPENING:
// Should see OPENING after we have asked it to OPEN or additional
Modified:
hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
---
hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
(original)
+++
hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
Wed Sep 14 06:45:34 2011
@@ -106,7 +106,11 @@ public class OpenRegionHandler extends E
// Open region. After a successful open, failures in subsequent
// processing needs to do a close as part of cleanup.
region = openRegion();
- if (region == null) return;
+ if (region == null) {
+ tryTransitionToFailedOpen(regionInfo);
+ return;
+ }
+
boolean failed = true;
if (tickleOpening("post_region_open")) {
if (updateMeta(region)) failed = false;
@@ -114,10 +118,17 @@ public class OpenRegionHandler extends E
if (failed || this.server.isStopped() ||
this.rsServices.isStopping()) {
cleanupFailedOpen(region);
+ tryTransitionToFailedOpen(regionInfo);
return;
}
if (!transitionToOpened(region)) {
+ // If we fail to transition to opened, it's because of one of two
cases:
+ // (a) we lost our ZK lease
+ // OR (b) someone else opened the region before us
+ // In either case, we don't need to transition to FAILED_OPEN state.
+ // In case (a), the Master will process us as a dead server. In case
+ // (b) the region is already being handled elsewhere anyway.
cleanupFailedOpen(region);
return;
}
@@ -137,7 +148,7 @@ public class OpenRegionHandler extends E
* state meantime so master doesn't timeout our region-in-transition.
* Caller must cleanup region if this fails.
*/
- private boolean updateMeta(final HRegion r) {
+ boolean updateMeta(final HRegion r) {
if (this.server.isStopped() || this.rsServices.isStopping()) {
return false;
}
@@ -270,33 +281,34 @@ public class OpenRegionHandler extends E
}
return result;
}
-
+
/**
- * @return Instance of HRegion if successful open else null.
+ * @param Region we're working on.
+ * This is not guaranteed to succeed, we just do our best.
+ * @return Transition znode to CLOSED state.
*/
- HRegion openRegion(Path tableDir) {
- HRegion region = null;
+ private boolean tryTransitionToFailedOpen(final HRegionInfo hri) {
+ boolean result = false;
+ final String name = hri.getRegionNameAsString();
try {
- // Instantiate the region. This also periodically tickles our zk OPENING
- // state so master doesn't timeout this region in transition.
- region = HRegion.openHRegion(tableDir, this.regionInfo, this.htd,
- this.rsServices.getWAL(), this.server.getConfiguration(),
- this.rsServices,
- new CancelableProgressable() {
- public boolean progress() {
- // We may lose the znode ownership during the open. Currently its
- // too hard interrupting ongoing region open. Just let it complete
- // and check we still have the znode after region open.
- return tickleOpening("open_region_progress");
- }
- });
- } catch (IOException e) {
- // We failed open. Let our znode expire in regions-in-transition and
- // Master will assign elsewhere. Presumes nothing to close.
- LOG.error("Failed open of region=" +
- this.regionInfo.getRegionNameAsString(), e);
+ LOG.info("Opening of region " + hri + " failed, marking as FAILED_OPEN
in ZK");
+ if (ZKAssign.transitionNode(
+ this.server.getZooKeeper(), hri,
+ this.server.getServerName(),
+ EventType.RS_ZK_REGION_OPENING,
+ EventType.RS_ZK_REGION_FAILED_OPEN,
+ this.version) == -1) {
+ LOG.warn("Unable to mark region " + hri + " as FAILED_OPEN. " +
+ "It's likely that the master already timed out this open " +
+ "attempt, and thus another RS already has the region.");
+ } else {
+ result = true;
+ }
+ } catch (KeeperException e) {
+ LOG.error("Failed transitioning node " + name +
+ " from OPENING to FAILED_OPEN", e);
}
- return region;
+ return result;
}
/**
@@ -318,11 +330,12 @@ public class OpenRegionHandler extends E
return tickleOpening("open_region_progress");
}
});
- } catch (IOException e) {
- // We failed open. Let our znode expire in regions-in-transition and
- // Master will assign elsewhere. Presumes nothing to close.
+ } catch (Throwable t) {
+ // We failed open. Our caller will see the 'null' return value
+ // and transition the node back to FAILED_OPEN. If that fails,
+ // we rely on the Timeout Monitor in the master to reassign.
LOG.error("Failed open of region=" +
- this.regionInfo.getRegionNameAsString(), e);
+ this.regionInfo.getRegionNameAsString(), t);
}
return region;
}
@@ -390,4 +403,4 @@ public class OpenRegionHandler extends E
private boolean isGoodVersion() {
return this.version != -1;
}
-}
\ No newline at end of file
+}
Modified:
hbase/trunk/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java?rev=1170449&r1=1170448&r2=1170449&view=diff
==============================================================================
---
hbase/trunk/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java
(original)
+++
hbase/trunk/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java
Wed Sep 14 06:45:34 2011
@@ -25,11 +25,16 @@ import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.Server;
+import org.apache.hadoop.hbase.ZooKeeperConnectionException;
+import org.apache.hadoop.hbase.executor.RegionTransitionData;
+import org.apache.hadoop.hbase.executor.EventHandler.EventType;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.RegionServerServices;
import org.apache.hadoop.hbase.zookeeper.ZKAssign;
@@ -40,6 +45,9 @@ import org.apache.zookeeper.KeeperExcept
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
/**
* Test of the {@link OpenRegionHandler}.
@@ -47,6 +55,11 @@ import org.junit.Test;
public class TestOpenRegionHandler {
static final Log LOG = LogFactory.getLog(TestOpenRegionHandler.class);
private final static HBaseTestingUtility HTU = new HBaseTestingUtility();
+ private static final HTableDescriptor TEST_HTD =
+ new HTableDescriptor("TestOpenRegionHandler.java");
+ private static final HRegionInfo TEST_HRI =
+ new HRegionInfo(TEST_HTD.getName(), HConstants.EMPTY_END_ROW,
+ HConstants.EMPTY_END_ROW);
@BeforeClass public static void before() throws Exception {
HTU.startMiniZKCluster();
@@ -69,10 +82,8 @@ public class TestOpenRegionHandler {
final Server server = new MockServer(HTU);
final RegionServerServices rss = new MockRegionServerServices();
- HTableDescriptor htd = new HTableDescriptor("TestOpenRegionHandler.java");
- final HRegionInfo hri =
- new HRegionInfo(htd.getName(), HConstants.EMPTY_END_ROW,
- HConstants.EMPTY_END_ROW);
+ HTableDescriptor htd = TEST_HTD;
+ final HRegionInfo hri = TEST_HRI;
HRegion region =
HRegion.createHRegion(hri, HBaseTestingUtility.getTestDir(), HTU
.getConfiguration(), htd);
@@ -80,9 +91,8 @@ public class TestOpenRegionHandler {
OpenRegionHandler handler = new OpenRegionHandler(server, rss, hri, htd) {
HRegion openRegion() {
// Open region first, then remove znode as though it'd been hijacked.
- //HRegion region = super.openRegion();
- HRegion region = super.openRegion(HBaseTestingUtility.getTestDir());
-
+ HRegion region = super.openRegion();
+
// Don't actually open region BUT remove the znode as though it'd
// been hijacked on us.
ZooKeeperWatcher zkw = this.server.getZooKeeper();
@@ -103,4 +113,55 @@ public class TestOpenRegionHandler {
// post OPENING; again will expect it to come back w/o NPE or exception.
handler.process();
}
+
+ @Test
+ public void testFailedOpenRegion() throws Exception {
+ Server server = new MockServer(HTU);
+ RegionServerServices rsServices = Mockito.mock(RegionServerServices.class);
+
+ // Create it OFFLINE, which is what it expects
+ ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI,
server.getServerName());
+
+ // Create the handler
+ OpenRegionHandler handler =
+ new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
+ @Override
+ HRegion openRegion() {
+ // Fake failure of opening a region due to an IOE, which is caught
+ return null;
+ }
+ };
+ handler.process();
+
+ // Handler should have transitioned it to FAILED_OPEN
+ RegionTransitionData data =
+ ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName());
+ assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, data.getEventType());
+ }
+
+ @Test
+ public void testFailedUpdateMeta() throws Exception {
+ Server server = new MockServer(HTU);
+ RegionServerServices rsServices = Mockito.mock(RegionServerServices.class);
+
+ // Create it OFFLINE, which is what it expects
+ ZKAssign.createNodeOffline(server.getZooKeeper(), TEST_HRI,
server.getServerName());
+
+ // Create the handler
+ OpenRegionHandler handler =
+ new OpenRegionHandler(server, rsServices, TEST_HRI, TEST_HTD) {
+ @Override
+ boolean updateMeta(final HRegion r) {
+ // Fake failure of updating META
+ return false;
+ }
+ };
+ handler.process();
+
+ // Handler should have transitioned it to FAILED_OPEN
+ RegionTransitionData data =
+ ZKAssign.getData(server.getZooKeeper(), TEST_HRI.getEncodedName());
+ assertEquals(EventType.RS_ZK_REGION_FAILED_OPEN, data.getEventType());
+ }
+
}
\ No newline at end of file