Back in-memory caches with Guava, disk caches with H2
Instead of using Ehcache for in-memory caches, use Guava. The Guava cache code has been more completely tested by Google in high load production environments, and it tends to have fewer bugs. It enables caches to be built at any time, rather than only at server startup. By creating a Guava cache as soon as it is declared, rather than during the LifecycleListener.start() for the CachePool, we can promise any downstream consumer of the cache that the cache is ready to execute requests the moment it is supplied by Guice. This fixes a startup ordering problem in the GroupCache and the ProjectCache, where code wants to use one of these caches during startup to resolve a group or project by name. Tracking the Gauva backend caches with a DynamicMap makes it possible for plugins to define their own in-memory caches using CacheModule's cache() function to declare the cache. It allows the core server to make the cache available to administrators over SSH with the gerrit show-caches and gerrit flush-caches commands. Persistent caches store in a private H2 database per cache, with a simple one-table schema that stores each entry in a table row as a pair of serialized objects (key and value). Database reads are gated by a BloomFilter, to reduce the number of calls made to H2 during cache misses. In theory less than 3% of cache misses will reach H2 and find nothing. Stores happen on a background thread quickly after the put is made to the cache, reducing the risk that a diff or web_session record is lost during an ungraceful shutdown. Cache databases are capped around 128M worth of stored data by running a prune cycle each day at 1 AM local server time. Records are removed from the database by ordering on the last access time, where last accessed is the last time the record was moved from disk to memory. Change-Id: Ia82d056796b5af9bcb1f219fe06d905c9c0fbc84
This commit is contained in:
@@ -26,9 +26,9 @@ import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.server.cache.Cache;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.inject.Inject;
|
||||
@@ -55,11 +55,11 @@ class WebSessionManager {
|
||||
|
||||
private final long sessionMaxAgeMillis;
|
||||
private final SecureRandom prng;
|
||||
private final Cache<Key, Val> self;
|
||||
private final Cache<String, Val> self;
|
||||
|
||||
@Inject
|
||||
WebSessionManager(@GerritServerConfig Config cfg,
|
||||
@Named(CACHE_NAME) final Cache<Key, Val> cache) {
|
||||
@Named(CACHE_NAME) final Cache<String, Val> cache) {
|
||||
prng = new SecureRandom();
|
||||
self = cache;
|
||||
|
||||
@@ -76,7 +76,7 @@ class WebSessionManager {
|
||||
prng.nextBytes(rnd);
|
||||
|
||||
buf = new ByteArrayOutputStream(3 + nonceLen);
|
||||
writeVarInt32(buf, (int) Key.serialVersionUID);
|
||||
writeVarInt32(buf, (int) Val.serialVersionUID);
|
||||
writeVarInt32(buf, who.get());
|
||||
writeBytes(buf, rnd);
|
||||
|
||||
@@ -120,7 +120,7 @@ class WebSessionManager {
|
||||
|
||||
Val val = new Val(who, refreshCookieAt, remember,
|
||||
lastLogin, xsrfToken, expiresAt);
|
||||
self.put(key, val);
|
||||
self.put(key.token, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -141,21 +141,19 @@ class WebSessionManager {
|
||||
}
|
||||
|
||||
Val get(final Key key) {
|
||||
Val val = self.get(key);
|
||||
Val val = self.getIfPresent(key.token);
|
||||
if (val != null && val.expiresAt <= now()) {
|
||||
self.remove(key);
|
||||
self.invalidate(key.token);
|
||||
return null;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void destroy(final Key key) {
|
||||
self.remove(key);
|
||||
self.invalidate(key.token);
|
||||
}
|
||||
|
||||
static final class Key implements Serializable {
|
||||
static final long serialVersionUID = 2L;
|
||||
|
||||
static final class Key {
|
||||
private transient String token;
|
||||
|
||||
Key(final String t) {
|
||||
@@ -175,18 +173,10 @@ class WebSessionManager {
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Key && token.equals(((Key) obj).token);
|
||||
}
|
||||
|
||||
private void writeObject(final ObjectOutputStream out) throws IOException {
|
||||
writeString(out, token);
|
||||
}
|
||||
|
||||
private void readObject(final ObjectInputStream in) throws IOException {
|
||||
token = readString(in);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Val implements Serializable {
|
||||
static final long serialVersionUID = Key.serialVersionUID;
|
||||
static final long serialVersionUID = 2L;
|
||||
|
||||
private transient Account.Id accountId;
|
||||
private transient long refreshCookieAt;
|
||||
|
||||
Reference in New Issue
Block a user