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:
@@ -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
|
||||||
|
@@ -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());
|
||||||
}
|
}
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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();
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
1
java/com/google/gerrit/server/cache/h2/BUILD
vendored
1
java/com/google/gerrit/server/cache/h2/BUILD
vendored
@@ -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",
|
||||||
|
@@ -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")
|
||||||
|
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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",
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user