Caching
LightSpeed provides two mechanisms for caching of entity instances – a first level and second level cache. Both caches are designed to help in reducing the number of database queries that need to be executed. The first level cache, which is also known as the Identity Map, is always in use, will cover all entities and cannot be disabled; second level caching, however, must be explicitly enabled and configured for use as it is disabled by default and only extends to caching entities which you have explicitly opted in through use of the Cached option.
First level caching is scoped to the current unit of work and therefore any cached objects will be unavailable as cached objects inside a second unit of work. The second level cache provide application wide caching – beyond the bounds of a unit of work.
An example of how the first and second level caches will be hit |
using (var unitOfWork = context.CreateUnitOfWork()) |
Enabling Second Level Caching
You do not need to use second level caching at all, but you may wish to enable it to improve performance of your application. If you’re noticing that your application is querying the database frequently for the same information (for example, reference data) then adding caching will provide an improvement in performance immediately.
Web applications also benefit well from the second level cache as generally pages are constructed relatively quickly within a tightly defined unit of work and therefore the first level cache is not going to be of use across multiple requests.
LightSpeed supports a pluggable cache provider model and provides implementations for two cache providers out of the box:
· DefaultCache – Uses the System.Web.Caching.Cache provider (which can be used in non-web apps too). This cache is a simple in-process cache.
· MemcachedCache – Uses the popular memcached distributed cache. Memcached can be distributed over an arbitrary number of servers and is suitable for high-load scenarios.
Additionally you can implement your own cache provider by implementing the ICache interface.
To enable second level caching, you can either do this programmatically or through configuration as shown below:
Enabling second level caching programmatically |
context.Cache = new CacheBroker(new DefaultCache()); |
Enabling Second Level Caching using configuration |
<lightSpeedContexts> |
Configuring the Second Level Cache
Caching may be applied declaratively at the model level by applying the CachedAttribute.
[Cached(ExpiryMinutes = 15)] |
Alternatively it can also be applied using the LightSpeed designer by selecting an entity and setting the cache to be enabled. Cache expiry times can also be configured with the designer if desired.
When LightSpeed loads an entity decorated with the CachedAttribute it will immediately add it to the second-level cache. Any subsequent request for that entity by its id will cause a cache hit and the database load will be avoided.
Understanding the Second Level Cache
For the first level cache, LightSpeed will check if it already has an entity loaded for the given Id when you perform any type of fetch where you specify an ID. For example, if you use the FindById() method, LightSpeed will check the internal first level cache first and, if found, return that and save a database call. If an explicit ID is not provided (for example, a query that would return all entities from a table) then as LightSpeed receives the result and looks to hydrate an entity it will first check if it already has an entity in the first level cache and, if found, return that entity. This type of logic applies when fetching explicitly as well as when lazy loading occurs.
The second level cache, if enabled, will also be checked in all of the same situations however it is more likely to contain objects as they will live across the unit of work boundaries.
To summarise when a cache hit would occur:
· You fetch an entity by Id
· You fetch a collection of entities
· You lazy load a collection or entity
Note: If you are dealing with projections then no cache hits will be possible, even if the projection contains the entity identifier.
Collection Caching
If you wish to cache an entire entity set in memory (e.g. for reference data) you can use FindAll caching. To apply this to an entity open the LightSpeed Model Explorer (View > Other Windows > LightSpeed Model) and in the tree view locate the entity that you want to cache. Right-click it and choose Add New Caching Options. Then expand the entity, select the Caching Options node, and you will see the option for Cache Find All Result in the Properties grid.
Caching Considerations
Caching can sound like a wonderful way to easily improve performance of your application however it is not a silver bullet. Primary concerns to consider is cache item expiry and if stale data could potentially impact your system. Cache expiry is a complex topic and worth reading more about if you are seriously considering second level caching options for a production application.
Consider why you are trying to cache data more aggressively and if LightSpeed caching is the best solution. For example, with web applications it may be far more efficient to enable output caching on the web pages. The initial load of a page may cause several database hits however subsequent requests will not cause any queries to fire and will not invoke the page creation process with .NET.
Working with the Cache Manually
Second level cache items can be interacted with directly by accessing the UnitOfWork.Context.Cache object. Through this object cache items can be added, updated and removed.