diff --git a/hbase-rest/pom.xml b/hbase-rest/pom.xml index d276048dd610..2dbe2bce5e36 100644 --- a/hbase-rest/pom.xml +++ b/hbase-rest/pom.xml @@ -140,6 +140,10 @@ org.apache.commons commons-lang3 + + com.github.ben-manes.caffeine + caffeine + org.slf4j slf4j-api diff --git a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/Constants.java b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/Constants.java index f0d1edc986af..e924be3d7fe8 100644 --- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/Constants.java +++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/Constants.java @@ -69,6 +69,12 @@ public interface Constants { String REST_DNS_NAMESERVER = "hbase.rest.dns.nameserver"; String REST_DNS_INTERFACE = "hbase.rest.dns.interface"; + String REST_SCANNERCACHE_SIZE = "hbase.rest.scannercache.size"; + final int DEFAULT_REST_SCANNERCACHE_SIZE = 10000; + + String REST_SCANNERCACHE_EXPIRE_TIME = "hbase.rest.scannercache.expire.time"; + final long DEFAULT_REST_SCANNERCACHE_EXPIRE_TIME_MS = 60 * 60 * 1000; + String FILTER_CLASSES = "hbase.rest.filter.classes"; String SCAN_START_ROW = "startrow"; String SCAN_END_ROW = "endrow"; diff --git a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/ScannerResource.java b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/ScannerResource.java index d15537b1c87f..b8a290e06174 100644 --- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/ScannerResource.java +++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/ScannerResource.java @@ -19,11 +19,14 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; import java.io.IOException; import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.filter.Filter; import org.apache.hadoop.hbase.rest.model.ScannerModel; @@ -46,9 +49,7 @@ public class ScannerResource extends ResourceBase { private static final Logger LOG = LoggerFactory.getLogger(ScannerResource.class); - static final Map scanners = - Collections.synchronizedMap(new HashMap()); - + private static final Cache scanners = setupScanners(); TableResource tableResource; /** @@ -59,8 +60,23 @@ public ScannerResource(TableResource tableResource) throws IOException { this.tableResource = tableResource; } + private static Cache setupScanners() { + final Configuration conf = HBaseConfiguration.create(); + + int size = conf.getInt(REST_SCANNERCACHE_SIZE, DEFAULT_REST_SCANNERCACHE_SIZE); + long evictTimeoutMs = conf.getTimeDuration(REST_SCANNERCACHE_EXPIRE_TIME, + DEFAULT_REST_SCANNERCACHE_EXPIRE_TIME_MS, TimeUnit.MILLISECONDS); + + Cache cache = + Caffeine.newBuilder().removalListener(ScannerResource::removalListener).maximumSize(size) + .expireAfterAccess(evictTimeoutMs, TimeUnit.MILLISECONDS) + . build(); + + return cache; + } + static boolean delete(final String id) { - ScannerInstanceResource instance = scanners.remove(id); + ScannerInstanceResource instance = scanners.asMap().remove(id); if (instance != null) { instance.generator.close(); return true; @@ -69,6 +85,12 @@ static boolean delete(final String id) { } } + static void removalListener(String key, ScannerInstanceResource value, RemovalCause cause) { + if (cause.wasEvicted()) { + delete(key); + } + } + Response update(final ScannerModel model, final boolean replace, final UriInfo uriInfo) { servlet.getMetrics().incrementRequests(1); if (servlet.isReadOnly()) { @@ -140,7 +162,7 @@ public Response post(final ScannerModel model, final @Context UriInfo uriInfo) { @Path("{scanner: .+}") public ScannerInstanceResource getScannerInstanceResource(final @PathParam("scanner") String id) throws IOException { - ScannerInstanceResource instance = scanners.get(id); + ScannerInstanceResource instance = scanners.getIfPresent(id); if (instance == null) { servlet.getMetrics().incrementFailedGetRequests(1); return new ScannerInstanceResource();