-
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
SqlTransaction and TransactionScope leak isolation level #96
Comments
TransactionsScope is in S.Transactions. Of course I don't know where the bug is.. |
Further investigations suggest that this may be a problem with |
The fact that the isolation level change goes on to pollute the connection pool makes the much more worrisome. However, even on a single connection we can see weird behavior where the transaction isolation level leaks out of the transaction scope:
|
@saurabh500 Including you because it looks like you were the last person to touch SqlConnection. This looks like a question for SqlConnection or SqlCommand. |
Right now SqlClient doesn't support TransactionScope in .Net Core . |
I performed some investigation, and realized this behavior is by design.
Default isolation level is This behavior is intended and described in TDS protocol design document (https://msdn.microsoft.com/en-us/library/dd339887.aspx). When a transaction object is disposed, it internally executes Detail payload of the
You can see this payload implementation at:
According to the spec document, |
@geleems it isn't true that isolation level only affects transaction. As long as SQL is in autocommit mode (the default), each statement executes in an implicit transaction using the current isolation level. This is what caused me to report the bug. This is particularly problematic because of connection pooling, since the isolation levels can leak and affect any other part of the application. |
I meant isolation level parameter is only consumed by method that creates transaction, which is
However, I identified it is a problem that isolation level is not cleaned up when connection moves back to pool. |
@geleems I agree that the behavior with pooling represents the biggest problem here; that's what burned us. Maybe it's a separate issue, but I do think that most callers to the API would expect the isolation level to reset on dispose despite the documentation that says otherwise. It seems weird that the recommended usage pattern would be to follow each transaction with another empty transaction just to get reasonable behavior. Much like the scoping of the foreach var (which changed in C#5), this seems like it could be one of those cases where despite the current behavior being intentional it is so confusing that it might be worth changing. |
Here's a link with some additional context on this issue: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/916b3d8a-c464-4ad5-8901-6f845a2a3447/sql-server-2014-reseting-isolation-level?forum=sqldatabaseengine |
As recently announced in the .NET Blog, focus on new SqlClient features an improvements is moving to the new Microsoft.Data.SqlClient package. For this reason, we are moving this issue to the new repo at https://github.com/dotnet/SqlClient. We will still use https://github.com/dotnet/corefx to track issues on other providers like System.Data.Odbc and System.Data.OleDB, and general ADO.NET and .NET data access issues. |
Thanks @divega . Hopefully this new package will provide a chance to fix wonky behaviors like this one! |
This behavior is surprising and the sort of thing that is hard to notice in happy-path testing, but causes issues in prod. The way we've worked around it is to change the application name for any connections that opt-in to use snapshot isolation. This has the practical effect of ADO.NET creating two connection pools ( |
I am encountering this now too, and I am surprised it is still unresolved. Any update? |
Additional repro: #1098 |
Actually, it can hurt if you use that connection to run stored procedures that start their own transaction. The stored procedure may not have used I think the confusion of all of this comes from the fact that the isolation level can be set via the construction of a disposable object (the transaction), rather than just a call directly on the connection. Confusing:
Not confusing:
|
@justinbhopper agree that is also a problem, but that's a whole separate issue which would cause pain even if the isolation level did not stick to a disposed connection. |
Also related: #146 |
We are still running into this problem, without using TransactionScope. |
Agreed, I think this should be fixed, at least with an option at connection instantiation time to reset the "new" (reused) connection to the database-default isolation level if necessary. It isn't confined to usages of TransactionScope. The desired example code: SqlConnection sqlConnection = new(connectionString) {ResetIsolationLevelOnReuse = true}; Or perhaps the setting should cause a reset at "close" time, after any commit or rollback, so that a network traffic penalty isn't incurred before every query? SqlConnection sqlConnection = new(connectionString) {ResetIsolationLevelOnClose = true}; An alternative might be to have an option in the TransactionScope. I had written some sample code for that, but I've now edited this to remove it, as I don't think it adequately solves the problem because the problem exists even without any explicit transactions. Without any of these, I am methodically going through my code and adding this T-SQL workaround at the end of every query that changes the isolation level. The GO; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; /* workaround for https://github.com/dotnet/SqlClient/issues/96 */ @DavoudEshtehari or @JRahnama what would it take to get this into High Priority or otherwise put it up for reconsideration? It is easily reproduced and perhaps one of the solutions above is elegant and simple? |
FWIW this seems like a bug to me, rather than something that the user should have to fix via some special opt-in flag. If there's worry of the fix affecting people who are relying on the current (buggy) behavior, I'd suggest an appcontext switch to maintain the current behavior at most... |
The TransactionScope class seems to "leak" it's isolation level to future queries on the same (pooled) connection.
I would expect the isolation level of the connection to be restored when the transaction ends or is disposed.
Here is a snippet which reproduces the behavior:
The text was updated successfully, but these errors were encountered: