AppFabric Cache并发问题?

时间:2011-07-28 13:12:42

标签: appfabric

在对我们全新的主系统进行压力测试时,我遇到了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很遗憾会留下这样的错误。有没有人遇到过类似的问题?

2 个答案:

答案 0 :(得分:7)

我也看到了相同的行为,我的理解是这是设计的。缓存包含两个并发模型:

  • 乐观并发模型方法: GetPut,...
  • 悲观并发模型: GetAndLockPutAndLockUnlock

如果你使用像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。