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);
}
}
}
onNotCached
is awaitable, and in others it's synchronous. Is that intentional? – Gigi Mar 6 '15 at 13:41