2011-07-28 3 views
7

Tout en testant le prototype de notre tout nouveau système principal, je rencontre un problème simultané avec AppFabric Cache. En appelant simultanément plusieurs DataCache.Get() et Put() avec le même cacheKey, où j'essaie de stocker un objet relativement volumineux, je reçois "ErrorCode: SubStatus: Il y a un échec temporaire. Veuillez réessayer plus tard." Il est reproductible par le code suivant:Problème de concurrence d'AppFabric Cache?

 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(); 
     } 

Lorsque vous appelez Get() avec une clé différente ou DataCache est synchronisé, cette question n'apparaît pas. Si DataCache est obtenu avec chaque appel de DataCacheFactory (DataCache est censé être thread-safe) ou si les délais d'attente sont prolongés, il n'a aucun effet et une erreur est toujours reçue. Il me semble très étrange que MS laisserait un tel bug. Quelqu'un at-il fait face à un problème similaire?

+0

Retry plus tard est une erreur très générique. Essayez de regarder l'exécution interne ou le sous-statut de l'exception, qui peut vous donner une idée de ce qui se passe. L'exception peut encore avoir besoin d'être gérée mais cela la rendra au moins rationnelle. – user4444

Répondre

7

Je vois aussi le même comportement et je crois comprendre que c'est par conception. Le cache contient deux modèles Parallélisme:

  • Optimiste Concurrency méthodes Modèle:Get, Put ...
  • Pessimiste Concurrency Modèle:GetAndLock, PutAndLock, Unlock

Si vous utilisez méthodes de modèle de concurrence optimiste comme Get alors vous devez être prêt à obtenir DataCacheErrorCode.RetryLater et gérer cette de manière appropriée - j'utilise aussi une approche de réessayer.

Vous trouverez peut-être plus d'informations à MSDN: Concurrency Models

3

Nous avons également vu ce problème dans notre code. Nous résolvons cela en surchargeant la méthode Get pour intercepter les expections, puis réessayer l'appel N fois avant de revenir à une requête directe à SQL.

Voici un code que nous utilisons pour obtenir des données à partir du cache

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; 
     } 
    } 
} 

Alors quand nous avons besoin d'obtenir quelque chose du cache que nous faisons:

TryGetFromCache(key, region, out cachedMapping) 

Cela nous permet d'utiliser Essayez méthodes qui encapsule les exceptions. Si elle renvoie false, nous savons que quelque chose ne va pas dans le cache et nous pouvons accéder directement à SQL.

+0

Merci pour votre réponse, je suis heureux que je ne suis pas seul :-) Mais je ne peux pas concilier avec une telle solution de contournement et je ne peux pas l'imaginer pour l'application de mission critique dans la production. Je vais essayer de le signaler à Microsoft ou peut-être utiliser Memcached à la place. –

+0

Je vous attrape. Nous utilisons cette configuration dans l'un de nos services Web les plus importants, avec plus d'un million de visites par jour. Un serveur peut traiter plus de 4000 transactions par minute. Cette configuration s'assurera que le cache a le temps de répondre. (ainsi que traiter avec des exceptions aussi localement que possible.) J'aime méthodes Try :) –

+0

S'il vous plaît lire http://appfabriccat.com/2011/07/reaching-stable-performance-in-appfabric-cache-with-a- non-inactif-cache-canal / –