6

I'm trying to create a pool of connections to a third-party API, and have connections expire after an interval if they are not in use. When they expire, they need to be disconnected via the third-party API.

It appeared that MemoryCache (System.Runtime.Caching) would handle this. UpdateCallback seems to behave oddly, though.

A simple LINQPad example:

void Main()
{
    var cache = MemoryCache.Default;
    var policy = new CacheItemPolicy();
    policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(1);
    policy.UpdateCallback = Update;
    cache.Set("figkey", "fig", policy);

    Thread.Sleep(2000);

    object result = cache.Get("figkey");

    Console.WriteLine(result == null ? "null" : result);
}

public static void Update(CacheEntryUpdateArguments arguments)
{
    Console.WriteLine("got here");
}

If I run this, the output is:

fig

It does NOT output "got here".

If I comment out the line that starts with policy.UpdateCallback, the output is:

null

What am I doing wrong?

If there's a better way to accomplish my task, I'm open to alternative suggestions.

TrueWill
  • 25,132
  • 10
  • 101
  • 150

3 Answers3

4

I Think that the problem may be the Thread.Sleep because that also blocks the cache, as they run in the same thread. If you try to make dummy loops you will se that the update handler is triggered:

var i = 0;
for (var j = 0; j < 10000000; j++)
{
    for (var k = 0; k < 1000000; k++)
        i++;
    i--;
}
Console.WriteLine(i);

Instead of the sleep.

aweis
  • 5,350
  • 4
  • 30
  • 46
  • Your example works. I can get "got here" followed by "null" (which is what I would expect). This seems odd - I thought the cache expiration would occur on another thread. Thanks! – TrueWill Mar 10 '12 at 23:19
1

Just add Console.ReadLine() at the end of Main or replace Sleep with Console.ReadLine(), start your sample and wait about 10 seconds. You'll get the expected message. The timer inside MemoryCache is a bit slow.

But anyway, there certainly is a bug in MemoryCache: assigning UpdateCallback changes the behaviour, but it shouldn't.

In case if AbsoluteExpiration is set and the time is passed the method Get must return null without a dependency on existence of UpdateCallback.

BTW, RemovedCallback doesn't change the behavior.

SergeyT
  • 800
  • 8
  • 15
0

Just increase the sleep time to something much larger and you'll see the callback get executed.

Expiration does occur on another thread via a timer (you can see as much if you set a breakpoint within the callback). This is why either sleep or busy wait works.

Ryan Burbidge
  • 192
  • 3
  • 13