alastairtree/LazyCache

Using ExpirationMode.ImmediateEviction ignores sliding expiration value

ExLuzZziVo opened this issue · 7 comments

Describe the bug
Hi, I am trying to use ExpirationMode.ImmediateEviction with sliding expiration in my project and this makes the entry to update every time I access it, ignoring the sliding expiration value

To Reproduce

            for (var i = 1; i <= 5; i++)
            {
                await _cache.GetOrAddAsync("1", entry => Task.FromResult(new Random().Next(1, 10)),
                    new LazyCacheEntryOptions {ExpirationMode = ExpirationMode.ImmediateEviction}
                        .SetSlidingExpiration(TimeSpan.FromSeconds(5))
                        .RegisterPostEvictionCallback((key, value, reason, state) =>
                        {
                            Debug.WriteLine($"{key} {((Task<int>) value).Result}");
                        }));
            }

Expected behavior
The sliding expiration time shouldn't be ignored

** Framework and Platform

  • OS: Windows 10 21H1
  • Framework: netcoreapp3.1
  • LazyCache Version: 2.1.3

I suspect the bug is because the immediate expiration stuff was never tested with sliding expiration. Looking at https://github.com/alastairtree/LazyCache/blob/master/LazyCache/Providers/MemoryCacheProvider.cs#L55 you can see it seems to cancel based on absolute expiration so some additional code would be needed detect sliding to make it work.

expiryTokenSource.CancelAfter(lazyPolicy.ImmediateAbsoluteExpirationRelativeToNow);

and note the related code in https://github.com/alastairtree/LazyCache/blob/master/LazyCache/MemoryCacheEntryOptionsExtensions.cs

Just released a new version, may well fix this. Let us know how you get on?

Install-Package LazyCache -Version 2.4.0

Just released a new version, may well fix this. Let us know how you get on?

Install-Package LazyCache -Version 2.4.0

Unfortunately this doesn't fix it

A workaround is to use MemoryCacheEntryOptions instead of LazyCacheEntryOptions

Hi @ExLuzZziVo, i am also seeing this issue please could you detail the workaround ?

A PR with a failing unit test would help track this down if you are willing?

Hi @ExLuzZziVo, i am also seeing this issue please could you detail the workaround ?

Just replace new LazyCacheEntryOptions {ExpirationMode = ExpirationMode.ImmediateEviction} with new MemoryCacheEntryOptions() in above example and it will work as expected

A PR with a failing unit test would help track this down if you are willing?

I will try to make a PR next week

@alastairtree There is a strange thing with the unit test for this issue. If I run the test method separately, it fails. If I run all the tests, it passes. I have tried to set the cache key manually but the result was the same.
My test method:

        [Test]
        public void GetFromCacheTwiceAtSameTimeOnlyAddsOnceWithSliding()
        {
            var times = 0;

            var lazyCacheEntryOptions = new LazyCacheEntryOptions { ExpirationMode = ExpirationMode.ImmediateEviction }
                .SetSlidingExpiration(TimeSpan.FromSeconds(20));

            var t1 = Task.Factory.StartNew(() =>
            {
                sut.GetOrAdd(TestKey, () =>
                {
                    Interlocked.Increment(ref times);
                    return new DateTime(2001, 01, 01);
                }, lazyCacheEntryOptions);
            });

            var t2 = Task.Factory.StartNew(() =>
            {
                sut.GetOrAdd(TestKey, () =>
                {
                    Interlocked.Increment(ref times);
                    return new DateTime(2001, 01, 01);
                }, lazyCacheEntryOptions);
            });

            Task.WaitAll(t1, t2);

            Assert.AreEqual(1, times);
        }