For my own understanding I decided to try and grasp the various forms of Orchard caching and how they relate to one another and to the underlying platform. After spending a few hours digging through blog posts and source code, I decided to write down my conclusions.
I'd like to know, does this correspond to your understanding? Have I interpreted the caching landscape correctly? Any feedback would be very much appreciated!
First of all, please take a look at the diagram: Orchard caching architecture
Orchard has two built-in forms of caching: application caching and nHibernate second-level caching:
- Application caching is part of the core, and it is used by Orchard Core and all modules that have a caching need. Storage is provided by the abstration ICacheHolder. There is a default implementation that simply stores cached data in-memory.
nHibernate second-level caching is enabled by the SysCache module, which configures nHibernate to use the SysCache2 provider for its second-level cache. The SysCache2 provider uses ASP.NET Application Caching under the hood.
Additionally, a third form of caching can be enabled by the Contrib.Cache module written by Sebastien. This module adds an Orchard equivalent of ASP.NET Output Caching. Storage is provided by the abstraction IOutputCacheStorageProvider. There is a default implementation shipped with the module that uses ASP.NET Application Cache under the hood.
ASP.NET provides two forms of caching: output caching and application caching. As mentioned, ASP.NET Application Caching is used by default for both nHibernate second-level caching and Orchard output caching. ASP.NET Output Caching however is never used in an Orchard site.
In the default caching scheme outlined above, all cached data eventually ends up in local memory, which is less than ideal if you’re running your site in a web farm. In such a scenario it is desirable to enable some sort of distributed cache. To enable distributed caching scenarios, there is the Contrib.Cache.Memcached module, also written by Sebastien. This module provides two new implementations of ICacheHolder and IOutputCacheStorageProvider respectively. The former is actually used by the latter, and effectively makes sure that all Orchard output caching and application caching is done in a Memcached service of your choice (this can be configured in the site settings for the Contrib.Cache.Memcached module).
One implementation of Memcached is Windows Azure Cache, which can be enabled in an Azure Cloud Service. Windows Azure Cache provides a Memcached shim that is installed locally in your role instances. This means you can configure the Contrib.Cache.Memcached module to work against a Memcached service running on localhost. The local Memcached shim integrates with the managed Windows Azure Cache client assembly, which in turn talks to the Windows Azure Cache service.
(As a side note, Windows Azure Cache is also wire-level compatible with Memcached, so using the local shim is optional but highly recommended because of some intricate details involving differences in how hash keys are generated in Memcached vs. Windows Azure Cache. Without the shim extra network hops are necessary for cache items to end up on the right cache cluster node, which is bad for performance. Also some of the more advanced caching features are only available when using the local shim.)
The Contrib.Cache.Memcached does not however adress the need to direct the nHibernate second-level cache to a distributed cache. For this purpose, there is another third-party module named Webmoco.AzureMemcached which provides an alternative nHibernate caching provider that also uses Memcached as its underlying storage. This module is preconfigured to automatically cache to a locally running Memcached shim, but could easily be modified to cache to any Memcached service.
Does the above sound about right?