Slow read from SqlDataReader
See original GitHub issueDescribe the bug
We have an issue with an Azure Function that runs a query against a Azure SQL Server. The function uses a SqlDataReader, and the command / query execution is pretty fast, but reading the results is sometimes slow. It does not happen very often, but when it happens, there is a certain pattern, the TDS packets seem to be delayed. For example, this is a log snippet:
8/6/2021, 8:46:55.128 AM 20288 - ScopeEnter - SqlDataReader.ReadAsync | API | Object Id 1
8/6/2021, 8:46:55.128 AM 20288 - ScopeEnter - SqlDataReader.TryReadInternal | API | Object Id 1
8/6/2021, 8:46:55.128 AM 20288 - ScopeLeave - Exit Scope 214540
8/6/2021, 8:46:55.128 AM 20288 - ScopeLeave - Exit Scope 214539
8/6/2021, 8:46:55.128 AM 20288 - ScopeEnter - SqlDataReader.ReadAsync | API | Object Id 1
8/6/2021, 8:46:55.128 AM 20288 - ScopeEnter - SqlDataReader.TryReadInternal | API | Object Id 1
8/6/2021, 8:46:55.128 AM 20288 - ScopeLeave - Exit Scope 214542
8/6/2021, 8:46:55.129 AM 20288 - ScopeLeave - Exit Scope 214541
8/6/2021, 8:46:55.374 AM 20288 - SNITrace - SNITCPHandle.Receive | INFO | Connection Id e8b7430d-7f47-410e-83dc-f7bccbe47f49, Data read from stream synchronously
8/6/2021, 8:46:55.375 AM 20288 - Trace - TdsParserStateObjectManaged.ReadSyncOverAsync | Info | State Object Id 4, Session Id e8b7430d-7f47-410e-83dc-f7bccbe47f49
8/6/2021, 8:46:55.375 AM 20288 - Trace - TdsParserStateObjectManaged.ReleasePacket | Info | State Object Id 4, Session Id e8b7430d-7f47-410e-83dc-f7bccbe47f49, Packet DataLeft 8000
8/6/2021, 8:46:55.375 AM 20288 - ScopeEnter - SqlDataReader.ReadAsync | API | Object Id 1
8/6/2021, 8:46:55.375 AM 20288 - ScopeEnter - SqlDataReader.TryReadInternal | API | Object Id 1
8/6/2021, 8:46:55.375 AM 20288 - ScopeLeave - Exit Scope 214544
8/6/2021, 8:46:55.375 AM 20288 - ScopeLeave - Exit Scope 214543
8/6/2021, 8:46:55.375 AM 20288 - ScopeEnter - SqlDataReader.ReadAsync | API | Object Id 1
8/6/2021, 8:46:55.375 AM 20288 - ScopeEnter - SqlDataReader.TryReadInternal | API | Object Id 1
If I understand the log correctly, there is a ~250ms delay when receiving the TDS packet, and, for this particular query, there are 100 packets which adds up to 20-30 seconds.
This seems to happen sporadically without any clear cause. Also, the SQL server isn’t busy at all at this time (<5% CPU and IO usage).
Further technical details
Microsoft.Data.SqlClient version: 3.0.0 .NET target: Core 3.1 SQL Server version: Azure SQL Server Operating system: Azure Function v3 on Windows
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (4 by maintainers)
Top Related StackOverflow Question
@valentiniliescu I made a Benchmark testing for this matter. The sample code could be found in this gist.
I have tested different scenarios.
Synccall with noCommandBehaviorSynccall withCommandBehavior.SequentialAccessAsynccall with noCommandBehaviorAsynccall with noCommandBehavior.SequentialAccessAsynccall withCommandBehavior.SequentialAccessand noConfigureAwait(false)Asynccall with noCommandBehaviorand noConfigureAwait(false)The results are as below:
// * Summary *
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1151 (21H1/May2021Update) Intel Core i7-8665U CPU 1.90GHz (Coffee Lake), 1 CPU, 8 logical and 4 physical cores .NET SDK=6.0.100-preview.4.21255.9 [Host] : .NET Core 3.1.17 (CoreCLR 4.700.21.31506, CoreFX 4.700.21.31502), X64 RyuJIT .NET Core 3.1 : .NET Core 3.1.17 (CoreCLR 4.700.21.31506, CoreFX 4.700.21.31502), X64 RyuJIT
Job=.NET Core 3.1 Runtime=.NET Core 3.1
It seems reading with Sequential access is causing the delay. Can you test it please?
For those who want to follow:
Run the following script on SSMS and make a database with 12000 rows
to create Stored procedure:
Can you also confirm that the stored procedure is something similar to what you have?
I’ve done some investigation. The difference in speed between sequential and non-sequential reads is caused by the extra overhead of using an
SqlSequentialTextReaderthat can handle forward only uncached reads from the tds data stream. In the default case the entire field can be read as a stream and a standard StringReader returned.For the case where individual reads are extremely slow there isn’t much that can be done without a reproduction.