From 60c681ff749f276c1ce99c4ec835668fbcf205bd Mon Sep 17 00:00:00 2001 From: Par Winzell Date: Thu, 8 Jan 2009 18:22:40 +0000 Subject: [PATCH] Introduce a new cache strategy that does collection queries in two stages, but does not cache the keyset. This is a safe strategy, and we use it in places where an out-of-date result set is dangerous or annoying. --- .../com/samskivert/depot/DepotRepository.java | 56 +++++++++++-------- .../samskivert/depot/impl/FindAllQuery.java | 10 +++- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/java/com/samskivert/depot/DepotRepository.java b/src/java/com/samskivert/depot/DepotRepository.java index 0cbc65e..753e2f2 100644 --- a/src/java/com/samskivert/depot/DepotRepository.java +++ b/src/java/com/samskivert/depot/DepotRepository.java @@ -78,32 +78,37 @@ public abstract class DepotRepository /** * Resolve this collection query in two steps: first we enumerate the primary keys for - * all the records that satisfy the query, then we acquire the actual requested data for - * each key. The cache is consulted and updated in both steps: for the first, see if we've - * already executed precisely this query and cached the resulting key set. For the second - * phase, search the cache for each key in said set, and retrieve the corresponding data - * record from there when possible. Finally execute a database query to retrieve the data - * for any keys that were not in the cache, making sure to cache them in the process. + * all the records that satisfy the query, then we acquire the actual data corresponding + * to each key -- first by consulting the cache, and then only loading from the database + * the records for the keys that were not located in the cache. * - * Note: There is currently no cache invalidation of collection queries. If records are - * inserted, deleted or modified, cached keysets will not be updated. The keyset cannot be - * guaranteed to be up to date. For sensitive operations, use Cache.NONE. + * 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. + */ + RECORDS, + + /** + * 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. * - * Note: The KEYS 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. + * 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, /** - * This cache strategy is direct and explicit, eschewing the dual phases of the KEYS - * approach. However, before the database is invoked at all, we consult the cache hoping - * to find the entire result set already stashed away in there, using the entire query - * as the key. If we failed to find it, we execute the query and update the cache with the - * result. + * This cache strategy is direct and explicit, eschewing the dual phases of the + * {@link #RECORDS} and {@link #KEYS} approaches. However, before the database is invoked + * at all, we consult the cache hoping to find the entire result set already stashed away + * in there, using the entire query as the key. If we failed to find it, we execute the + * query and update the cache with the result. * - * This strategy has none of the limitations of KEYS and can be used with key-less and - * @Computed records and arbitrarily complicated queries. Note however that as with KEYS, - * there is currently no automatic invalidation and it is potentially very memory intensive. + * This strategy has none of the limitations of {@link #KEYS} and can be used with key-less + * and @Computed records and arbitrarily complicated queries. Note however that as with + * {@link #KEYS}, there is no automatic invalidation. It is also potentially very memory + * intensive. */ CONTENTS }; @@ -345,7 +350,8 @@ public abstract class DepotRepository { DepotMarshaller marsh = _ctx.getMarshaller(type); - if (cache == CacheStrategy.KEYS || cache == CacheStrategy.BEST) { + switch (cache) { + case KEYS: case BEST: case RECORDS: String reason = null; if (marsh.getTableName() == null) { reason = type + " is computed"; @@ -365,8 +371,9 @@ public abstract class DepotRepository cache = (reason != null) ? CacheStrategy.NONE : CacheStrategy.KEYS; } else if (reason != null) { - // if user explicitly asked for the KEYS strategy and we can't do it, protest - throw new IllegalArgumentException("Cannot use KEYS strategy because " + reason); + // if user explicitly asked for a strategy we can't do, protest + throw new IllegalArgumentException( + "Cannot use " + cache + " strategy because " + reason); } } @@ -374,8 +381,9 @@ public abstract class DepotRepository cache = CacheStrategy.NONE; } - if (cache == CacheStrategy.KEYS) { - return _ctx.invoke(new FindAllQuery.WithCache(_ctx, type, clauses)); + if (cache == CacheStrategy.KEYS || cache == CacheStrategy.RECORDS) { + return _ctx.invoke(new FindAllQuery.WithCache( + _ctx, type, clauses, cache == CacheStrategy.KEYS)); } return _ctx.invoke(new FindAllQuery.Explicitly( _ctx, type, clauses, cache == CacheStrategy.CONTENTS)); diff --git a/src/java/com/samskivert/depot/impl/FindAllQuery.java b/src/java/com/samskivert/depot/impl/FindAllQuery.java index 7ca98a1..25d5980 100644 --- a/src/java/com/samskivert/depot/impl/FindAllQuery.java +++ b/src/java/com/samskivert/depot/impl/FindAllQuery.java @@ -66,7 +66,7 @@ public abstract class FindAllQuery extends Query extends FindAllQuery { public WithCache (PersistenceContext ctx, Class type, - Collection clauses) + Collection clauses, boolean cacheKeys) throws DatabaseException { super(ctx, type); @@ -83,7 +83,11 @@ public abstract class FindAllQuery extends Query(_type, _marsh.getPrimaryKeyFields(), clauses); - _qkey = new SimpleCacheKey(_marsh.getTableName() + "Keys", _select.toString()); + if (cacheKeys) { + _qkey = new SimpleCacheKey(_marsh.getTableName() + "Keys", _select.toString()); + } else { + _qkey = null; + } } @Override // from Query @@ -127,7 +131,7 @@ public abstract class FindAllQuery extends Query