This is an automated email from the ASF dual-hosted git repository. radu pushed a commit to branch feature/SLING-9655 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git
commit 94484b13e8d50ca86e592ef8f8a553f4b2f1eb42 Author: Radu Cotescu <[email protected]> AuthorDate: Mon Aug 31 11:32:50 2020 +0200 SLING-9655 - Caching support for the GraphQL core * minor code optimizations in the SimpleGraphQLCacheProvider --- .../core/cache/SimpleGraphQLCacheProvider.java | 50 ++++++++++++++-------- .../core/cache/SimpleGraphQLCacheProviderTest.java | 5 +++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java b/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java index 8b56865..b2def0f 100644 --- a/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java +++ b/src/main/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProvider.java @@ -23,10 +23,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.sling.graphql.api.cache.GraphQLCacheProvider; import org.apache.sling.graphql.core.engine.SlingGraphQLException; @@ -59,17 +61,16 @@ public class SimpleGraphQLCacheProvider implements GraphQLCacheProvider { type = AttributeType.INTEGER, min = "0" ) - int capacity() default DEFAULT_CACHE_SIZE; + int capacity() default 0; @AttributeDefinition( name = "Max Values in Bytes", description = "The maximum amount of memory the values stored in the cache can use." ) - int maxSize() default 104857600; + long maxSize() default 10 * FileUtils.ONE_MB; } - private static final int DEFAULT_CACHE_SIZE = 1024; private static final Logger LOGGER = LoggerFactory.getLogger(SimpleGraphQLCacheProvider.class); private InMemoryLRUCache persistedQueriesCache; @@ -86,13 +87,13 @@ public class SimpleGraphQLCacheProvider implements GraphQLCacheProvider { cacheSize = 0; LOGGER.debug("Cache capacity set to {}. Defaulting to 0.", config.capacity()); } - int maxSize = config.maxSize(); + long maxSize = config.maxSize(); if (maxSize < 0) { maxSize = 0; LOGGER.debug("Cache size set to {}. Defaulting to 0.", config.maxSize()); } persistedQueriesCache = new InMemoryLRUCache(cacheSize, maxSize); - LOGGER.debug("Initialized the in-memory cache for a maximum of {} queries.", config.capacity()); + LOGGER.debug("In-memory cache initialized: cacheSize={}, maxSize={}.", cacheSize, maxSize); } @@ -150,30 +151,31 @@ public class SimpleGraphQLCacheProvider implements GraphQLCacheProvider { return buffer.toString(); } + /** + * This implementation provides a simple LRU eviction based on either the number of entries or the memory used by the stored values. + * Synchronization has to happen externally. + */ private static class InMemoryLRUCache extends LinkedHashMap<String, String> { private final int capacity; - private final int maxSizeInBytes; - private int currentSizeInBytes; + private final long maxSizeInBytes; + private long currentSizeInBytes; - public InMemoryLRUCache(int capacity, int maxSizeInBytes) { + public InMemoryLRUCache(int capacity, long maxSizeInBytes) { this.capacity = Math.max(capacity, 0); this.maxSizeInBytes = Math.max(maxSizeInBytes, 0); this.currentSizeInBytes = 0; } @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - boolean willRemove; + protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { + boolean willRemove = false; if (capacity > 0) { willRemove = size() > capacity; - } else { + } else if (maxSizeInBytes > 0) { willRemove = currentSizeInBytes > maxSizeInBytes; - } - if (willRemove) { - String head = values().iterator().next(); - if (StringUtils.isNotEmpty(head)) { - currentSizeInBytes -= getApproximateStringSizeInBytes(head); + if (willRemove) { + currentSizeInBytes -= getApproximateStringSizeInBytes(eldest.getValue()); } } return willRemove; @@ -188,6 +190,20 @@ public class SimpleGraphQLCacheProvider implements GraphQLCacheProvider { int getApproximateStringSizeInBytes(@NotNull String string) { return 8 * (((string.length() * 2) + 45) / 8); } - } + @Override + public boolean equals(Object obj) { + if (obj instanceof InMemoryLRUCache) { + InMemoryLRUCache other = (InMemoryLRUCache) obj; + return Objects.equals(capacity, other.capacity) && Objects.equals(maxSizeInBytes, other.maxSizeInBytes) && + Objects.equals(currentSizeInBytes, other.currentSizeInBytes) && super.equals(obj); + } + return false; + } + + @Override + public int hashCode() { + return super.hashCode() + capacity + ((int) (maxSizeInBytes + currentSizeInBytes)); + } + } } diff --git a/src/test/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProviderTest.java b/src/test/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProviderTest.java index 550f117..fa13429 100644 --- a/src/test/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProviderTest.java +++ b/src/test/java/org/apache/sling/graphql/core/cache/SimpleGraphQLCacheProviderTest.java @@ -24,6 +24,7 @@ import org.junit.Rule; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; public class SimpleGraphQLCacheProviderTest { @@ -35,6 +36,7 @@ public class SimpleGraphQLCacheProviderTest { public void getHash() { context.registerInjectActivateService(new SimpleGraphQLCacheProvider()); SimpleGraphQLCacheProvider provider = (SimpleGraphQLCacheProvider) context.getService(GraphQLCacheProvider.class); + assertNotNull(provider); assertEquals("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", provider.getHash("hello world")); } @@ -42,6 +44,7 @@ public class SimpleGraphQLCacheProviderTest { public void testMemoryLimits() { context.registerInjectActivateService(new SimpleGraphQLCacheProvider(), "capacity", 0, "maxSize", 40); SimpleGraphQLCacheProvider provider = (SimpleGraphQLCacheProvider) context.getService(GraphQLCacheProvider.class); + assertNotNull(provider); String aHash = provider.cacheQuery("a", "a/b/c", null); assertEquals("a", provider.getQuery(aHash, "a/b/c", null)); @@ -60,6 +63,7 @@ public class SimpleGraphQLCacheProviderTest { public void testCapacityLimits() { context.registerInjectActivateService(new SimpleGraphQLCacheProvider(), "capacity", 3, "maxSize", 0); SimpleGraphQLCacheProvider provider = (SimpleGraphQLCacheProvider) context.getService(GraphQLCacheProvider.class); + assertNotNull(provider); String aHash = provider.cacheQuery("a", "a/b/c", null); assertEquals("a", provider.getQuery(aHash, "a/b/c", null)); @@ -86,6 +90,7 @@ public class SimpleGraphQLCacheProviderTest { public void testCapacityHasPriorityOverMemory() { context.registerInjectActivateService(new SimpleGraphQLCacheProvider(), "capacity", 2, "maxSize", 40); SimpleGraphQLCacheProvider provider = (SimpleGraphQLCacheProvider) context.getService(GraphQLCacheProvider.class); + assertNotNull(provider); String aHash = provider.cacheQuery("a", "a/b/c", null); assertEquals("a", provider.getQuery(aHash, "a/b/c", null));
