Table of Contents

Implementing a Custom Client Cache

If the four types of client caches provided by the Scaleout.Client library do not meet your requirements, a custom client cache can by implemented by inheriting from the ClientCache<TValue> class.

Procedure

  • Client cache implementations must be thread safe.
  • The TValue type parameter to the base ClientCache class must match the type of values being stored in the Cache instances that use the implementation.

Example

The following example implements a custom client cache and uses the .NET Framework's System.Runtime.Caching.MemoryCache to store object references.

Implementing the Custom Client Cache

using System;
using System.Text;
using System.Runtime.Caching;
using Scaleout.Client.Net;
using Scaleout.Client.InProc;

namespace CustomClientCache
{
    /// <summary>
    /// Allows a <see cref="System.Runtime.Caching.MemoryCache"/> instance to be used
    /// as an in-process client cache for a Scaleout.Client.Cache. 
    /// </summary>
    public class MemoryCacheAdapter<TValue> : ClientCache<TValue>
    {
        public MemoryCache MemoryCache { get; private set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="memoryCache">System.Runtime.Caching.MemoryCache instance.</param>
        public MemoryCacheAdapter(MemoryCache memoryCache)
        {
            MemoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
        }

        // Converts a ScaleOut ObjectKey identifier to a string that 
        // can be used as a System.Runtime.Caching.MemoryCache string key.
        private static string KeyToString(ref ObjectKey key)
        {
            return Convert.ToBase64String(key.ToByteArray());
        }

        public override CacheEntry<TValue> Get(ref ObjectKey objectKey)
        {
            string key = KeyToString(ref objectKey);

            // Returning null is OK; null tells the Scaleout.Client library
            // that the item wasn't found.
            return MemoryCache.Get(key) as CacheEntry<TValue>;
        }

        public override void Remove(ref ObjectKey objectKey)
        {
            string key = KeyToString(ref objectKey);
            MemoryCache.Remove(key);
        }

        public override void Set(ref ObjectKey objectKey, CacheEntry<TValue> cacheEntry)
        {
            string key = KeyToString(ref objectKey);

            // Passing null policy to use the MemoryCache's default.
            MemoryCache.Set(key, cacheEntry, policy: null);
        }

        public override void Touch(ref ObjectKey objectKey)
        {
            var entry = Get(ref objectKey);
            entry?.Touch(DateTime.UtcNow);
        }
    }
}

Using the Custom Client Cache

Use CacheBuilder.SetClientCache to register a custom client cache with a Cache instance.

using Scaleout.Client;
using Scaleout.Client.InProc;
using System;
using System.Collections.Specialized;
using System.Runtime.Caching;

public class Program
{
    public void Main()
    {
        var conn = GridConnection.Connect("bootstrapGateways=localhost:721");

        // Configure the .NET MemoryCache to trim memory every 15 seconds.
        var memCacheConfig = new NameValueCollection();
        memCacheConfig.Add("cacheMemoryLimitMegabytes", "100"); // 100 MB
        memCacheConfig.Add("pollingInterval", "00:00:15"); // 15 seconds (default is 2 mins)
        var memCache = new MemoryCache("ScaleOut MemoryCache", memCacheConfig);
        
        // Register the custom client cache using the CacheBuilder.
        var memCacheAdapter = new MemoryCacheAdapter<MyClass>(memCache);
        var cb = new CacheBuilder<string, MyClass>("My Cache", conn)
                            .SetClientCache(memCacheAdapter);
        var cache = cb.Build();

        //...
    }
}