LoginSignup
5
2

More than 1 year has passed since last update.

MemoryCacheのキャッシュは有効期限が過ぎても自動では消してくれない

Last updated at Posted at 2022-11-17

2022/11/18 デバッグ時のキャッシュの状態を追記

初めに

Microsoft.Extensions.Caching.MemoryのMemoryCacheは便利ですが、有効期限が過ぎたキャッシュを自動では消しません。気を付けないとメモリリークの原因になります。

Microsoftのドキュメントにもさらっと書いてあって見落としがちです。

有効期限切れはバックグラウンドでは発生しません。
キャッシュで期限切れのアイテムをアクティブにスキャンするタイマーはありません。
キャッシュでのあらゆるアクティビティ (Get、Set、Remove) によって、
バックグラウンドでの期限切れ項目のスキャンをトリガーできます。

そうなのです。Get, Set, Removeを実行した場合、かつ、実行時に指定したキーのキャッシュのみが削除されるのです。

試してみる

有効期限が異なる2個のキャッシュを登録し、有効期限切れ後のキャッシュ数を取得してみます。

private static void Test1()
{
    var memoryCache = new MemoryCache(new MemoryCacheOptions());

    // set
    memoryCache.Set("a", "a dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(1)));
    memoryCache.Set("b", "b dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(10)));
    Console.WriteLine("set");

    // wait
    Thread.Sleep(15000);
    Console.WriteLine("... wait 15 sec");

    // count
    Console.WriteLine($"count : {memoryCache.Count}");
}

→実行後

set
... wait 15 sec
count : 2

→デバッグでキャッシュの状態を確認
image.png

有効期限が過ぎてもキャッシュが残っていることが確認できました。

では、

キャッシュでのあらゆるアクティビティ (Get、Set、Remove) によって、
バックグラウンドでの期限切れ項目のスキャンをトリガーできます。

についてはどういうことか。
こちらも試してみます。

"a"というキーをGetしてみました。

private static void Test2()
{
    var memoryCache = new MemoryCache(new MemoryCacheOptions());

    // set
    memoryCache.Set("a", "a dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(1)));
    memoryCache.Set("b", "b dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(10)));
    Console.WriteLine("set");

    // wait
    Thread.Sleep(15000);
    Console.WriteLine("... wait 15 sec");

    // get
    Console.WriteLine($"get a : {(memoryCache.TryGetValue("a", out string a) ? a : "nothing")}");

    // count
    Console.WriteLine($"count : {memoryCache.Count}");
}

→実行後

set
... wait 15 sec
get a : nothing
count : 1

→デバッグでキャッシュの状態を確認
image.png

はい。キャッシュの数が1個減りました。
Getしたことで初めて有効期限のチェックが行われ、該当キー(上記の例では"a")のキャッシュが削除されたのです。
一方でGetしなかった"b"のキャッシュは残ったままです。

有効期限が過ぎたキャッシュを全て削除するには

Compactメソッドを実行します。
memoryCache.Compact(0);

private static void Test3()
{
    var memoryCache = new MemoryCache(new MemoryCacheOptions());

    // set
    memoryCache.Set("a", "a dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(1)));
    memoryCache.Set("b", "b dummy", new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(10)));
    Console.WriteLine("set");

    // wait
    Thread.Sleep(15000);
    Console.WriteLine("... wait 15 sec");

    // count
    Console.WriteLine($"count : {memoryCache.Count}");

    // compact
    memoryCache.Compact(0);
    Console.WriteLine($"compact");

    // count after compact
    Console.WriteLine($"count : {memoryCache.Count}");
}

→実行後

set
... wait 15 sec
count : 2
compact
count : 0

→デバッグでキャッシュの状態を確認
image.png

Compactメソッド実行後に有効期限が過ぎたキャッシュが全て消えました。

なお、Compactメソッドの引数に圧縮率を指定できますが、有効期限が過ぎたキャッシュのみ削除したい場合は"0"を指定すれば良いと思います。
"0"よりも大きい値を指定すると有効期限内のキャッシュも含めて(Priorityが低い順から)削除されます。

CompactメソッドはIMemoryCacheのインターフェースとして定義されていないため、Dependency Injectionを使用している場合はキャストすれば良いです。

IMemoryCache memoryCache;
:
if (memoryCache is MemoryCache)
{
    ((MemoryCache)memoryCache).Compact(0);
}
5
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2