Allow for distinguishing long-term caching from brief caching. For example, a public list of high scores may well lag 5 minutes behind database updates, whereas responses in a forum thread should perhaps never be more than 10 seconds out of date. The exact numbers are up to to the ehcache.xml configuration.

This commit is contained in:
Par Winzell
2009-02-06 16:09:10 +00:00
parent ce03952b3c
commit bb510ca506
4 changed files with 50 additions and 19 deletions
@@ -44,7 +44,7 @@ import com.samskivert.depot.impl.FindAllQuery;
*/
public interface CacheAdapter
{
public enum CacheCategory { RECORD, KEYSET, RESULT };
public enum CacheCategory { RECORD, SHORT_KEYSET, LONG_KEYSET, RESULT };
/** The encapsulated result of a cache lookup. */
public interface CachedValue<T>
@@ -93,10 +93,25 @@ public abstract class DepotRepository
* there is no invalidation of the keyset query: If records are inserted, deleted or
* modified, cached keysets will not be updated.
*
* Keysets cached using this strategy should have a short time-to-live.
*
* Note: This strategy may not be used on @Computed records, for records that do not in
* fact have a primary key, or for queries that use @FieldOverrides.
*/
KEYS,
SHORT_KEYS,
/**
* This strategy is identical to {@link #RECORDS}, but we also cache the keyset fetched
* in the first pass. This makes it much more efficient, but also less reliable because
* there is no invalidation of the keyset query: If records are inserted, deleted or
* modified, cached keysets will not be updated.
*
* Keysets cached using this strategy may have a long time-to-live.
*
* Note: This strategy may not be used on @Computed records, for records that do not in
* fact have a primary key, or for queries that use @FieldOverrides.
*/
LONG_KEYS,
/**
* This cache strategy is direct and explicit, eschewing the dual phases of the
@@ -363,7 +378,7 @@ public abstract class DepotRepository
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
switch (cache) {
case KEYS: case BEST: case RECORDS:
case LONG_KEYS: case SHORT_KEYS: case BEST: case RECORDS:
String reason = null;
if (marsh.getTableName() == null) {
reason = type + " is computed";
@@ -380,7 +395,7 @@ public abstract class DepotRepository
}
}
if (cache == CacheStrategy.BEST) {
cache = (reason != null) ? CacheStrategy.NONE : CacheStrategy.KEYS;
cache = (reason != null) ? CacheStrategy.NONE : CacheStrategy.SHORT_KEYS;
} else if (reason != null) {
// if user explicitly asked for a strategy we can't do, protest
@@ -393,13 +408,15 @@ public abstract class DepotRepository
cache = CacheStrategy.NONE;
}
if (cache == CacheStrategy.KEYS || cache == CacheStrategy.RECORDS) {
return _ctx.invoke(new FindAllQuery.WithCache<T>(
_ctx, type, clauses, cache == CacheStrategy.KEYS));
}
switch(cache) {
case SHORT_KEYS: case LONG_KEYS: case RECORDS:
return _ctx.invoke(new FindAllQuery.WithCache<T>(_ctx, type, clauses, cache));
default:
return _ctx.invoke(new FindAllQuery.Explicitly<T>(
_ctx, type, clauses, cache == CacheStrategy.CONTENTS));
}
}
/**
* Looks up and returns {@link Key} records for all rows that match the supplied query clauses.
@@ -42,16 +42,17 @@ import static com.samskivert.depot.Log.log;
* combinations within one category is stuffed into the same {@link Ehcache}, and all elements are
* cached under {@link EHCacheKey}, which basically wraps just such a tuple.
*
* Thus there are currently only three Ehcaches in play, called 'depotRecord', 'depotKeyset', and
* 'depotResult'. These must be defined in your ehcache.xml configuration. If you use distributed
* replication/invalidation, you should replicate updates and removes but not puts nor
* updates-via-copy.
* Thus there are currently only four Ehcaches in play, called 'depotRecord', 'depotLongKeyset',
* 'depotShortKeyset' and 'depotResult'. These must be defined in your ehcache.xml configuration.
* If you use distributed replication/invalidation, you should replicate updates and removes but
* not puts nor updates-via-copy.
*/
public class EHCacheAdapter
implements CacheAdapter
{
public static final String EHCACHE_RECORD_CACHE = "depotRecord";
public static final String EHCACHE_KEYSET_CACHE = "depotKeyset";
public static final String EHCACHE_SHORT_KEYSET_CACHE = "depotShortKeyset";
public static final String EHCACHE_LONG_KEYSET_CACHE = "depotLongKeyset";
public static final String EHCACHE_RESULT_CACHE = "depotResult";
public static class EHCachePerformance
@@ -70,7 +71,8 @@ public class EHCacheAdapter
public EHCacheAdapter (CacheManager cachemgr)
{
bindEHCache(cachemgr, CacheCategory.RECORD, EHCACHE_RECORD_CACHE);
bindEHCache(cachemgr, CacheCategory.KEYSET, EHCACHE_KEYSET_CACHE);
bindEHCache(cachemgr, CacheCategory.SHORT_KEYSET, EHCACHE_SHORT_KEYSET_CACHE);
bindEHCache(cachemgr, CacheCategory.LONG_KEYSET, EHCACHE_LONG_KEYSET_CACHE);
bindEHCache(cachemgr, CacheCategory.RESULT, EHCACHE_RESULT_CACHE);
}
@@ -47,6 +47,7 @@ import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.SimpleCacheKey;
import com.samskivert.depot.Stats;
import com.samskivert.depot.CacheAdapter.CacheCategory;
import com.samskivert.depot.DepotRepository.CacheStrategy;
import com.samskivert.depot.clause.FieldOverride;
import com.samskivert.depot.clause.QueryClause;
import com.samskivert.depot.clause.SelectClause;
@@ -67,7 +68,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
public static class WithCache<T extends PersistentRecord> extends FindAllQuery<T>
{
public WithCache (PersistenceContext ctx, Class<T> type,
Collection<? extends QueryClause> clauses, boolean cacheKeys)
Collection<? extends QueryClause> clauses, CacheStrategy strategy)
throws DatabaseException
{
super(ctx, type);
@@ -84,11 +85,21 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
}
_select = new SelectClause<T>(_type, _marsh.getPrimaryKeyFields(), clauses);
if (cacheKeys) {
switch(strategy) {
case SHORT_KEYS: case LONG_KEYS:
_qkey = new SimpleCacheKey(_marsh.getTableName() + "Keys", _select.toString());
} else {
_category = (strategy == CacheStrategy.SHORT_KEYS) ?
CacheCategory.SHORT_KEYSET : CacheCategory.LONG_KEYSET;
break;
case RECORDS:
_qkey = null;
break;
default:
throw new IllegalArgumentException("Unexpected cache strategy: " + strategy);
}
}
@Override // from Query
@@ -136,7 +147,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
}
if (_qkey != null) {
// cache the resulting key set
ctx.cacheStore(CacheCategory.KEYSET, _qkey, _keys);
ctx.cacheStore(_category, _qkey, _keys);
}
// and fetch any records we can from the cache
_fetchKeys = loadFromCache(ctx, _keys, _entities);
@@ -147,6 +158,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<Lis
}
protected SimpleCacheKey _qkey;
protected CacheCategory _category;
protected SelectClause<T> _select;
protected KeySet<T> _keys;
protected Set<Key<T>> _fetchKeys;