SqlTransaction and TransactionScope leak isolation level
See original GitHub issueThe 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:
SqlConnection.ClearAllPools();
var conn = new SqlConnection(new SqlConnectionStringBuilder { DataSource = @".\sqlexpress", IntegratedSecurity = true }.ConnectionString);
Action printIsolationLevel = () =>
{
var cmd = conn.CreateCommand();
cmd.CommandText = @"SELECT CASE transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncommitted'
WHEN 2 THEN 'ReadCommitted'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions
where session_id = @@SPID";
Console.WriteLine(cmd.ExecuteScalar());
};
conn.Open();
printIsolationLevel(); // "ReadCommitted"
conn.Close();
using (var scope = new TransactionScope(
TransactionScopeOption.RequiresNew,
new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.Serializable }))
{
conn.Open();
printIsolationLevel(); // "Serializable"
conn.Close();
}
conn.Open();
printIsolationLevel(); // "Serializable" !!?
conn.Close();
Issue Analytics
- State:
- Created 6 years ago
- Reactions:8
- Comments:20 (11 by maintainers)
Top Results From Across the Web
How to prevent leak of transaction isolation level in pooled ...
I am using System.Data.SqlClient (4.6.1) in a dot net core 2.2 project. SqlClient maintains a pool of connections, and it has been reported...
Read more >Plugging isolation level leaks in SQL Server
As a quick refersher, the isolation level of a transaction defines how the database handles locking and concurrency. A lower level provides ...
Read more >SET TRANSACTION ISOLATION LEVEL (Transact-SQL)
A transaction cannot be set to SNAPSHOT isolation level that started with another isolation level; doing so will cause the transaction to abort....
Read more >Transactions in .NET: From Basics to Best Practices
This isolation level provides a snapshot of the data at the start of a transaction, allowing for consistent reads without acquiring locks. It ......
Read more >Things I've learned about SQL Server the hard way - PJSen Blog
The isolation level is an attribute of a connection between a client and the database server. A connection is called session in SQL...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@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.
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 (
my-service
andmy-service-snapshot
), so when the isolation mode sticks to the connections in the snapshot pool it doesn’t hurt anything. It also helps with troubleshooting on the server-side because we can see which isolation mode the application was intending to use.