2011-07-28 11 views
7

Mientras que el prototipo de prueba de estrés de nuestro nuevo sistema primario, me encuentro con un problema simultáneo con AppFabric Cache. Cuando simultáneamente invoco a muchos DataCache.Get() y Put() con el mismo cacheKey, donde intento almacenar objetos relativamente grandes, recibo "ErrorCode: SubStatus: hay un error temporal. Vuelva a intentarlo más tarde". Es reproducible mediante el siguiente código:Problema de simultaneidad de la caché de AppFabric?

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

Cuando se llama a get() con diferentes tecla o DataCache está sincronizado, no aparecerá este problema. Si DataCache se obtiene con cada llamada de DataCacheFactory (se supone que DataCache es seguro para la ejecución de subprocesos) o si los tiempos de espera se prolongan, no tiene efecto y aún se recibe un error. Me parece muy extraño que MS deje ese error. ¿Alguien se enfrentó a un problema similar?

+0

reintento posterior es un error muy genérico. Intente ver la excepción interna o el subestado de la excepción, que pueden darle una pista de lo que está sucediendo. La excepción aún puede necesitar ser manejada, pero esto por lo menos lo hará racional. – user4444

Respuesta

7

También veo el mismo comportamiento y tengo entendido que esto es por diseño. El caché contiene dos modelos de concurrencia:

  • optimistas métodos de concurrencia Modelo:Get, Put, ...
  • modelo de concurrencia pesimista:GetAndLock, PutAndLock, Unlock

Si utiliza métodos de modelo de concurrencia optimista como Get, entonces usted debe estar listo para obtener DataCacheErrorCode.RetryLater y manejar eso apropiadamente, también uso un enfoque de reintento.

Usted puede encontrar más información en MSDN: Concurrency Models

3

Hemos visto este problema también en nuestro código. Resolvemos esto sobrecargando el método Get para atrapar las expeciones y luego reintentamos la llamada N veces antes de volver a una solicitud directa a SQL.

Aquí es un código que se utiliza para obtener datos de la caché

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

Luego, cuando tenemos que sacar algo de la memoria caché que hacemos:

TryGetFromCache(key, region, out cachedMapping) 

Esto nos permite utilizar Pruebe métodos que encasulan las excepciones. Si devuelve falso, sabemos que algo está mal con la memoria caché y podemos acceder directamente a SQL.

+0

Gracias por su respuesta, estoy contento de que no estoy solo :-) Pero no puedo reconciliarme con esta solución y no me lo puedo imaginar para la aplicación de misión crítica en la producción. Trataré de informarlo a Microsoft o tal vez usar Memcached en su lugar. –

+0

Te entiendo. Usamos esta configuración en uno de nuestros servicios web más importantes, con más de un millón de visitas por día. Un servidor puede procesar más de 4000 transacciones por minuto. Esta configuración se asegurará de que la memoria caché tenga tiempo para responder. (así como tratar con excepciones lo más localmente posible.) Me encantan los métodos de prueba :) –

+0

Por favor, lea http://appfabriccat.com/2011/07/reaching-stable-performance-in-appfabric-cache-with-a- no-inactivo-caché-canal / –

Cuestiones relacionadas