diff --git a/core/src/main/resources/restheart-default-config-no-mongodb.yml b/core/src/main/resources/restheart-default-config-no-mongodb.yml index 16f033cfb7..aa4900e1ea 100644 --- a/core/src/main/resources/restheart-default-config-no-mongodb.yml +++ b/core/src/main/resources/restheart-default-config-no-mongodb.yml @@ -242,6 +242,7 @@ mongo: doc: OPTIONAL # get collection cache speedups GET /coll?cache requests + get-collection-cache-enabled: true get-collection-cache-size: 100 get-collection-cache-ttl: 10_000 # Time To Live, default 10 seconds get-collection-cache-docs: 1000 # number of documents to cache for each request diff --git a/core/src/main/resources/restheart-default-config.yml b/core/src/main/resources/restheart-default-config.yml index ac0539f693..b890114b6e 100644 --- a/core/src/main/resources/restheart-default-config.yml +++ b/core/src/main/resources/restheart-default-config.yml @@ -241,6 +241,7 @@ mongo: doc: OPTIONAL # get collection cache speedups GET /coll?cache requests + get-collection-cache-enabled: true get-collection-cache-size: 100 get-collection-cache-ttl: 10_000 # Time To Live, default 10 seconds get-collection-cache-docs: 1000 # number of documents to cache for each request diff --git a/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfiguration.java b/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfiguration.java index b72c4ad417..b9f0c46afb 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfiguration.java +++ b/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfiguration.java @@ -20,8 +20,6 @@ */ package org.restheart.mongodb; -import com.mongodb.ConnectionString; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -31,14 +29,56 @@ import java.util.Map; import org.restheart.configuration.ConfigurationException; +import static org.restheart.configuration.Utils.asBoolean; +import static org.restheart.configuration.Utils.asInteger; +import static org.restheart.configuration.Utils.asListOfMaps; +import static org.restheart.configuration.Utils.asLong; +import static org.restheart.configuration.Utils.asMap; +import static org.restheart.configuration.Utils.asMapOfMaps; +import static org.restheart.configuration.Utils.asString; import org.restheart.exchange.ExchangeKeys.ETAG_CHECK_POLICY; import org.restheart.exchange.ExchangeKeys.REPRESENTATION_FORMAT; - +import static org.restheart.mongodb.MongoServiceConfigurationKeys.AGGREGATION_CHECK_OPERATORS; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.AGGREGATION_TIME_LIMIT_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.CURSOR_BATCH_SIZE_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_COLL_ETAG_CHECK_POLICY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_CURSOR_BATCH_SIZE; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_DB_ETAG_CHECK_POLICY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_DEFAULT_PAGESIZE; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_DOC_ETAG_CHECK_POLICY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_MAX_PAGESIZE; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_MONGO_MOUNT_WHAT; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_MONGO_MOUNT_WHERE; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_MONGO_URI; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_PAGESIZE_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.DEFAULT_REPRESENTATION_FORMAT; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.ETAG_CHECK_POLICY_COLL_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.ETAG_CHECK_POLICY_DB_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.ETAG_CHECK_POLICY_DOC_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.ETAG_CHECK_POLICY_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.GET_COLLECTION_CACHE_DOCS_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.GET_COLLECTION_CACHE_ENABLED_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.GET_COLLECTION_CACHE_SIZE_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.GET_COLLECTION_CACHE_TTL_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.INSTANCE_BASE_URL_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.LOCAL_CACHE_ENABLED_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.LOCAL_CACHE_TTL_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.MAX_PAGESIZE_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.METRICS_GATHERING_LEVEL_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.MONGO_MOUNTS_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.MONGO_MOUNT_WHAT_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.MONGO_MOUNT_WHERE_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.MONGO_URI_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.PLUGINS_ARGS_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.QUERY_TIME_LIMIT_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.REPRESENTATION_FORMAT_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.REQUESTS_LIMIT_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.SCHEMA_CACHE_ENABLED_KEY; +import static org.restheart.mongodb.MongoServiceConfigurationKeys.SCHEMA_CACHE_TTL_KEY; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.restheart.configuration.Utils.*; -import static org.restheart.mongodb.MongoServiceConfigurationKeys.*; +import com.mongodb.ConnectionString; /** * Utility class to help dealing with the restheart configuration file. @@ -71,6 +111,7 @@ public class MongoServiceConfiguration { private final boolean schemaCacheEnabled; private final long schemaCacheTtl; private final int requestsLimit; + private final boolean getCollectionCacheEnabled; private final int getCollectionCacheSize; private final int getCollectionCacheTTL; private final int getCollectionCacheDocs; @@ -178,6 +219,7 @@ private MongoServiceConfiguration(Map conf, boolean silent) thro schemaCacheEnabled = asBoolean(conf, SCHEMA_CACHE_ENABLED_KEY, true, silent); schemaCacheTtl = asLong(conf, SCHEMA_CACHE_TTL_KEY, (long) 1000, silent); + getCollectionCacheEnabled = asBoolean(conf, GET_COLLECTION_CACHE_ENABLED_KEY, true, silent); getCollectionCacheSize = asInteger(conf, GET_COLLECTION_CACHE_SIZE_KEY, 100, silent); getCollectionCacheTTL = asInteger(conf, GET_COLLECTION_CACHE_TTL_KEY, 10_000, silent); getCollectionCacheDocs = asInteger(conf, GET_COLLECTION_CACHE_DOCS_KEY, 1_000, silent); @@ -259,7 +301,7 @@ public String toString() { + ", mongoMounts=" + mongoMounts + ", pluginsArgs=" + getPluginsArgs() + ", localCacheEnabled=" + localCacheEnabled + ", localCacheTtl=" + localCacheTtl + ", schemaCacheEnabled=" + schemaCacheEnabled + ", schemaCacheTtl=" + schemaCacheTtl + ", requestsLimit=" + requestsLimit - + ", cacheSize=" + getCollectionCacheSize + ", cacheTTL" + getCollectionCacheTTL + + ", cacheEnabled=" + getCollectionCacheEnabled + ", cacheSize=" + getCollectionCacheSize + ", cacheTTL" + getCollectionCacheTTL + ", dbEtagCheckPolicy=" + dbEtagCheckPolicy + ", collEtagCheckPolicy=" + collEtagCheckPolicy + ", docEtagCheckPolicy=" + docEtagCheckPolicy + ", connectionOptions=" + connectionOptions + ", queryTimeLimit=" + queryTimeLimit + ", aggregationTimeLimit=" + aggregationTimeLimit + ", aggregationCheckOperators=" @@ -318,6 +360,13 @@ public boolean getAggregationCheckOperators() { return aggregationCheckOperators; } + /** + * @return the getCollectionCacheEnabled + */ + public boolean isGetCollectionCacheEnabled() { + return getCollectionCacheEnabled; + } + /** * @return the getCollectionCacheSize */ diff --git a/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfigurationKeys.java b/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfigurationKeys.java index 7ede4c51e4..9b8bd90378 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfigurationKeys.java +++ b/mongodb/src/main/java/org/restheart/mongodb/MongoServiceConfigurationKeys.java @@ -160,6 +160,12 @@ public interface MongoServiceConfigurationKeys { */ public static final String REPRESENTATION_FORMAT_KEY = "default-representation-format"; + + /** + * the key for the get-collection-cache-enabled property. + */ + public static final String GET_COLLECTION_CACHE_ENABLED_KEY = "get-collection-cache-enabled"; + /** * the key for the get-collection-cache-size property. */ diff --git a/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCache.java b/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCache.java index afd5b6a7b9..e926657a7d 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCache.java +++ b/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCache.java @@ -27,8 +27,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; + import org.bson.BsonDocument; -import com.mongodb.client.MongoCollection; import static org.fusesource.jansi.Ansi.Color.GREEN; import static org.fusesource.jansi.Ansi.Color.RED; import static org.fusesource.jansi.Ansi.ansi; @@ -39,6 +39,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.mongodb.client.MongoCollection; + /** * @@ -48,6 +50,7 @@ public class GetCollectionCache { private static final Logger LOGGER = LoggerFactory.getLogger(GetCollectionCache.class); + private static final boolean CACHE_ENABLED = MongoServiceConfiguration.get() == null ? true : MongoServiceConfiguration.get().isGetCollectionCacheEnabled(); private static final long CACHE_SIZE = MongoServiceConfiguration.get() == null ? 100 : MongoServiceConfiguration.get().getGetCollectionCacheSize(); private static final long CACHE_TTL = MongoServiceConfiguration.get() == null ? 10_000 : MongoServiceConfiguration.get().getGetCollectionCacheTTL(); @@ -56,39 +59,51 @@ public class GetCollectionCache { * @return */ public static GetCollectionCache getInstance() { - return DBCursorPoolSingletonHolder.INSTANCE; + return SingletonHolder.INSTANCE; } private final Cache> cache; private GetCollectionCache() { - cache = CacheFactory.createLocalCache(CACHE_SIZE, Cache.EXPIRE_POLICY.AFTER_WRITE, CACHE_TTL); - - if (LOGGER.isTraceEnabled()) { - // print stats every 1 minute - Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { - getCacheSizes().forEach((s, c) -> { - LOGGER.debug("get collection cache size: {}\t{}", s, c); - }); - - LOGGER.trace("get collection cache entries: {}", cache.asMap().keySet()); - }, 1, 1, TimeUnit.MINUTES); + if (CACHE_ENABLED) { + cache = CacheFactory.createLocalCache(CACHE_SIZE, Cache.EXPIRE_POLICY.AFTER_WRITE, CACHE_TTL); + + if (LOGGER.isTraceEnabled()) { + // print stats every 1 minute + Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { + getCacheSizes().forEach((s, c) -> { + LOGGER.debug("get collection cache size: {}\t{}", s, c); + }); + + LOGGER.trace("get collection cache entries: {}", cache.asMap().keySet()); + }, 1, 1, TimeUnit.MINUTES); + } + } else { + cache = null; } } public synchronized void put(GetCollectionCacheKey key, List value) { + if (cache == null) return; + cache.put(key, value); } public synchronized Pair> find(GetCollectionCacheKey key) { + if (cache == null) return null; + return _get(key, false); } public synchronized List get(GetCollectionCacheKey key) { + if (cache == null) return null; + return _get(key, false).getValue(); } public synchronized List remove(GetCollectionCacheKey key) { + if (cache == null) return null; + return _get(key, true).getValue(); } @@ -99,6 +114,8 @@ public synchronized List remove(GetCollectionCacheKey key) { * @return */ private synchronized Pair> _get(GetCollectionCacheKey key, boolean remove) { + if (cache == null) return null; + // return the first entry with all avaible documents var _bestKey = cache.asMap().keySet().stream() .filter(cacheKeyFilter(key)) @@ -121,10 +138,14 @@ private synchronized Pair> _get(GetCol } public synchronized void invalidate(GetCollectionCacheKey key) { + if (cache == null) return; + cache.invalidate(key); } public void invalidateAll(String db, String coll) { + if (cache == null) return; + cache.asMap().keySet().stream() .filter(k -> k.collection().getNamespace().getDatabaseName().equals(db)) .filter(k -> k.collection().getNamespace().getCollectionName().equals(coll)) @@ -132,12 +153,16 @@ public void invalidateAll(String db, String coll) { } public void invalidateAll(MongoCollection coll) { + if (cache == null) return; + cache.asMap().keySet().stream() .filter(k -> k.collection().getNamespace().equals(coll.getNamespace())) .forEach(k -> cache.invalidate(k)); } private Predicate cacheKeyFilter(GetCollectionCacheKey requested) { + if (cache == null) return null; + return cached -> Objects.equals(cached.collection().getNamespace(), requested.collection().getNamespace()) && Objects.equals(cached.filter(), requested.filter()) @@ -148,16 +173,18 @@ private Predicate cacheKeyFilter(GetCollectionCac } private TreeMap getCacheSizes() { + if (cache == null) return null; + return new TreeMap<>(cache.asMap() .keySet() .stream() .collect(Collectors.groupingBy(GetCollectionCacheKey::getCacheStatsGroup, Collectors.counting()))); } - private static class DBCursorPoolSingletonHolder { + private static class SingletonHolder { private static final GetCollectionCache INSTANCE = new GetCollectionCache(); - private DBCursorPoolSingletonHolder() { + private SingletonHolder() { } }; } diff --git a/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java b/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java index 3cf31dba56..a0e09882ae 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java +++ b/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java @@ -29,6 +29,7 @@ import org.restheart.exchange.MongoRequest; import org.restheart.exchange.MongoResponse; import org.restheart.handlers.PipelinedHandler; +import org.restheart.mongodb.MongoServiceConfiguration; import org.restheart.mongodb.db.Databases; import org.restheart.mongodb.utils.ResponseHelper; import org.restheart.utils.HttpStatus; @@ -42,10 +43,12 @@ * @author Andrea Di Cesare {@literal } */ public class GetCollectionHandler extends PipelinedHandler { - private Databases dbs = Databases.get(); + private final Databases dbs = Databases.get(); + private final boolean isGetCollectionCacheEnabled = MongoServiceConfiguration.get().isGetCollectionCacheEnabled(); private static final Logger LOGGER = LoggerFactory.getLogger(GetCollectionHandler.class); + /** * */ @@ -125,8 +128,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception { filter, request.getHintDocument(), request.getProjectionDocument(), - request.isCache()); - + request.isCache() && isGetCollectionCacheEnabled); } if (exchange.isComplete()) {