left-icon

ServiceStack Succinctly®
by Zoran Maksimovic

Previous
Chapter

of
A
A
A

CHAPTER 8

Caching

Caching


In Chapter 1, we briefly mentioned that ServiceStack contains built-in support for server-side caching.

Caching in ServiceStack

  1. Caching in ServiceStack

ServiceStack provides ICacheClient, a clean caching interface, for a number of different cache providers:

  • In Memory: Uses RAM as the caching mechanism.
  • Redis: An open-source, BSD-licensed, advanced key-value store.[28]
  • Memcached: A high-performance, distributed memory, object-caching system intended for use in speeding up dynamic web applications by alleviating database load.[29]
  • Azure Client: Used to interface with Microsoft Azure.
  • AWS Cache Client: Used to interface with Amazon's DynamoDB back end hosted on Amazon Web Services.
  • Disk: Writes to the hard disk.

Which cache provider you use depends mainly on the needs of your application.

MemCached, Azure, or AwsDynamoDb providers need to be installed separately through NuGet.

NuGet caching providers

  1. NuGet caching providers

Configuring Caching

The ICacheClient interface is the starting point for all of the supported caching options as it defines the basic interface that every cache provider has to implement.

To start using the caching mechanism, it is as simple as registering the provider in the application host. Once registered, an instance of the ICacheClient will be injected to the service at run time, and will be available as a Cache property, which is part of the ServiceBase class.

The following example shows how to configure the various providers in the application host.

public override void Configure(Container container)

{

    //MemoryCacheClient

    container.Register<ICacheClient>(new MemoryCacheClient());

    

    //Redis

    container.Register<IRedisClientsManager>

                 (c => new PooledRedisClientManager("<redis_address_here>"));

    container.Register

                 (c => c.Resolve<IRedisClientsManager>().GetCacheClient())

                                              .ReusedWithin(ReuseScope.None);

    

    //MemCached

    container.Register<ICacheClient>

                      (new MemcachedClientCache(new[] {"<memcached_host>"}));

    

    //Azure

    container.Register<ICacheClient>(new AzureCacheClient("CacheName")); 

    

    //AWS DynamoDB

    container.Register<ICacheClient>(new DynamoDbCacheClient(...))

}

Response Caching

To demonstrate how to use caching, we will reuse our ProductService. One important thing to note here is that the cache will contain the full response rather than the Response DTO itself.

In order to support this scenario, we have to change the output of the method to object instead of the existing ProductResponse. We should know two things about caching:

  • Cache is basically a dictionary and it needs a unique key to identify a cached object. The key is a string and it can be generated by the UrnId helper class.
  • RequestContext.ToOptimizedResultUsingCache is the method responsible for returning data from the cache.

The following code example contains the modified Get() method. The highlighted code will be executed in case nothing is found in the cache with the given key (this always happens the first time a particular item is requested from the cache) and, for all the subsequent calls, it will be ignored.

public object Get(GetProduct request)

{

    var key = UrnId.Create<GetProduct>(request.Id.ToString());

 

    var cachedProduct = 

        RequestContext.ToOptimizedResultUsingCache(this.Cache, key, () =>

        {

            var product = ProductRepository.GetById(request.Id);

            if (product == null)

            {

                Response.StatusCode = (intHttpStatusCode.NotFound;

                return default(ProductResponse);

            }

            return ProductMapper.ToProductResponse(product);

        });

    return cachedProduct;

}

Caching with Time Expiration

The basic caching method works fine for static objects that we know won’t change over a long period of time. But for items that can change, there is an overload of the method that supports time expiration through a TimeSpan, which defines a period after which the cached item will be automatically deleted and therefore refreshed in the next call.

RequestContext.ToOptimizedResultUsingCache(this.Cache, key, new TimeSpan(0,1,0), 

    () =>

        {

            //…

        }

Cached Items Removal

However, the time expiration method still doesn’t ensure that the object returned from the cache is actually the latest version stored in the repository. The object can, theoretically, be updated (changed) during the time frame when the object is supposed to be in the cache.

The way to fix this is to manually ensure that the object is refreshed in the cache in case the object is changed. One possible way of doing this would be to change the Put() (update) method to remove the changed objects from the cache.

public ProductResponse Put(UpdateProduct request)

{

    var key = UrnId.Create<GetProduct>(request.Id.ToString());

    base.RequestContext.RemoveFromCache(base.Cache, key); 

}

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.