53

I have a problem with an MVC 3 application that is using the new .NET 4 System.Runtime.Caching MemoryCache. I notice that after a seemingly unpredictable time, it stops caching stuff, and acts like it's empty. Consider this bit of code that I took straight from a test View in ASP.NET MVC:

MemoryCache.Default.Set("myname","fred", new CacheItemPolicy() { SlidingExpiration = new TimeSpan(0,5,0) });
Response.Write(MemoryCache.Default["myname"]);

When it's working, predictably "fred" gets printed. However, when the problem starts to occur, despite the Set(), the value of MemoryCache.Default["myname"] is null. I can prove this by setting a breakpoint on the Response.Write() line and directly setting and reading from the cache using the Immediate Window - It just won't set it and stays null! The only way to get it working again then is to cause an AppDomain recycle.

Intriguingly I can provoke the problem into occurring when the app is working normally by breaking on the Response.Write() line and running MemoryCache.Default.Dispose(). After that, MemoryCache.Default is not null itself (why is this?), but won't save anything set on it. It doesn't cause any errors, but just won't save anything.

Can anybody verify this and explain? As I believe I have discovered, when the app stops working on its own, something is Disposing MemoryCache.Default, but it's not me!


UPDATE

Well, I'm sick of this prob now! CLRProfiler doesn't seem to work with MVC 3. SciTech's CLR tool was good - so was RedGate ANTS. But all they told me was that the MemoryCache object is being disposed by something! I also proved (via a timestamp print) that a PartialView on my page that should be cached (specified by OutputCacheAttribute) stops being cached after a few minutes - it starts refreshing with every call to the page. Just to clarify the environment, I am running directly on the IIS 7.5 server on my development workstation running Win 7 Ultimate. The memory tools mentioned above suggest I am only using about 9mb of memory in terms of objects in play.

In desperation I have changed my caching code to first search for an ambient HttpContext to hook onto and use its Caching functionality, if one's available. Early tests show this is reliable, but it feels like a nasty hack.

Am getting the feeling that MemoryCache and OutputCache aren't warranted to work with MVC 3...

James McCormack
  • 9,217
  • 3
  • 47
  • 57
  • 1
    According to msdn, the dispose method is called on appdomain unload. http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx – gislikonrad Sep 14 '11 at 21:14
  • 1
    That does seem to be what's happening. However, to service the next web request another appdomain instance must be spun up, right? So I wonder why MemoryCache.Default isn't then binding to the *new* one? – James McCormack Sep 14 '11 at 21:17
  • Irregardless of the .net-4.0 bug, isn’t calling `Set()` immediately followed by `Get()` incorrect? The value you set could have been flushed in the meantime even without the cache being disposed… – binki Aug 20 '15 at 20:27

5 Answers5

71

So, here's some news. We looked into this and YES, this is a bug in .NET 4.

The good news is that it was fixed in .NET 4.5, so if you can, update your installation to .NET 4.5 and you're solid.

The other good news it that this fix has been back-ported to .NET 4 and will be available as a QFE (Quick Fix...a one off fix you'll apply) #578315. It was backported/fixed just days ago and it should be out ASAP. I'll try to get an exact date, but it's soon.

The other other good news is that there's a workaround for this on .NET 4 before the QFE. The workaround is weird, but it could unblock you.

using (ExecutionContext.SuppressFlow())     {
          // Create memory cache instance under disabled execution context flow
         return new YourCacheThing.GeneralMemoryCache(…);
}

Hope this helps.

UPDATE: The Hotfix is http://support.microsoft.com/kb/2828843 and you can request it here: https://support.microsoft.com/contactus/emailcontact.aspx?scid=sw;%5BLN%5D;1422

Scott Hanselman
  • 17,712
  • 6
  • 74
  • 89
  • Yes, edited above and duplicated here: The Hotfix is http://support.microsoft.com/kb/2828843 and you can request it here: https://support.microsoft.com/contactus/emailcontact.aspx?scid=sw;%5BLN%5D;1422 – Scott Hanselman Jun 04 '13 at 18:06
  • Am I missing something? It seems like none of the six issues fixed in that hotfix have anything to do with MemoryCache. – John Fouhy Jun 04 '13 at 23:00
  • 2
    Nope, you're not missing anything. I'm talking to the guy who wrote that article. It's lame, I know, but the fix is in there, promise. – Scott Hanselman Jun 05 '13 at 23:41
  • They are adding the details to the KB. – Scott Hanselman Jun 06 '13 at 17:44
  • 1
    We've updated the KB with details. This is the fix: http://support.microsoft.com/kb/2828843 – Scott Hanselman Jun 15 '13 at 23:44
  • Sorry to ressurect this but I stumbled upon this randomly. Just curious, was the cause the same as http://connect.microsoft.com/VisualStudio/feedback/details/675188/a-memorycache-hosted-in-asp-net-sometimes-breaks-in-its-timer-callback-to-update-the-cache-size ? I reported that back in 2011 with the comment "you might want to look into this behavior", looks like someone might have! Looks like I can get rid of that code and comment of "DONT TOUCH THIS!!!" – Steve Dec 28 '13 at 22:27
  • 3
    If you have sth like in your config, you may still have the bug. – myuce Nov 02 '15 at 09:49
  • @myuce I have the entry and I can still reproduce the bug. What should I do? I am on .NET 4.5, this is driving me crazy! – hyankov Dec 17 '16 at 09:15
6

We have the same problem. I confirm that after some period of time cache became disposed. It's private field _disposed became 1. I am sure that I don't have call to cache.Dispose in my code. But when I looked at code of MemoryCache with Reflector I saw, that in constructor it subscribes on two events

domain.DomainUnload += eventHandler;
domain.UnhandledException += exceptionEventHandler;

private void OnAppDomainUnload(object unusedObject, EventArgs unusedEventArgs)
{
  this.Dispose();
}

private void OnUnhandledException(object sender, UnhandledExceptionEventArgs eventArgs)
{
  if (!eventArgs.IsTerminating)
    return;
  this.Dispose();
}

Both of these event handlers have call to Dispose. May be after some domain recycling in IIS it causes domain unload, but keeps cache in memory(i'am not shure if it is possible).

Dima
  • 177
  • 1
  • 4
3

I have been experiencing the exact same symptoms. I have finally resulted to using the System.Web.Cache class instead and hooking into HttpContext.Cache. It has been working perfectly for the last 3 days..

Grant
  • 11,138
  • 32
  • 94
  • 140
2

See also these links related to the same problem.

MemoryCache gets disposed after PollingInterval when used in WebApp in Integrated Pipeline mode

http://connect.microsoft.com/VisualStudio/feedback/details/764911/memorycache-gets-disposed-after-pollinginterval-when-used-in-webapp-in-integrated-pipeline-mode

MemoryCache get in Disposed state Magically

http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/1233ffb3-6480-431b-94ca-1190f96cf5f6

Alex Nolasco
  • 18,750
  • 9
  • 86
  • 81
1

The MemoryCache will automatically evict items if it hits it's memory limit. This could be happening in your case, do you have a lot of items in the cache?

You can control the limits with configuration. By default it optimises based on the available memory.

Certainly calling Dispose will stop the MemoryCache instance working as it will clean up all unmanaged resources ready for disposal. You should only call Dispose if you do not intend to use the MemoryCache any more. I don't think this is necessary the problem in your case, other than when you call it.

TheCodeKing
  • 19,064
  • 3
  • 47
  • 70
  • Here we have another problem. The default config is to allow the cache to use up to 98% of available memory and apparently a hard limit of 0.8GB. I'm absolutely sure I'm only using a few hundred Kb at most, but I can't find a way to show how much memory MemoryCache is actually using at any moment of time - any ideas? – James McCormack Sep 14 '11 at 21:20
  • Also I'm pretty sure that eviction doesn't Dispose the cache instance - in which case when I add the simple string "fred", it should store it, right? – James McCormack Sep 14 '11 at 21:24
  • I'd add a CacheEntryChangeMonitor and see what events fire when you add the item that doesn't stick. See if it gets evicted immediately. Unfortunately I don't think there is a way get the current cache usage, perhaps [CLRProfiler](http://www.microsoft.com/download/en/details.aspx?id=14727) could help? Yes eviction doesn't call dispose, the only reason I can think it doesn't add is because it's being evicted immediately. – TheCodeKing Sep 14 '11 at 21:31
  • Thanks for the tips, I'll let you know what I find. – James McCormack Sep 14 '11 at 21:35
  • It looks like MemoryCache has a bug where items [don't actually get evicted](http://connect.microsoft.com/VisualStudio/feedback/details/661340/memorycache-evictions-do-not-fire-when-memory-limits-are-reached). It seems more likely that some code somewhere in your codebase is calling dispose. A `RemovedCallback` should confirm if the reason is `CacheSpecificEviction`. – TheCodeKing Sep 14 '11 at 22:08
  • 2
    @JamesMcCormack Did you find a solution to this? I am having the same issue and I notice that this answer has been accepted but I am unsure if you actually found a solution. – zaq Dec 02 '11 at 21:08
  • See the Update to my Question above - I ended up creating a caching service class in my logic layer that uses `HttpContext.Current` if it's available, otherwise it falls back to MemoryCache. Seems to work, even though it introduces a dependency on `System.Web` that I didn't want. – James McCormack Dec 03 '11 at 17:18
  • 1
    Thanks. It's frustrating to have to add that dependency, I had also hoped to avoid this. Hopefully this bug will get worked out soon since it makes the memory cache more or less useless. – zaq Dec 05 '11 at 17:54
  • 2
    I have the same problem...memory cache get disposed magically – Felipe Pessoto Aug 31 '12 at 12:02
  • Same here. Once disposed, it stops caching, the irony! – Alex Nolasco Mar 22 '13 at 03:12
  • We've updated the KB with details. This is the fix: http://support.microsoft.com/kb/2828843 – Scott Hanselman Jun 15 '13 at 23:44