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 e7228f3..bd10940 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 @@ -59,6 +59,7 @@ import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; 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.CoprocessorService; import org.apache.hadoop.hbase.coprocessor.EndpointObserver; @@ -100,6 +101,9 @@ public class RegionCoprocessorHost private static ReferenceMap sharedDataMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); + // optimization: no need to call postScannerFilterRow, if no coprocessor implements it + private final boolean hasCustomPostScannerFilterRow; + /** * * Encapsulation of the environment of each coprocessor @@ -226,6 +230,35 @@ public class RegionCoprocessorHost // load Coprocessor From HDFS loadTableCoprocessors(conf); + + // now check whether any coprocessor implements postScannerFilterRow + boolean hasCustomPostScannerFilterRow = false; + out: for (RegionEnvironment env: coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + Class clazz = env.getInstance().getClass(); + for(;;) { + if (clazz == null) { + // we must have directly implemented RegionObserver + hasCustomPostScannerFilterRow = true; + break out; + } + if (clazz == BaseRegionObserver.class) { + // we reached BaseRegionObserver, try next coprocessor + break; + } + try { + clazz.getDeclaredMethod("postScannerFilterRow", ObserverContext.class, + InternalScanner.class, byte[].class, int.class, short.class, boolean.class); + // this coprocessor has a custom version of postScannerFilterRow + hasCustomPostScannerFilterRow = true; + break out; + } catch (NoSuchMethodException ignore) { + } + clazz = clazz.getSuperclass(); + } + } + } + this.hasCustomPostScannerFilterRow = hasCustomPostScannerFilterRow; } static List getTableCoprocessorAttrsFromSchema(Configuration conf, @@ -1362,6 +1395,8 @@ public class RegionCoprocessorHost */ public boolean postScannerFilterRow(final InternalScanner s, final byte[] currentRow, final int offset, final short length) throws IOException { + // short circuit for performance + if (!hasCustomPostScannerFilterRow) return true; return execOperationWithResult(true, coprocessors.isEmpty() ? null : new RegionOperationWithResult() { @Override diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java index bf53518..4308ab5 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java @@ -119,6 +119,7 @@ public class SimpleRegionObserver extends BaseRegionObserver { final AtomicInteger ctPostWALRestored = new AtomicInteger(0); final AtomicInteger ctPreScannerNext = new AtomicInteger(0); final AtomicInteger ctPostScannerNext = new AtomicInteger(0); + final AtomicInteger ctPostScannerFilterRow = new AtomicInteger(0); final AtomicInteger ctPreScannerClose = new AtomicInteger(0); final AtomicInteger ctPostScannerClose = new AtomicInteger(0); final AtomicInteger ctPreScannerOpen = new AtomicInteger(0); @@ -315,6 +316,14 @@ public class SimpleRegionObserver extends BaseRegionObserver { } @Override + public boolean postScannerFilterRow(final ObserverContext e, + final InternalScanner s, final byte[] currentRow, final int offset, final short length, + final boolean hasMore) throws IOException { + ctPostScannerFilterRow.incrementAndGet(); + return hasMore; + } + + @Override public void preScannerClose(final ObserverContext c, final InternalScanner s) throws IOException { ctPreScannerClose.incrementAndGet(); @@ -804,6 +813,9 @@ public class SimpleRegionObserver extends BaseRegionObserver { public boolean wasScannerNextCalled() { return ctPreScannerNext.get() > 0 && ctPostScannerNext.get() > 0; } + public boolean wasScannerFilterRowCalled() { + return ctPostScannerFilterRow.get() > 0; + } public boolean wasScannerCloseCalled() { return ctPreScannerClose.get() > 0 && ctPostScannerClose.get() > 0; } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java index 72fbd03..95c1b61 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java @@ -58,6 +58,7 @@ import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.RowMutations; import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.filter.FilterAllFilter; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.io.hfile.HFileContext; @@ -379,6 +380,34 @@ public class TestRegionObserverInterface { } @Test + public void testHBASE14489() throws IOException { + TableName tableName = + TableName.valueOf("testHBASE14489"); + HTable table = util.createTable(tableName, new byte[][] {A}); + Put put = new Put(ROW); + put.add(A, A, A); + table.put(put); + + Scan s = new Scan(); + s.setFilter(new FilterAllFilter()); + ResultScanner scanner = table.getScanner(s); + try { + for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { + } + } finally { + scanner.close(); + } + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"wasScannerFilterRowCalled"}, + tableName, + new Boolean[] {true} + ); + util.deleteTable(tableName); + table.close(); + + } + + @Test // HBase-3758 public void testHBase3758() throws IOException { TableName tableName =