gpt4 book ai didi

c# - 为什么当我达到大小限制时,我需要在我的大小受限的 MemoryCache 上调用两次 Set?

转载 作者:行者123 更新时间:2023-12-03 21:49:16 26 4
gpt4 key购买 nike

我们即将使用 ASP.NET Core 的内置内存缓存解决方案来缓存外部系统响应。 (稍后我们可能会从内存转移到 IDistributedCache。)
我们想使用 Mircosoft.Extensions.Caching.MemoryIMemoryCache作为MSDN suggests .
我们需要限制缓存的大小,因为默认情况下它是无界的。
因此,在将其集成到我们的项目中之前,我创建了以下 POC 应用程序来使用它。
我的自定义 MemoryCache 以指定大小限制

public interface IThrottledCache
{
IMemoryCache Cache { get; }
}

public class ThrottledCache: IThrottledCache
{
private readonly MemoryCache cache;

public ThrottledCache()
{
cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 2
});
}

public IMemoryCache Cache => cache;
}
将此实现注册为单例
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IThrottledCache>(new ThrottledCache());
}
我创建了一个非常简单的 Controller 来使用这个缓存。
用于玩 MemoryCache 的沙盒 Controller
[Route("api/[controller]")]
[ApiController]
public class MemoryController : ControllerBase
{
private readonly IMemoryCache cache;
public MemoryController(IThrottledCache cacheSource)
{
this.cache = cacheSource.Cache;
}

[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (cache.TryGetValue(id, out var cachedEntry))
{
return Ok(cachedEntry);
}
else
{
var options = new MemoryCacheEntryOptions { Size = 1, SlidingExpiration = TimeSpan.FromMinutes(1) };
cache.Set(id, $"{id} - cached", options);
return Ok(id);
}
}
}
如您所见,我的 /api/memory/{id}端点可以在两种模式下工作:
  • 从缓存中检索数据
  • 将数据存储到缓存中

  • 我观察到以下奇怪的行为:
  • 获取 /api/memory/first1.1) 返回first1.2) 缓存条目:first
  • 获取 /api/memory/first2.1) 返回first - cached2.2) 缓存条目:first
  • 获取 /api/memory/second3.1) 返回second3.2) 缓存条目:first , second
  • 获取 /api/memory/second4.1) 返回second - cached4.2) 缓存条目:first , second
  • 获取 /api/memory/third5.1) 返回third5.2) 缓存条目:first , second
  • 获取 /api/memory/third6.1) 返回third6.2) 缓存条目:second , third
  • 获取 /api/memory/third7.1) 返回third - cached7.2) 缓存条目:second , third

  • 正如您在第 5 个端点调用中看到的那样,我达到了极限。所以我的期望如下:
  • 缓存逐出策略删除 first最早的条目
  • 缓存存储 third作为最新的

  • 但是这种期望的行为只发生在第 6 次通话中。
    所以,我的问题是为什么我必须调用两次 Set为了在达到大小限制时将新数据放入 MemoryCache?

    编辑 : 也添加时间相关信息
    在测试期间,整个请求流/链大约需要 15 秒甚至更少。
    即使我更改 SlidingExpiration到 1 小时,行为仍然完全相同。

    最佳答案

    downloaded , built并在 Microsoft.Extensions.Caching.Memory 中调试单元测试;似乎没有真正涵盖这种情况的测试。
    原因是:一旦您尝试添加一个会使缓存超出容量的项目,MemoryCache triggers a compaction在后台。这将驱逐最旧的 (MRU) 缓存条目,直到 a certain difference .在这种情况下,它会尝试删除总大小为 1 的缓存项,在您的情况下为“第一个”,因为它是最后访问的。
    然而,由于这个紧凑的循环在后台运行,而 SetEntry() 中的代码方法是already on the code path for a full cache ,它会继续而不将项目添加到缓存中。
    下一次尝试,它成功了。
    复制:

    class Program
    {
    private static MemoryCache _cache;
    private static MemoryCacheEntryOptions _options;

    static void Main(string[] args)
    {
    _cache = new MemoryCache(new MemoryCacheOptions
    {
    SizeLimit = 2
    });

    _options = new MemoryCacheEntryOptions
    {
    Size = 1
    };
    _options.PostEvictionCallbacks.Add(new PostEvictionCallbackRegistration
    {
    EvictionCallback = (key, value, reason, state) =>
    {
    if (reason == EvictionReason.Capacity)
    {
    Console.WriteLine($"Evicting '{key}' for capacity");
    }
    }
    });

    Console.WriteLine(TestCache("first"));
    Console.WriteLine(TestCache("second"));
    Console.WriteLine(TestCache("third")); // starts compaction

    Thread.Sleep(1000);

    Console.WriteLine(TestCache("third"));
    Console.WriteLine(TestCache("third")); // now from cache
    }

    private static object TestCache(string id)
    {
    if (_cache.TryGetValue(id, out var cachedEntry))
    {
    return cachedEntry;
    }

    _cache.Set(id, $"{id} - cached", _options);
    return id;
    }
    }

    关于c# - 为什么当我达到大小限制时,我需要在我的大小受限的 MemoryCache 上调用两次 Set?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63342763/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com