开发者

Asp.Net 4.0 CacheItemPolicy sliding expiration incorrect?

开发者 https://www.devze.com 2023-01-19 00:07 出处:网络
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Caching;
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"));
        }
    }
}


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.


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
}


I had the same problem. But actually the problem is what @Colin said. The Thread.Sleep is taking over than a second.

So I made a test with a bigger delay

[TestMethod]
public void Sliding()
{
    MemoryCache.Default.Add("test", 1,
        new CacheItemPolicy
        {
            SlidingExpiration = new TimeSpan(0, 0, seconds: 5)
        });

    for (int i = 0; i < 15; ++i)
    {
        Assert.IsNotNull(MemoryCache.Default.Get("test"));
        Thread.Sleep(700);
    }
}

And it passed. I am not sure if it will always work -- probably not -- but I just confirmed that the sliding actually works.

0

精彩评论

暂无评论...
验证码 换一张
取 消