You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by jd...@apache.org on 2011/04/27 23:46:59 UTC

svn commit: r1097255 - in /hbase/branches/0.90: ./ src/main/java/org/apache/hadoop/hbase/regionserver/ src/main/java/org/apache/hadoop/hbase/regionserver/handler/ src/test/java/org/apache/hadoop/hbase/master/ src/test/java/org/apache/hadoop/hbase/regio...

Author: jdcryans
Date: Wed Apr 27 21:46:58 2011
New Revision: 1097255

URL: http://svn.apache.org/viewvc?rev=1097255&view=rev
Log:
HBASE-3741  Make HRegionServer aware of the regions it's opening/closing

Added:
    hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionAlreadyInTransitionException.java
Modified:
    hbase/branches/0.90/CHANGES.txt
    hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
    hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java
    hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/CloseRegionHandler.java
    hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
    hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/master/TestZKBasedOpenCloseRegion.java
    hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java

Modified: hbase/branches/0.90/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/CHANGES.txt?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/CHANGES.txt (original)
+++ hbase/branches/0.90/CHANGES.txt Wed Apr 27 21:46:58 2011
@@ -35,6 +35,7 @@ Release 0.90.3 - Unreleased
                (Harsh J Chouraria)
    HBASE-3749  Master can't exit when open port failed (gaojinchao)
    HBASE-3794  TestRpcMetrics fails on machine where region server is running
+   HBASE-3741  Make HRegionServer aware of the regions it's opening/closing
 
   IMPROVEMENT
    HBASE-3717  deprecate HTable isTableEnabled() methods in favor of HBaseAdmin

Modified: hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Wed Apr 27 21:46:58 2011
@@ -44,7 +44,7 @@ import java.util.Random;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -77,7 +77,6 @@ import org.apache.hadoop.hbase.Stoppable
 import org.apache.hadoop.hbase.UnknownRowLockException;
 import org.apache.hadoop.hbase.UnknownScannerException;
 import org.apache.hadoop.hbase.YouAreDeadException;
-import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
 import org.apache.hadoop.hbase.catalog.CatalogTracker;
 import org.apache.hadoop.hbase.catalog.MetaEditor;
 import org.apache.hadoop.hbase.catalog.RootLocationEditor;
@@ -270,6 +269,9 @@ public class HRegionServer implements HR
   // Replication services. If no replication, this handler will be null.
   private Replication replicationHandler;
 
+  private final Set<byte[]> regionsInTransitionInRS =
+      new ConcurrentSkipListSet<byte[]>(Bytes.BYTES_COMPARATOR);
+
   /**
    * Starts a HRegionServer at the default location
    *
@@ -2060,7 +2062,10 @@ public class HRegionServer implements HR
   @Override
   @QosPriority(priority=HIGH_QOS)
   public void openRegion(HRegionInfo region)
-  throws RegionServerStoppedException {
+  throws IOException {
+    if (this.regionsInTransitionInRS.contains(region.getEncodedNameAsBytes())) {
+      throw new RegionAlreadyInTransitionException("open", region.getEncodedName());
+    }
     LOG.info("Received request to open region: " +
       region.getRegionNameAsString());
     if (this.stopped) throw new RegionServerStoppedException();
@@ -2076,7 +2081,7 @@ public class HRegionServer implements HR
   @Override
   @QosPriority(priority=HIGH_QOS)
   public void openRegions(List<HRegionInfo> regions)
-  throws RegionServerStoppedException {
+  throws IOException {
     LOG.info("Received request to open " + regions.size() + " region(s)");
     for (HRegionInfo region: regions) openRegion(region);
   }
@@ -2084,14 +2089,14 @@ public class HRegionServer implements HR
   @Override
   @QosPriority(priority=HIGH_QOS)
   public boolean closeRegion(HRegionInfo region)
-  throws NotServingRegionException {
+  throws IOException {
     return closeRegion(region, true);
   }
 
   @Override
   @QosPriority(priority=HIGH_QOS)
   public boolean closeRegion(HRegionInfo region, final boolean zk)
-  throws NotServingRegionException {
+  throws IOException {
     LOG.info("Received close region: " + region.getRegionNameAsString());
     boolean hasit = this.onlineRegions.containsKey(region.getEncodedName());
     if (!hasit) {
@@ -2100,6 +2105,9 @@ public class HRegionServer implements HR
       throw new NotServingRegionException("Received close for "
         + region.getRegionNameAsString() + " but we are not serving it");
     }
+    if (this.regionsInTransitionInRS.contains(region.getEncodedNameAsBytes())) {
+      throw new RegionAlreadyInTransitionException("close", region.getEncodedName());
+    }
     return closeRegion(region, false, zk);
   }
 
@@ -2113,6 +2121,11 @@ public class HRegionServer implements HR
    */
   protected boolean closeRegion(HRegionInfo region, final boolean abort,
       final boolean zk) {
+    if (this.regionsInTransitionInRS.contains(region.getEncodedNameAsBytes())) {
+      LOG.warn("Received close for region we are already opening or closing; " +
+          region.getEncodedName());
+      return false;
+    }
     CloseRegionHandler crh = null;
     if (region.isRootRegion()) {
       crh = new CloseRootHandler(this, this, region, abort, zk);
@@ -2604,6 +2617,10 @@ public class HRegionServer implements HR
     return this.compactSplitThread;
   }
 
+  public Set<byte[]> getRegionsInTransitionInRS() {
+    return this.regionsInTransitionInRS;
+  }
+
   //
   // Main program and support routines
   //

Added: hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionAlreadyInTransitionException.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionAlreadyInTransitionException.java?rev=1097255&view=auto
==============================================================================
--- hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionAlreadyInTransitionException.java (added)
+++ hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionAlreadyInTransitionException.java Wed Apr 27 21:46:58 2011
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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;
+
+/**
+ * This exception is thrown when a region server is asked to open or close
+ * a region but it's already processing it
+ */
+public class RegionAlreadyInTransitionException extends IOException {
+
+  public RegionAlreadyInTransitionException(String action, String region) {
+    super("Received " + action + " for region we are" +
+          " already opening or closing; " + region);
+  }
+}

Modified: hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java (original)
+++ hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java Wed Apr 27 21:46:58 2011
@@ -26,6 +26,7 @@ import org.apache.hadoop.hbase.catalog.C
 import org.apache.hadoop.hbase.ipc.HBaseRpcMetrics;
 import org.apache.hadoop.hbase.regionserver.wal.HLog;
 import org.apache.zookeeper.KeeperException;
+import java.util.Set;
 
 /**
  * Services provided by {@link HRegionServer}
@@ -72,4 +73,10 @@ public interface RegionServerServices ex
    * Returns a reference to the RPC server metrics.
    */
   public HBaseRpcMetrics getRpcMetrics();
+
+  /**
+   * Get the regions that are currently being opened or closed in the RS
+   * @return set of regions in transition in this RS
+   */
+  public Set<byte[]> getRegionsInTransitionInRS();
 }
\ No newline at end of file

Modified: hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/CloseRegionHandler.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/CloseRegionHandler.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/CloseRegionHandler.java (original)
+++ hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/CloseRegionHandler.java Wed Apr 27 21:46:58 2011
@@ -95,45 +95,51 @@ public class CloseRegionHandler extends 
 
   @Override
   public void process() {
-    String name = regionInfo.getRegionNameAsString();
-    LOG.debug("Processing close of " + name);
-    String encodedRegionName = regionInfo.getEncodedName();
-    // Check that this region is being served here
-    HRegion region = this.rsServices.getFromOnlineRegions(encodedRegionName);
-    if (region == null) {
-      LOG.warn("Received CLOSE for region " + name + " but currently not serving");
-      return;
-    }
-
-    int expectedVersion = FAILED;
-    if (this.zk) {
-      expectedVersion = setClosingState();
-      if (expectedVersion == FAILED) return;
-    }
-
-    // Close the region
     try {
-      // TODO: If we need to keep updating CLOSING stamp to prevent against
-      // a timeout if this is long-running, need to spin up a thread?
-      if (region.close(abort) == null) {
-        // This region got closed.  Most likely due to a split. So instead
-        // of doing the setClosedState() below, let's just ignore and continue.
-        // The split message will clean up the master state.
-        LOG.warn("Can't close region: was already closed during close(): " +
-          regionInfo.getRegionNameAsString());
+      String name = regionInfo.getRegionNameAsString();
+      LOG.debug("Processing close of " + name);
+      String encodedRegionName = regionInfo.getEncodedName();
+      // Check that this region is being served here
+      HRegion region = this.rsServices.getFromOnlineRegions(encodedRegionName);
+      if (region == null) {
+        LOG.warn("Received CLOSE for region " + name +
+            " but currently not serving");
         return;
       }
-    } catch (IOException e) {
-      LOG.error("Unrecoverable exception while closing region " +
-        regionInfo.getRegionNameAsString() + ", still finishing close", e);
-    }
 
-    this.rsServices.removeFromOnlineRegions(regionInfo.getEncodedName());
+      int expectedVersion = FAILED;
+      if (this.zk) {
+        expectedVersion = setClosingState();
+        if (expectedVersion == FAILED) return;
+      }
 
-    if (this.zk) setClosedState(expectedVersion, region);
+      // Close the region
+      try {
+        // TODO: If we need to keep updating CLOSING stamp to prevent against
+        // a timeout if this is long-running, need to spin up a thread?
+        if (region.close(abort) == null) {
+          // This region got closed.  Most likely due to a split. So instead
+          // of doing the setClosedState() below, let's just ignore cont
+          // The split message will clean up the master state.
+          LOG.warn("Can't close region: was already closed during close(): " +
+            regionInfo.getRegionNameAsString());
+          return;
+        }
+      } catch (IOException e) {
+        LOG.error("Unrecoverable exception while closing region " +
+          regionInfo.getRegionNameAsString() + ", still finishing close", e);
+      }
 
-    // Done!  Region is closed on this RS
-    LOG.debug("Closed region " + region.getRegionNameAsString());
+      this.rsServices.removeFromOnlineRegions(regionInfo.getEncodedName());
+
+      if (this.zk) setClosedState(expectedVersion, region);
+
+      // Done!  Region is closed on this RS
+      LOG.debug("Closed region " + region.getRegionNameAsString());
+    } finally {
+      this.rsServices.getRegionsInTransitionInRS().
+          remove(this.regionInfo.getEncodedNameAsBytes());
+    }
   }
 
   /**

Modified: hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java (original)
+++ hbase/branches/0.90/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java Wed Apr 27 21:46:58 2011
@@ -69,51 +69,57 @@ public class OpenRegionHandler extends E
 
   @Override
   public void process() throws IOException {
-    final String name = regionInfo.getRegionNameAsString();
-    LOG.debug("Processing open of " + name);
-    if (this.server.isStopped() || this.rsServices.isStopping()) {
-      LOG.info("Server stopping or stopped, skipping open of " + name);
-      return;
-    }
-    final String encodedName = regionInfo.getEncodedName();
+    try {
+      final String name = regionInfo.getRegionNameAsString();
+      LOG.debug("Processing open of " + name);
+      if (this.server.isStopped() || this.rsServices.isStopping()) {
+        LOG.info("Server stopping or stopped, skipping open of " + name);
+        return;
+      }
+      final String encodedName = regionInfo.getEncodedName();
 
-    // Check that this region is not already online
-    HRegion region = this.rsServices.getFromOnlineRegions(encodedName);
-    if (region != null) {
-      LOG.warn("Attempted open of " + name +
-        " but already online on this server");
-      return;
-    }
-
-    // If fails, just return.  Someone stole the region from under us.
-    // Calling transitionZookeeperOfflineToOpening initalizes this.version.
-    if (!transitionZookeeperOfflineToOpening(encodedName)) {
-      LOG.warn("Region was hijacked? It no longer exists, encodedName=" +
-        encodedName);
-      return;
-    }
+      // Check that this region is not already online
+      HRegion region = this.rsServices.getFromOnlineRegions(encodedName);
+      if (region != null) {
+        LOG.warn("Attempted open of " + name +
+          " but already online on this server");
+        return;
+      }
 
-    // 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;
-    boolean failed = true;
-    if (tickleOpening("post_region_open")) {
-      if (updateMeta(region)) failed = false;
-    }
+      // If fails, just return.  Someone stole the region from under us.
+      // Calling transitionZookeeperOfflineToOpening initalizes this.version.
+      if (!transitionZookeeperOfflineToOpening(encodedName)) {
+        LOG.warn("Region was hijacked? It no longer exists, encodedName=" +
+          encodedName);
+        return;
+      }
 
-    if (failed || this.server.isStopped() || this.rsServices.isStopping()) {
-      cleanupFailedOpen(region);
-      return;
-    }
+      // 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;
+      boolean failed = true;
+      if (tickleOpening("post_region_open")) {
+        if (updateMeta(region)) failed = false;
+      }
 
-    if (!transitionToOpened(region)) {
-      cleanupFailedOpen(region);
-      return;
-    }
+      if (failed || this.server.isStopped() ||
+          this.rsServices.isStopping()) {
+        cleanupFailedOpen(region);
+        return;
+      }
+
+      if (!transitionToOpened(region)) {
+        cleanupFailedOpen(region);
+        return;
+      }
 
-    // Done!  Successful region open
-    LOG.debug("Opened " + name);
+      // Done!  Successful region open
+      LOG.debug("Opened " + name);
+    } finally {
+      this.rsServices.getRegionsInTransitionInRS().
+          remove(this.regionInfo.getEncodedNameAsBytes());
+    }
   }
 
   /**

Modified: hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/master/TestZKBasedOpenCloseRegion.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/master/TestZKBasedOpenCloseRegion.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/master/TestZKBasedOpenCloseRegion.java (original)
+++ hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/master/TestZKBasedOpenCloseRegion.java Wed Apr 27 21:46:58 2011
@@ -49,6 +49,7 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Test open and close of regions using zk.
@@ -244,6 +245,59 @@ public class TestZKBasedOpenCloseRegion 
     }
   }
 
+  /**
+   * This test shows how a region won't be able to be assigned to a RS
+   * if it's already "processing" it.
+   * @throws Exception
+   */
+  @Test
+  public void testRSAlreadyProcessingRegion() throws Exception {
+    MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
+
+    HRegionServer hr0 =
+        cluster.getLiveRegionServerThreads().get(0).getRegionServer();
+    HRegionServer hr1 =
+        cluster.getLiveRegionServerThreads().get(1).getRegionServer();
+    HRegionInfo hri = getNonMetaRegion(hr0.getOnlineRegions());
+
+    // fake that hr1 is processing the region
+    hr1.getRegionsInTransitionInRS().add(hri.getEncodedNameAsBytes());
+
+    AtomicBoolean reopenEventProcessed = new AtomicBoolean(false);
+    EventHandlerListener openListener =
+      new ReopenEventListener(hri.getRegionNameAsString(),
+          reopenEventProcessed, EventType.RS_ZK_REGION_OPENED);
+    cluster.getMaster().executorService.
+      registerListener(EventType.RS_ZK_REGION_OPENED, openListener);
+
+    // now ask the master to move the region to hr1, will fail
+    TEST_UTIL.getHBaseAdmin().move(hri.getEncodedNameAsBytes(),
+        Bytes.toBytes(hr1.getServerName()));
+
+    while (!reopenEventProcessed.get()) {
+      Threads.sleep(100);
+    }
+
+    // make sure the region came back
+    assertTrue(hr1.getOnlineRegion(hri.getEncodedNameAsBytes()) == null);
+
+    // remove the block and reset the boolean
+    hr1.getRegionsInTransitionInRS().remove(hri.getEncodedNameAsBytes());
+    reopenEventProcessed.set(false);
+
+    // move the region again, but this time it will work
+    TEST_UTIL.getHBaseAdmin().move(hri.getEncodedNameAsBytes(),
+        Bytes.toBytes(hr1.getServerName()));
+
+    while (!reopenEventProcessed.get()) {
+      Threads.sleep(100);
+    }
+
+    // make sure the region has moved from the original RS
+    assertTrue(hr0.getOnlineRegion(hri.getEncodedNameAsBytes()) == null);
+
+  }
+
   private static void waitUntilAllRegionsAssigned(final int countOfRegions)
   throws IOException {
     HTable meta = new HTable(TEST_UTIL.getConfiguration(),

Modified: hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java?rev=1097255&r1=1097254&r2=1097255&view=diff
==============================================================================
--- hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java (original)
+++ hbase/branches/0.90/src/test/java/org/apache/hadoop/hbase/regionserver/handler/TestOpenRegionHandler.java Wed Apr 27 21:46:58 2011
@@ -21,7 +21,9 @@ package org.apache.hadoop.hbase.regionse
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -121,6 +123,7 @@ public class TestOpenRegionHandler {
   static class MockRegionServerServices implements RegionServerServices {
     final Map<String, HRegion> regions = new HashMap<String, HRegion>();
     boolean stopping = false;
+    Set<byte[]> rit = new HashSet<byte[]>();
 
     @Override
     public boolean removeFromOnlineRegions(String encodedRegionName) {
@@ -161,7 +164,12 @@ public class TestOpenRegionHandler {
     public HBaseRpcMetrics getRpcMetrics() {
       return null;
     }
-    
+
+    @Override
+    public Set<byte[]> getRegionsInTransitionInRS() {
+      return rit;
+    }
+
     @Override
     public FlushRequester getFlushRequester() {
       return null;
@@ -171,6 +179,7 @@ public class TestOpenRegionHandler {
     public CompactionRequestor getCompactionRequester() {
       return null;
     }
+
   };
 
   /**