在对我们全新的主系统进行压力测试时,我遇到了AppFabric Cache的并发问题。当同时使用相同的cacheKey调用许多DataCache.Get()和Put()时,我尝试存储相对较大的objet,我收到“ErrorCode:SubStatus:暂时失败。请稍后重试。”它可以通过以下代码重现:
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();
}
使用不同的密钥调用Get()或同步DataCache时,不会出现此问题。如果DataCache是从DataCacheFactory的每次调用获得的(DataCache应该是线程安全的)或延长超时,它就没有效果,仍然会收到错误。 在我看来MS很遗憾会留下这样的错误。有没有人遇到过类似的问题?
答案 0 :(得分:7)
我也看到了相同的行为,我的理解是这是设计的。缓存包含两个并发模型:
Get
,Put
,... GetAndLock
,PutAndLock
,Unlock
如果你使用像Get
这样的乐观并发模型方法,那么你必须准备好DataCacheErrorCode.RetryLater
并适当地处理它 - 我也使用重试方法。
您可以在MSDN上找到更多信息:Concurrency Models
答案 1 :(得分:3)
我们在代码中也看到了这个问题。我们通过重载Get方法来捕获预期然后在回退到SQL的直接请求之前重试N次来解决这个问题。
这是我们用来从缓存中获取数据的代码
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;
}
}
}
然后,当我们需要从缓存中获取内容时,我们会这样做:
TryGetFromCache(key, region, out cachedMapping)
这允许我们使用包含异常的Try方法。如果它返回false,我们知道缓存有问题,我们可以直接访问SQL。