efcore: Distinguish cancellation from failure to allow different logging strategies
What problem are you trying to solve?
Right now, logs for exceptions thrown due to CancellationToken
cancellation are categorized as Microsoft.EntityFrameworkCore.Query[10100]
. I want to be able to keep logging all other kinds of query errors, but not ones for intentional cancellations. In my application, I’m passing aspnetcore’s HttpContext.RequestedAborted
token to my async EF query evaluations. If the client hangs up, there’s no reason to keep querying the database working to evaluate what are in some cases quite complex search queries in an intranet application (e.g. if the client changed their search criteria, or just left the page).
We’d like to not pollute our logs with these exceptions that are fully expected. We have an ActionFilter
where we handle the exception as far as aspnetcore is concerned, but EF itself still logs the error.
For example:
fail: Microsoft.EntityFrameworkCore.Query[10100]
An exception occurred while iterating over the results of a query for context type 'My.App.AppDbContext'.
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\AsyncIterator.cs:line 98
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\AsyncIterator.cs:line 98
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
Describe the solution you’d like
Create a dedicated log category for OperationCanceledException
s that are caused by userspace-provided CancellationTokens that I can then setup a regular logging filter against.
Alternatively, just do not log OperationCanceledException
at all if the CancellationToken
has been triggered, since this is generally always an intentional outcome if the user has passed a CancellationToken
to their query evaulations. This would be a much simpler path, but may have unintended consequences.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 48
- Comments: 28 (9 by maintainers)
Commits related to this issue
- Distinguish cancellation from failure Closes #19526 — committed to roji/efcore by roji 3 years ago
- Distinguish cancellation from failure (#26988) Closes #19526 — committed to dotnet/efcore by roji 3 years ago
I think this should have a label urgent-fix not consider-for-next-release. Logging system is spammed with those errors and we are waiting for this issue to be solved for a year.
I’m having this problem as well. I would like to provide some additional context. Sometimes cancellation can actually generate multiple errors, some of which cannot be identified as being caused by cancellation.
For example, if you trigger the cancellation token before the database connection is made, you’ll actually get two errors logged:
Error 1
Error 2
Error 2 is noise – and I’d love to be able to suppress it – but it is at least possible to conclude that the error is likely due to a cancellation token. You can use the presence of “TaskCanceledException” to filter it out.
However, if you look at Error 1, there is no indication that this was actually caused by cancellation. This can lead to unnecessary investigation. And there is also no way to filter it out as “noise”.
@ajcvickers since this is not a feature request but a bug report, I think that prioritizing it by putting it on the list containing feature requests ordered by the number of “thumbs up” is probably not a good approach. Can we start by correctly labeling it as a bug? Unless there is someone who doesn’t think this is a bug? 😃
This is now merged - cancellations are now logged via log new cancellation events (which are debug by default) rather than failure.
The default behavior is to identify OperationCanceledException as cancellation events. Providers can create their own IExecutionDetector and add specific logic for identifying cancellation exceptions as necessary. For SqlClient, since there’s apparently no way to identify cancellation from the exception, the cancellation token is checked instead, which isn’t ideal. We can fix this if SqlClient correct the problem on their side.
@ajcvickers Hi, is there any timeline where we can expect a fix for this bug? The way it is working currently basically renders all APIs that use
CancellationToken
useless. Unless someone doesn’t mind polluting his logs with flood of errors that aren’t really errors (highly unlikely for any production application). My scenario is the same as others in this issue - passing ASP.CORE HTTP request relatedCancellationToken
to handle scenarios like browser closed, F5, frontend auto complete searches etc.Please, reconsider fixing this sooner than v7.0. There is no workaround and it is causing serious troubles under heavy workload
Same here. My ASP.NET Core application is unnecessarily logging thousands of TaskCanceledException errors every day due to CancellationToken being passed to EF Core queries. It is insane that this hasn’t been resolved yet.
@sommmen This issue was about modifying the logging that EF does when there’s a cancellation, not about changing the exceptions that SqlClient throws. See https://github.com/dotnet/SqlClient/issues/26, which was linked earlier in this issue.
Similar in my case, when request is aborted I see 2 log entries with
Error
severity level:and
Second one contains information regarding cause which was
OperationCanceledException
, but first one has no such indicator. Still I’m far away from parsing some string property to check if it is operation cancelled - I would need than to recheck this with my request cancellation token to be 100% sure this something I can suppress or not.Best solution for my scenario would be a setting on DbContext allowing to lower severity of all logs from
Error
toDebug
that are outcome of a cancellation token being cancelled but only for the one that was passed to the root invocation.Here’s a helper function I use in my log filter to determine if it’s an exception that I can suppress.
@brockallen we plan to address this in EF Core 7.0.
Just wondering if this is still partially an issue? I’m still seeing some
Microsoft.EntityFrameworkCore.Database.Connection.ConnectionError
error logs when a task is cancelled. MyOperationCanceledException
handler is properly triggering for the same request, so it seems the newOperationCanceledException
is throwing, but EF still logs thatDatabase.Connection.ConnectionError
somewhere along the way. It seems that @roji had some back and forth with someone here about the same/similar issue, but I don’t see a resolution.I was thinking about configuring EF to drop
RelationalEventId.ConnectionError
logs to warning, but that doesn’t seem great if there’s actually some other non-cancellation-related error.More details in case my specific settings are related:
Microsoft.EntityFrameworkCore.SqlServer
7.0.3TransactionScope
withIsolationLevel.ReadUncommitted
Database.SetCommandTimeout(60)
AsNoTracking()
Thank you @roji!
EF itself considers any OperationCanceledException as meaning that the operation was canceled, e.g. for the purposes of logging an error vs. a cancellation; I’d suggest you do the same. In the general case you shouldn’t be catching TaskCanceledException, but rather OperationCanceledException.
@Liero
The project I’m working on uses a custom middleware class to suppress the unneeded exceptions. Although everyone’s use case will be different, I’ll share my approach here in case it’s useful for anyone else.
The middleware class:
And we add the middleware to the
Startup.cs
’sConfigure
method as so:@Liero I do intend to do this relatively early in the 7.0 work cycle, and at that point you’ll be able to use 7.0.0 previews (which are generally very stable). However, there will not be a minor release before 7.0 with this change.
You may be able to work around this by implementing filtering outside of EF Core, i.e. by searching for TaskCanceledException in the log message text etc.