Microsoft.ApplicationServer.Caching.DataCacheException: ErrorCode<ERRCA0017>:SubStatus<ES0006>:There is a temporary failure. Please retry later.
Microsoft.ApplicationServer.Caching.DataCacheException: ErrorCode<ERRCA0018>:SubStatus<ES0001>:The request timed out.
Microsoft.ApplicationServer.Caching.DataCacheException: ErrorCode<ERRCA0016>:SubStatus<ES0001>:The connection was terminated, possibly due to server or network problems or serialized Object size is greater than MaxBufferSize on server.
For that reason it is a best practice to implement some kind of retry logic around your code calling the cache server. We could have used the Transient Application Block to manage that. But a few months ago I found somewhere that from time to time the DataCache object lose it's internal connection to the cache server. A simple way to fix this is to re-create a DataCache instance and retry the operation.
In the implementation below I'm keeping a reference to the DataCacheFactory and DataCache objects (another best practice). The CreateDataCache factory method will come handy later.
public class CachingService
{
private DataCacheFactory cacheFactory;
private DataCache cache;
private DataCache Cache
{
get
{
if (this.cache == null)
{
this.CreateDataCache();
}
return this.cache;
}
}
private void CreateDataCache()
{
this.cacheFactory = new DataCacheFactory();
this.cache = this.cacheFactory.GetDefaultCache();
}
// ...
}
Then I have this SafeCallFunction I use whenever I want to work with the DataCache object. Notice that the only thing I do to retry the operation is to call the factory method to re-create the DataCache object.
private object SafeCallFunction(Func<object> function)
{
try
{
return function.Invoke();
}
catch (DataCacheException)
{
// Retry by first re-creating the DataCache
try
{
this.CreateDataCache();
return function.Invoke();
}
catch (DataCacheException)
{
// Log error
}
}
return null;
}
Finally in the rest of the class I can use the SafeCallFunction like this
public object CacheGet(string key)
{
return this.SafeCallFunction(() => this.Cache.Get(key));
}
public void CachePut(string key, object cacheObject)
{
this.SafeCallFunction(() => this.Cache.Put(key, cacheObject));
}
public void CacheRemove(string key)
{
this.SafeCallFunction(() => this.Cache.Remove(key));
}
So far after a few weeks of using this implementation the single retry never failed on us. Before that we had around 5-10 failures daily for about 500k calls to the cache server. I would still recommend using a more robust retry policy with Windows Azure Caching but I think it's interesting to know that simply instantiating a new DataCache can fix most failures.
References
Caching in Windows AzureBest Practices for using Windows Azure Cache
Optimization Guidance for Windows Azure Caching
The Transient Fault Handling Application Block
1 comment:
Great post! It would be nice to have an example using the Enterprise Library Transient Fault Block.
Post a Comment