Merge changes from topic "expire-after-access"

* changes:
  Use expireAfterAccess with a larger cache size for ExternalIdCache
  Use CacheModule for the external ID cache again
  Support expiring from in-memory caches after last access
  Convert cache expireAfterWrite to nullable Duration
This commit is contained in:
Dave Borowitz
2018-06-07 12:27:35 +00:00
committed by Gerrit Code Review
19 changed files with 149 additions and 71 deletions

View File

@@ -753,6 +753,7 @@ Default is 1024 for most caches, except:
* `"diff"`: default is `10m` (10 MiB of memory) * `"diff"`: default is `10m` (10 MiB of memory)
* `"diff_intraline"`: default is `10m` (10 MiB of memory) * `"diff_intraline"`: default is `10m` (10 MiB of memory)
* `"diff_summary"`: default is `10m` (10 MiB of memory) * `"diff_summary"`: default is `10m` (10 MiB of memory)
* `"external_ids_map"`: default is `2` and should not be changed
* `"groups"`: default is unlimited * `"groups"`: default is unlimited
* `"groups_byname"`: default is unlimited * `"groups_byname"`: default is unlimited
* `"groups_byuuid"`: default is unlimited * `"groups_byuuid"`: default is unlimited
@@ -762,6 +763,16 @@ Default is 1024 for most caches, except:
If set to 0 the cache is disabled. Entries are removed immediately If set to 0 the cache is disabled. Entries are removed immediately
after being stored by the cache. This is primarily useful for testing. after being stored by the cache. This is primarily useful for testing.
[[cache.name.expireFromMemoryAfterAccess]]cache.<name>.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.<name>.diskLimit:: [[cache.name.diskLimit]]cache.<name>.diskLimit::
+ +
Total size in bytes of the keys and values stored on disk. Caches that Total size in bytes of the keys and values stored on disk. Caches that
@@ -844,6 +855,16 @@ Ideally, disk limit of this cache is large enough to cover all changes.
This should significantly speed up change reindexing, especially This should significantly speed up change reindexing, especially
full offline reindexing. full offline reindexing.
cache `"external_ids_map"`::
+
A singleton cache whose sole entry is a map of the parsed representation
of link:config-accounts.html#external-ids[all current external IDs]. The
cache may temporarily contain 2 entries, but the second one is promptly
expired.
+
It is not recommended to change the attributes of this cache away from
the defaults.
cache `"git_tags"`:: cache `"git_tags"`::
+ +
If branch or reference level READ access controls are used, this If branch or reference level READ access controls are used, this

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.common;
import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.util.function.LongSupplier; import java.util.function.LongSupplier;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -36,6 +37,10 @@ public class TimeUtil {
return currentMillisSupplier.getAsLong(); return currentMillisSupplier.getAsLong();
} }
public static Instant now() {
return Instant.ofEpochMilli(nowMs());
}
public static Timestamp nowTs() { public static Timestamp nowTs() {
return new Timestamp(nowMs()); return new Timestamp(nowMs());
} }

View File

@@ -44,10 +44,10 @@ import com.google.inject.Singleton;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@@ -119,7 +119,7 @@ public class GitOverHttpServlet extends GitServlet {
protected void configure() { protected void configure() {
cache(ID_CACHE, AdvertisedObjectsCacheKey.class, new TypeLiteral<Set<ObjectId>>() {}) cache(ID_CACHE, AdvertisedObjectsCacheKey.class, new TypeLiteral<Set<ObjectId>>() {})
.maximumWeight(4096) .maximumWeight(4096)
.expireAfterWrite(10, TimeUnit.MINUTES); .expireAfterWrite(Duration.ofMinutes(10));
} }
}); });
} }

View File

@@ -14,8 +14,6 @@
package com.google.gerrit.httpd; package com.google.gerrit.httpd;
import static java.util.concurrent.TimeUnit.MINUTES;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
@@ -30,6 +28,7 @@ import com.google.inject.Provider;
import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import com.google.inject.servlet.RequestScoped; import com.google.inject.servlet.RequestScoped;
import java.time.Duration;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -41,10 +40,8 @@ public class H2CacheBasedWebSession extends CacheBasedWebSession {
protected void configure() { protected void configure() {
persist(WebSessionManager.CACHE_NAME, String.class, Val.class) persist(WebSessionManager.CACHE_NAME, String.class, Val.class)
.maximumWeight(1024) // reasonable default for many sites .maximumWeight(1024) // reasonable default for many sites
.expireAfterWrite( // expire sessions if they are inactive
CacheBasedWebSession.MAX_AGE_MINUTES, .expireAfterWrite(Duration.ofMinutes(CacheBasedWebSession.MAX_AGE_MINUTES));
MINUTES) // expire sessions if they are inactive
;
install(new FactoryModuleBuilder().build(WebSessionManagerFactory.class)); install(new FactoryModuleBuilder().build(WebSessionManagerFactory.class));
DynamicItem.itemOf(binder(), WebSession.class); DynamicItem.itemOf(binder(), WebSession.class);
DynamicItem.bind(binder(), WebSession.class) DynamicItem.bind(binder(), WebSession.class)

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.server.account.externalids;
import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
@@ -29,6 +28,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.name.Named;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
@@ -43,31 +43,17 @@ import org.eclipse.jgit.lib.ObjectId;
class ExternalIdCacheImpl implements ExternalIdCache { class ExternalIdCacheImpl implements ExternalIdCache {
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String CACHE_NAME = "external_ids_map";
private final LoadingCache<ObjectId, AllExternalIds> extIdsByAccount; private final LoadingCache<ObjectId, AllExternalIds> extIdsByAccount;
private final ExternalIdReader externalIdReader; private final ExternalIdReader externalIdReader;
private final Lock lock; private final Lock lock;
@Inject @Inject
ExternalIdCacheImpl(ExternalIdReader externalIdReader) { ExternalIdCacheImpl(
this.extIdsByAccount = @Named(CACHE_NAME) LoadingCache<ObjectId, AllExternalIds> extIdsByAccount,
CacheBuilder.newBuilder() ExternalIdReader externalIdReader) {
// The cached data is potentially pretty large and we are always only interested this.extIdsByAccount = extIdsByAccount;
// in the latest value, hence the maximum cache size is set to 1.
// This can lead to extra cache loads in case of the following race:
// 1. thread 1 reads the notes ref at revision A
// 2. thread 2 updates the notes ref to revision B and stores the derived value
// for B in the cache
// 3. thread 1 attempts to read the data for revision A from the cache, and misses
// 4. later threads attempt to read at B
// In this race unneeded reloads are done in step 3 (reload from revision A) and
// step 4 (reload from revision B, because the value for revision B was lost when the
// reload from revision A was done, since the cache can hold only one entry).
// These reloads could be avoided by increasing the cache size to 2. However the race
// window between reading the ref and looking it up in the cache is small so that
// it's rare that this race happens. Therefore it's not worth to double the memory
// usage of this cache, just to avoid this.
.maximumSize(1)
.build(new Loader(externalIdReader));
this.externalIdReader = externalIdReader; this.externalIdReader = externalIdReader;
this.lock = new ReentrantLock(true /* fair */); this.lock = new ReentrantLock(true /* fair */);
} }
@@ -159,9 +145,10 @@ class ExternalIdCacheImpl implements ExternalIdCache {
} }
} }
private static class Loader extends CacheLoader<ObjectId, AllExternalIds> { static class Loader extends CacheLoader<ObjectId, AllExternalIds> {
private final ExternalIdReader externalIdReader; private final ExternalIdReader externalIdReader;
@Inject
Loader(ExternalIdReader externalIdReader) { Loader(ExternalIdReader externalIdReader) {
this.externalIdReader = externalIdReader; this.externalIdReader = externalIdReader;
} }

View File

@@ -14,11 +14,27 @@
package com.google.gerrit.server.account.externalids; package com.google.gerrit.server.account.externalids;
import com.google.inject.AbstractModule; import com.google.gerrit.server.account.externalids.ExternalIdCacheImpl.AllExternalIds;
import com.google.gerrit.server.account.externalids.ExternalIdCacheImpl.Loader;
import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.TypeLiteral;
import java.time.Duration;
import org.eclipse.jgit.lib.ObjectId;
public class ExternalIdModule extends AbstractModule { public class ExternalIdModule extends CacheModule {
@Override @Override
protected void configure() { protected void configure() {
cache(ExternalIdCacheImpl.CACHE_NAME, ObjectId.class, new TypeLiteral<AllExternalIds>() {})
// The cached data is potentially pretty large and we are always only interested
// in the latest value. However, due to a race condition, it is possible for different
// threads to observe different values of the meta ref, and hence request different keys
// from the cache. Extend the cache size by 1 to cover this case, but expire the extra
// object after a short period of time, since it may be a potentially large amount of
// memory.
.maximumWeight(2)
.expireFromMemoryAfterAccess(Duration.ofMinutes(1))
.loader(Loader.class);
bind(ExternalIdCacheImpl.class); bind(ExternalIdCacheImpl.class);
bind(ExternalIdCache.class).to(ExternalIdCacheImpl.class); bind(ExternalIdCache.class).to(ExternalIdCacheImpl.class);
} }

View File

@@ -14,8 +14,6 @@
package com.google.gerrit.server.auth.ldap; package com.google.gerrit.server.auth.ldap;
import static java.util.concurrent.TimeUnit.HOURS;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -25,6 +23,7 @@ import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@@ -37,18 +36,18 @@ public class LdapModule extends CacheModule {
@Override @Override
protected void configure() { protected void configure() {
cache(GROUP_CACHE, String.class, new TypeLiteral<Set<AccountGroup.UUID>>() {}) cache(GROUP_CACHE, String.class, new TypeLiteral<Set<AccountGroup.UUID>>() {})
.expireAfterWrite(1, HOURS) .expireAfterWrite(Duration.ofHours(1))
.loader(LdapRealm.MemberLoader.class); .loader(LdapRealm.MemberLoader.class);
cache(USERNAME_CACHE, String.class, new TypeLiteral<Optional<Account.Id>>() {}) cache(USERNAME_CACHE, String.class, new TypeLiteral<Optional<Account.Id>>() {})
.loader(LdapRealm.UserLoader.class); .loader(LdapRealm.UserLoader.class);
cache(GROUP_EXIST_CACHE, String.class, new TypeLiteral<Boolean>() {}) cache(GROUP_EXIST_CACHE, String.class, new TypeLiteral<Boolean>() {})
.expireAfterWrite(1, HOURS) .expireAfterWrite(Duration.ofHours(1))
.loader(LdapRealm.ExistenceLoader.class); .loader(LdapRealm.ExistenceLoader.class);
cache(PARENT_GROUPS_CACHE, String.class, new TypeLiteral<ImmutableSet<String>>() {}) cache(PARENT_GROUPS_CACHE, String.class, new TypeLiteral<ImmutableSet<String>>() {})
.expireAfterWrite(1, HOURS); .expireAfterWrite(Duration.ofHours(1));
bind(Helper.class); bind(Helper.class);
bind(Realm.class).to(LdapRealm.class).in(Scopes.SINGLETON); bind(Realm.class).to(LdapRealm.class).in(Scopes.SINGLETON);

View File

@@ -16,15 +16,18 @@ package com.google.gerrit.server.cache;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher; import com.google.common.cache.Weigher;
import java.util.concurrent.TimeUnit; import java.time.Duration;
/** Configure a cache declared within a {@link CacheModule} instance. */ /** Configure a cache declared within a {@link CacheModule} instance. */
public interface CacheBinding<K, V> { public interface CacheBinding<K, V> {
/** Set the total size of the cache. */ /** Set the total size of the cache. */
CacheBinding<K, V> maximumWeight(long weight); CacheBinding<K, V> 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<K, V> expireAfterWrite(long duration, TimeUnit durationUnits); CacheBinding<K, V> expireAfterWrite(Duration duration);
/** Set the time an element lives after last access before being expired. */
CacheBinding<K, V> expireFromMemoryAfterAccess(Duration duration);
/** Populate the cache with items from the CacheLoader. */ /** Populate the cache with items from the CacheLoader. */
CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz); CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz);

View File

@@ -18,7 +18,7 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher; import com.google.common.cache.Weigher;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit; import java.time.Duration;
public interface CacheDef<K, V> { public interface CacheDef<K, V> {
/** /**
@@ -45,7 +45,10 @@ public interface CacheDef<K, V> {
long maximumWeight(); long maximumWeight();
@Nullable @Nullable
Long expireAfterWrite(TimeUnit unit); Duration expireAfterWrite();
@Nullable
Duration expireFromMemoryAfterAccess();
@Nullable @Nullable
Weigher<K, V> weigher(); Weigher<K, V> weigher();

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.server.cache;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
@@ -27,7 +26,7 @@ import com.google.gerrit.extensions.annotations.PluginName;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit; import java.time.Duration;
class CacheProvider<K, V> implements Provider<Cache<K, V>>, CacheBinding<K, V>, CacheDef<K, V> { class CacheProvider<K, V> implements Provider<Cache<K, V>>, CacheBinding<K, V>, CacheDef<K, V> {
private final CacheModule module; private final CacheModule module;
@@ -36,7 +35,8 @@ class CacheProvider<K, V> implements Provider<Cache<K, V>>, CacheBinding<K, V>,
private final TypeLiteral<V> valType; private final TypeLiteral<V> valType;
private String configKey; private String configKey;
private long maximumWeight; private long maximumWeight;
private Long expireAfterWrite; private Duration expireAfterWrite;
private Duration expireFromMemoryAfterAccess;
private Provider<CacheLoader<K, V>> loader; private Provider<CacheLoader<K, V>> loader;
private Provider<Weigher<K, V>> weigher; private Provider<Weigher<K, V>> weigher;
@@ -69,9 +69,16 @@ class CacheProvider<K, V> implements Provider<Cache<K, V>>, CacheBinding<K, V>,
} }
@Override @Override
public CacheBinding<K, V> expireAfterWrite(long duration, TimeUnit unit) { public CacheBinding<K, V> expireAfterWrite(Duration duration) {
checkNotFrozen(); checkNotFrozen();
expireAfterWrite = SECONDS.convert(duration, unit); expireAfterWrite = duration;
return this;
}
@Override
public CacheBinding<K, V> expireFromMemoryAfterAccess(Duration duration) {
checkNotFrozen();
expireFromMemoryAfterAccess = duration;
return this; return this;
} }
@@ -126,8 +133,14 @@ class CacheProvider<K, V> implements Provider<Cache<K, V>>, CacheBinding<K, V>,
@Override @Override
@Nullable @Nullable
public Long expireAfterWrite(TimeUnit unit) { public Duration expireAfterWrite() {
return expireAfterWrite != null ? unit.convert(expireAfterWrite, SECONDS) : null; return expireAfterWrite;
}
@Override
@Nullable
public Duration expireFromMemoryAfterAccess() {
return expireFromMemoryAfterAccess;
} }
@Override @Override

View File

@@ -16,7 +16,7 @@ package com.google.gerrit.server.cache;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher; import com.google.common.cache.Weigher;
import java.util.concurrent.TimeUnit; import java.time.Duration;
/** Configure a persistent cache declared within a {@link CacheModule} instance. */ /** Configure a persistent cache declared within a {@link CacheModule} instance. */
public interface PersistentCacheBinding<K, V> extends CacheBinding<K, V> { public interface PersistentCacheBinding<K, V> extends CacheBinding<K, V> {
@@ -24,7 +24,7 @@ public interface PersistentCacheBinding<K, V> extends CacheBinding<K, V> {
PersistentCacheBinding<K, V> maximumWeight(long weight); PersistentCacheBinding<K, V> maximumWeight(long weight);
@Override @Override
PersistentCacheBinding<K, V> expireAfterWrite(long duration, TimeUnit durationUnits); PersistentCacheBinding<K, V> expireAfterWrite(Duration duration);
@Override @Override
PersistentCacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz); PersistentCacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz);

View File

@@ -24,7 +24,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.io.Serializable; import java.io.Serializable;
import java.util.concurrent.TimeUnit; import java.time.Duration;
class PersistentCacheProvider<K, V> extends CacheProvider<K, V> class PersistentCacheProvider<K, V> extends CacheProvider<K, V>
implements Provider<Cache<K, V>>, PersistentCacheBinding<K, V>, PersistentCacheDef<K, V> { implements Provider<Cache<K, V>>, PersistentCacheBinding<K, V>, PersistentCacheDef<K, V> {
@@ -53,8 +53,8 @@ class PersistentCacheProvider<K, V> extends CacheProvider<K, V>
} }
@Override @Override
public PersistentCacheBinding<K, V> expireAfterWrite(long duration, TimeUnit durationUnits) { public PersistentCacheBinding<K, V> expireAfterWrite(Duration duration) {
return (PersistentCacheBinding<K, V>) super.expireAfterWrite(duration, durationUnits); return (PersistentCacheBinding<K, V>) super.expireAfterWrite(duration);
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ java_library(
srcs = glob(["**/*.java"]), srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server", "//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/lifecycle", "//java/com/google/gerrit/lifecycle",

View File

@@ -16,11 +16,12 @@ package com.google.gerrit.server.cache.h2;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher; import com.google.common.cache.Weigher;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.cache.CacheSerializer; import com.google.gerrit.server.cache.CacheSerializer;
import com.google.gerrit.server.cache.PersistentCacheDef; import com.google.gerrit.server.cache.PersistentCacheDef;
import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder; import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit; import java.time.Duration;
class H2CacheDefProxy<K, V> implements PersistentCacheDef<K, V> { class H2CacheDefProxy<K, V> implements PersistentCacheDef<K, V> {
private final PersistentCacheDef<K, V> source; private final PersistentCacheDef<K, V> source;
@@ -30,8 +31,15 @@ class H2CacheDefProxy<K, V> implements PersistentCacheDef<K, V> {
} }
@Override @Override
public Long expireAfterWrite(TimeUnit unit) { @Nullable
return source.expireAfterWrite(unit); public Duration expireAfterWrite() {
return source.expireAfterWrite();
}
@Override
@Nullable
public Duration expireFromMemoryAfterAccess() {
return source.expireFromMemoryAfterAccess();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -215,7 +215,6 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
if (h2AutoServer) { if (h2AutoServer) {
url.append(";AUTO_SERVER=TRUE"); url.append(";AUTO_SERVER=TRUE");
} }
Long expireAfterWrite = def.expireAfterWrite(TimeUnit.SECONDS);
return new SqlStore<>( return new SqlStore<>(
url.toString(), url.toString(),
def.keyType(), def.keyType(),
@@ -223,6 +222,6 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
def.valueSerializer(), def.valueSerializer(),
def.version(), def.version(),
maxSize, maxSize,
expireAfterWrite == null ? 0 : expireAfterWrite.longValue()); def.expireAfterWrite());
} }
} }

View File

@@ -23,6 +23,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.common.hash.BloomFilter; import com.google.common.hash.BloomFilter;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.server.cache.CacheSerializer; import com.google.gerrit.server.cache.CacheSerializer;
import com.google.gerrit.server.cache.PersistentCache; import com.google.gerrit.server.cache.PersistentCache;
@@ -35,6 +36,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Duration;
import java.util.Calendar; import java.util.Calendar;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
@@ -254,7 +256,7 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements Per
private final CacheSerializer<V> valueSerializer; private final CacheSerializer<V> valueSerializer;
private final int version; private final int version;
private final long maxSize; private final long maxSize;
private final long expireAfterWrite; @Nullable private final Duration expireAfterWrite;
private final BlockingQueue<SqlHandle> handles; private final BlockingQueue<SqlHandle> handles;
private final AtomicLong hitCount = new AtomicLong(); private final AtomicLong hitCount = new AtomicLong();
private final AtomicLong missCount = new AtomicLong(); private final AtomicLong missCount = new AtomicLong();
@@ -268,7 +270,7 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements Per
CacheSerializer<V> valueSerializer, CacheSerializer<V> valueSerializer,
int version, int version,
long maxSize, long maxSize,
long expireAfterWrite) { @Nullable Duration expireAfterWrite) {
this.url = jdbcUrl; this.url = jdbcUrl;
this.keyType = createKeyType(keyType, keySerializer); this.keyType = createKeyType(keyType, keySerializer);
this.valueSerializer = valueSerializer; this.valueSerializer = valueSerializer;
@@ -421,11 +423,11 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements Per
} }
private boolean expired(Timestamp created) { private boolean expired(Timestamp created) {
if (expireAfterWrite == 0) { if (expireAfterWrite == null) {
return false; return false;
} }
long age = TimeUtil.nowMs() - created.getTime(); Duration age = Duration.between(created.toInstant(), TimeUtil.now());
return 1000 * expireAfterWrite < age; return age.compareTo(expireAfterWrite) > 0;
} }
private void touch(SqlHandle c, K key) throws IOException, SQLException { private void touch(SqlHandle c, K key) throws IOException, SQLException {

View File

@@ -3,6 +3,7 @@ java_library(
srcs = glob(["*.java"]), srcs = glob(["*.java"]),
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api", "//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server", "//java/com/google/gerrit/server",
"//lib:guava", "//lib:guava",

View File

@@ -14,19 +14,23 @@
package com.google.gerrit.server.cache.mem; 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.base.Strings;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.cache.Weigher; 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.CacheDef;
import com.google.gerrit.server.cache.ForwardingRemovalListener; import com.google.gerrit.server.cache.ForwardingRemovalListener;
import com.google.gerrit.server.cache.MemoryCacheFactory; import com.google.gerrit.server.cache.MemoryCacheFactory;
import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.concurrent.TimeUnit; import java.time.Duration;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
class DefaultMemoryCacheFactory implements MemoryCacheFactory { class DefaultMemoryCacheFactory implements MemoryCacheFactory {
@@ -66,19 +70,38 @@ class DefaultMemoryCacheFactory implements MemoryCacheFactory {
} }
builder.weigher(weigher); builder.weigher(weigher);
Long age = def.expireAfterWrite(TimeUnit.SECONDS); Duration expireAfterWrite = def.expireAfterWrite();
if (has(def.configKey(), "maxAge")) { if (has(def.configKey(), "maxAge")) {
builder.expireAfterWrite( builder.expireAfterWrite(
ConfigUtil.getTimeUnit( ConfigUtil.getTimeUnit(
cfg, "cache", def.configKey(), "maxAge", age != null ? age : 0, TimeUnit.SECONDS), cfg, "cache", def.configKey(), "maxAge", toSeconds(expireAfterWrite), SECONDS),
TimeUnit.SECONDS); SECONDS);
} else if (age != null) { } else if (expireAfterWrite != null) {
builder.expireAfterWrite(age, TimeUnit.SECONDS); builder.expireAfterWrite(expireAfterWrite.toNanos(), NANOSECONDS);
}
Duration expireAfterAccess = def.expireFromMemoryAfterAccess();
if (has(def.configKey(), "expireFromMemoryAfterAccess")) {
builder.expireAfterAccess(
ConfigUtil.getTimeUnit(
cfg,
"cache",
def.configKey(),
"expireFromMemoryAfterAccess",
toSeconds(expireAfterAccess),
SECONDS),
SECONDS);
} else if (expireAfterAccess != null) {
builder.expireAfterAccess(expireAfterAccess.toNanos(), NANOSECONDS);
} }
return builder; return builder;
} }
private static long toSeconds(@Nullable Duration duration) {
return duration != null ? duration.getSeconds() : 0;
}
private boolean has(String name, String var) { private boolean has(String name, String var) {
return !Strings.isNullOrEmpty(cfg.getString("cache", name, var)); return !Strings.isNullOrEmpty(cfg.getString("cache", name, var));
} }

View File

@@ -47,7 +47,7 @@ public class H2CacheTest {
StringSerializer.INSTANCE, StringSerializer.INSTANCE,
version, version,
1 << 20, 1 << 20,
0); null);
return new H2CacheImpl<>(MoreExecutors.directExecutor(), store, KEY_TYPE, mem); return new H2CacheImpl<>(MoreExecutors.directExecutor(), store, KEY_TYPE, mem);
} }