Today we had a big break in production, where memory quickly disappeared from our web servers. This was traced back to the caching mechanism in Ninject (I think it was an activation cache or something else - not quite sure). After examining the issue, we came to the conclusion that we have round links in our callback.
class View { Presenter presenter; View() { //service locators are smelly, but webforms forces this uglyness this.presenter = ServiceLocator.Get<Func<View, Presenter>>()(this); this.presenter.InitUI(); } } class Presenter { CookieContainer cookieContainer; View view; Presenter(View view, CookieContainer cookieContainer) { this.view = view; this.cookieContainer = cookieContainer; } } class CookieContainer { HttpRequest request; HttpResponse response; CookieContainer() { this.request = HttpRequest.Current.Request; this.response = HttpRequest.Current.Response; } } Bind<Func<View, Presenter>>().ToMethod(ctx => view => ctx.Kernel.Get<Presenter>(new ConstructorArgument("view", view))); Bind<Presenter>().ToSelf().InTransientScope(); Bind<CookieContainer>().ToSelf().InRequestScope();
This is a representation of our code causing the problem. Apparently, what happened was that the area callback for CookieContainer was HttpContext.Current, and HttpContext.Current also referred to CookieContainer. This way, Ninject will never be able to trim cookieContainer instances from its cache, like CookieContainer instances where object callbacks are stored. When we change the CookieContainer area to transition, everything works fine, as we expected. But I'm still not quite sure why this happened, as it seems like it's a pretty common thing to do the right thing? Maybe not...
I am also confused, because I thought that if the callback object remained alive, as it were, then it should not be that Ninject simply dropped the same instance from the cache, seeing that the callback was still alive, so the instance should appear to be in scope? Why does ninject continue to receive new cookieContainer instances and cache them? I assume that there will be other problems associated with returning the wrong object, but this will at least be an error, not a memory leak.
My question is: a) have we fixed this error correctly? b) is there a recommended approach so that this does not happen again? c) Can I put a fix in the code to check for this type of cyclic dependency (if we got the diagnosis right)?
source share