The progress of the query cache

Dec 13, 2011 at 8:25 AM

Can you tell me what's the progress of the implementation of the query cache? i'm really looking forward to this! is there a way i can implement this already?

Coordinator
Dec 13, 2011 at 2:06 PM

Znowman - The status is that there are a handful of difficulties with this that I haven't been able to spend a significant time working on.  Not only that, but the way in which we create one-to-many and many-to-many relationships in orchard (using LazyFields) directly interferes with the nhibernate caching process, as NHibernate really wants to be in charge of the entire lifecycle of object loading, including lazy loading of child collections.  

I've documented some of the issues with the current implementation in this thread: http://orchard.codeplex.com/discussions/274921.  I'll copy and paste the body of those comments in this thread for posterity's sake. 

When you ask if there is a way that you can implement this already, are you offering to contribute?  The issues we're facing require some in-depth understanding of NHibernate, Fluent NHibernate, and the way in which orchard creates the dynamic mappings, but I would love for someone to help out as I don't have the time.  I'd be happy to provide any assistance you might need if you are up for the task.  Just let me know.

Coordinator
Dec 13, 2011 at 2:08 PM

Copied from the Performance Feature Team thread:

Sebastien reached out to me via email to get my input on the second level caching, and I'm responding on here so that it's included in this discussion.

My first point to make is that I need some clarification as to what you mean when you say second level caching.  I'm assuming you mean both second level caching and query caching.  The major difference is that second level caching only will affect objects that are loaded by ID, so in orchard's instance it would be when you call _someRepository.Get(someId);  This is implemented fully by the module I have in the gallery, and you can see the source and everything here.

As an overview of what had to be done, I implemented:

  • A fluent nhibernate convention that basically tells it to cache all the stuff that orchard knows about
  • An overridden AbstractDataServicesProvider that
    • Registers the cache convention
    • Does a hack that apparently works around the unused ContentPartRecord proxies on ContentItemRecord or ContentItemVersionRecord.  This has cause an intermittent issue that I've seen when the appdomain reloads.  This should be investigated further.  I believe Louis said that with NHib3, these hacks wouldn't be necessary, but I have no idea if that is the case.
  • An override for both of the database providers that configure them to use the syscache.

All that said, a few things should be looked at in terms of integrating this into the core.  1) The bug I mentioned with the unused proxies needs to be addressed.  It's been so long since I worked on this, that I don't even really remember what the exact issue was.  Also, the user should be able to enable/disable the cache, as well as select a different cache provider.  I haven't looked into the current state of caching with nhibernate 3, but I'm pretty sure the official source for providers is here.  The user should be able to select from the providers, and need to be able to provide configuration for any providers that require it (like memcached or velocity/appfabric).

Let me know if you have any questions regarding the second level cache.  I will do another post following this one regarding the query cache.

Coordinator
Dec 13, 2011 at 2:08 PM

Copied from the Performance Feature Team thread:

Beyond the second level cache, you have the query cache, which caches queries that go beyond "get by id".  In my experience, this has been difficult because NHibernate needs to be able to be smart about invalidating the cache when something changes, and I've had trouble with that.  That said, I believe due to the rigidity of the Orchard data model, it has seemed in my testing to be very good (perfect in fact) in terms of invalidating the cache when necessary.  So it would be fantastic to get working.  I've gotten it partially working with changes to the Orchard core, but I ran into significant issues getting it to work for every query, and haven't had the chance to come up with a solution. 

As far as tracking down why certain queries weren't getting cached, these are my best guesses:

  • I think the CacheConvention isn't getting applied to the objects that I'm having issues with.  If I remember correctly (and I probably don't), I was seeing issues with items that were records without a corresponding part.  For example, if you look at the docs on creating 1-N and N-N relationships, the StateRecord class does not have a corresponding part and does not inherit from ContentPartRecord.  I believe the CacheConvention would not get applied to this class.
  •  I also think that there's a problem with NHibernate not being aware of some of the relationships that are lazy loaded, but I'm not sure.  Firstly, I'm wondering if NHibernate is not really able to deduce the true relationship of the lazy-loaded items (by this I mean items that are marked LazyField<T>) because the types don't match up properly.  In addition, I think that the way that lazy loading is handled in orchard (at least in the examples I've seen with a delegate in the handler) is inherently going to be in conflict with NHibernate caching in general, as the delegates (I think) will preempt NHibernates native ability to lazy-load which takes advantage of the cache.  These are guesses, however I believe that they are issues that need to be addressed.

Here are the archives I made that include the modifications I made the the DBCache module as well as Orchard Core (this was prior to the 1.3 release).  Note that these just contain the modified files.

  • http://dl.dropbox.com/u/563147/querycache-Orchard.zip
  • http://dl.dropbox.com/u/563147/querycache-Contrib.DBCache.zip

Let me know if there are any questions regarding the query cache.