Подтвердить что ты не робот

СкользящийExpiration и MemoryCache

Глядя на документацию для MemoryCache Я ожидал, что если бы объект был доступен в течение периода истечения срока, период был бы обновлен. Честно говоря, я думаю, что я вывел из названия "Скользящий" столько, сколько угодно.

Однако, как видно из этого теста

   [Test]
    public void SlidingExpiryNotRefreshedOnTouch()
    {
        var memoryCache = new MemoryCache("donkey")
        {
            {
                "1",
                "jane",
                new CacheItemPolicy {SlidingExpiration = TimeSpan.FromSeconds(1) }
            }
        };
        var enumerable = Enumerable.Repeat("1", 100)
            .TakeWhile((id, index) =>
            {
                Thread.Sleep(100);
                return memoryCache.Get(id) != null; // i.e. it still exists
            })
            .Select((id, index) => (index+2)*100.0/1000); // return the elapsed time
        var expires = enumerable.Last(); // gets the last existing entry 
        expires.Should().BeGreaterThan(1.0);
    }

Он терпит неудачу и демонстрирует поведение, которое объект выбрасывается после завершения TimeSpan независимо от того, был ли доступ к объекту. Запрос Linq выполняется в операторе enumerable.Last();, после чего он будет выполняться только в том случае, если кеш не истек. Как только он остановится, последний элемент в списке покажет, как долго элемент находился в кеше.

Для ясности Этот вопрос касается поведения MemoryCache. Не запрос linq.

Ожидается ли это еще одно ожидание (т.е. истечение не с каждым касанием)? Существует ли режим, который продлевает срок жизни объектов, которые "касаются"?

Обновить. Я нашел, даже если я написал обертку вокруг кеша и повторно добавил объект обратно в кеш каждый раз, когда я его получил, а другой SlidingExpiration по-прежнему только удостоил начальную настройку. Чтобы заставить его работать так, как я хотел, мне пришлось физически удалить его из кеша, прежде чем снова его добавить! Это может привести к нежелательным условиям гонки в многопоточной среде.

4b9b3361

Ответ 1

 ... new CacheItemPolicy {SlidingExpiration = TimeSpan.FromSeconds(1) }

Это не описано в MSDN. Вам было немного не повезло, 1 секунды недостаточно. По волосам используйте 2 секунды, и вы увидите, что он работает так же, как вы надеялись. Тинкер еще немного с FromMilliseconds(), и вы увидите, что ~ 1.2 секунды - это счастливый минимум в этой программе.

Объяснение этого довольно сложно, я должен говорить о том, как MemoryCache избегает необходимости обновлять скользящий таймер каждый раз, когда вы получаете доступ к кешу. Как вы думаете, это относительно дорого. Возьмем ярлык и перейдем к соответствующему Исходному исходному коду. Достаточно мало, чтобы вставить здесь:

    internal void UpdateSlidingExp(DateTime utcNow, CacheExpires expires) {
        if (_slidingExp > TimeSpan.Zero) {
            DateTime utcNewExpires = utcNow + _slidingExp;
            if (utcNewExpires - _utcAbsExp >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < _utcAbsExp) {
                expires.UtcUpdate(this, utcNewExpires);
            }
        }
    }

CacheExpires.MIN_UPDATE_DELTA - это главное, это предотвращает вызов функции UtcUpdate(). Или, говоря иначе, нужно как минимум MIN_UPDATE_DELTA потратить время, прежде чем оно будет обновлять скользящий таймер. Класс CacheExpired не проиндексирован справочным источником, намек на то, что он не совсем доволен тем, как он работает:) Но достойный декомпилятор может показать вам:

static CacheExpires()
{
    MIN_UPDATE_DELTA = new TimeSpan(0, 0, 1);
    MIN_FLUSH_INTERVAL = new TimeSpan(0, 0, 1);
    // etc...
}

Иными словами, жестко закодировано до 1 секунды. Невозможно изменить его прямо сейчас, это довольно уродливо. Требуется ~ 1,2 секунды для значения SlidingExpiration в этой тестовой программе, потому что Thread.Sleep(100) фактически не спит в течение 100 миллисекунд, это требует немного больше. Или, говоря иначе, это будет 11-й вызов Get(), который позволяет скользящему таймеру скользить в этой тестовой программе. Вы так далеко не достигли.

Ну, это должно быть документировано, но я предполагаю, что это может измениться. На данный момент вам нужно предположить, что практическое время истечения срока действия должно быть не менее 2 секунд.