From 07218cb039c8aeb339b650cf65269b4517386b26 Mon Sep 17 00:00:00 2001 From: ramkrishna Date: Fri, 24 Aug 2012 18:43:33 +0000 Subject: [PATCH] HBASE-6633 Adding new hooks to the split flow - For roll backs and one final hook after split is completed either successfully or failed Submitted by:Ram Reviewed by:Stack git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1377033 13f79535-47bb-0310-9956-ffa450edef68 --- .../hbase/coprocessor/BaseRegionObserver.java | 20 +++++ .../hbase/coprocessor/RegionObserver.java | 33 ++++++- .../regionserver/RegionCoprocessorHost.java | 85 ++++++++++++++++++- .../hbase/regionserver/SplitRequest.java | 9 ++ .../hbase/regionserver/SplitTransaction.java | 14 +++ .../security/access/AccessController.java | 6 ++ .../coprocessor/TestCoprocessorInterface.java | 9 +- .../regionserver/TestSplitTransaction.java | 29 +++++++ .../security/access/TestAccessController.java | 16 ++++ 9 files changed, 218 insertions(+), 3 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java index f1339ac3b0c..30ae51567ac 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java @@ -105,6 +105,26 @@ public abstract class BaseRegionObserver implements RegionObserver { @Override public void preSplit(ObserverContext e) throws IOException { } + + @Override + public void preSplit(ObserverContext c, + byte[] splitRow) throws IOException { + } + + @Override + public void preRollBackSplit(ObserverContext ctx) + throws IOException { + } + + @Override + public void postRollBackSplit( + ObserverContext ctx) throws IOException { + } + + @Override + public void postCompleteSplit( + ObserverContext ctx) throws IOException { + } @Override public void postSplit(ObserverContext e, HRegion l, HRegion r) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java index ab973e2030a..263b338012c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java @@ -213,8 +213,17 @@ public interface RegionObserver extends Coprocessor { * @param c the environment provided by the region server * (e.getRegion() returns the parent region) * @throws IOException if an error occurred on the coprocessor + * @deprecated Use preSplit(final ObserverContext c, byte[] splitRow) */ void preSplit(final ObserverContext c) throws IOException; + + /** + * Called before the region is split. + * @param c the environment provided by the region server + * (e.getRegion() returns the parent region) + * @throws IOException if an error occurred on the coprocessor + */ + void preSplit(final ObserverContext c, byte[] splitRow) throws IOException; /** * Called after the region is split. @@ -223,10 +232,32 @@ public interface RegionObserver extends Coprocessor { * @param l the left daughter region * @param r the right daughter region * @throws IOException if an error occurred on the coprocessor + * @deprecated Use postCompleteSplit() instead */ void postSplit(final ObserverContext c, final HRegion l, final HRegion r) throws IOException; - + + /** + * This will be called before the roll back of the split region is completed + * @param ctx + * @throws IOException + */ + void preRollBackSplit(final ObserverContext ctx) throws IOException; + + /** + * This will be called after the roll back of the split region is completed + * @param ctx + * @throws IOException + */ + void postRollBackSplit(final ObserverContext ctx) throws IOException; + + /** + * Called after any split request is processed. This will be called irrespective of success or + * failure of the split. + * @param ctx + * @throws IOException + */ + void postCompleteSplit(final ObserverContext ctx) throws IOException; /** * Called before the region is reported as closed to the master. * @param c the environment provided by the region server diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java index 6a4c06a3d22..1d13d7ddaf1 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java @@ -601,6 +601,27 @@ public class RegionCoprocessorHost } } } + + /** + * Invoked just before a split + * @throws IOException + */ + public void preSplit(byte[] splitRow) throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env: coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver)env.getInstance()).preSplit(ctx, splitRow); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } /** * Invoked just after a split @@ -624,7 +645,69 @@ public class RegionCoprocessorHost } } } - + + /** + * Invoked just before the rollback of a failed split is started + * @throws IOException + */ + public void preRollBackSplit() throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).preRollBackSplit(ctx); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } + + /** + * Invoked just after the rollback of a failed split is done + * @throws IOException + */ + public void postRollBackSplit() throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).postRollBackSplit(ctx); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } + + /** + * Invoked after a split is completed irrespective of a failure or success. + * @throws IOException + */ + public void postCompleteSplit() throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).postCompleteSplit(ctx); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } // RegionObserver support /** diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java index f82e298ae03..e7b735fc26e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java @@ -105,6 +105,15 @@ class SplitRequest implements Runnable { .checkIOException(ex)); this.server.getMetrics().incrementSplitFailureCount(); server.checkFileSystem(); + } finally { + if (this.parent.getCoprocessorHost() != null) { + try { + this.parent.getCoprocessorHost().postCompleteSplit(); + } catch (IOException io) { + LOG.error("Split failed " + this, + RemoteExceptionHandler.checkIOException(io)); + } + } } } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java index ed3b2efcfe1..3abfe5f42ea 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java @@ -229,6 +229,11 @@ public class SplitTransaction { if (this.parent.getCoprocessorHost() != null) { this.parent.getCoprocessorHost().preSplit(); } + + // Coprocessor callback + if (this.parent.getCoprocessorHost() != null) { + this.parent.getCoprocessorHost().preSplit(this.splitrow); + } // If true, no cluster to write meta edits to or to update znodes in. boolean testing = server == null? true: @@ -727,6 +732,11 @@ public class SplitTransaction { */ public boolean rollback(final Server server, final RegionServerServices services) throws IOException { + // Coprocessor callback + if (this.parent.getCoprocessorHost() != null) { + this.parent.getCoprocessorHost().preRollBackSplit(); + } + boolean result = true; FileSystem fs = this.parent.getFilesystem(); ListIterator iterator = @@ -793,6 +803,10 @@ public class SplitTransaction { throw new RuntimeException("Unhandled journal entry: " + je); } } + // Coprocessor callback + if (this.parent.getCoprocessorHost() != null) { + this.parent.getCoprocessorHost().postRollBackSplit(); + } return result; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index 92ca7b1f099..b1459481dc2 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -799,6 +799,12 @@ public class AccessController extends BaseRegionObserver public void preSplit(ObserverContext e) throws IOException { requirePermission(getTableName(e.getEnvironment()), null, null, Action.ADMIN); } + + @Override + public void preSplit(ObserverContext e, + byte[] splitRow) throws IOException { + requirePermission(getTableName(e.getEnvironment()), null, null, Action.ADMIN); + } @Override public InternalScanner preCompact(ObserverContext e, diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java index 990a0c65ae6..974d7f2a0e3 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java @@ -140,6 +140,7 @@ public class TestCoprocessorInterface extends HBaseTestCase { private boolean postFlushCalled; private boolean preSplitCalled; private boolean postSplitCalled; + private boolean preSplitWithSplitRowCalled; private ConcurrentMap sharedData; @Override @@ -195,6 +196,12 @@ public class TestCoprocessorInterface extends HBaseTestCase { public void preSplit(ObserverContext e) { preSplitCalled = true; } + + @Override + public void preSplit(ObserverContext c, + byte[] splitRow) throws IOException { + preSplitWithSplitRowCalled = true; + } @Override public void postSplit(ObserverContext e, HRegion l, HRegion r) { postSplitCalled = true; @@ -225,7 +232,7 @@ public class TestCoprocessorInterface extends HBaseTestCase { return (preCompactCalled && postCompactCalled); } boolean wasSplit() { - return (preSplitCalled && postSplitCalled); + return (preSplitCalled && postSplitCalled && preSplitWithSplitRowCalled); } Map getSharedData() { return sharedData; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java index bd4e0e4786c..4c23a8c0f5d 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java @@ -35,6 +35,10 @@ import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.*; import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.regionserver.wal.HLog; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.PairOfSameType; @@ -63,13 +67,19 @@ public class TestSplitTransaction { private static final byte [] GOOD_SPLIT_ROW = new byte [] {'d', 'd', 'd'}; private static final byte [] CF = HConstants.CATALOG_FAMILY; + private static boolean preRollBackCalled = false; + private static boolean postRollBackCalled = false; + @Before public void setup() throws IOException { this.fs = FileSystem.get(TEST_UTIL.getConfiguration()); + TEST_UTIL.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, CustomObserver.class.getName()); this.fs.delete(this.testdir, true); this.wal = new HLog(fs, new Path(this.testdir, "logs"), new Path(this.testdir, "archive"), TEST_UTIL.getConfiguration()); this.parent = createRegion(this.testdir, this.wal); + RegionCoprocessorHost host = new RegionCoprocessorHost(this.parent, null, TEST_UTIL.getConfiguration()); + this.parent.setCoprocessorHost(host); TEST_UTIL.getConfiguration().setBoolean("hbase.testing.nocluster", true); } @@ -280,6 +290,11 @@ public class TestSplitTransaction { assertEquals(rowcount, daughtersRowCount); // Assert the write lock is no longer held on parent assertTrue(!this.parent.lock.writeLock().isHeldByCurrentThread()); + assertTrue("Rollback hooks should be called.", wasRollBackHookCalled()); + } + + private boolean wasRollBackHookCalled(){ + return (preRollBackCalled && postRollBackCalled); } /** @@ -318,6 +333,20 @@ public class TestSplitTransaction { return HRegion.openHRegion(testdir, hri, htd, wal, TEST_UTIL.getConfiguration()); } + + public static class CustomObserver extends BaseRegionObserver{ + @Override + public void preRollBackSplit( + ObserverContext ctx) throws IOException { + preRollBackCalled = true; + } + + @Override + public void postRollBackSplit( + ObserverContext ctx) throws IOException { + postRollBackCalled = true; + } + } @org.junit.Rule public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu = diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index 765f0af805c..7c70f9e0d95 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -459,6 +459,22 @@ public class TestAccessController { verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE); } + @Test + public void testSplitWithSplitRow() throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public Object run() throws Exception { + ACCESS_CONTROLLER.preSplit( + ObserverContext.createAndPrepare(RCP_ENV, null), + Bytes.toBytes("row2")); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER); + verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE); + } + + @Test public void testFlush() throws Exception { PrivilegedExceptionAction action = new PrivilegedExceptionAction() {