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 1589100) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java (working copy) @@ -44,6 +44,7 @@ import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; +import org.apache.hadoop.hbase.regionserver.DeleteTracker; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.InternalScanner; @@ -501,4 +502,10 @@ public void postCloseRegionOperation(final ObserverContext ctx, Operation op) throws IOException { } + + @Override + public DeleteTracker preInstantiateDeleteTracker(ObserverContext ctx) + throws IOException { + return null; + } } 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 1589100) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (working copy) @@ -42,6 +42,7 @@ import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.io.hfile.CacheConfig; +import org.apache.hadoop.hbase.regionserver.DeleteTracker; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.HRegion.Operation; import org.apache.hadoop.hbase.regionserver.InternalScanner; @@ -1109,4 +1110,16 @@ */ Cell postMutationBeforeWAL(ObserverContext ctx, MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException; + + /** + * Called just before the ScanQueryMatcher creates a DeleteTracker + * + * @param ctx + * @param cell + * @param deleteTracker + * @return the Delete Tracker + * @throws IOException + */ + DeleteTracker preInstantiateDeleteTracker( + final ObserverContext ctx) throws IOException; } 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 1589100) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java (working copy) @@ -2152,4 +2152,27 @@ } } + public DeleteTracker preInstantiateDeleteTracker() throws IOException { + ObserverContext ctx = null; + DeleteTracker deleteTracker = null; + for (RegionEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + Thread currentThread = Thread.currentThread(); + ClassLoader cl = currentThread.getContextClassLoader(); + try { + currentThread.setContextClassLoader(env.getClassLoader()); + deleteTracker = ((RegionObserver) env.getInstance()).preInstantiateDeleteTracker(ctx); + } catch (Throwable e) { + handleCoprocessorThrowable(env, e); + } finally { + currentThread.setContextClassLoader(cl); + } + if (ctx.shouldComplete()) { + break; + } + } + } + return deleteTracker; + } } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java (revision 1589100) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java (working copy) @@ -112,6 +112,8 @@ * first column. * */ private boolean hasNullColumn = true; + + private RegionCoprocessorHost regionCoprocessorHost= null; // By default, when hbase.hstore.time.to.purge.deletes is 0ms, a delete // marker is always removed during a major compaction. If set to non-zero @@ -145,13 +147,16 @@ * @param earliestPutTs Earliest put seen in any of the store files. * @param oldestUnexpiredTS the oldest timestamp we are interested in, * based on TTL + * @param regionCoprocessorHost + * @throws IOException */ - public ScanQueryMatcher(Scan scan, ScanInfo scanInfo, - NavigableSet columns, ScanType scanType, - long readPointToUse, long earliestPutTs, long oldestUnexpiredTS) { + public ScanQueryMatcher(Scan scan, ScanInfo scanInfo, NavigableSet columns, + ScanType scanType, long readPointToUse, long earliestPutTs, long oldestUnexpiredTS, + RegionCoprocessorHost regionCoprocessorHost) throws IOException { this.tr = scan.getTimeRange(); this.rowComparator = scanInfo.getComparator(); - this.deletes = new ScanDeleteTracker(); + this.regionCoprocessorHost = regionCoprocessorHost; + this.deletes = instantiateDeleteTracker(); this.stopRow = scan.getStopRow(); this.startKey = KeyValueUtil.createFirstDeleteFamilyOnRow(scan.getStartRow(), scanInfo.getFamily()); @@ -194,6 +199,17 @@ this.isReversed = scan.isReversed(); } + private DeleteTracker instantiateDeleteTracker() throws IOException { + DeleteTracker tracker = null; + if (regionCoprocessorHost != null) { + tracker = regionCoprocessorHost.preInstantiateDeleteTracker(); + } + if (tracker == null) { + tracker = new ScanDeleteTracker(); + } + return tracker; + } + /** * Construct a QueryMatcher for a scan that drop deletes from a limited range of rows. * @param scan @@ -204,12 +220,14 @@ * based on TTL * @param dropDeletesFromRow The inclusive left bound of the range; can be EMPTY_START_ROW. * @param dropDeletesToRow The exclusive right bound of the range; can be EMPTY_END_ROW. + * @param regionCoprocessorHost + * @throws IOException */ public ScanQueryMatcher(Scan scan, ScanInfo scanInfo, NavigableSet columns, - long readPointToUse, long earliestPutTs, long oldestUnexpiredTS, - byte[] dropDeletesFromRow, byte[] dropDeletesToRow) { + long readPointToUse, long earliestPutTs, long oldestUnexpiredTS, byte[] dropDeletesFromRow, + byte[] dropDeletesToRow, RegionCoprocessorHost regionCoprocessorHost) throws IOException { this(scan, scanInfo, columns, ScanType.COMPACT_RETAIN_DELETES, readPointToUse, earliestPutTs, - oldestUnexpiredTS); + oldestUnexpiredTS, regionCoprocessorHost); Preconditions.checkArgument((dropDeletesFromRow != null) && (dropDeletesToRow != null)); this.dropDeletesFromRow = dropDeletesFromRow; this.dropDeletesToRow = dropDeletesToRow; @@ -219,10 +237,10 @@ * Constructor for tests */ ScanQueryMatcher(Scan scan, ScanInfo scanInfo, - NavigableSet columns, long oldestUnexpiredTS) { + NavigableSet columns, long oldestUnexpiredTS) throws IOException { this(scan, scanInfo, columns, ScanType.USER_SCAN, Long.MAX_VALUE, /* max Readpoint to track versions */ - HConstants.LATEST_TIMESTAMP, oldestUnexpiredTS); + HConstants.LATEST_TIMESTAMP, oldestUnexpiredTS, null); } /** Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (revision 1589100) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreScanner.java (working copy) @@ -162,7 +162,7 @@ } matcher = new ScanQueryMatcher(scan, scanInfo, columns, ScanType.USER_SCAN, Long.MAX_VALUE, HConstants.LATEST_TIMESTAMP, - oldestUnexpiredTS); + oldestUnexpiredTS, store.getCoprocessorHost()); this.store.addChangedReaderObserver(this); @@ -226,11 +226,11 @@ this(store, false, scan, null, scanInfo.getTtl(), scanInfo.getMinVersions(), ((HStore)store).getHRegion().getReadpoint(IsolationLevel.READ_COMMITTED)); if (dropDeletesFromRow == null) { - matcher = new ScanQueryMatcher(scan, scanInfo, null, scanType, - smallestReadPoint, earliestPutTs, oldestUnexpiredTS); + matcher = new ScanQueryMatcher(scan, scanInfo, null, scanType, smallestReadPoint, + earliestPutTs, oldestUnexpiredTS, store.getCoprocessorHost()); } else { - matcher = new ScanQueryMatcher(scan, scanInfo, null, smallestReadPoint, - earliestPutTs, oldestUnexpiredTS, dropDeletesFromRow, dropDeletesToRow); + matcher = new ScanQueryMatcher(scan, scanInfo, null, smallestReadPoint, earliestPutTs, + oldestUnexpiredTS, dropDeletesFromRow, dropDeletesToRow, store.getCoprocessorHost()); } // Filter the list of scanners using Bloom filters, time range, TTL, etc. @@ -270,7 +270,7 @@ this(null, scan.getCacheBlocks(), scan, columns, scanInfo.getTtl(), scanInfo.getMinVersions(), readPt); this.matcher = new ScanQueryMatcher(scan, scanInfo, columns, scanType, - Long.MAX_VALUE, earliestPutTs, oldestUnexpiredTS); + Long.MAX_VALUE, earliestPutTs, oldestUnexpiredTS, null); // In unit tests, the store could be null if (this.store != null) { Index: hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestQueryMatcher.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestQueryMatcher.java (revision 1589100) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestQueryMatcher.java (working copy) @@ -19,13 +19,14 @@ package org.apache.hadoop.hbase.regionserver; +import static org.apache.hadoop.hbase.regionserver.ScanQueryMatcher.MatchCode.INCLUDE; +import static org.apache.hadoop.hbase.regionserver.ScanQueryMatcher.MatchCode.SKIP; + import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.NavigableSet; -import static org.apache.hadoop.hbase.regionserver.ScanQueryMatcher.MatchCode.*; - import org.apache.hadoop.hbase.HBaseTestCase; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; @@ -341,7 +342,7 @@ NavigableSet cols = get.getFamilyMap().get(fam2); ScanQueryMatcher qm = new ScanQueryMatcher(scan, scanInfo, cols, Long.MAX_VALUE, - HConstants.OLDEST_TIMESTAMP, HConstants.OLDEST_TIMESTAMP, from, to); + HConstants.OLDEST_TIMESTAMP, HConstants.OLDEST_TIMESTAMP, from, to, null); List actual = new ArrayList(rows.length); byte[] prevRow = null;