MemoryCache AbsoluteExpiration действует странно
Я пытаюсь использовать MemoryCache
в .net 4.5, чтобы отслеживать и автоматически обновлять различные элементы, но похоже, что независимо от того, что я установил как AbsoluteExpiration
Он всегда истекает только через 15 секунд или более.
Я хочу, чтобы элементы кэша истекали каждые 5 секунд, но он всегда истекает по крайней мере через 15 секунд, и если я перемещаю время истечения, это будет что-то вроде 15 секунд + мой интервал обновления, но никогда не менее 15 секунд.
есть внутреннее разрешение таймера, которое я не вижу? Я посмотрел сквозь немного отраженного System.Runtime.Caching.MemoryCache
код и ничего не выделялись для меня, и я не смог найти никого, у кого есть эта проблема в интернете.
у меня есть очень простой пример, который иллюстрирует проблему.
Я хочу CacheEntryUpdate
попадать каждые 5 секунд или около того и обновлять новые данные, но, как я уже сказал, он только когда-либо попадает в 15+ секунд.
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;
}
4 ответов
Я все понял. Есть internal static readonly TimeSpan
о системе.Во время выполнения.Кэширование.CacheExpires называется _tsPerBucket, который жестко закодирован в 20 секунд.
по-видимому, это поле используется для внутренних таймеров, которые запускаются и проверяют, истекли ли элементы кэша.
Я работаю над этим, перезаписывая значение с помощью отражения и очищая экземпляр MemoryCache по умолчанию для сброса всего. Кажется, это работает, даже если это гигантский Хак.
здесь обновленный код:
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;
}
to MatteoSp-pollingInterval в конфигурации или NameValueCollection в конструкторе является другим таймером. Это интервал, который при вызове будет использовать два других свойства конфигурации, чтобы определить, находится ли память на уровне, требующем удаления записей с помощью метода Trim.
хотели бы вы / могли бы изменить старую систему.Во время выполнения.Кэширование к новому узел Майкрософт.Увеличение.Кэширование? Версия 1.x поддерживает netstandard 1.3 и net451. Если это так, то улучшенный API будет поддерживать использование, которое вы описываете без взлома с отражением.
объект MemoryCacheOptions имеет свойство ExpirationScanFrequency, позволяющее управлять частотой сканирования очистки кэша, см. https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.memory.memorycacheoptions.expirationscanfrequency?view=aspnetcore-2.0
имейте в виду, что больше нет срока действия на основе таймеров (это решение о дизайне производительности), и поэтому теперь давление памяти или вызов одного из методов Get() для кэшированных элементов теперь являются триггерами для истечения срока действия. Однако вы можете заставить срок действия на основе времени с помощью токенов отмены, см. Этот ответ SO для пример https://stackoverflow.com/a/47949111/3140853.
обновленная версия на основе ответа @Jared. Insread изменения экземпляра MemoryCache по умолчанию, здесь создает новый.
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");
}
}