Index: hbase-server/src/main/java/org/apache/hadoop/hbase/client/ClientSideRegionScanner.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/client/ClientSideRegionScanner.java (revision 1550690) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/client/ClientSideRegionScanner.java (working copy) @@ -104,8 +104,8 @@ } } if (this.region != null) { - this.region.closeRegionOperation(); try { + this.region.closeRegionOperation(); this.region.close(true); this.region = null; } catch (IOException ex) { 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 1550690) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java (working copy) @@ -45,6 +45,7 @@ import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.KeyValueScanner; import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; @@ -322,6 +323,11 @@ } @Override + public void postBatchMutateIndispensably(final ObserverContext ctx, + MiniBatchOperationInProgress miniBatchOp, final boolean success) throws IOException { + } + + @Override public boolean preCheckAndPut(final ObserverContext e, final byte [] row, final byte [] family, final byte [] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator, @@ -481,4 +487,14 @@ MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException { return newCell; } + + @Override + public void postStartRegionOperation(final ObserverContext ctx, + Operation op) throws IOException { + } + + @Override + public void postCloseRegionOperation(final ObserverContext ctx, + Operation op) throws IOException { + } } 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 1550690) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (working copy) @@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.KeyValueScanner; import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; @@ -619,6 +620,36 @@ final MiniBatchOperationInProgress miniBatchOp) throws IOException; /** + * This will be called for region operations where read lock is acquired in + * {@link HRegion#startRegionOperation()}. + * @param ctx + * @param operation The operation is about to be taken on the region + * @throws IOException + */ + void postStartRegionOperation(final ObserverContext ctx, + Operation operation) throws IOException; + + /** + * Called after releasing read lock in {@link HRegion#closeRegionOperation(Operation)}. + * @param ctx + * @param operation + * @throws IOException + */ + void postCloseRegionOperation(final ObserverContext ctx, + Operation operation) throws IOException; + + /** + * Called after the completion of batch put/delete and will be called even if the batch operation + * fails + * @param ctx + * @param miniBatchOp + * @param success true if batch operation is successful otherwise false. + * @throws IOException + */ + void postBatchMutateIndispensably(final ObserverContext ctx, + MiniBatchOperationInProgress miniBatchOp, final boolean success) throws IOException; + + /** * Called before checkAndPut *

* Call CoprocessorEnvironment#bypass to skip default actions Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (revision 1550690) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (working copy) @@ -225,7 +225,7 @@ * operations have to be defined here. It's only needed when a special check is need in * startRegionOperation */ - protected enum Operation { + public enum Operation { ANY, GET, PUT, DELETE, SCAN, APPEND, INCREMENT, SPLIT_REGION, MERGE_REGION, BATCH_MUTATE, REPLAY_BATCH_MUTATE, COMPACT_REGION } @@ -1731,7 +1731,7 @@ } return result; } finally { - closeRegionOperation(); + closeRegionOperation(Operation.GET); } } @@ -1771,7 +1771,7 @@ } return instantiateRegionScanner(scan, additionalScanners); } finally { - closeRegionOperation(); + closeRegionOperation(Operation.SCAN); } } @@ -1824,7 +1824,7 @@ // All edits for the given row (across all column families) must happen atomically. doBatchMutate(delete); } finally { - closeRegionOperation(); + closeRegionOperation(Operation.DELETE); } } @@ -1920,7 +1920,7 @@ // All edits for the given row (across all column families) must happen atomically. doBatchMutate(put); } finally { - closeRegionOperation(); + closeRegionOperation(Operation.PUT); } } @@ -2070,11 +2070,9 @@ checkResources(); long newSize; - if (batchOp.isInReplay()) { - startRegionOperation(Operation.REPLAY_BATCH_MUTATE); - } else { - startRegionOperation(Operation.BATCH_MUTATE); - } + Operation op = Operation.BATCH_MUTATE; + if (batchOp.isInReplay()) op = Operation.REPLAY_BATCH_MUTATE; + startRegionOperation(op); try { if (!initialized) { @@ -2087,7 +2085,7 @@ long addedSize = doMiniBatchMutation(batchOp); newSize = this.addAndGetGlobalMemstoreSize(addedSize); } finally { - closeRegionOperation(); + closeRegionOperation(op); } if (isFlushSize(newSize)) { requestFlush(); @@ -2469,6 +2467,16 @@ } } } + // call the coprocessor hook to do to any finalization steps + // after the put is done + if (coprocessorHost != null) { + MiniBatchOperationInProgress miniBatchOp = + new MiniBatchOperationInProgress(batchOp.getMutationsForCoprocs(), + batchOp.retCodeDetails, batchOp.walEditsFromCoprocessors, firstIndex, + lastIndexExclusive); + coprocessorHost.postBatchMutateIndispensably(miniBatchOp, success); + } + batchOp.nextIndexToProcess = lastIndexExclusive; } } @@ -3648,7 +3656,7 @@ try { return nextRaw(outResults, limit); } finally { - closeRegionOperation(); + closeRegionOperation(Operation.SCAN); } } @@ -5021,7 +5029,7 @@ if (w != null) { mvcc.completeMemstoreInsert(w); } - closeRegionOperation(); + closeRegionOperation(Operation.APPEND); } if (this.metricsRegion != null) { @@ -5199,7 +5207,7 @@ if (w != null) { mvcc.completeMemstoreInsert(w); } - closeRegionOperation(); + closeRegionOperation(Operation.INCREMENT); if (this.metricsRegion != null) { this.metricsRegion.updateIncrement(); } @@ -5521,23 +5529,17 @@ * modifies data. It has to be called just before a try. * #closeRegionOperation needs to be called in the try's finally block * Acquires a read lock and checks if the region is closing or closed. - * @throws NotServingRegionException when the region is closing or closed - * @throws RegionTooBusyException if failed to get the lock in time - * @throws InterruptedIOException if interrupted while waiting for a lock + * @throws IOException */ - public void startRegionOperation() - throws NotServingRegionException, RegionTooBusyException, InterruptedIOException { + public void startRegionOperation() throws IOException { startRegionOperation(Operation.ANY); } /** * @param op The operation is about to be taken on the region - * @throws NotServingRegionException - * @throws RegionTooBusyException - * @throws InterruptedIOException + * @throws IOException */ - protected void startRegionOperation(Operation op) throws NotServingRegionException, - RegionTooBusyException, InterruptedIOException { + protected void startRegionOperation(Operation op) throws IOException { switch (op) { case INCREMENT: case APPEND: @@ -5572,14 +5574,36 @@ lock.readLock().unlock(); throw new NotServingRegionException(getRegionNameAsString() + " is closed"); } + try { + if (coprocessorHost != null) { + coprocessorHost.postStartRegionOperation(op); + } + } catch (Exception e) { + lock.readLock().unlock(); + throw new IOException(e); + } } /** * Closes the lock. This needs to be called in the finally block corresponding * to the try block of #startRegionOperation + * @throws IOException */ - public void closeRegionOperation() { + public void closeRegionOperation() throws IOException { + closeRegionOperation(Operation.ANY); + } + + /** + * Closes the lock. This needs to be called in the finally block corresponding + * to the try block of {@link #startRegionOperation(Operation)} + * @param operation + * @throws IOException + */ + public void closeRegionOperation(Operation operation) throws IOException { lock.readLock().unlock(); + if (coprocessorHost != null) { + coprocessorHost.postCloseRegionOperation(operation); + } } /** 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 1550690) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java (working copy) @@ -62,6 +62,7 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; +import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest; import org.apache.hadoop.hbase.regionserver.wal.HLogKey; import org.apache.hadoop.hbase.regionserver.wal.WALEdit; @@ -1077,6 +1078,26 @@ } } + public void postBatchMutateIndispensably( + final MiniBatchOperationInProgress miniBatchOp, final boolean success) + throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).postBatchMutateIndispensably(ctx, miniBatchOp, + success); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } + /** * @param row row to check * @param family column family @@ -1641,6 +1662,40 @@ return hasLoaded; } + public void postStartRegionOperation(Operation op) throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).postStartRegionOperation(ctx, op); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } + + public void postCloseRegionOperation(Operation op) throws IOException { + ObserverContext ctx = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + try { + ((RegionObserver) env.getInstance()).postCloseRegionOperation(ctx, op); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } + if (ctx.shouldComplete()) { + break; + } + } + } + } + /** * @param fs fileystem to read from * @param p path to the file Index: hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java (revision 1550690) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java (working copy) @@ -52,6 +52,7 @@ import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.KeyValueScanner; import org.apache.hadoop.hbase.regionserver.Leases; @@ -119,6 +120,9 @@ final AtomicInteger ctPreSplitAfterPONR = new AtomicInteger(0); final AtomicInteger ctPreStoreFileReaderOpen = new AtomicInteger(0); final AtomicInteger ctPostStoreFileReaderOpen = new AtomicInteger(0); + final AtomicInteger ctPostBatchMutateIndispensably = new AtomicInteger(0); + final AtomicInteger ctPostStartRegionOperation = new AtomicInteger(0); + final AtomicInteger ctPostCloseRegionOperation = new AtomicInteger(0); final AtomicBoolean throwOnPostFlush = new AtomicBoolean(false); static final String TABLE_SKIPPED = "SKIPPED_BY_PREWALRESTORE"; @@ -464,6 +468,26 @@ } @Override + public void postStartRegionOperation(final ObserverContext ctx, + Operation op) throws IOException { + ctPostStartRegionOperation.incrementAndGet(); + } + + @Override + public void postCloseRegionOperation(final ObserverContext ctx, + Operation op) throws IOException { + if (ctPostStartRegionOperation.get() > 0) { + ctPostCloseRegionOperation.incrementAndGet(); + } + } + + @Override + public void postBatchMutateIndispensably(final ObserverContext ctx, + MiniBatchOperationInProgress miniBatchOp, final boolean success) throws IOException { + ctPostBatchMutateIndispensably.incrementAndGet(); + } + + @Override public void preGetClosestRowBefore(final ObserverContext c, final byte[] row, final byte[] family, final Result result) throws IOException { @@ -597,11 +621,31 @@ public boolean hadPostBatchMutate() { return ctPostBatchMutate.get() > 0; } - + + public boolean hadPostBatchMutateIndispensably() { + return ctPostBatchMutateIndispensably.get() > 0; + } + + public boolean hadPostStartRegionOperation() { + return ctPostStartRegionOperation.get() > 0; + } + + public boolean hadPostCloseRegionOperation() { + return ctPostCloseRegionOperation.get() > 0; + } + public boolean hadDelete() { return !(ctBeforeDelete.get() > 0); } + public int getCtPostStartRegionOperation() { + return ctPostStartRegionOperation.get(); + } + + public int getCtPostCloseRegionOperation() { + return ctPostCloseRegionOperation.get(); + } + public boolean hadPreIncrement() { return ctPreIncrement.get() > 0; } Index: hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java (revision 1550690) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java (working copy) @@ -115,11 +115,10 @@ // coprocessor. HTable table = util.createTable(tableName, new byte[][] {A, B, C}); try { - verifyMethodResult(SimpleRegionObserver.class, - new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", - "hadDelete"}, - tableName, - new Boolean[] {false, false, false, false, false}); + verifyMethodResult(SimpleRegionObserver.class, new String[] { "hadPreGet", "hadPostGet", + "hadPrePut", "hadPostPut", "hadDelete", "hadPostStartRegionOperation", + "hadPostCloseRegionOperation", "hadPostBatchMutateIndispensably" }, tableName, + new Boolean[] { false, false, false, false, false, false, false, false }); Put put = new Put(ROW); put.add(A, A, A); @@ -127,12 +126,11 @@ put.add(C, C, C); table.put(put); - verifyMethodResult(SimpleRegionObserver.class, - new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", - "hadPreBatchMutate", "hadPostBatchMutate", "hadDelete"}, - tableName, - new Boolean[] {false, false, true, true, true, true, false} - ); + verifyMethodResult(SimpleRegionObserver.class, new String[] { "hadPreGet", "hadPostGet", + "hadPrePut", "hadPostPut", "hadPreBatchMutate", "hadPostBatchMutate", "hadDelete", + "hadPostStartRegionOperation", "hadPostCloseRegionOperation", + "hadPostBatchMutateIndispensably" }, TEST_TABLE, new Boolean[] { false, false, true, + true, true, true, false, true, true, true }); verifyMethodResult(SimpleRegionObserver.class, new String[] {"getCtPreOpen", "getCtPostOpen", "getCtPreClose", "getCtPostClose"},