Index: hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java (working copy) @@ -105,6 +105,26 @@ @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) Index: hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (working copy) @@ -213,8 +213,17 @@ * @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,11 +232,33 @@ * @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 * @param abortRequested true if the region server is aborting Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java (working copy) @@ -601,6 +601,27 @@ } } } + + /** + * 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 @@ } } } - + + /** + * 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 /** Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitRequest.java (working copy) @@ -105,6 +105,15 @@ .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)); + } + } } } } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransaction.java (working copy) @@ -229,6 +229,11 @@ 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 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 @@ throw new RuntimeException("Unhandled journal entry: " + je); } } + // Coprocessor callback + if (this.parent.getCoprocessorHost() != null) { + this.parent.getCoprocessorHost().postRollBackSplit(); + } return result; } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java (revision 1376385) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java (working copy) @@ -799,6 +799,12 @@ 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, Index: hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java (revision 1376385) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java (working copy) @@ -140,6 +140,7 @@ private boolean postFlushCalled; private boolean preSplitCalled; private boolean postSplitCalled; + private boolean preSplitWithSplitRowCalled; private ConcurrentMap sharedData; @Override @@ -195,7 +196,13 @@ 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 @@ return (preCompactCalled && postCompactCalled); } boolean wasSplit() { - return (preSplitCalled && postSplitCalled); + return (preSplitCalled && postSplitCalled && preSplitWithSplitRowCalled); } Map getSharedData() { return sharedData; Index: hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java (revision 1376385) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransaction.java (working copy) @@ -35,6 +35,10 @@ 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 @@ 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,7 +290,12 @@ 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); + } /** * Exception used in this class only. @@ -318,6 +333,20 @@ 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 = Index: hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java (revision 1376385) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java (working copy) @@ -460,6 +460,22 @@ } @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() { public Object run() throws Exception {