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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user