Custom Caching with Sitecore
Caching has been always black box and full of surprise, if we define caching strategy in our application it will do surprise with the performance and if not then will create mess.
Let's first understand Caching A cache is a temporary storage area. Caching works best with data that change infrequently. Caching makes a copy of data that can be returned much faster than from the original source.
What we have in Sitecore for cacheing Sitecore internally uses various caches to Store data, Render presentation logic In-memory information in order to improve performance and response time.These are caches that inherit from Sitecore.Caching.CustomCache class .
Let's talk about how we can implement custom caching in SItecore Custom Caching component provides capability to cache application specific items. e.g., Site configuration, Custom View Model etc. We can have following caching implementations which are injected at runtime based on the configuration.
- In Memory cache [Default]
- Redis cache
- Mem cache
<sitecore><services>
<register zsitecore.caching="" lifetime="Singleton"
servicetype="Sitecore.Core.Interfaces.Caching.ICacheManager,Sitecore.Core.InterfacesimplementationType=" zsitecore.caching.inmemorycacheprovider=""></register></services></sitecore>
Package Details 1.Create a new package source to refer Sitecore Nuget packages. 3. Browse and install the packages. Package Contents
- 1. Base Package
- 2. MemCache Package
- 3. Redis package
In Memory Caching: InMemory cache uses MemoryCache (System.Runtime.Caching) to store commonly used expensive data on server side.
- The cache stored in the memory of web server.
- In memory cache can store simple types and any objects.
- In memory cache life ends with application pool life.
- Using Cache duration we can configure when cache will expiry.
- To make the In-memory cache to work,
- 1. Specify the “InMemoryCacheProvider” assembly identifier in services section
<services>
<registerservicetype
implementationtype="Sitecore.Caching.InMemoryCacheProvider,Sitecore.Foundation.Interfaces lifetime="
itecore.core.interfaces.caching.icachemanager=""
itecore.foundation.interfaces.caching=""
singleton="">
</registerservicetype></services>
<setting name="DefaultCacheExpirationTimeinMinutes" value="30">
Caching.Redis Redis Cache is distributed cache, just pass a key-value pair to store data and use the same key to retrieve its associated values. The services can be hosted externally, So that even the running web server goes done, it will not affect the Redis caching Server.
It stores the simple type and serialized object in string format.
Host the Redis Setup as Services and start the service.
To make the Redis cache to work:
- 1. Specify the “Redis” provider in Sitecore Settings
<services>
<registerservicetype
implementationtype="Sitecore.Caching.Redis.RedisCacheProvider,Sitecore.Found lifetime="
sitecore.core.interfaces.caching.icachemanager=""
sitecore.foundation.interface.caching=""
singleton="">
</registerservicetype></services>
<!--Redis Caching Settings-->
<setting name="RedisHost" value="127.0.0.1">
<setting name="RedisPort" value="6379">
<setting name="RedisCacheTimeout" value="10">
<setting name="RedisIsSecured" value="false">
<setting name="RedisAccountKey" value="foobared">
Caching.Memcached Mem Cache is also a distributed memory object caching system.
Mem cache is running as separate service, so all the web servers can point to same location for caching.
Host the Services and start the service.
To make the MemCache to work,
- 1.Specify the configuration node and memcache section.
<configsections>
<sectiongroup name="enyim.com">
<section
name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"></section></sectiongroup>
</configsections><enyim .com=""><memcached protocol="Binary"><servers>
<add address="127.0.0.1" port="11211"></add></servers> </memcached>
</enyim>
<services>
<register Sitecore.foun="" lifetime="Singleton"
servicetype="Sitecore.Core.Interfaces.Caching.ICacheManager,Sitecore.Foundat implementationType="
sitecore.caching.memcached.memcachemanager="" <
</register></services>
<!--Memcache Settings-->
<setting name="CacheDurationInHours" value="10">
How to Use
- 1.Define appropriate cache setting in configuration.
- 2.Use CacheManager class methods to Add/Get/Remove Items from Cache.
string key= "site_metadata";
var metadata= CacheManager.Get(key); if(null==metadata) { metadata = FUNCTIONTOGETMETADATA();
CacheManager.Add(key,metadata); }
or you can use GetOrSet method
var points = CacheManager.GetOrSet($"MapPoints_{itemId}", ()=>
{ var item = SC.Context.Database.GetItem(new
SC.Data.ID(itemId));
return this.mapPointRepository.GetAll(item); });
Do' and Don't
- 1.Make sure all cacheable objects are serializable.
- 2.Sitecore Items are not serializable, so you won’t be able to cache them using Redis / Memcache. However in-memory cache can cache them.
- 3.Redis cache uses JsonSerilezer to serialize item to string and cache it.
- 4.Ensure cachekey should be very specific. Since Sitecore supports multiple site and languages. Ideally key should be like "sitename_language_keyname"
- 5.Make sure to clear cache when Sitecore item is published. Already a processor is added to take care of this.
- 6.Request specific data should not be cached using this module. Extension We can have different type of caching requirement for different type of application. Sitecore Caching module can provide different implementation of caching i.e. InMemory,Redis and Memcache, beyond this we can implement other caching providers.
Custom Cache Manager: We can implement custom Cache manager. Cache Manager follows DI pattern, so it can be extended to support any CacheProvider of choice.
- 1.Implement Sitecore.Core.Interfaces.Caching.ICacheManager in your Provider namespace MyAssembly.Cache{ public class MyCustomProvider : ICacheMaanger{ ... } }
- 2. Specify your provider in appsetting
<<sitecore>
<services>
<register
lifetime="Singleton"
myassembly.cache.mycustomprovider=""
servicetype="Sitecore.Core.Interfaces.Caching.ICacheManager,Sitecore.Core.I implementationType="
yassembly="">>
</register></services>
</sitecore>
Use Multiple Caching Providers By Default the CacheManager uses DI to find out current
caching provider from configuration. In case we need multiple providers we can instantiate
the specific provider. Other Caching considerations for Sitecore application
HtmlCache: We have HtmlCache is the top level cache, sometimes call the web cache. This Cache caches the actual HTML generated from renderings or sublayouts. It is used to store the rendered Html from renderings and sublayouts. Unfortunately most of the solutions we’ve seen don’t use HTML caching at all and on top of that run with default data cache sizes, which can cause performance issues due to frequent cache clearing and fails to utilize server memory properly. Sitecore does not limit cache size growth and ignores any maximum cache sizes specified in the web.config file. Enabling this setting can improve the application’s performance in 64-bit environments by allowing Sitecore to take full advantage of the available memory. After setting this value to true, monitor the system at regular intervals, as this configuration can cause Sitecore to consume too much memory and cause Out Of Memory errors. It is only recommended to set the setting to true in 64-bit environments. When use this cache, make sure to select appropriate vary by parameters
CDN Cache: CDN cache is the cache which helps to serve anything from web page assets (CSS, java-script and media library items) to the page itself to the end users browser. All CDN provides PULL and PUSH mechanism to cache the data, PULL will allow edge server to pull the data from application server as per defined TTL and PUSH is the mechanism where we define when do we need to cache the item. The most important asset which needs more attention is your media item and for that we an have configuration to get the item from CDN network:
<settings>
<setting name="MediaResponse.Cacheability">
<patch:attribute name="value">public</patch:attribute>
</setting>
<setting name="Media.AlwaysIncludeServerUrl">
<patch:attribute name="value">false</patch:attribute>
</setting>
<setting name="Media.MediaLinkServerUrl">
<patch:attribute name="value">https://SOMENAME.azureedge.net/</patch:attribute>
</setting>
</settings>
We will require custom implementation for versioning and CDN path for media item and add the CDN Media provider in patch config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<medialibrary>
<mediaprovider>
<patch:attribute name="type">YOURSOLUTIONNAMESPACE.Business.SC.Providers.CDNMediaProvider,
YOURSOLUTIONNAMESPACE.Busine
</patch:attribute></mediaprovider>
</medialibrary>
</sitecore>
</configuration>
There are some Sitecore modules available for some of the CDN,we can leverage them also.
We can cache the glassmapper model Let's first do Configurations to enable GlassMapper of Foundation.GlassMapper Second step is to configure first Base Classes and Repository to Get glass object How to use
- 1. Add Reference to "Sitecore.Foundation.Glassmapper"
- 2. It's good to create an Interface of your model implementing IGlassBase
[SitecoreType(AutoMap = true)]
public interface IMetaKeyword : IGlassBase
{
[SitecoreField("Keyword")]
string Keyword { get; set; }
}
readonly IGlassRepository repository;
public MetadataRepository(IGlassRepository repository)
{
this.repository = repository;
}
MetaViewModel metaDataModel = new MetaViewModel();
var metaDataItem = repository.GetCurrentItem();
metaDataModel.MetaDataItem = metaDataItem;
return metaDataItem;
Caching GlassModels
Glass Mapper v4 comes with caching enabled to imporve the performance.
We have implemented a custom Caching component with uses Foundation.Caching to cache glass models.
public static IDependencyResolver CreateResolver()
{
var config = new Glass.Mapper.Sc.Config();
var dependencyResolver = new DependencyResolver(config);
dependencyResolver.CacheManager = () => new Extensions.Caching.CustomCacheManager();
return dependencyResolver;
}
To make a GlassModel cachable, make sure to do the followings
- 1. Use Concrete Class Types instead of Interface types. (Interface type will work for inmemory cache but it might fail for redis caching) instead of
[SitecoreType(AutoMap = true)]
public interface IMetaKeyword : IGlassBase
{
[SitecoreField("Keyword")]
string Keyword { get; set; }
}
use
[SitecoreType(AutoMap = true)]
public class MetaKeyword : GlassBase
{
[SitecoreField("Keyword")]
public string Keyword { get; set; }
}
[SitecoreType(AutoMap = true,Cachable=true)]
public class MetaKeyword : GlassBase
{
[SitecoreField("Keyword")]
public string Keyword { get; set; }
}
- 1. Install foundation component "Sitecore.Foundation.Caching.Redis".
- 2. Install-Package Sitecore.Foundation.Caching.Redis
- 3. Configure the redis cache provider in cache configuration.
<setting name="RedisHost" value="127.0.0.1"/>
<setting name="RedisPort" value="6379"/>
<setting name="RedisCacheTimeout" value="10"/>
<setting name="RedisIsSecured" value="false"/>
<setting name="RedisAccountKey" value="foobared"/>
Foundation Caching component doesnot support Azure Redis Caching. We need to create custom caching to support that.
Extension
We can implement the interface ICacheManager to use any custom caching implementation. Or Extend the Foundation.Caching component to provide custom caching.
Do's and Dont's
Cacheble attribute need to be specified in all models. In following example Metakeyword will not be cached even if its derievd from a cachable parent.
[SitecoreType(AutoMap = true,Cachable=true)]
public class GlassBase
{
[SitecoreField("Title")]
public string Title { get; set; }
}
[SitecoreType(AutoMap = true)]
public class MetaKeyword : GlassBase
{
[SitecoreField("Keyword")]
public string Keyword { get; set; }
}
When implementing a caching you should follow the following rules for you Glass models:
- Ensure that all properties are serializable.
- Turn off lazy loading, by default later versions of Glass will disable lazy load when a model is marked as cachable.
- Don't create models that have large object graphs, since this will require a lot of data to be pulled from Sitecore and also a lot of data to be serialized which may affect performance.
- When lazy loading is disable Glass will perform a depth check to try and limit the size of the object graph that gets created. This is limited to a depth of 8 at the moment, e.g myModel.Level2.Level3.Level4.Level5.Level6.Level7.Level8.
- Certain custom datamappers may mean it isn't possible to cache a model.
Comments
Post a Comment
Please post your comments