Wrong isolation level with Sql Azure and TransactionScope
See original GitHub issueDescribe the bug
On SQL Server, executing two queries inside a common TransactionScope, both are executed using the isolation level defined in the TransactionScope, as expected.
The same does not happen on SQL Azure: the second query is executed with the default Azure isolation level that is “Read Committed Snapshot”.
To reproduce
Here the code to reproduce the issue.
class Program
{
const string AzureConnectionString = "Server=tcp:xxxx.database.windows.net,1433;Initial Catalog=xxxx; Persist Security Info=False;User ID=xxx; Password=xxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout = 30;";
const string SqlServerConnectionString = "Server=localhost,1433;Initial Catalog=LCMS;Integrated Security=True";
static void Main(string[] args)
{
Console.WriteLine("SQL SERVER");
string connectionString = SqlServerConnectionString;
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
TestExec(connectionString); // Expected print: "read uncommitted"
TestExec(connectionString); // Expected print: "read uncommitted"
}
Console.WriteLine("SQL AZURE");
connectionString = AzureConnectionString;
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
TestExec(connectionString); // Expected print: "read uncommitted"
TestExec(connectionString); // Expected print: "read uncommitted", Actual: "read committed snapshot"
}
}
static void TestExec(string connectionString)
{
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var cmd = new SqlCommand()
{
CommandText = "dbcc useroptions",
Connection = conn
};
var reader = cmd.ExecuteReader();
while (reader.Read())
{
if (reader.GetString(0) == "isolation level")
Console.WriteLine(reader.GetString(1));
}
}
}
}
Expected behavior
A a new SqlConnection opened inside a TransactionScope must have the same isolation level defined in the TransactionScope.
Further technical details
Additional context The issue seems related to the connection pooling and the sp_reset_connection, because does not happen using Pooling=No in the connection string.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:9 (4 by maintainers)
Top Results From Across the Web
Is there some fundamental reason that TransactionScope ...
In the "On Premise" case the isolation level is not reset when the scope ends and a new transaction starts. This causes many...
Read more >entity framework transactions and sql azure default ...
a) Yes. By default the isolation level will be Serializable. http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx.
Read more >Snapshot Isolation in SQL Server - ADO.NET
It opens a second connection and initiates a second transaction using the SNAPSHOT isolation level to read the data in the TestSnapshot table....
Read more >SET TRANSACTION ISOLATION LEVEL (Transact-SQL)
The exception occurs when changing from any isolation level to SNAPSHOT isolation. Doing this causes the transaction to fail and roll back.
Read more >Working with Transactions - EF6
In either case, the isolation level of the transaction is whatever isolation level the database provider considers its default setting.
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
What is the progress on the fix for this and why is it marked as “low priority”?
We have an application using Snapshot isolation.
It does a read and if a condition is met then does a write.
We are dependent on the documented behaviour
If a snapshot transaction attempts to commit modifications to data that has changed since the transaction began, the transaction will roll back and an error will be raised.
to give us an optimistic concurrency exception - this is broken for us because the isolation level gets reset before the write that would raise this exception so we don’t get the expected exception,
EDIT:
Though a workaround is to add an explicit
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
in the relevant SQL
I am surprised this problem is still relevant.
See this code, which simply use a new TransactionScope() without any option:
And the output:
That means thay basically, the transaction scope transaction level is ignored when running more that one connection per TransactionScope (which is the recommended way to code : do not share a connection, instead open and close it when not needed, and expect the pooling will manage them.
I am surprise that all documentation still state that TransactionScope sets a Serializable scope by default and it’s never documented that the isolation level is not supported on Azure Sql Db (or buggy because it’s only ok for the first SqlConnection opened).
It does not seems that this bug is specific to Microsoft.Data.SqlClient (System.Data.SqlClient has the same behavior), and it should be redirected to the dotnet/runtime repo if TransactionScope or the documentation must be amended.
See also this: