diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 7ed0e17b76..ec04e73ba4 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -762,6 +762,16 @@ Default is 1024 for most caches, except: If set to 0 the cache is disabled. Entries are removed immediately after being stored by the cache. This is primarily useful for testing. +[[cache.name.expireFromMemoryAfterAccess]]cache..expireFromMemoryAfterAccess:: ++ +Time after last access to automatically expire entries from an in-memory +cache. If 0 or not specified, entries are never expired in this manner. +Values may use unit suffixes as in link:#cache.name.maxAge[maxAge]. ++ +This option only applies to in-memory caches; persistent cache values are +not expired in this manner, and are only pruned via +link:#cache.name.diskLimit[diskLimit]. + [[cache.name.diskLimit]]cache..diskLimit:: + Total size in bytes of the keys and values stored on disk. Caches that diff --git a/java/com/google/gerrit/server/cache/CacheBinding.java b/java/com/google/gerrit/server/cache/CacheBinding.java index a8e7419f04..9d90d073d5 100644 --- a/java/com/google/gerrit/server/cache/CacheBinding.java +++ b/java/com/google/gerrit/server/cache/CacheBinding.java @@ -23,9 +23,12 @@ public interface CacheBinding { /** Set the total size of the cache. */ CacheBinding maximumWeight(long weight); - /** Set the time an element lives before being expired. */ + /** Set the time an element lives after last write before being expired. */ CacheBinding expireAfterWrite(Duration duration); + /** Set the time an element lives after last access before being expired. */ + CacheBinding expireFromMemoryAfterAccess(Duration duration); + /** Populate the cache with items from the CacheLoader. */ CacheBinding loader(Class> clazz); diff --git a/java/com/google/gerrit/server/cache/CacheDef.java b/java/com/google/gerrit/server/cache/CacheDef.java index 574ea27b3c..d0c633e6fc 100644 --- a/java/com/google/gerrit/server/cache/CacheDef.java +++ b/java/com/google/gerrit/server/cache/CacheDef.java @@ -47,6 +47,9 @@ public interface CacheDef { @Nullable Duration expireAfterWrite(); + @Nullable + Duration expireFromMemoryAfterAccess(); + @Nullable Weigher weigher(); diff --git a/java/com/google/gerrit/server/cache/CacheProvider.java b/java/com/google/gerrit/server/cache/CacheProvider.java index c3d3394d61..2bd570e434 100644 --- a/java/com/google/gerrit/server/cache/CacheProvider.java +++ b/java/com/google/gerrit/server/cache/CacheProvider.java @@ -36,6 +36,7 @@ class CacheProvider implements Provider>, CacheBinding, private String configKey; private long maximumWeight; private Duration expireAfterWrite; + private Duration expireFromMemoryAfterAccess; private Provider> loader; private Provider> weigher; @@ -74,6 +75,13 @@ class CacheProvider implements Provider>, CacheBinding, return this; } + @Override + public CacheBinding expireFromMemoryAfterAccess(Duration duration) { + checkNotFrozen(); + expireFromMemoryAfterAccess = duration; + return this; + } + @Override public CacheBinding loader(Class> impl) { checkNotFrozen(); @@ -129,6 +137,12 @@ class CacheProvider implements Provider>, CacheBinding, return expireAfterWrite; } + @Override + @Nullable + public Duration expireFromMemoryAfterAccess() { + return expireFromMemoryAfterAccess; + } + @Override @Nullable public Weigher weigher() { diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java b/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java index d8edebdc2f..78de67dd02 100644 --- a/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java +++ b/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java @@ -36,6 +36,12 @@ class H2CacheDefProxy implements PersistentCacheDef { return source.expireAfterWrite(); } + @Override + @Nullable + public Duration expireFromMemoryAfterAccess() { + return source.expireFromMemoryAfterAccess(); + } + @SuppressWarnings("unchecked") @Override public Weigher weigher() { diff --git a/java/com/google/gerrit/server/cache/mem/BUILD b/java/com/google/gerrit/server/cache/mem/BUILD index ef297f1cdb..4106714b3d 100644 --- a/java/com/google/gerrit/server/cache/mem/BUILD +++ b/java/com/google/gerrit/server/cache/mem/BUILD @@ -3,6 +3,7 @@ java_library( srcs = glob(["*.java"]), visibility = ["//visibility:public"], deps = [ + "//java/com/google/gerrit/common:annotations", "//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/server", "//lib:guava", diff --git a/java/com/google/gerrit/server/cache/mem/DefaultMemoryCacheFactory.java b/java/com/google/gerrit/server/cache/mem/DefaultMemoryCacheFactory.java index 2463847ab4..ad1d396668 100644 --- a/java/com/google/gerrit/server/cache/mem/DefaultMemoryCacheFactory.java +++ b/java/com/google/gerrit/server/cache/mem/DefaultMemoryCacheFactory.java @@ -14,12 +14,16 @@ package com.google.gerrit.server.cache.mem; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + import com.google.common.base.Strings; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.Weigher; +import com.google.gerrit.common.Nullable; import com.google.gerrit.server.cache.CacheDef; import com.google.gerrit.server.cache.ForwardingRemovalListener; import com.google.gerrit.server.cache.MemoryCacheFactory; @@ -27,7 +31,6 @@ import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; import com.google.inject.Inject; import java.time.Duration; -import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lib.Config; class DefaultMemoryCacheFactory implements MemoryCacheFactory { @@ -67,24 +70,38 @@ class DefaultMemoryCacheFactory implements MemoryCacheFactory { } builder.weigher(weigher); - Duration age = def.expireAfterWrite(); + Duration expireAfterWrite = def.expireAfterWrite(); if (has(def.configKey(), "maxAge")) { builder.expireAfterWrite( + ConfigUtil.getTimeUnit( + cfg, "cache", def.configKey(), "maxAge", toSeconds(expireAfterWrite), SECONDS), + SECONDS); + } else if (expireAfterWrite != null) { + builder.expireAfterWrite(expireAfterWrite.toNanos(), NANOSECONDS); + } + + Duration expireAfterAccess = def.expireFromMemoryAfterAccess(); + if (has(def.configKey(), "expireFromMemoryAfterAccess")) { + builder.expireAfterAccess( ConfigUtil.getTimeUnit( cfg, "cache", def.configKey(), - "maxAge", - age != null ? age.getSeconds() : 0, - TimeUnit.SECONDS), - TimeUnit.SECONDS); - } else if (age != null) { - builder.expireAfterWrite(age.toNanos(), TimeUnit.NANOSECONDS); + "expireFromMemoryAfterAccess", + toSeconds(expireAfterAccess), + SECONDS), + SECONDS); + } else if (expireAfterAccess != null) { + builder.expireAfterAccess(expireAfterAccess.toNanos(), NANOSECONDS); } return builder; } + private static long toSeconds(@Nullable Duration duration) { + return duration != null ? duration.getSeconds() : 0; + } + private boolean has(String name, String var) { return !Strings.isNullOrEmpty(cfg.getString("cache", name, var)); }