> I think that would be too limiting. I would prefer to be able to use caches > with both behavoirs/lifecycles in the same context.
Could you please elaborate on use cases? > The choice of the scope or lifetime of the cache should be at the query level > rather than the context level. But your current change does not do it at the query level. Both caches are the properties of the context, not query. I am not (yet) worried about the unit tests. Those will become important once we figure out a design. But I'd like this feature to be conceptually clear to the users. When I am teaching Cayenne classes or just having an informal conversation about it, I always feel that our cache "story" is too complicated. I can show various knobs, but I often have hard time explaining when to use them and in what combination to solve a given problem. We already have LOCAL vs SHARED cache. Having another type of LOCAL cache is going to cause someone's head to explode :) So trying to figure out where the new feature fits in, and how to avoid further complicating the cache model. So maybe let's take a look at the use cases as I mentioned above to see why per-query switching of the physical caches is important, and then brainstorm on how to best accomplish it. > I'm out of the office until Monday, so I can't work on it until then, but > we can keep discussing beforehand. Appreciate that! Andrus > On Jul 4, 2019, at 5:50 PM, John Huss <johnth...@gmail.com> wrote: > > On Thu, Jul 4, 2019 at 2:45 AM Andrus Adamchik <and...@objectstyle.org> > wrote: > >>> My intention was to separate the local and shared query caches while not >>> eliminating either, so that is the reason both are used. So if you have >>> explicitly set a custom localQueryCache then calling getQueryCache() will >>> allow you to still access the *shared* query cache as well. >> >> But this is called by the framework, not the user. And from what I see, >> some of Cayenne code calls the former, and other - the later. >> >> I general I am thinking we should make the type of per-context QueryCache >> a property of the entire runtime (it is either a segment of the main cache, >> or a separate per-context object ... either one or the other for all >> contexts). Is this too limiting? >> > > I think that would be too limiting. I would prefer to be able to use caches > with both behavoirs/lifecycles in the same context. The choice of the scope > or lifetime of the cache should be at the query level rather than the > context level. > > I realize now this commit didn't have unit tests to target the new > behaviors when the localQueryCache is set. I have some tests for it in my > application, but those didn't make it into Cayenne. And I need to write > some more to check the behavior of the shared query cache when the > localQueryCache is set. > > I'm out of the office until Monday, so I can't work on it until then, but > we can keep discussing beforehand. Thanks. > >> On Jul 3, 2019, at 5:34 PM, John Huss <johnth...@gmail.com> wrote: >>> >>> On Wed, Jul 3, 2019 at 5:33 AM Andrus Adamchik <and...@objectstyle.org> >>> wrote: >>> >>>> Hi John, >>>> >>>> I think I understand where you are going with this feature. As we >>>> discussed before, sometimes keeping a context cache as a transparent >> region >>>> of the main cache is undesirable, e.g. for memory management reasons. >> But I >>>> have some questions about the implementation committed to "master": >>>> >>>> 1. The commit seems incomplete - sometimes we use the new >>>> "getLocalQueryCache()", sometimes - the old "getQueryCache()". >>>> >>> >>> My intention was to separate the local and shared query caches while not >>> eliminating either, so that is the reason both are used. So if you have >>> explicitly set a custom localQueryCache then calling getQueryCache() will >>> allow you to still access the *shared* query cache as well. >>> >>> >>> >>>> 2. Having both "queryCache" and "localQueryCache" as ivars of >> BaseContext >>>> may be confusing. What do you think of moving cache type selection >> logic in >>>> ObjectContextFactory? >>>> >>> >>> Do you mean exposing this in the API of the interface? Currently I am >>> configuring this with a DataContextFactory subclass like this: >>> >>> *public* *static* *class* Factory *extends* DataContextFactory { >>> >>> @Override >>> >>> *protected* DataContext newInstance(DataChannel parent, ObjectStore >>> objectStore) { >>> >>> *return* *new* IcsDataContext(parent, objectStore); >>> >>> } >>> >>> @Override >>> >>> *protected* ObjectContext createdFromDataDomain(DataDomain parent) { >>> >>> DataContext result = (DataContext) *super*.createdFromDataDomain(parent); >>> >>> result.setLocalQueryCache(*new* MapQueryCache(Integer.*MAX_VALUE*)); // >> use >>> separate unbounded cache whose lifetime is bound to the ObjectContext >> itself >>> >>> *return* result; >>> >>> } >>> >>> @Override >>> >>> *protected* ObjectContext createFromDataContext(DataContext parent) { >>> >>> DataContext result = (DataContext) *super*.createFromDataContext(parent); >>> >>> result.setLocalQueryCache(*new* MapQueryCache(Integer.*MAX_VALUE*)); >>> >>> *return* result; >>> >>> } >>> >>> @Override >>> >>> *protected* ObjectContext createFromGenericChannel(DataChannel parent) { >>> >>> DataContext result = (DataContext) >> *super*.createFromGenericChannel(parent); >>> >>> result.setLocalQueryCache(*new* MapQueryCache(Integer.*MAX_VALUE*)); >>> >>> *return* result; >>> >>> } >>> >>> } >>> >>> I'm open to other ways of configuring this if you have ideas, perhaps >> like >>> using DI. Thanks for your feedback. >>> >>> >>> >>>> >>>> Thanks, >>>> Andrus >>>> >>>> >>>>> On Jul 1, 2019, at 5:53 PM, johnth...@apache.org wrote: >>>>> >>>>> This is an automated email from the ASF dual-hosted git repository. >>>>> >>>>> johnthuss pushed a commit to branch master >>>>> in repository https://gitbox.apache.org/repos/asf/cayenne.git >>>>> >>>>> >>>>> The following commit(s) were added to refs/heads/master by this push: >>>>> new 597376a CAY-2589 Allow optionally using a local query cache >>>> that is separate from the shared query cache. >>>>> 597376a is described below >>>>> >>>>> commit 597376ae558b9a0c5ddc4390da188b9530204e3d >>>>> Author: John Huss <johnth...@apache.org> >>>>> AuthorDate: Mon Jun 3 16:57:20 2019 -0500 >>>>> >>>>> CAY-2589 Allow optionally using a local query cache that is separate >>>> from the shared query cache. >>>>> >>>>> The local query cache can be customized by creating a custom >>>> DataContextFactory. >>>>> >>>>> A separate cache can prevent memory leaks from occurring if you used >>>> non-expiring cache >>>>> groups (like the default cache group perhaps) along with the local >>>> cache, which wasn't >>>>> intuitive if you expected the lifetime of the cache to match the >>>> lifetime of the ObjectContext. >>>>> --- >>>>> RELEASE-NOTES.txt | 1 + >>>>> .../main/java/org/apache/cayenne/BaseContext.java | 27 >>>> +++++++++++++++++++++- >>>>> .../cayenne/util/ObjectContextQueryAction.java | 9 +++++++- >>>>> 3 files changed, 35 insertions(+), 2 deletions(-) >>>>> >>>>> diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt >>>>> index 6f45986..3aa2fa8 100644 >>>>> --- a/RELEASE-NOTES.txt >>>>> +++ b/RELEASE-NOTES.txt >>>>> @@ -36,6 +36,7 @@ CAY-2569 Custom 'Naming Strategy' in Cayenne Modeler >>>>> CAY-2570 Use MySQL adapter for latest versions of MariaDB >>>>> CAY-2579 Review and possibly relax usage of readonly flag of >>>> ObjRelationship >>>>> CAY-2585 Rename scalarQuery and params methods in SQLSelect >>>>> +CAY-2589 - Allow optionally using a local query cache that is separate >>>> from the shared query cache. >>>>> >>>>> Bug Fixes: >>>>> >>>>> diff --git >>>> a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java >>>> b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java >>>>> index 981db73..ffae953 100644 >>>>> --- a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java >>>>> +++ b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java >>>>> @@ -98,6 +98,7 @@ public abstract class BaseContext implements >>>> ObjectContext { >>>>> // registry >>>>> protected transient DataChannel channel; >>>>> protected transient QueryCache queryCache; >>>>> + protected transient QueryCache localQueryCache; >>>>> protected transient EntityResolver entityResolver; >>>>> >>>>> protected boolean validatingObjectsOnCommit = true; >>>>> @@ -469,17 +470,41 @@ public abstract class BaseContext implements >>>> ObjectContext { >>>>> @Override >>>>> public abstract Collection<?> uncommittedObjects(); >>>>> >>>>> + /** >>>>> + * Used for storing cached query results available to all >>>> ObjectContexts. >>>>> + */ >>>>> public QueryCache getQueryCache() { >>>>> attachToRuntimeIfNeeded(); >>>>> return queryCache; >>>>> } >>>>> >>>>> /** >>>>> - * Sets a QueryCache to be used for storing cached query results. >>>>> + * Sets a QueryCache to be used for storing cached query results >>>> available to all ObjectContexts. >>>>> */ >>>>> public void setQueryCache(QueryCache queryCache) { >>>>> this.queryCache = queryCache; >>>>> } >>>>> + >>>>> + /** >>>>> + * Used for storing cached query results available only to this >>>> ObjectContext. >>>>> + * By default the local query cache and the shared query cache >>>> will use the same underlying storage. >>>>> + * >>>>> + * @since 4.2 >>>>> + */ >>>>> + public QueryCache getLocalQueryCache() { >>>>> + attachToRuntimeIfNeeded(); >>>>> + return localQueryCache != null ? localQueryCache : >>>> getQueryCache(); >>>>> + } >>>>> + >>>>> + /** >>>>> + * Sets a QueryCache to be used for storing cached query results >>>> available only to this ObjectContext. >>>>> + * By default the local query cache and the shared query cache >>>> will use the same underlying storage. >>>>> + * >>>>> + * @since 4.2 >>>>> + */ >>>>> + public void setLocalQueryCache(QueryCache queryCache) { >>>>> + this.localQueryCache = queryCache; >>>>> + } >>>>> >>>>> /** >>>>> * Returns EventManager associated with the ObjectStore. >>>>> diff --git >>>> >> a/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java >>>> >> b/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java >>>>> index 75dbdfa..acef54e 100644 >>>>> --- >>>> >> a/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java >>>>> +++ >>>> >> b/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java >>>>> @@ -360,7 +360,7 @@ public abstract class ObjectContextQueryAction { >>>>> return !DONE; >>>>> } >>>>> >>>>> - QueryCache queryCache = getQueryCache(); >>>>> + QueryCache queryCache = getLocalQueryCache(); >>>>> QueryCacheEntryFactory factory = getCacheObjectFactory(); >>>>> >>>>> if (cache) { >>>>> @@ -385,6 +385,13 @@ public abstract class ObjectContextQueryAction { >>>>> protected QueryCache getQueryCache() { >>>>> return ((BaseContext) actingContext).getQueryCache(); >>>>> } >>>>> + >>>>> + /** >>>>> + * @since 4.2 >>>>> + */ >>>>> + protected QueryCache getLocalQueryCache() { >>>>> + return ((BaseContext) actingContext).getLocalQueryCache(); >>>>> + } >>>>> >>>>> /** >>>>> * @since 3.0 >>>>> >>>> >>>> >> >>