-
Notifications
You must be signed in to change notification settings - Fork 285
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
SqlDataReader.DisposeAsync throw SqlException Execution: Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding #672
Comments
@LIFEfreedom Can you provide more information on your connection string options and the query or the issue happens with any kind of query? |
@JRahnama |
@JRahnama are there any news |
Same problem. Last release didn't fix the issue. |
@LIFEfreedom on some other issues with similar error message we noticed that wrapping your connection, command and readers in |
@JRahnama This solution didn't help. Main problem is that Dispose and DisposeAsync shouldn't throw exceptions. https://docs.microsoft.com/ru-ru/previous-versions/visualstudio/visual-studio-2015/code-quality/ca1065-do-not-raise-exceptions-in-unexpected-locations?view=vs-2015&redirectedfrom=MSDN#dispose-methods |
I agree. As per my understanding .NET5 runtime is enforcing that behavior. We will adjust the code based on this rules to be compatible with .NET5 runtimes in future when we add .NET5 support to our driver. I do not have a exact date for that change, but I will look into changing this particular one to see if we can get the exception out of the dispose method in a simple PR at the middle of next week. |
@DenSmoke, @LIFEfreedom can you try with the fix proposed in PR #901 as well to see if that solves this issue please? That PR solves some issue with leaked exceptions in the driver. |
Looking at the history of SqlDataReader.cs, it looks like the Dispose() method was added in the initial commit of the NetCore code in 2015 but was never part of the NetFx code. I suspect it might have been added to ensure cleanup of server-side resources but the Close() call should have been wrapped in a try/catch to ensure Dispose() never throws. I don't know why it would have been added in NetCore but not NetFx. Makes me wonder whether the correct fix is to remove the method completely or add the try/catch around Close(). I lean towards adding the try/catch as there is a lot of important cleanup that happens in Close(). |
@David-Engel If I remember correctly, then DbDataReader is always disposable in .NET Framework because it inherits from Component, which is disposable. Initially Component was not included in .NET Core, and hence at that time DbDataReader was made explicitly disposable. There is a general problem here in that since all Db... classes were inherited from Component, it means that they were all disposable. This means some things are not designed to be disposable at the ADO.NET level, and yet have always implemented the IDisposable interface anyway. So, for Core, we tried to look at the existing provider ecosystem and decide which objects really need to be disposed. DbDataReader is in that list since some providers associate native resources with the data reader. So even though some providers such as SqlClient traditionally don't need to dispose the DbDataReader, at the ADO.NET level the contract from the consuming side is that it must be disposed. |
That's odd... I'm pretty sure that even back in .NET Framework with System.Data.SqlClient, disposing a reader on a non-MARS SqlConnection would allow executing another command on that same connection; not doing so would trigger a "reader is already open" exception. As @ajcvickers says, some ADO.NET objects are indeed IDisposable even though disposing them generally isn't required (this is usually the case of DbCommand); one could that this is a good thing given that System.Data is an abstraction, and some provider out there may need their DbCommand to be disposed too for some reason. But in any case, I think DbDataReader generally must be disposed across database providers before another command can be executed on the connection. And as is standard in .NET (long before .NET 5), Dispose should indeed never throw. |
@roji, @David-Engel while going through this issue, I noticed there are lots of |
Those are part of async calls so the disposable objects outlive the stack frame they are instantiated in. In particular the cancellation registration is created and becomes part of the call context and is then disposed of when the call has completed in CompleteAsyncCall which calls Dispose, which calls Clear.
The copy is taken so we can access the reference, the field is nulled so we have a safely cleared object for re-use (which is the major reason for the class existing), then dispose is called. At this point if dispose throws an error we can't end up with an invalid state on the call context. If dispose takes a long time it doesn't matter because the context was already ready to re-use. You could argue that dispose should never throw but the existence of this thread shows that it might be worth assuming that it could. So the logic there is thought through and deliberate and you need to be aware of the way that async calls are constructed and flow to follow the lifetimes. |
Describe the bug
I got an exception when trying to call DisposeAsync on a SqlDataReader
To reproduce
Start Sql Server, start command execution when Sql Server is under heavy load.
My fuction:
Expected behavior
I expected the application to continue working, but not an exception being thrown.
Further technical details
Microsoft.Data.SqlClient version: 2.0.0 (Assembly Microsoft.Data.SqlClient, Version=2.0.20168.4)
.NET target: Сore 3.1.6
SQL Server version: SQL Server 2016
Operating system: Ubuntu 18.04
The text was updated successfully, but these errors were encountered: