AppFabric 缓存并发问题?

7

在测试我们全新的主系统原型时,我遇到了与AppFabric Cache同时发生的问题。当同时使用许多DataCache.Get()和Put()来调用相同的cacheKey时,我试图存储相对较大的对象,我会收到“ErrorCode:SubStatus:There is a temporary failure. Please retry later.”的错误提示。这可以通过以下代码重现:

        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进行同步时,这个问题就不会出现。如果每次从DataCacheFactory获取DataCache(DataCache应该是线程安全的)或超时时间被延长,则没有影响,仍然会收到错误信息。

我觉得微软会留下这样的bug非常奇怪。有人遇到过类似的问题吗?


“稍后重试”是一个非常通用的错误。尝试查看异常的内部 execption 或子状态,这可以给你一些提示,了解发生了什么。异常可能仍然需要处理,但至少这样做会让它更加合理。 - user4444
2个回答

7
我看到了相同的行为,我的理解是这是设计上的。缓存包含两个并发模型:
- 乐观并发模型方法:`Get`,`Put`,... - 悲观并发模型:`GetAndLock`,`PutAndLock`,`Unlock`
如果您使用像 `Get` 这样的乐观并发模型方法,则必须准备好接收 `DataCacheErrorCode.RetryLater` 并适当地处理它 - 我也使用重试方法。
您可以在 MSDN 上找到更多信息:并发模型

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。

谢谢您的回复,我很高兴我不是一个人 :-) 但我无法接受这样的解决方法,也无法想象它在生产中用于关键任务的应用。我将尝试向微软报告此问题,或者考虑使用Memcached代替。 - Frantisek Jandos
我明白你的意思。我们在我们最重要的网络服务之一中使用这个设置,每天有超过一百万次点击量。一个服务器可能会处理超过4000个交易每分钟。这个设置将确保缓存有时间响应(以及尽可能地处理本地异常)。我喜欢Try方法 :) - Frode Stenstrøm
请阅读http://appfabriccat.com/2011/07/reaching-stable-performance-in-appfabric-cache-with-a-non-idle-cache-channel/。 - Frode Stenstrøm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接