如果缓存已满,第二次调用 MemoryCache.Set() 使用相同的键擦除条目
2nd call to MemoryCache.Set() with the same key erases entry if cache is full
这有点边缘情况,如果我能找到它,我会将其作为错误提交到 repo 中...
考虑以下 LINQPad snippet:
void Main()
{
var memoryCache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 1 // <-- Setting to 2 fixes the issue
});
Set(memoryCache);
memoryCache.Get("A").Dump(); // Yields 1
Set(memoryCache);
memoryCache.Get("A").Dump(); // Yields null
}
private void Set(MemoryCache memoryCache)
{
//memoryCache.Remove("A"); // <-- Also fixes the issue
memoryCache.Set("A", 1, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1),
SlidingExpiration = TimeSpan.FromDays(1),
Size = 1
});
}
我的问题是,当使用 .Set()
时,是否添加了新条目,然后删除了旧条目,因此需要在缓存中分配额外的 space?
这似乎与 bug I logged (which was rejected). You can see the source for this here 有关,根据我的阅读,您(和我)案例中的逻辑是这样的:
- 请求添加项目。
- 如果要添加项目将超出大小 (
UpdateCacheSizeExceedsCapacity()
) 因此拒绝请求(默默地,这是我反对的)。
- 此外,由于检测到这种情况,请启动
OvercapacityCompaction()
,这将删除您的项目。
似乎存在竞争条件,因为压缩工作排队到后台线程;也许偶尔您的测试会发现该项目仍然存在?
回答你的具体问题 - 不,它不是先添加然后删除多余的。
编辑:
关于第二个 Get()
总是返回空...我错过了对现有条目的一些额外处理,如果找到的话(虽然它不会改善结果)。在检查添加项目是否超过尺寸之前,有这样的:
if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
{
priorEntry.SetExpired(EvictionReason.Replaced);
}
即如果它找到您现有的条目,它会将其标记为已驱逐。然后它应用 UpdateCacheSizeExceedsCapacity()
测试,没有考虑它只是驱逐了现有条目(可以说它可以)。
稍后,仍然在Set()
/SetEntry()
,在超出容量的情况下,它会这样做:
if (priorEntry != null)
{
RemoveEntry(priorEntry);
}
...这会立即删除之前的条目。所以 OvercapacityCompaction()
(或 ScanForExpiredItems()
)是否以及何时会得到它并不重要,它在从 Set()
/SetEntry()
.
返回之前就消失了
(我还将上面的来源 link 更新为当前值;不改变逻辑)。
这有点边缘情况,如果我能找到它,我会将其作为错误提交到 repo 中...
考虑以下 LINQPad snippet:
void Main()
{
var memoryCache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 1 // <-- Setting to 2 fixes the issue
});
Set(memoryCache);
memoryCache.Get("A").Dump(); // Yields 1
Set(memoryCache);
memoryCache.Get("A").Dump(); // Yields null
}
private void Set(MemoryCache memoryCache)
{
//memoryCache.Remove("A"); // <-- Also fixes the issue
memoryCache.Set("A", 1, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1),
SlidingExpiration = TimeSpan.FromDays(1),
Size = 1
});
}
我的问题是,当使用 .Set()
时,是否添加了新条目,然后删除了旧条目,因此需要在缓存中分配额外的 space?
这似乎与 bug I logged (which was rejected). You can see the source for this here 有关,根据我的阅读,您(和我)案例中的逻辑是这样的:
- 请求添加项目。
- 如果要添加项目将超出大小 (
UpdateCacheSizeExceedsCapacity()
) 因此拒绝请求(默默地,这是我反对的)。 - 此外,由于检测到这种情况,请启动
OvercapacityCompaction()
,这将删除您的项目。
似乎存在竞争条件,因为压缩工作排队到后台线程;也许偶尔您的测试会发现该项目仍然存在?
回答你的具体问题 - 不,它不是先添加然后删除多余的。
编辑:
关于第二个 Get()
总是返回空...我错过了对现有条目的一些额外处理,如果找到的话(虽然它不会改善结果)。在检查添加项目是否超过尺寸之前,有这样的:
if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
{
priorEntry.SetExpired(EvictionReason.Replaced);
}
即如果它找到您现有的条目,它会将其标记为已驱逐。然后它应用 UpdateCacheSizeExceedsCapacity()
测试,没有考虑它只是驱逐了现有条目(可以说它可以)。
稍后,仍然在Set()
/SetEntry()
,在超出容量的情况下,它会这样做:
if (priorEntry != null)
{
RemoveEntry(priorEntry);
}
...这会立即删除之前的条目。所以 OvercapacityCompaction()
(或 ScanForExpiredItems()
)是否以及何时会得到它并不重要,它在从 Set()
/SetEntry()
.
(我还将上面的来源 link 更新为当前值;不改变逻辑)。