Проблема параллелизма в AppFabric Cache? - PullRequest
7 голосов
/ 28 июля 2011

Во время стресс-тестирования прототипа нашей новой первичной системы у меня возникла параллельная проблема с 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 оставил бы такой баг. Кто-нибудь сталкивался с подобной проблемой?

Ответы [ 2 ]

7 голосов
/ 01 сентября 2011

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

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

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

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

3 голосов
/ 29 июля 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.

...