Skip to content

Switch SqlDataReader.ReadAsync to SqlDataReader.Read in SqlSessionStateProviderAsync because of a known performance issue with reading large data asynchronously - #119

Closed
tjamin wants to merge 1 commit into
aspnet:mainfrom
tjamin:bugfix/switch_part_of_SqlSessionState_implementation_from_async_to_sync
Closed

Switch SqlDataReader.ReadAsync to SqlDataReader.Read in SqlSessionStateProviderAsync because of a known performance issue with reading large data asynchronously#119
tjamin wants to merge 1 commit into
aspnet:mainfrom
tjamin:bugfix/switch_part_of_SqlSessionState_implementation_from_async_to_sync

Conversation

@tjamin

@tjamin tjamin commented Dec 3, 2025

Copy link
Copy Markdown

In one project, we suddenly encountered a major performance issue. The problem arose after deploying a new release. We spent a considerable amount of time searching for the cause within our release. Ultimately, the issue was traced back to the SqlSessionStateProviderAsync.

Since we use Application Insights for logging and tracing, we observed unusual gaps after reading the ASP.NET session. Initially, it appeared as if the session had already been read, and then the gap occurred. After extensive analysis and significantly extending the SqlSessionStateProviderAsync with telemetry, we (my colleague @opneu and I) discovered that time was being lost within this session provider implementation.

The calls to SqlDataReader.ReadAsync and SqlDataReader.GetFieldValueAsync within SqlSessionStateProviderAsync were taking an extremely long time. Further research led me to this ticket:

dotnet/SqlClient#593

After switching to the synchronous versions of the aforementioned methods, the session could be loaded again within just a few milliseconds.

We attribute the sudden occurrence of the massive performance problems to the fact that, with the new release, we are writing more to the ASP.NET session than before the release.

Looking back at past logs, we can say that we were not only able to resolve the new, truly severe performance issue, but the average access times to the ASP.NET session have also been faster since the fix.

…nchronous versions in SqlSessionStateProvicerAsync because there exists a performance issue with reading large data asynchronously (dotnet/SqlClient#593)
@tjamin
tjamin force-pushed the bugfix/switch_part_of_SqlSessionState_implementation_from_async_to_sync branch from 565cf6a to 38675a3 Compare December 3, 2025 12:10
@tjamin

tjamin commented Dec 3, 2025

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

@StephenMolloy

Copy link
Copy Markdown
Contributor

Thank you for investigating this performance problem and submitting this change. The poor performance of large asynchronous reads in older versions of Microsoft.Data.SqlClient is real, and this PR demonstrates a substantial improvement for individual reads.

However, the change replaces the provider’s primary session-payload read with synchronous calls. That holds an ASP.NET worker thread while data is read from SQL Server and works against the central purpose of this async session-state provider. Although blocking reads can provide lower latency in isolation, making them the default could reduce scalability and increase request queuing under network latency or high concurrency. And the primary expectation for SqlSessionStateProvider**Async** is to be, well, asynchronous.

Microsoft.Data.SqlClient 7.0 provides a more direct solution: packet multiplexing for large async reads. Applications can upgrade their SqlClient reference and enable the improved async path by setting both compatibility switches to false:

<runtime>
  <AppContextSwitchOverrides value="Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour=false;Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni=false" />
</runtime>

This substantially reduces the large-read performance problem while preserving asynchronous I/O and its worker-thread scalability benefits. Packet multiplexing remains opt-in for compatibility, so applications should validate it against their workloads before deployment.

Given that an application-level upgrade addresses the underlying SqlClient behavior without making this provider synchronous, we have decided not to merge this PR and will close it. I'll add documentation recommending this configuration for applications with large session-state payloads (#124)

Microsoft documentation:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants