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:
Shawn O. Pearce
2012-05-24 14:28:40 -07:00
parent 34d4d1929a
commit 2e1cb2b849
76 changed files with 2432 additions and 1674 deletions

View File

@@ -1,35 +0,0 @@
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
/**
* A fast in-memory and/or on-disk based cache.
*
* @type <K> type of key used to lookup entries in the cache.
* @type <V> type of value stored within each cache entry.
*/
public interface Cache<K, V> {
/** Get the element from the cache, or null if not stored in the cache. */
public V get(K key);
/** Put one element into the cache, replacing any existing value. */
public void put(K key, V value);
/** Remove any existing value from the cache, no-op if not present. */
public void remove(K key);
/** Remove all cached items. */
public void removeAll();
}

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher;
import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/** Configure a cache declared within a {@link CacheModule} instance. */
public interface CacheBinding<K, V> {
/** Set the total size of the cache. */
CacheBinding<K, V> maximumWeight(long weight);
/** Set the time an element lives before being expired. */
CacheBinding<K, V> expireAfterWrite(long duration, TimeUnit durationUnits);
/** Populate the cache with items from the CacheLoader. */
CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz);
/** Algorithm to weigh an object with a method other than the unit weight 1. */
CacheBinding<K, V> weigher(Class<? extends Weigher<K, V>> clazz);
String name();
TypeLiteral<K> keyType();
TypeLiteral<V> valueType();
long maximumWeight();
@Nullable Long expireAfterWrite(TimeUnit unit);
@Nullable Weigher<K, V> weigher();
@Nullable CacheLoader<K, V> loader();
}

View File

@@ -14,33 +14,41 @@
package com.google.gerrit.server.cache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.Weigher;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.name.Names;
import com.google.inject.util.Types;
import java.io.Serializable;
import java.lang.reflect.Type;
/**
* Miniature DSL to support binding {@link Cache} instances in Guice.
*/
public abstract class CacheModule extends AbstractModule {
private static final TypeLiteral<Cache<?, ?>> ANY_CACHE =
new TypeLiteral<Cache<?, ?>>() {};
/**
* Declare an unnamed in-memory cache.
* Declare a named in-memory cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @param type type literal for the cache, this literal will be used to match
* injection sites.
* @return binding to describe the cache. Caller must set at least the name on
* the returned binding.
* @return binding to describe the cache.
*/
protected <K, V> UnnamedCacheBinding<K, V> core(
final TypeLiteral<Cache<K, V>> type) {
return core(Key.get(type));
protected <K, V> CacheBinding<K, V> cache(
String name,
Class<K> keyType,
Class<V> valType) {
return cache(name, TypeLiteral.get(keyType), TypeLiteral.get(valType));
}
/**
@@ -48,74 +56,127 @@ public abstract class CacheModule extends AbstractModule {
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @return binding to describe the cache.
*/
protected <K, V> NamedCacheBinding<K, V> core(
final TypeLiteral<Cache<K, V>> type, final String name) {
return core(Key.get(type, Names.named(name))).name(name);
}
private <K, V> UnnamedCacheBinding<K, V> core(final Key<Cache<K, V>> key) {
final boolean disk = false;
final CacheProvider<K, V> b = new CacheProvider<K, V>(disk, this);
bind(key).toProvider(b).in(Scopes.SINGLETON);
return b;
protected <K, V> CacheBinding<K, V> cache(
String name,
Class<K> keyType,
TypeLiteral<V> valType) {
return cache(name, TypeLiteral.get(keyType), valType);
}
/**
* Declare an unnamed in-memory/on-disk cache.
* Declare a named in-memory cache.
*
* @param <K> type of key used to find entries, must be {@link Serializable}.
* @param <V> type of value stored by the cache, must be {@link Serializable}.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @return binding to describe the cache. Caller must set at least the name on
* the returned binding.
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @return binding to describe the cache.
*/
protected <K extends Serializable, V extends Serializable> UnnamedCacheBinding<K, V> disk(
final TypeLiteral<Cache<K, V>> type) {
return disk(Key.get(type));
protected <K, V> CacheBinding<K, V> cache(
String name,
TypeLiteral<K> keyType,
TypeLiteral<V> valType) {
Type type = Types.newParameterizedType(
Cache.class,
keyType.getType(), valType.getType());
@SuppressWarnings("unchecked")
Key<Cache<K, V>> key = (Key<Cache<K, V>>) Key.get(type, Names.named(name));
CacheProvider<K, V> m =
new CacheProvider<K, V>(this, name, keyType, valType);
bind(key).toProvider(m).in(Scopes.SINGLETON);
bind(ANY_CACHE).annotatedWith(Exports.named(name)).to(key);
return m.maximumWeight(1024);
}
<K,V> Provider<CacheLoader<K,V>> bindCacheLoader(
CacheProvider<K, V> m,
Class<? extends CacheLoader<K,V>> impl) {
Type type = Types.newParameterizedType(
Cache.class,
m.keyType().getType(), m.valueType().getType());
Type loadingType = Types.newParameterizedType(
LoadingCache.class,
m.keyType().getType(), m.valueType().getType());
Type loaderType = Types.newParameterizedType(
CacheLoader.class,
m.keyType().getType(), m.valueType().getType());
@SuppressWarnings("unchecked")
Key<LoadingCache<K, V>> key =
(Key<LoadingCache<K, V>>) Key.get(type, Names.named(m.name));
@SuppressWarnings("unchecked")
Key<LoadingCache<K, V>> loadingKey =
(Key<LoadingCache<K, V>>) Key.get(loadingType, Names.named(m.name));
@SuppressWarnings("unchecked")
Key<CacheLoader<K, V>> loaderKey =
(Key<CacheLoader<K, V>>) Key.get(loaderType, Names.named(m.name));
bind(loaderKey).to(impl).in(Scopes.SINGLETON);
bind(loadingKey).to(key);
return getProvider(loaderKey);
}
<K,V> Provider<Weigher<K,V>> bindWeigher(
CacheProvider<K, V> m,
Class<? extends Weigher<K,V>> impl) {
Type weigherType = Types.newParameterizedType(
Weigher.class,
m.keyType().getType(), m.valueType().getType());
@SuppressWarnings("unchecked")
Key<Weigher<K, V>> key =
(Key<Weigher<K, V>>) Key.get(weigherType, Names.named(m.name));
bind(key).to(impl).in(Scopes.SINGLETON);
return getProvider(key);
}
/**
* Declare a named in-memory/on-disk cache.
*
* @param <K> type of key used to find entries, must be {@link Serializable}.
* @param <V> type of value stored by the cache, must be {@link Serializable}.
* @param type type literal for the cache, this literal will be used to match
* injection sites. Injection sites are matched by this type literal
* and with {@code @Named} annotations.
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @return binding to describe the cache.
*/
protected <K extends Serializable, V extends Serializable> NamedCacheBinding<K, V> disk(
final TypeLiteral<Cache<K, V>> type, final String name) {
return disk(Key.get(type, Names.named(name))).name(name);
protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
String name,
Class<K> keyType,
Class<V> valType) {
return persist(name, TypeLiteral.get(keyType), TypeLiteral.get(valType));
}
private <K, V> UnnamedCacheBinding<K, V> disk(final Key<Cache<K, V>> key) {
final boolean disk = true;
final CacheProvider<K, V> b = new CacheProvider<K, V>(disk, this);
bind(key).toProvider(b).in(Scopes.SINGLETON);
return b;
/**
* Declare a named in-memory/on-disk cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @return binding to describe the cache.
*/
protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
String name,
Class<K> keyType,
TypeLiteral<V> valType) {
return persist(name, TypeLiteral.get(keyType), valType);
}
<K, V> Provider<EntryCreator<K, V>> getEntryCreator(CacheProvider<K, V> cp,
Class<? extends EntryCreator<K, V>> type) {
Key<EntryCreator<K, V>> key = newKey();
bind(key).to(type).in(Scopes.SINGLETON);
return getProvider(key);
}
@SuppressWarnings("unchecked")
private static <K, V> Key<EntryCreator<K, V>> newKey() {
return (Key<EntryCreator<K, V>>) newKeyImpl();
}
private static Key<?> newKeyImpl() {
return Key.get(EntryCreator.class, UniqueAnnotations.create());
/**
* Declare a named in-memory/on-disk cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
* @return binding to describe the cache.
*/
protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
String name,
TypeLiteral<K> keyType,
TypeLiteral<V> valType) {
return ((CacheProvider<K, V>) cache(name, keyType, valType))
.persist(true);
}
}

View File

@@ -1,19 +0,0 @@
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
public interface CachePool {
public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 The Android Open Source Project
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,130 +14,156 @@
package com.google.gerrit.server.cache;
import static com.google.gerrit.server.cache.EvictionPolicy.LFU;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.Weigher;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit;
public final class CacheProvider<K, V> implements Provider<Cache<K, V>>,
NamedCacheBinding<K, V>, UnnamedCacheBinding<K, V> {
import javax.annotation.Nullable;
class CacheProvider<K, V>
implements Provider<Cache<K, V>>,
CacheBinding<K, V> {
private final CacheModule module;
private final boolean disk;
private int memoryLimit;
private int diskLimit;
private long maxAge;
private EvictionPolicy evictionPolicy;
private String cacheName;
private ProxyCache<K, V> cache;
private Provider<EntryCreator<K, V>> entryCreator;
final String name;
private final TypeLiteral<K> keyType;
private final TypeLiteral<V> valType;
private boolean persist;
private long maximumWeight;
private Long expireAfterWrite;
private Provider<CacheLoader<K, V>> loader;
private Provider<Weigher<K, V>> weigher;
CacheProvider(final boolean disk, CacheModule module) {
this.disk = disk;
private String plugin;
private MemoryCacheFactory memoryCacheFactory;
private PersistentCacheFactory persistentCacheFactory;
private boolean frozen;
CacheProvider(CacheModule module,
String name,
TypeLiteral<K> keyType,
TypeLiteral<V> valType) {
this.module = module;
this.name = name;
this.keyType = keyType;
this.valType = valType;
}
memoryLimit(1024);
maxAge(90, DAYS);
evictionPolicy(LFU);
if (disk) {
diskLimit(16384);
}
@Inject(optional = true)
void setPluginName(@PluginName String pluginName) {
this.plugin = pluginName;
}
@Inject
void setCachePool(final CachePool pool) {
this.cache = pool.register(this);
void setMemoryCacheFactory(MemoryCacheFactory factory) {
this.memoryCacheFactory = factory;
}
public void bind(Cache<K, V> impl) {
if (cache == null) {
throw new ProvisionException("Cache was never registered");
}
cache.bind(impl);
@Inject(optional = true)
void setPersistentCacheFactory(@Nullable PersistentCacheFactory factory) {
this.persistentCacheFactory = factory;
}
public EntryCreator<K, V> getEntryCreator() {
return entryCreator != null ? entryCreator.get() : null;
}
public String getName() {
if (cacheName == null) {
throw new ProvisionException("Cache has no name");
}
return cacheName;
}
public boolean disk() {
return disk;
}
public int memoryLimit() {
return memoryLimit;
}
public int diskLimit() {
return diskLimit;
}
public long maxAge() {
return maxAge;
}
public EvictionPolicy evictionPolicy() {
return evictionPolicy;
}
public NamedCacheBinding<K, V> name(final String name) {
if (cacheName != null) {
throw new IllegalStateException("Cache name already set");
}
cacheName = name;
return this;
}
public NamedCacheBinding<K, V> memoryLimit(final int objects) {
memoryLimit = objects;
return this;
}
public NamedCacheBinding<K, V> diskLimit(final int objects) {
if (!disk) {
// TODO This should really be a compile time type error, but I'm
// too lazy to create the mess of permutations required to setup
// type safe returns for bindings in our little DSL.
//
throw new IllegalStateException("Cache is not disk based");
}
diskLimit = objects;
return this;
}
public NamedCacheBinding<K, V> maxAge(final long duration, final TimeUnit unit) {
maxAge = SECONDS.convert(duration, unit);
CacheBinding<K, V> persist(boolean p) {
Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
persist = p;
return this;
}
@Override
public NamedCacheBinding<K, V> evictionPolicy(final EvictionPolicy policy) {
evictionPolicy = policy;
public CacheBinding<K, V> maximumWeight(long weight) {
Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
maximumWeight = weight;
return this;
}
public NamedCacheBinding<K, V> populateWith(
Class<? extends EntryCreator<K, V>> creator) {
entryCreator = module.getEntryCreator(this, creator);
@Override
public CacheBinding<K, V> expireAfterWrite(long duration, TimeUnit unit) {
Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
expireAfterWrite = SECONDS.convert(duration, unit);
return this;
}
public Cache<K, V> get() {
if (cache == null) {
throw new ProvisionException("Cache \"" + cacheName + "\" not available");
@Override
public CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> impl) {
Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
loader = module.bindCacheLoader(this, impl);
return this;
}
@Override
public CacheBinding<K, V> weigher(Class<? extends Weigher<K, V>> impl) {
Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
weigher = module.bindWeigher(this, impl);
return this;
}
@Override
public String name() {
if (!Strings.isNullOrEmpty(plugin)) {
return plugin + "." + name;
}
return name;
}
@Override
public TypeLiteral<K> keyType() {
return keyType;
}
@Override
public TypeLiteral<V> valueType() {
return valType;
}
@Override
public long maximumWeight() {
return maximumWeight;
}
@Override
@Nullable
public Long expireAfterWrite(TimeUnit unit) {
return expireAfterWrite != null
? unit.convert(expireAfterWrite, SECONDS)
: null;
}
@Override
@Nullable
public Weigher<K, V> weigher() {
return weigher != null ? weigher.get() : null;
}
@Override
@Nullable
public CacheLoader<K, V> loader() {
return loader != null ? loader.get() : null;
}
@Override
public Cache<K, V> get() {
frozen = true;
if (loader != null) {
CacheLoader<K, V> ldr = loader.get();
if (persist && persistentCacheFactory != null) {
return persistentCacheFactory.build(this, ldr);
}
return memoryCacheFactory.build(this, ldr);
} else if (persist && persistentCacheFactory != null) {
return persistentCacheFactory.build(this);
} else {
return memoryCacheFactory.build(this);
}
return cache;
}
}

View File

@@ -1,48 +0,0 @@
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.package com.google.gerrit.server.git;
package com.google.gerrit.server.cache;
import java.util.concurrent.ConcurrentHashMap;
/**
* An infinitely sized cache backed by java.util.ConcurrentHashMap.
* <p>
* This cache type is only suitable for unit tests, as it has no upper limit on
* number of items held in the cache. No upper limit can result in memory leaks
* in production servers.
*/
public class ConcurrentHashMapCache<K, V> implements Cache<K, V> {
private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();
@Override
public V get(K key) {
return map.get(key);
}
@Override
public void put(K key, V value) {
map.put(key, value);
}
@Override
public void remove(K key) {
map.remove(key);
}
@Override
public void removeAll() {
map.clear();
}
}

View File

@@ -1,40 +0,0 @@
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
/**
* Creates a cache entry on demand when its not found.
*
* @param <K> type of the cache's key.
* @param <V> type of the cache's value element.
*/
public abstract class EntryCreator<K, V> {
/**
* Invoked on a cache miss, to compute the cache entry.
*
* @param key entry whose content needs to be obtained.
* @return new cache content. The caller will automatically put this object
* into the cache.
* @throws Exception the cache content cannot be computed. No entry will be
* stored in the cache, and {@link #missing(Object)} will be invoked
* instead. Future requests for the same key will retry this method.
*/
public abstract V createEntry(K key) throws Exception;
/** Invoked when {@link #createEntry(Object)} fails, by default return null. */
public V missing(K key) {
return null;
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 The Android Open Source Project
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,9 +14,14 @@
package com.google.gerrit.server.cache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/** Configure a cache declared within a {@link CacheModule} instance. */
public interface UnnamedCacheBinding<K, V> {
/** Set the name of the cache. */
public NamedCacheBinding<K, V> name(String cacheName);
public interface MemoryCacheFactory {
<K, V> Cache<K, V> build(CacheBinding<K, V> def);
<K, V> LoadingCache<K, V> build(
CacheBinding<K, V> def,
CacheLoader<K, V> loader);
}

View File

@@ -1,35 +0,0 @@
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
import java.util.concurrent.TimeUnit;
/** Configure a cache declared within a {@link CacheModule} instance. */
public interface NamedCacheBinding<K, V> {
/** Set the number of objects to cache in memory. */
public NamedCacheBinding<K, V> memoryLimit(int objects);
/** Set the number of objects to cache in memory. */
public NamedCacheBinding<K, V> diskLimit(int objects);
/** Set the time an element lives before being expired. */
public NamedCacheBinding<K, V> maxAge(long duration, TimeUnit durationUnits);
/** Set the eviction policy for elements when the cache is full. */
public NamedCacheBinding<K, V> evictionPolicy(EvictionPolicy policy);
/** Populate the cache with items from the EntryCreator. */
public NamedCacheBinding<K, V> populateWith(Class<? extends EntryCreator<K, V>> creator);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 The Android Open Source Project
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
package com.google.gerrit.server.cache;
/** How entries should be evicted from the cache. */
public enum EvictionPolicy {
/** Least recently used is evicted first. */
LRU,
import com.google.common.cache.Cache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/** Least frequently used is evicted first. */
LFU;
public interface PersistentCacheFactory {
<K, V> Cache<K, V> build(CacheBinding<K, V> def);
<K, V> LoadingCache<K, V> build(
CacheBinding<K, V> def,
CacheLoader<K, V> loader);
}

View File

@@ -1,40 +0,0 @@
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.cache;
/** Proxy around a cache which has not yet been created. */
public final class ProxyCache<K, V> implements Cache<K, V> {
private volatile Cache<K, V> self;
public void bind(Cache<K, V> self) {
this.self = self;
}
public V get(K key) {
return self.get(key);
}
public void put(K key, V value) {
self.put(key, value);
}
public void remove(K key) {
self.remove(key);
}
public void removeAll() {
self.removeAll();
}
}