You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by en...@apache.org on 2013/03/21 01:27:30 UTC

svn commit: r1459103 - in /hbase/branches/0.95/hbase-server/src: main/java/org/apache/hadoop/hbase/master/ main/java/org/apache/hadoop/hbase/regionserver/ test/java/org/apache/hadoop/hbase/master/ test/java/org/apache/hadoop/hbase/util/

Author: enis
Date: Thu Mar 21 00:27:30 2013
New Revision: 1459103

URL: http://svn.apache.org/r1459103
Log:
HBASE-7546 Obtain a table read lock on region split operations

Modified:
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableLockManager.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestTableLockManager.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableLockManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableLockManager.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableLockManager.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableLockManager.java Thu Mar 21 00:27:30 2013
@@ -149,7 +149,7 @@ public abstract class TableLockManager {
    * A null implementation
    */
   @InterfaceAudience.Private
-  static class NullTableLockManager extends TableLockManager {
+  public static class NullTableLockManager extends TableLockManager {
     static class NullTableLock implements TableLock {
       @Override
       public void acquire() throws IOException {

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Thu Mar 21 00:27:30 2013
@@ -115,6 +115,7 @@ import org.apache.hadoop.hbase.ipc.RpcCl
 import org.apache.hadoop.hbase.ipc.RpcServer;
 import org.apache.hadoop.hbase.exceptions.ServerNotRunningYetException;
 import org.apache.hadoop.hbase.ipc.ServerRpcController;
+import org.apache.hadoop.hbase.master.TableLockManager;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.protobuf.ReplicationProtbufUtil;
 import org.apache.hadoop.hbase.protobuf.RequestConverter;
@@ -437,6 +438,9 @@ public class HRegionServer implements Cl
   /** Handle all the snapshot requests to this server */
   RegionServerSnapshotManager snapshotManager;
 
+  // Table level lock manager for locking for region operations
+  private TableLockManager tableLockManager;
+
   /**
    * Starts a HRegionServer at the default location
    *
@@ -634,6 +638,8 @@ public class HRegionServer implements Cl
     } catch (KeeperException e) {
       this.abort("Failed to reach zk cluster when creating snapshot handler.");
     }
+    this.tableLockManager = TableLockManager.createTableLockManager(conf, zooKeeper,
+        new ServerName(isa.getHostName(), isa.getPort(), startcode));
   }
 
   /**
@@ -1130,6 +1136,11 @@ public class HRegionServer implements Cl
     return regionServerAccounting;
   }
 
+  @Override
+  public TableLockManager getTableLockManager() {
+    return tableLockManager;
+  }
+
   /*
    * @param r Region to get RegionLoad for.
    *

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerServices.java Thu Mar 21 00:27:30 2013
@@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.HRegionIn
 import org.apache.hadoop.hbase.catalog.CatalogTracker;
 import org.apache.hadoop.hbase.executor.ExecutorService;
 import org.apache.hadoop.hbase.ipc.RpcServer;
+import org.apache.hadoop.hbase.master.TableLockManager;
 import org.apache.hadoop.hbase.regionserver.wal.HLog;
 import org.apache.zookeeper.KeeperException;
 
@@ -60,6 +61,11 @@ public interface RegionServerServices ex
   public RegionServerAccounting getRegionServerAccounting();
 
   /**
+   * @return RegionServer's instance of {@link TableLockManager}
+   */
+  public TableLockManager getTableLockManager();
+
+  /**
    * Tasks to perform after region open to complete deploy of region on
    * regionserver
    *

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java Thu Mar 21 00:27:30 2013
@@ -24,6 +24,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.RemoteExceptionHandler;
+import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.util.StringUtils;
 
@@ -38,6 +39,7 @@ class SplitRequest implements Runnable {
   private final HRegion parent;
   private final byte[] midKey;
   private final HRegionServer server;
+  private TableLock tableLock;
 
   SplitRequest(HRegion region, byte[] midKey, HRegionServer hrs) {
     Preconditions.checkNotNull(hrs);
@@ -61,6 +63,18 @@ class SplitRequest implements Runnable {
     try {
       final long startTime = System.currentTimeMillis();
       SplitTransaction st = new SplitTransaction(parent, midKey);
+
+      //acquire a shared read lock on the table, so that table schema modifications
+      //do not happen concurrently
+      tableLock = server.getTableLockManager().readLock(parent.getTableDesc().getName()
+          , "SPLIT_REGION:" + parent.getRegionNameAsString());
+      try {
+        tableLock.acquire();
+      } catch (IOException ex) {
+        tableLock = null;
+        throw ex;
+      }
+
       // If prepare does not return true, for some reason -- logged inside in
       // the prepare call -- we are not ready to split just now. Just return.
       if (!st.prepare()) return;
@@ -110,6 +124,18 @@ class SplitRequest implements Runnable {
               RemoteExceptionHandler.checkIOException(io));
         }
       }
+      releaseTableLock();
+    }
+  }
+
+  protected void releaseTableLock() {
+    if (this.tableLock != null) {
+      try {
+        this.tableLock.release();
+      } catch (IOException ex) {
+        LOG.warn("Could not release the table lock", ex);
+        //TODO: if we get here, and not abort RS, this lock will never be released
+      }
     }
   }
 }

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java Thu Mar 21 00:27:30 2013
@@ -38,6 +38,7 @@ import org.apache.hadoop.hbase.client.Re
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.executor.ExecutorService;
 import org.apache.hadoop.hbase.ipc.RpcServer;
+import org.apache.hadoop.hbase.master.TableLockManager.NullTableLockManager;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionRequest;
 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
@@ -148,7 +149,7 @@ class MockRegionServer implements AdminP
 
   /**
    * @param sn Name of this mock regionserver
-   * @throws IOException 
+   * @throws IOException
    * @throws org.apache.hadoop.hbase.exceptions.ZooKeeperConnectionException
    */
   MockRegionServer(final Configuration conf, final ServerName sn)
@@ -290,6 +291,10 @@ class MockRegionServer implements AdminP
     return null;
   }
 
+  public TableLockManager getTableLockManager() {
+    return new NullTableLockManager();
+  }
+
   @Override
   public void postOpenDeployTasks(HRegion r, CatalogTracker ct)
       throws KeeperException, IOException {

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestTableLockManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestTableLockManager.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestTableLockManager.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestTableLockManager.java Thu Mar 21 00:27:30 2013
@@ -21,9 +21,12 @@ package org.apache.hadoop.hbase.master;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.util.List;
+import java.util.Random;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -34,10 +37,12 @@ import java.util.concurrent.Future;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Chore;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
 import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.LargeTests;
 import org.apache.hadoop.hbase.ServerName;
 import org.apache.hadoop.hbase.exceptions.LockTimeoutException;
 import org.apache.hadoop.hbase.exceptions.TableNotDisabledException;
@@ -45,7 +50,10 @@ import org.apache.hadoop.hbase.client.HB
 import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.LoadTestTool;
+import org.apache.hadoop.hbase.util.StoppableImplementation;
 import org.apache.hadoop.hbase.util.Threads;
 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
@@ -56,7 +64,7 @@ import org.junit.experimental.categories
 /**
  * Tests the default table lock manager
  */
-@Category(MediumTests.class)
+@Category(LargeTests.class)
 public class TestTableLockManager {
 
   private static final Log LOG =
@@ -291,4 +299,103 @@ public class TestTableLockManager {
     executor.shutdownNow();
   }
 
+  @Test(timeout = 600000)
+  public void testTableReadLock() throws Exception {
+    // test plan: write some data to the table. Continuously alter the table and
+    // force splits
+    // concurrently until we have 10 regions. verify the data just in case.
+    // Every region should contain the same table descriptor
+    // This is not an exact test
+    prepareMiniCluster();
+    LoadTestTool loadTool = new LoadTestTool();
+    loadTool.setConf(TEST_UTIL.getConfiguration());
+    int numKeys = 10000;
+    final byte[] tableName = Bytes.toBytes("testTableReadLock");
+    final HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
+    final HTableDescriptor desc = new HTableDescriptor(tableName);
+    final byte[] family = Bytes.toBytes("test_cf");
+    desc.addFamily(new HColumnDescriptor(family));
+    admin.createTable(desc); // create with one region
+
+    // write some data, not much
+    int ret = loadTool.run(new String[] { "-tn", Bytes.toString(tableName), "-write",
+        String.format("%d:%d:%d", 1, 10, 10), "-num_keys", String.valueOf(numKeys), "-skip_init" });
+    if (0 != ret) {
+      String errorMsg = "Load failed with error code " + ret;
+      LOG.error(errorMsg);
+      fail(errorMsg);
+    }
+
+    int familyValues = admin.getTableDescriptor(tableName).getFamily(family).getValues().size();
+    StoppableImplementation stopper = new StoppableImplementation();
+
+    //alter table every 10 sec
+    Chore alterThread = new Chore("Alter Chore", 10000, stopper) {
+      @Override
+      protected void chore() {
+        Random random = new Random();
+        try {
+          HTableDescriptor htd = admin.getTableDescriptor(tableName);
+          String val = String.valueOf(random.nextInt());
+          htd.getFamily(family).setValue(val, val);
+          desc.getFamily(family).setValue(val, val); // save it for later
+                                                     // control
+          admin.modifyTable(tableName, htd);
+        } catch (Exception ex) {
+          LOG.warn("Caught exception", ex);
+          fail(ex.getMessage());
+        }
+      }
+    };
+
+    //split table every 5 sec
+    Chore splitThread = new Chore("Split thread", 5000, stopper) {
+      @Override
+      public void chore() {
+        try {
+          Random random = new Random();
+          List<HRegionInfo> regions = admin.getTableRegions(tableName);
+          byte[] regionName = regions.get(random.nextInt(regions.size())).getRegionName();
+          admin.flush(regionName);
+          admin.compact(regionName);
+          admin.split(regionName);
+        } catch (Exception ex) {
+          LOG.warn("Caught exception", ex);
+          fail(ex.getMessage());
+        }
+      }
+    };
+
+    alterThread.start();
+    splitThread.start();
+    while (true) {
+      List<HRegionInfo> regions = admin.getTableRegions(tableName);
+      LOG.info(String.format("Table #regions: %d regions: %s:", regions.size(), regions));
+      assertEquals(admin.getTableDescriptor(tableName), desc);
+      for (HRegion region : TEST_UTIL.getMiniHBaseCluster().getRegions(tableName)) {
+        assertEquals(desc, region.getTableDesc());
+      }
+      if (regions.size() >= 10) {
+        break;
+      }
+      Threads.sleep(1000);
+    }
+    stopper.stop("test finished");
+
+    int newFamilyValues = admin.getTableDescriptor(tableName).getFamily(family).getValues().size();
+    LOG.info(String.format("Altered the table %d times", newFamilyValues - familyValues));
+    assertTrue(newFamilyValues > familyValues); // at least one alter went
+                                                // through
+
+    ret = loadTool.run(new String[] { "-tn", Bytes.toString(tableName), "-read", "100:10",
+        "-num_keys", String.valueOf(numKeys), "-skip_init" });
+    if (0 != ret) {
+      String errorMsg = "Verify failed with error code " + ret;
+      LOG.error(errorMsg);
+      fail(errorMsg);
+    }
+
+    admin.close();
+  }
+
 }

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java?rev=1459103&r1=1459102&r2=1459103&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/util/MockRegionServerServices.java Thu Mar 21 00:27:30 2013
@@ -31,6 +31,8 @@ import org.apache.hadoop.hbase.catalog.C
 import org.apache.hadoop.hbase.executor.ExecutorService;
 import org.apache.hadoop.hbase.fs.HFileSystem;
 import org.apache.hadoop.hbase.ipc.RpcServer;
+import org.apache.hadoop.hbase.master.TableLockManager;
+import org.apache.hadoop.hbase.master.TableLockManager.NullTableLockManager;
 import org.apache.hadoop.hbase.regionserver.CompactionRequestor;
 import org.apache.hadoop.hbase.regionserver.FlushRequester;
 import org.apache.hadoop.hbase.regionserver.HRegion;
@@ -131,6 +133,11 @@ public class MockRegionServerServices im
   }
 
   @Override
+  public TableLockManager getTableLockManager() {
+    return new NullTableLockManager();
+  }
+
+  @Override
   public ServerName getServerName() {
     return this.serverName;
   }