Ini sedikit kasus Edge, dan saya akan mengirimkan ini sebagai bug di repo jika saya bisa menemukannya ...

Pertimbangkan cuplikan LINQPad berikut:

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
    });
}

Pertanyaan saya adalah, ketika menggunakan .Set(), apakah entri baru ditambahkan, kemudian yang lama dihapus, sehingga membutuhkan ruang ekstra yang dialokasikan dalam cache?

1
Anders 11 Maret 2020, 00:38

1 menjawab

Jawaban Terbaik

Ini sepertinya berhubungan dengan bug yang saya log (yang ditolak). Anda dapat melihat sumbernya di sini, dan dari apa yang saya baca logika dalam kasus Anda (dan saya) berfungsi seperti ini:

  • Tambahkan item yang diminta.
  • Ukuran akan terlampaui jika item itu akan ditambahkan (UpdateCacheSizeExceedsCapacity()) oleh karena itu tolak permintaan (diam-diam, itulah yang saya keberatan).
  • Selain itu, karena kondisi ini terdeteksi, mulailah OvercapacityCompaction(), yang akan menghapus item Anda.

Tampaknya ada kondisi balapan, karena pekerjaan pemadatan diantrekan ke utas latar belakang; mungkin kadang-kadang tes Anda menemukan item itu masih ada?

Untuk menjawab pertanyaan spesifik Anda - tidak, itu tidak terlebih dahulu ditambahkan kemudian kelebihannya dihapus.

Sunting:

Kembali Get() kedua selalu mengembalikan null... Saya melewatkan beberapa penanganan ekstra dari entri yang ada, jika ditemukan (meskipun tidak meningkatkan hasilnya). Sebelum memeriksa apakah ukurannya akan terlampaui dengan menambahkan item, ada ini:

if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
{
    priorEntry.SetExpired(EvictionReason.Replaced);
}

Yaitu. jika menemukan entri Anda yang ada, itu menandainya sebagai diusir. Itu kemudian menerapkan tes UpdateCacheSizeExceedsCapacity(), tanpa memperhitungkan bahwa itu hanya mengusir entri yang ada (yang bisa dibilang bisa).

Kemudian, masih dalam Set()/SetEntry(), dalam kasus melebihi kapasitas, ia melakukan ini:

if (priorEntry != null)
{
    RemoveEntry(priorEntry);
}

...yang segera menghapus entri sebelumnya. Jadi apakah dan kapan OvercapacityCompaction() (atau ScanForExpiredItems()) akan mendapatkannya tidak masalah, itu hilang sebelum kembali dari Set()/SetEntry().

(Saya juga telah memperbarui tautan sumber di atas ke nilai saat ini; tidak mengubah logika).

2
sellotape 11 Maret 2020, 08:23