Skip to content

SqlDataReader returns stale data #980

@dawust

Description

@dawust

Describe the bug

On rare occasions SqlDataReader returns stale data from a previous query.
We read the columns of a SELECT statement sequentially using ExecuteReader and the corresponding Get operations, eg.

using (var sqlConnection = new SqlConnection(connectionString))
{
    using (var sqlCommand = new SqlCommand(sqlText, sqlConnection))
    {
        sqlConnection.Open();

        using (var reader = sqlCommand.ExecuteReader(CommandBehavior.SequentialAccess))
        {
            while (reader.Read())
            {
                var column1 = reader.GetInt32(0);
                var column2 = reader.GetDateTime(1);
                var column3 = reader.GetString(2);
                ...
            }
        }
    }
}

Eventhough the Get operations match the data types of the given SELECT statement in the SqlCommand, sometimes System.InvalidCastException is thrown.

System.InvalidCastException:
--
at Microsoft.Data.SqlClient.SqlBuffer.get_DateTime (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
at Microsoft.Data.SqlClient.SqlDataReader.GetDateTime (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)

When investating the DataTable of reader.GetSchemaTable() it was obvious that the result belonged to a preceding unrelated SELECT statement. We experience this behaviour a few dozen times a day over millions of queries. It seems that the SqlConnection is corrupted, then is put back in the ConnectionPool and subsequent queries using the same SqlConnection are affected as well until the connection is doomed eventually.

This behaviour is often accompanied by a subsequent request failing with the following InvalidOperationException exception "Internal .NET Framework Data Provider error 60."

System.InvalidOperationException:
   at Microsoft.Data.SqlClient.SqlDelegatedTransaction.Initialize (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at System.Transactions.TransactionStatePSPEOperation.PSPEInitialize (System.Transactions.Local, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)
   at System.Transactions.TransactionStateActive.EnlistPromotableSinglePhase (System.Transactions.Local, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)
   at System.Transactions.Transaction.EnlistPromotableSinglePhase (System.Transactions.Local, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)
   at System.Transactions.Transaction.EnlistPromotableSinglePhase (System.Transactions.Local, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)
   at Microsoft.Data.SqlClient.SqlInternalConnection.EnlistNonNull (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.ProviderBase.DbConnectionPool.PrepareConnection (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)
   at Microsoft.Data.SqlClient.SqlConnection.Open (Microsoft.Data.SqlClient, Version=2.0.20168.4, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5)

To reproduce

Unfortunately I don't have a repro. It seems to happen sporadically and is not related to heavy load.

Expected behavior

SqlDataReader only returns data corresponding to the given SqlCommand.

Further technical details

Microsoft.Data.SqlClient version: 2.1.2
.NET target: .NET 5.0
SQL Server version: SQL Server 2019
Operating system: Both Windows and Linux (Docker container mcr.microsoft.com/dotnet/aspnet:5.0)

Additional context
Both async and sync operations are affected. Some of our queries are wrapped in a TransactionScope.
To mitigate the issue, I exposed the Abort method of the SqlConnection using reflection, wrapped everything in a try/catch block and doom the connection manually after InvalidOperation or InvalidCast exceptions. By doing so subsequent queries are less prone to be affected.

Thank you in advance for any insights!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions