net framework example cache c# .net memorycache

framework - cache c# example



MemoryCache AbsoluteExpiration actuando extraño (4)

Estoy intentando usar un MemoryCache en .net 4.5 para realizar un seguimiento y actualizar automáticamente varios elementos, pero parece que no importa lo que establezca como AbsoluteExpiration , siempre caducará en 15 segundos o más.

Quiero que los elementos de la memoria caché caduquen cada 5 segundos, pero siempre caducan en al menos 15 segundos, y si muevo el tiempo de expiración, terminará siendo algo así como 15 segundos + mi intervalo de actualización, pero nunca menos de 15 segundos .

¿Hay alguna resolución interna del temporizador que no estoy viendo? Revisé un poco del código System.Runtime.Caching.MemoryCache reflejado y nada se destacó para mí, y no he podido encontrar a nadie más que tenga este problema en Internet.

Tengo un ejemplo muy básico a continuación que ilustra el problema.

Lo que quiero es que CacheEntryUpdate sea ​​golpeado cada 5 segundos aproximadamente y se actualice con nuevos datos, pero, como he dicho, solo se golpea en más de 15 segundos.

static MemoryCache MemCache; static int RefreshInterval = 5000; protected void Page_Load(object sender, EventArgs e) { if (MemCache == null) MemCache = new MemoryCache("MemCache"); if (!MemCache.Contains("cacheItem")) { var cacheObj = new object(); var policy = new CacheItemPolicy { UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate), AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval) }; var cacheItem = new CacheItem("cacheItem", cacheObj); MemCache.Set("cacheItem", cacheItem, policy); } } private void CacheEntryUpdate(CacheEntryUpdateArguments args) { var cacheItem = MemCache.GetCacheItem(args.Key); var cacheObj = cacheItem.Value; cacheItem.Value = cacheObj; args.UpdatedCacheItem = cacheItem; var policy = new CacheItemPolicy { UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate), AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval) }; args.UpdatedCacheItemPolicy = policy; }


¿Estaría dispuesto / sería capaz de cambiar desde el antiguo System.Runtime.Caching al nuevo Microsft.Extensions.Caching ? la versión 1.x admite netstandard 1.3 y net451. De ser así, la API mejorada admitiría el uso que describes sin hackers con reflexión.

El objeto MemoryCacheOptions tiene una propiedad ExpirationScanFrequency para permitirle controlar la frecuencia de escaneo de la limpieza del caché, consulte https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.memory.memorycacheoptions.expirationscanfrequency?view=aspnetcore-2.0

Tenga en cuenta que ya no hay una caducidad basada en temporizadores (esta es una decisión de diseño de rendimiento), por lo que ahora la presión de la memoria o la llamada a uno de los métodos basados ​​en Get () para los elementos almacenados en caché ahora son los desencadenantes de la caducidad. Sin embargo, puede forzar el vencimiento basado en el tiempo usando tokens de cancelación, consulte esta respuesta SO para ver un ejemplo https://.com/a/47949111/3140853 .


Lo he descubierto. Hay un internal static readonly TimeSpan en System.Runtime.Caching.CacheExpires llamado _tsPerBucket que está codificado en 20 segundos.

Aparentemente, este campo es lo que se usa en los temporizadores internos que se ejecutan y comprueban si los elementos de la caché han caducado.

Estoy trabajando en esto sobrescribiendo el valor usando la reflexión y borrando la instancia predeterminada de MemoryCache para restablecer todo. Parece funcionar, incluso si es un hack gigante.

Aquí está el código actualizado:

static MemoryCache MemCache; static int RefreshInterval = 1000; protected void Page_Load(object sender, EventArgs e) { if (MemCache == null) { const string assembly = "System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; var type = Type.GetType("System.Runtime.Caching.CacheExpires, " + assembly, true, true); var field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, TimeSpan.FromSeconds(1)); type = typeof(MemoryCache); field = type.GetField("s_defaultCache", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, null); MemCache = new MemoryCache("MemCache"); } if (!MemCache.Contains("cacheItem")) { var cacheObj = new object(); var policy = new CacheItemPolicy { UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate), AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval) }; var cacheItem = new CacheItem("cacheItem", cacheObj); MemCache.Set("cacheItem", cacheItem, policy); } } private void CacheEntryUpdate(CacheEntryUpdateArguments args) { var cacheItem = MemCache.GetCacheItem(args.Key); var cacheObj = cacheItem.Value; cacheItem.Value = cacheObj; args.UpdatedCacheItem = cacheItem; var policy = new CacheItemPolicy { UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate), AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval) }; args.UpdatedCacheItemPolicy = policy; }


Para MatteoSp: el pollingInterval en la configuración o el NameValueCollection en el constructor es un temporizador diferente. Es un intervalo en el que, cuando se invoca, utilizará las otras dos propiedades de configuración para determinar si la memoria se encuentra en un nivel que requiere que las entradas se eliminen utilizando el método Trim.


Una versión actualizada basada en la respuesta de @Jared. Si desea modificar la instancia predeterminada de MemoryCache, aquí crea una nueva.

class FastExpiringCache { public static MemoryCache Default { get; } = Create(); private static MemoryCache Create() { MemoryCache instance = null; Assembly assembly = typeof(CacheItemPolicy).Assembly; Type type = assembly.GetType("System.Runtime.Caching.CacheExpires"); if( type != null) { FieldInfo field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic); if(field != null && field.FieldType == typeof(TimeSpan)) { TimeSpan originalValue = (TimeSpan)field.GetValue(null); field.SetValue(null, TimeSpan.FromSeconds(3)); instance = new MemoryCache("FastExpiringCache"); field.SetValue(null, originalValue); // reset to original value } } return instance ?? new MemoryCache("FastExpiringCache"); } }