Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net 4.0 CacheItemPolicy sliding expiration incorrect?

Tags:

.net-4.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.Caching;

using Xunit;

namespace Demo.Caching.Test
{
    class MemoryCacheManagerTest
    {
        [Fact]
        public void Test()
        {
            CacheItemPolicy policy = new CacheItemPolicy();
            policy.SlidingExpiration = TimeSpan.FromSeconds(1);

            MemoryCache.Default.Set("cacheKey4", 4, policy);
            Assert.Equal(4, MemoryCache.Default.Get("cacheKey4"));
            System.Threading.Thread.Sleep(600);
            Assert.Equal(4, MemoryCache.Default.Get("cacheKey4"));
            System.Threading.Thread.Sleep(600);
            Assert.Equal(4, MemoryCache.Default.Get("cacheKey4"));
            // Here I get error
            // Expected: 4, Actual: (null)

            System.Threading.Thread.Sleep(1000);
            Assert.Null(MemoryCache.Default.Get("cacheKey4"));
        }
    }
}
like image 375
peng Avatar asked Oct 03 '10 04:10

peng


2 Answers

Possibly the reason is that Sleep is non-deterministic. It does not pause your thread for 600 milliseconds. It pauses the thread for at least 600 ms. It could very well go over the 1 second sliding expiry you've set without you realising.

like image 161
Colin Mackay Avatar answered Nov 12 '22 07:11

Colin Mackay


This is not, as the other answers have said, due to Thread.Sleep() taking longer than expected.

MemoryCache appears to be pretty imprecise with its sliding timer. I'm guessing that this is so they can avoid locking, and keep up the cache's performance.

  • If you set a sliding expiration of 1 second or less, the cache items will be evicted after a second regardless of how many times they are accessed.
  • Between 1 and 2 seconds, there still appears to be a chance of evicting the cache items. This may depend somewhat on the load that the computer is under, and it definitely depends largely on when the items are accessed, but not as predictably as you'd think. For example:
    • if I set the expiration to anywhere from 1.001 seconds to 1.24 seconds, and sleep for 200 or 250 milliseconds between accesses, it keeps the value in the cache; but if I sleep for 201 or 249 milliseconds, the item gets evicted.
    • if I set the expiration to 1.25 seconds, I can sleep for up to 624 milliseconds without a problem, but if I hit 625 milliseconds then the cache item gets evicted.
  • Once you hit a 2-second expiration, it appears to work correctly even if you only access the item once, just before the expiration deadline.

So I guess the lesson is to not rely on sliding expirations to work correctly for values under 2 seconds. This may have something to do with the PollingInterval being set to 2 seconds.

Code:

var span = TimeSpan.FromSeconds(1); // change as necessary
var sw = new Stopwatch();
var cache = new MemoryCache("testcache");
sw.Start();
cache.Add("test", "hello", new CacheItemPolicy{SlidingExpiration = span});
Console.WriteLine(sw.ElapsedMilliseconds);
for (int i = 0; i < 40; i++)
{
    Console.WriteLine(sw.ElapsedMilliseconds + ": " + cache.Get("test"));
    Thread.Sleep(50); // change as necessary
}
like image 43
StriplingWarrior Avatar answered Nov 12 '22 06:11

StriplingWarrior



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!