Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer #3300
Open
Use cancellation token in DbCommand.ExecuteReaderAsync call in DAB layer #3300
Conversation
…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
Contributor
There was a problem hiding this comment.
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
CancellationTokenintoDbCommand.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); |
Aniruddh25
reviewed
Mar 19, 2026
Aniruddh25
reviewed
Mar 19, 2026
Aniruddh25
requested changes
Mar 19, 2026
Collaborator
Aniruddh25
left a comment
There was a problem hiding this comment.
Needs some test deduplication.
1) merged two tests 2) updated the queryExecutor contrtructor parameter after merging main
Aniruddh25
approved these changes
Mar 21, 2026
Collaborator
|
/azp run |
|
Azure Pipelines successfully started running 6 pipeline(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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?
How was this tested?