Skip to content

Commit

Permalink
✨ Add /mongo/get-collection-cache-enabled configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
ujibang committed Nov 3, 2023
1 parent eebb18d commit 2a07d93
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions core/src/main/resources/restheart-default-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
*/
package org.restheart.mongodb;

import com.mongodb.ConnectionString;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -178,6 +219,7 @@ private MongoServiceConfiguration(Map<String, Object> 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);
Expand Down Expand Up @@ -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="
Expand Down Expand Up @@ -318,6 +360,13 @@ public boolean getAggregationCheckOperators() {
return aggregationCheckOperators;
}

/**
* @return the getCollectionCacheEnabled
*/
public boolean isGetCollectionCacheEnabled() {
return getCollectionCacheEnabled;
}

/**
* @return the getCollectionCacheSize
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,6 +39,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.client.MongoCollection;


/**
*
Expand All @@ -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();

Expand All @@ -56,39 +59,51 @@ public class GetCollectionCache {
* @return
*/
public static GetCollectionCache getInstance() {
return DBCursorPoolSingletonHolder.INSTANCE;
return SingletonHolder.INSTANCE;
}

private final Cache<GetCollectionCacheKey, List<BsonDocument>> 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<BsonDocument> value) {
if (cache == null) return;

cache.put(key, value);
}

public synchronized Pair<GetCollectionCacheKey, List<BsonDocument>> find(GetCollectionCacheKey key) {
if (cache == null) return null;

return _get(key, false);
}

public synchronized List<BsonDocument> get(GetCollectionCacheKey key) {
if (cache == null) return null;

return _get(key, false).getValue();
}

public synchronized List<BsonDocument> remove(GetCollectionCacheKey key) {
if (cache == null) return null;

return _get(key, true).getValue();
}

Expand All @@ -99,6 +114,8 @@ public synchronized List<BsonDocument> remove(GetCollectionCacheKey key) {
* @return
*/
private synchronized Pair<GetCollectionCacheKey, List<BsonDocument>> _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))
Expand All @@ -121,23 +138,31 @@ private synchronized Pair<GetCollectionCacheKey, List<BsonDocument>> _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))
.forEach(k -> cache.invalidate(k));
}

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<? super GetCollectionCacheKey> cacheKeyFilter(GetCollectionCacheKey requested) {
if (cache == null) return null;

return cached
-> Objects.equals(cached.collection().getNamespace(), requested.collection().getNamespace())
&& Objects.equals(cached.filter(), requested.filter())
Expand All @@ -148,16 +173,18 @@ private Predicate<? super GetCollectionCacheKey> cacheKeyFilter(GetCollectionCac
}

private TreeMap<String, Long> 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() {
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,10 +43,12 @@
* @author Andrea Di Cesare {@literal <[email protected]>}
*/
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);


/**
*
*/
Expand Down Expand Up @@ -125,8 +128,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
filter,
request.getHintDocument(),
request.getProjectionDocument(),
request.isCache());

request.isCache() && isGetCollectionCacheEnabled);
}

if (exchange.isComplete()) {
Expand Down

0 comments on commit 2a07d93

Please sign in to comment.