A quick look at CacheCow
I wanted to give this library a go since I had quite a few posts about it and log what I ended up doing with it as well as key features I need to take into account and have knowledge of.
CacheCow stores cache data on the Client-side while the Server-side will merely provide caching directives which we can rely on the defaults or tell how and when we want to cache our resources. What I think is kind of cool from this library is how it encourages the developer and facilitates deferring handlign cache state validation out of Application code into specific components, it provides a degree of flexibility and extensibility for it.
In-Memory and SQL (I assume MS SQL Server) are the only cache store options available at the time of writing this post, however, the library does provide a mechanism to define our own.
Concepts and Features
There are a bunch of Concept and Definitions which I personally find a mix between standard and abstract concepts as well as componentes and elements particular to the library, some of them I believe are worth listing here:
General
- Resource: An Application resource that uniquely IDs through some sort of representation.
- Representation: A specific embodiment of a resource through some format, goes hand-in-hand with Resource.
- ETag: Indicates a specific version of a resource, shorthand for Entity Tag.
- Expiry: Driven by the Server directives, it basically tells when a cached resource expires.
- Consistency: Conditional HTTP
GET/PUT/PATCH/DELETEare supported out of the box to counter common caching iconsistencies scenarios to ensure we get or affect the right version of the targeted resource. - Zero-Expiry Resources: Considered also those that have high consistency, basically they are expired the moment they reach the client, however, the cached copy may be used if the client makes a conditional HTTP GET.
Library specific
- CacheCow.Client: This the API to leverage Client-side caching, it integrates with .NET HttpClient (.NET Framework I am assuming)
- CacheCow.Server: This would be the API we can use for Server-side development such as Web Applications, Web Services, etc. At the time of this writing it supports both .NET Framework and .NET Core, their samples page provides an ASP.NET Core sample as well as the one I am linking here.
- ICacheStore: Represents a Store using by the client to store cacheable representation of resources, an interface type that is to be implemented.
- TimedETag: The ETag would be generated from the Last Modified timestamp.
- ITimedETagExtractor: Interface implemented by the module responsible of extracting a
TimedETagof a given type. - ITimedETagQueryProvider: Likely used when doing condtional queries, this would allow us to define modules that are responsible of fetching a resource of a given type from the backend in order to determine whether the resource has changed.
So, there are two things I wanted to try:
- Use the simplest way to cache resources.
- Control how e-tags are generated for my resources.
I have put together I small Web Application to test these features from CacheCow.
The simplest way to get going is simply install the package and register the library elements with unity and annotate the controller action of choice as follows:
// Register with Unity
services.AddHttpCachingMvc(); // comes right after the Mvc registration.
and then, decorate our controller action and let the library defaults work.
[HttpGet]
[HttpCacheFactory(20)] // This is attr we are looking for, 20 is the TTL is seconds.
public async Task<ActionResult<IEnumerable<ActivityLogEntryViewModel>>> Index()
{
await Task.Delay(5000);
return Json(_logsService.GetActivityLogs().Select(l => new ActivityLogEntryViewModel {
EventTimestamp = string.Concat(l.EventTimestamp.ToShortDateString(), " ", l.EventTimestamp.ToShortTimeString()),
UserIdentity = l.Who.ToString(),
SystemArea = l.SystemArea,
UserAction = l.UserAction
}));
Having defined and register our Timed ETag Extractor, we can put it to use, our Controlle method would look like:
[HttpGet]
[HttpCacheFactory(20, ViewModelType = typeof(ActivityLogEntryViewModel))]
public async Task<ActionResult<IEnumerable<ActivityLogEntryViewModel>>> Index2()
The type specified for ViewModelType has to match the one used in the ITimedETagExtractor implementation. In our Timed ETag Extractor, it is this line that calculatest the value of the ETag.
foreach (var b in SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(viewModel.EventTimestamp)))
{
tagValue.Append(b.ToString("x2"));
}
return new TimedEntityTagHeaderValue(tagValue.ToString());
If the resource has changed, the value calculated would differ from the one on the client side which is the whole purpose of the validation, additionally we could use ITimedETagQueryProvider if we want to validate the resource in the backend store.