Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This is my RedisDataSource code. What I am mainly interested in is how the retry policy could be improved and if using WindowsAzure Trainsient Fault Handling this case (using Redis Azure Cache) makes sense.

   using System;
    using System.Threading.Tasks;
    using AB.Common.Helpers;
    using AB.SiteCaching.CacheContainer;
    using Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling.Cache;
    using Microsoft.Practices.TransientFaultHandling;
    using Newtonsoft.Json;
    using StackExchange.Redis;

namespace AB.SiteCaching.Providers
{
    public class RedisDataSource : ICacheProvider
    {
        private readonly IDatabase _cache;
        private readonly RetryPolicy _retryPolicy;

        public string CacheRegion { get; set; }

        public RedisDataSource()
        {
            _cache = Connection.GetDatabase();
            var retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(2));
            _retryPolicy = new RetryPolicy<CacheTransientErrorDetectionStrategy>(retryStrategy);
        }

        private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() =>
        {
            return ConnectionMultiplexer.Connect("CONNECTIONSTRINGTHATWILLCOMEFROMWEBCONFIG");
        });

        public static ConnectionMultiplexer Connection
        {
            get
            {
                return LazyConnection.Value;
            }
        }

        public T RetrieveCached<T>(string key, Func<T> onNotCached, TimeSpan timeOut) where T : class
        {
            string fullCacheKey = string.Format("{0}_{1}", key, CacheRegion);

            var dataContainer = new CacheDataContainer<T>();

            var data = RetrieveCacheObject<T>(fullCacheKey);

            var getTask = new Task(() =>
            {
                var cached = onNotCached();

                dataContainer.CachedData = cached;
                StoreCacheObject(fullCacheKey, dataContainer, timeOut);
            });

            if (data != null)
            {
                if (DateTime.UtcNow > data.LastUpdated.AddMinutes(5))
                {
                    if (data.IsDirty == null) data.IsDirty = DateTime.UtcNow;

                    if (data.IsDirty != null && dataContainer.RequestSent == null)
                    {
                        dataContainer.RequestSent = DateTime.UtcNow;
                        getTask.Start();
                    }
                }

            }
            else
            {
                getTask.Start();
                getTask.Wait();
                data = RetrieveCacheObject<T>(fullCacheKey);
            }

            return data.CachedData;
        }

        public async Task<T> RetrieveCachedAsync<T>(string key, Func<Task<T>> onNotCached, TimeSpan timeOut) where T : class
        {
            string fullCacheKey = string.Format("{0}_{1}", key, CacheRegion);

            var dataContainer = new CacheDataContainer<T>();

            var data = RetrieveCacheObject<T>(fullCacheKey);

            if (data != null)
            {
                if (DateTime.UtcNow > data.LastUpdated.AddMinutes(5))
                {
                    if (data.IsDirty == null) data.IsDirty = DateTime.UtcNow;

                    if (data.IsDirty != null && dataContainer.RequestSent == null)
                    {
                        dataContainer.RequestSent = DateTime.UtcNow;
                        var cached = await onNotCached();
                        dataContainer.CachedData = cached;
                        StoreCacheObject(fullCacheKey, dataContainer, timeOut);
                    }
                }

            }
            else
            {
                var cached = await onNotCached();
                dataContainer.CachedData = cached;
                StoreCacheObject(fullCacheKey, dataContainer, timeOut);
                data = RetrieveCacheObject<T>(fullCacheKey);
            }

            return data.CachedData;
        }

        /// <summary>
        /// Retrieve cached object from Redis
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fullCacheKey">Cache Key</param>
        /// <returns></returns>
        private CacheDataContainer<T> RetrieveCacheObject<T>(string fullCacheKey) where T : class
        {
            CacheDataContainer<T> data = null;
            try
            {
                var dataCache = _cache;
                var cachedString = _retryPolicy.ExecuteAction(() => dataCache.StringGet(fullCacheKey));
                if (cachedString.HasValue)
                {
                    return JsonConvert.DeserializeObject<CacheDataContainer<T>>(cachedString);
                }

            }
            catch (RedisException ex)
            {
                LoggingHelper.Log(ex);
            }

            return data;
        }

        /// <summary>
        /// Store cache object in Redis cache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fullCacheKey"></param>
        /// <param name="cached"></param>
        /// <param name="timeOut"></param>
        private void StoreCacheObject<T>(string fullCacheKey, CacheDataContainer<T> cached, TimeSpan timeOut)
        {
            var dataCache = _cache;
            if (dataCache != null)
            {
                _retryPolicy.ExecuteAction(() =>
                {
                    cached.LastUpdated = DateTime.UtcNow;
                    cached.IsDirty = null;
                    cached.RequestSent = null;

                    var serialized = JsonConvert.SerializeObject(cached);

                    dataCache.StringSet(fullCacheKey, serialized, timeOut);
                });
            }

        }

        /// <summary>
        /// Invalidate a cache object in Redis
        /// </summary>
        public void Invalidate(string fullCacheKey)
        {
            _cache.SetRemove(fullCacheKey, RedisValue.Null);
        }

        public bool IsSet(string fullKey)
        {
            return _cache.KeyExists(fullKey);
        }
    }
}
share|improve this question
    
Just a small thing... in some places onNotCached is awaitable, and in others it's synchronous. Is that intentional? –  Gigi yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.