Проблема параллелизма AppFabric Cache?

Во время стресс-тестирования прототипа нашей совершенно новой основной системы я столкнулся с параллельной проблемой с AppFabric Cache. При одновременном вызове многих DataCache.Get() и Put() с одним и тем же cacheKey, где я пытаюсь сохранить относительно большой объект, я получаю "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 оставила бы такой баг. Кто-нибудь сталкивался с подобной проблемой?


person Frantisek Jandos    schedule 28.07.2011    source источник
comment
Повторить попытку позже — очень распространенная ошибка. Попробуйте посмотреть на внутреннее исключение или подстатус исключения, это может дать вам подсказку о том, что происходит. Исключение, возможно, все еще необходимо обработать, но это, по крайней мере, сделает его рациональным.   -  person user4444    schedule 31.07.2011


Ответы (2)


Я также вижу такое же поведение, и я понимаю, что это сделано по замыслу. Кэш содержит две модели параллелизма:

  • Методы модели оптимистического параллелизма: Get, Put, ...
  • Модель пессимистического параллелизма: GetAndLock, PutAndLock, Unlock

Если вы используете оптимистичные методы модели параллелизма, такие как Get, то вы должны быть готовы получить DataCacheErrorCode.RetryLater и соответствующим образом обработать его — я также использую подход с повторными попытками.

Дополнительную информацию можно найти в MSDN: Модели параллелизма

person David Pokluda    schedule 01.09.2011

Мы также видели эту проблему в нашем коде. Мы решаем эту проблему, перегружая метод Get для перехвата ожиданий, а затем повторяем вызов N раз, прежде чем вернуться к прямому запросу к SQL.

Вот код, который мы используем для получения данных из кеша

    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.

person Frode Stenstrøm    schedule 29.07.2011
comment
Спасибо за ваш ответ, я рад, что я не один :-) Но я не могу смириться с таким обходным путем и не могу представить его для критически важного приложения в производстве. Я попытаюсь сообщить об этом в Microsoft или, возможно, вместо этого использовать Memcached. - person Frantisek Jandos; 29.07.2011
comment
Я понимаю тебя. Мы используем эту настройку в одном из наших самых важных веб-сервисов с более чем миллионом посещений в день. Один сервер может обрабатывать более 4000 транзакций в минуту. Эта настройка гарантирует, что у кеша есть время ответить. (а также обрабатывать исключения как можно локальнее.) Я люблю методы Try :) - person Frode Stenstrøm; 31.07.2011
comment
Пожалуйста, прочитайте appfabriccat.com/2011/07/ - person Frode Stenstrøm; 01.08.2011