AppFabric Cache concurrency problem?

While a prototype of testing our new primary system, I am facing a parallel problem with AppFabric Cache. When I call many DataCache.Get () and Put () with the same cacheKey, where I try to save a relatively large object, I get "ErrorCode: SubStatus: there is a temporary error. Please try again later." It is reproduced with the following code:

var dcfc = new DataCacheFactoryConfiguration { Servers = new[] {new DataCacheServerEndpoint("localhost", 22233)}, SecurityProperties = new DataCacheSecurity(DataCacheSecurityMode.None, DataCacheProtectionLevel.None), }; var dcf = new DataCacheFactory(dcfc); var dc = dcf.GetDefaultCache(); const string key = "a"; var value = new int [256 * 1024]; // 1MB for (int i = 0; i < 300; i++) { var putT = new Thread(() => dc.Put(key, value)); putT.Start(); var getT = new Thread(() => dc.Get(key)); getT.Start(); } 

When Get () is called with another key or DataCache is synchronized, this problem will not appear. If a DataCache is received with every call from the DataCacheFactory (DataCache must be thread safe), or timeouts are extended, it has no effect, and the error is still received. It is very strange to me that MS will leave such an error. Has anyone encountered a similar problem?

+7
source share
2 answers

I also see the same behavior, and I understand that this is by design. The cache contains two concurrency models:

  • Optimistic concurrency Model Methods: Get , Put , ...
  • Pessimistic concurrency Model: GetAndLock , PutAndLock , Unlock

If you use optimistic concurrency modeling methods such as Get , then you should be prepared to get DataCacheErrorCode.RetryLater and DataCacheErrorCode.RetryLater it accordingly - I also use the retry method.

You can find more information on MSDN: Concurrency Models

+7
source

We also saw this problem in our code. We solve this by overriding the Get method to catch expectations, and then repeat the call N times before returning to the direct SQL query.

Here is the code we use to get data from the cache

  private static bool TryGetFromCache(string cacheKey, string region, out GetMappingValuesToCacheResult cacheResult, int counter = 0) { cacheResult = new GetMappingValuesToCacheResult(); try { // use as instead of cast, as this will return null instead of exception caused by casting. if (_cache == null) return false; cacheResult = _cache.Get(cacheKey, region) as GetMappingValuesToCacheResult; return cacheResult != null; } catch (DataCacheException dataCacheException) { switch (dataCacheException.ErrorCode) { case DataCacheErrorCode.KeyDoesNotExist: case DataCacheErrorCode.RegionDoesNotExist: return false; case DataCacheErrorCode.Timeout: case DataCacheErrorCode.RetryLater: if (counter > 9) return false; // we tried 10 times, so we will give up. counter++; Thread.Sleep(100); return TryGetFromCache(cacheKey, region, out cacheResult, counter); default: EventLog.WriteEntry(EventViewerSource, "TryGetFromCache: DataCacheException caught:\n" + dataCacheException.Message, EventLogEntryType.Error); return false; } } } 

Then, when we need to get something from the cache, we do:

 TryGetFromCache(key, region, out cachedMapping) 

This allows us to use Try methods that encapsulate exceptions. If it returns false, we know what is wrong in the cache, and we can access SQL directly.

+3
source

All Articles