-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query: Allow non static methods in top level projection #13048
Comments
Note for triage: I was able to reproduce this. The key part is that the query includes a call to an opaque instance method on the the repository: public IQueryable<int> GetInt()
{
return _context.TestEntities.Select(t => HashCode(t.DateRange1));
}
private int HashCode(string dateRange1)
{
return dateRange1.GetHashCode();
} Note that the repository holds a reference to the context. Removing the opaque call makes the leak go away: public IQueryable<int> GetInt()
{
return _context.TestEntities.Select(t => t.DateRange1.Length);
} |
Morning, Last Friday I tried to keep to just the bare bones so I did not run on and on. I'll add a bit more detail today. I took a memory dump of a web api process running at GB's of memory when it should have been under 500 MB like all the others. I found that it was holding onto 50k context objects each with 300 KB of memory reserved. After a review of how we are handling context disposal and knowing that every other web api was not having this issue. I reviewed commits till i found the culprit. A developer wrote a couple repository helper methods, that are called during the IQueryable. Best practices say they should have been static if they could have been and they could have. The helper methods are more complex than the GetHashCode I used in the example here. It seems that when EF Core compiles the IQueryable it stores any such methods in a MemoryCache. Probably a really good idea for performance. But. Disposing of the context does not release those methods from the MemoryCache. Which is the Memory Leak! The reason that making the helper methods static negates this memory leak is due to how static works in the language. Without static, EFCore is keeping a reference to each instance of the repository and it's helper method, which happened to hold a reference to the context. Thereby not allowing GC to pick it up. With static, EFCore only keeps 1 reference to the "static" helper method. So though its still arguably a memory leak, it is very minor. I hope this helps understanding the issue. |
@ksmithRenweb Sorry, I should have said that we know what the issue is. Deciding what to do about it is a different matter--it plays into #12795 because behavior is a consequence of how we evaluate the opaque method call on the client. We're considering changing some of this in 3.0. |
Outcomes from triage discussion: detect when something problematic is being captured and then warn and don't cache (since cache entry will never be used anyway). |
For triage: discuss workarounds. |
We investigated workarounds here, but the options are limited. In general, the workarounds are all about preventing state being captured where that state causes problems. In the specific cases here, that state is the context instance. For this, three things to try are:
|
We have encountered this issue yesterday. Actually this memory leaks was terrorizing us for quite long period, but recently we were able to trace source. I think, that EF should throw an exception if regular method being used inside Expression Tree used to generate DB query. I understand that adding such behavior now could break existing code (despite it's most likely work with memory leaks, unless methods are static), so I'd suggest adding configuration flag, that would cause Exception on attempt to use Expression, which is using method defined in code, which don't map to any SQL function. P.S. in our case Controllers was captured inside Expression, which pooled lots of objects inside cache, like HttpContext and Request/Response objects. |
@ajcvickers is there any good way of detecting code blocks which potentially could cause this issue via static code analysis? My team manages many different service endpoints with 1000s of lines of EF code doing all kinds of queries. Manually going through all this is pretty... .expensive though and waiting for and then upgrading to 3.0 is also not an option. |
@MichaCo you should consider rewriting the code using proper Repository pattern, or Command Query Separation (CQS), from my experience it's highly unlikely to have such bug when using these patterns. Regarding analysis, perhaps some rules could be written using Roslyn, but I'm not sure. I'm advocating, that checker be added to EF, to display a warning, or event fail with an exception if config flag provided, upon detection of such usage. EF 6 wasn't allowing such usage. |
@MichaCo Only thought I have is for a rule that enforces methods be static if they can be. I can think of scenario's where this wouldn't help, but it should catch most cases. |
@MichaCo @Mart-Bogdan Oops commented on the wrong issue. Sorry!
|
@MichaCo I'm not aware of any static analysis tools that exist, but calling instance methods on the DbContext would be the the thing to check for. |
We discussed this recently with a customer that hit this while porting a codebse that uses LINQ to SQL to EF Core, and wanted to capture some of the ideas discussed:
|
Looking at expression tree, it is not possible to handle reference to instance (or even properties) of enclosing class same way as other reference to local variables. We need to figure out a way to identify such references. In short term we can stop caching such tree. cc: @roji |
Okay to throw for 3.0. |
As a victim (and survivor) of this issue, I think it would be better if we can track the number of cached compiled query so it is easier to monitor whether we are leaking something into the cache or not. Now I have to do it by hacking into DbContext's service provider and get IMemoryCache, assuming that it is MemoryCache and read the Count property, again assuming that the cache instance is used exclusively for compiled query cache. |
Work for 3.0 is being tracked in #17069 |
Since 3.0, now we throw exception when we capture something problematic in the query cache entry.
|
I have the same problem, even if I dispose the context the Memory goes up. Any news ? |
@toffuser - Which version of EF Core are you using? Starting from EF Core 3.0, this throws exception and forces users to use static methods only so you will not run into memory growth. |
same problem here, any news ? |
@LucasLopesr see @smitpatel's comment just above - which version of EF Core are you using? |
Under a specific condition, disposed context's are not being released with a GC. In production it is rapidly increasing memory usage.
Memory Analysis done with Jetbrains dotMemory.
Attached working project:
ReproduceEFCoreMemoryLeakNonStaticHelperMethods.zip
Sample from attached code:
Occurs in:
EF Core 2.0.1
EF Core 2.1.1
The text was updated successfully, but these errors were encountered: