2-й вызов MemoryCache.Set () с тем же ключом стирает запись, если кеш заполнен - PullRequest
1 голос
/ 11 марта 2020

Это немного крайний случай, и я бы представил это как ошибку в репозитории, если бы смог ее найти ...

Рассмотрим следующий фрагмент LINQPad :

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() добавляется новая запись, а затем удаляется старая, что требует дополнительного места в кеше?

1 Ответ

2 голосов
/ 11 марта 2020

Это, похоже, относится к ошибке , которую я зарегистрировал (которая была отклонена). Вы можете увидеть источник этого здесь , и из того, что я прочитал, логика c в вашем (и моем) случае работает следующим образом:

  • Запрошенный элемент добавления.
  • Размер был бы превышен, если бы элемент должен был быть добавлен (UpdateCacheSizeExceedsCapacity()), поэтому отклоните запрос (молча, что я и возражал).
  • Кроме того, поскольку это условие было обнаружено, нажмите off OvercapacityCompaction(), который удалит ваш элемент.

Похоже, что существует состояние гонки, поскольку работа по уплотнению ставится в очередь в фоновом потоке; возможно, иногда ваш тест находит элемент, который все еще там?

Чтобы ответить на заданный вами c вопрос - нет, сначала он не добавляется, а затем удаляется избыток.

Редактировать:

Второй Get() всегда возвращает ноль ... Я пропустил некоторую дополнительную обработку существующей записи, если найдена (хотя это не улучшает результат). Прежде чем проверять, будет ли размер превышен путем добавления элемента, есть следующее:

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

Т.е., если он находит вашу существующую запись, он помечает ее как выселенную. Затем он применяет тест UpdateCacheSizeExceedsCapacity(), не считая того, что он просто выселил существующую запись (что, возможно, и могло бы).

Позже, все еще в Set() / SetEntry(), в превышающей емкости case, он делает это:

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

..., что немедленно удаляет предыдущую запись. Так что, если и когда OvercapacityCompaction() (или ScanForExpiredItems()) получит это, не имеет значения, он уйдет до возвращения из Set() / SetEntry().

(я также обновил источник ссылка выше на текущее значение; не меняет логи c).

...