Skip to content

Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer #3300

Open
naxing123 wants to merge 6 commits intomainfrom
naxing/canceltoken
Open

Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer #3300
naxing123 wants to merge 6 commits intomainfrom
naxing/canceltoken

Conversation

@naxing123
Copy link

@naxing123 naxing123 commented Mar 19, 2026

This is to address #3301

Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer

Problem:
RequestTimeoutAttribute only works during graphql workload execution, it doesn’t take effect during query execution as the logic to use cancellation token to DAB layer is NOT implemented, even though the code to pass cancellation token to DAB is already implemented.

Fix:
Use cancellation token to DAB layer in DbCommand.ExecuteReaderAsync call

Why make this change?

What is this change?

  • Summary of how your changes work to give reviewers context of your intent.
    • Use cancellation token(if any) in DbCommand.ExecuteReaderAsync call in DAB layer

How was this tested?

  • Integration Tests
  • Unit Tests
  • Also did manual test with long running(3 minutes) SQL query from graphql and cancellation token with 30 seconds timeout, from debugger I can see code goes to DbCommand.ExecuteReaderAsync call in DAB layer and times out after 30 seconds.

…execution

https://dev.azure.com/powerbi/AppDev/_workitems/edit/2004135

RequestTimeoutAttribute won’t take effect during query execution

Problem:
RequestTimeoutAttribute only works during graphql workload execution, it doesn’t take effect during query execution due to request executor does NOT implement cancellation token logic.

Fix:
pass cancellation token to DAB layer to DbCommand.ExecuteReaderAsync call
@naxing123 naxing123 requested a review from Aniruddh25 as a code owner March 19, 2026 18:44
Copilot AI review requested due to automatic review settings March 19, 2026 18:44
@naxing123 naxing123 changed the title pass cancellation token to DAB layer in DbCommand.ExecuteReaderAsync call Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer Mar 19, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ensures GraphQL request cancellations/timeouts propagate into the DAB database execution by passing HttpContext.RequestAborted into DbCommand.ExecuteReaderAsync, and adds unit tests to validate cancellation behavior with the Polly retry policy.

Changes:

  • Pass CancellationToken into DbCommand.ExecuteReaderAsync(CommandBehavior, CancellationToken) in the DAB query execution path.
  • Add unit tests covering cancellation/timeout exceptions and ensuring retries are not attempted.
  • Add a unit test asserting DB execution time is recorded even when an exception occurs during execution.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
src/Core/Resolvers/QueryExecutor.cs Passes HttpContext.RequestAborted into ExecuteReaderAsync while consolidating CommandBehavior selection.
src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs Adds multiple unit tests around cancellation propagation and non-retry behavior.

{
using DbDataReader dbDataReader = ConfigProvider.GetConfig().MaxResponseSizeLogicEnabled() ?
await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess) : await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection);
var commandBehavior = ConfigProvider.GetConfig().MaxResponseSizeLogicEnabled() ? CommandBehavior.SequentialAccess : CommandBehavior.CloseConnection;
Comment on lines +767 to +770
// Verify no retry log messages were emitted. Polly does not handle
// TaskCanceledException (subclass of OperationCanceledException), so
// the exception propagates immediately without any retry attempts.
Assert.AreEqual(0, queryExecutorLogger.Invocations.Count);
Comment on lines +685 to +703
RuntimeConfig mockConfig = new(
Schema: "",
DataSource: new(DatabaseType.MSSQL, "", new()),
Runtime: new(
Rest: new(),
GraphQL: new(),
Mcp: new(),
Host: new(null, null)
),
Entities: new(new Dictionary<string, Entity>())
);

MockFileSystem fileSystem = new();
fileSystem.AddFile(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME, new MockFileData(mockConfig.ToJson()));
FileSystemRuntimeConfigLoader loader = new(fileSystem);
RuntimeConfigProvider provider = new(loader)
{
IsLateConfigured = true
};
Comment on lines +982 to +997
try
{
await queryExecutor.Object.ExecuteQueryAgainstDbAsync<object>(
conn: null,
sqltext: string.Empty,
parameters: new Dictionary<string, DbConnectionParam>(),
dataReaderHandler: null,
dataSourceName: String.Empty,
httpContext: context,
args: null);
}
catch (Exception)
{
// SqlConnection is sealed and can't be mocked; conn is null so OpenAsync
// throws NullReferenceException. Ignore to verify the finally block behavior.
}
Comment on lines +298 to +299
CancellationToken cancellationToken = httpContext?.RequestAborted ?? CancellationToken.None;
using DbDataReader dbDataReader = await cmd.ExecuteReaderAsync(commandBehavior, cancellationToken);
Copy link
Collaborator

@Aniruddh25 Aniruddh25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs some test deduplication.

1) merged two tests
2) updated the queryExecutor contrtructor parameter after merging main
@Aniruddh25
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 6 pipeline(s).

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.

4 participants