2

The following test fails intermittently. It caches an item in MemoryCache with an absolute expiration time, and an update callback that should be called before the item is removed. However sometimes the callback is invoked before the test finishes, and sometimes not at all.

With a large enough buffer time it will always be invoked at least once. But that does not serve my purposes, since I require that the cache always attempts to update the data before it expired.

Now in my real world scenario I will not have 10 second expiration time and granularity, but it still bothers me that this test fails intermittently.

Anyone have thoughts on why this is happening?

Note: Also intermittently fails with 60 second expiry and 5 second buffer.

using System;
using System.Runtime.Caching;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class MemoryCacheTest
{
    private const double ExpiryInSeconds = 10;
    private const double ExpiryBufferInSeconds = 5;

    private readonly object updateItemCounterLock = new object();
    private int updateItemCounter = 0;

    [TestMethod]
    public async Task MemoryCacheUpdateTest()
    {
        // Set item in cache with absolute expiration defined above
        MemoryCache cache = MemoryCache.Default;
        CacheItem cacheItem = new CacheItem("key", "value");
        CacheItemPolicy cacheItemPolicy = new CacheItemPolicy
        {
            AbsoluteExpiration = DateTimeOffset.Now + TimeSpan.FromSeconds(ExpiryInSeconds),
            UpdateCallback = new CacheEntryUpdateCallback(this.UpdateItem)
        };
        cache.Set(cacheItem, cacheItemPolicy);

        // Delay for absolute expiration time + buffer
        await Task.Delay(TimeSpan.FromSeconds(ExpiryInSeconds) + TimeSpan.FromSeconds(ExpiryBufferInSeconds));

        // Test that the update callback was invoked once
        Assert.AreEqual(1, updateItemCounter);
    }

    // Incrememnts the updateItemCounter
    private void UpdateItem(CacheEntryUpdateArguments args)
    {
        lock (updateItemCounterLock)
        {
            updateItemCounter++;
        }
    }
}
Ryan Burbidge
  • 192
  • 3
  • 13
  • 1
    If you notice, the AbsoluteExpiration value indicates the amount of time after which the item will be evicted. What that means is that it's the minimum amount of time it will stay valid, no the (ironically) absolute time. Absolute here refers to the fact that it's not a sliding time limit... not that it will be evicted at that exact moment. Basically, the item can be evicted at any time after the expiration, and that may be several seconds, or even minutes later. My tests indicate that the expiration can happen any time from 0 to 30 seconds after the expiration time, but it could be longer. – Erik Funkenbusch Dec 04 '14 at 06:48
  • 1
    This post seems to indicate that the internal implementation has some kind of hard wired 20 second timer: http://stackoverflow.com/questions/12630168/memorycache-absoluteexpiration-acting-strange Not sure if that's correct or not, but it certainly matches up with what i'm seeing in my test results. – Erik Funkenbusch Dec 04 '14 at 07:08

2 Answers2

0

I suppose calling new CacheEntryUpdateCallback is redundant. You can call: UpdateCallback = new CacheEntryUpdateCallback(this.UpdateItem) instead

mzagozda
  • 121
  • 1
  • 4
0

Since there was no solution to this question, I abstracted the MemoryCache methods that I needed into an interface and tested against that. At that point the test became invalid because I would have just been testing my own implementation of the interface.

Ryan Burbidge
  • 192
  • 3
  • 13