Skip to content

Documentation/code mismatch: split-query buffering and BufferedDataReader #39130

Description

@KyMback

Bug description

The documentation for EF Core split queries states:

"When split query is used, the resultsets of all but the last query are buffered — unless MARS is enabled on SQL Server."

See: https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying#internal-buffering-by-ef

However, this appears to be different from the behavior implied by the current EF Core implementation.

In EF Core 10.x/11.x, SqlServerQueryCompilationContext.IsBuffering is implemented as:

public override bool IsBuffering
    => base.IsBuffering
        || (QuerySplittingBehavior == QuerySplittingBehavior.SplitQuery
            && !_multipleActiveResultSetsEnabled);

Source:

https://github.com/dotnet/efcore/blob/main/src/EFCore.SqlServer/Query/Internal/SqlServerQueryCompilationContext.cs

Why this appears inconsistent with the documentation

The documentation says that the resultsets of all but the last query are buffered.

This suggests that if a split query produces only one SQL query/resultset, that resultset should not need to be buffered, because it is the last (and only) resultset.

However, SqlServerQueryCompilationContext.IsBuffering is a query-level flag. It becomes true solely because QuerySplittingBehavior == SplitQuery and MARS is disabled, without considering whether the particular query execution actually has multiple SQL resultsets.

Therefore, even a split query that results in only one SQL query can cause BufferedDataReader to be used.

Expected documentation

Could the documentation be clarified to distinguish between:

  1. Why split queries may require buffering - all but the last resultset need to be buffered when MARS is disabled; and
  2. When EF Core's IsBuffering flag causes BufferedDataReader to be used - currently SplitQuery + MARS disabled is sufficient to set this flag, even if the particular execution has only one resultset.

For example, the documentation could clarify that the implementation may enable buffering for the split-query execution as a whole, rather than only buffering resultsets that are actually followed by another resultset.

Is this behavior intentional?

If it is intentional, I think the documentation should be updated to make the distinction explicit, because the current wording can lead to the expectation that a split query with only one resultset will not incur EF's internal BufferedDataReader buffering.

Your code

Consider a query configured with:


optionsBuilder
    .UseSqlServer(connectionString)
    .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);


with MARS disabled and no retrying execution strategy.

For a query that ultimately produces only one SQL command/resultset:


RetriesOnFailure = false
QuerySplittingBehavior = SplitQuery
MARS = false

SqlServerQueryCompilationContext.IsBuffering => true


As a result, EF Core creates `ReaderColumns` and uses `BufferedDataReader` => data streaming is not working

Stack traces


Verbose output


EF Core version

10.x/11.x

Database provider

Microsoft.EntityFrameworkCore.SqlServer

Target framework

.net 10

Operating system

Windows 11

IDE

No response

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions