-
Notifications
You must be signed in to change notification settings - Fork 53
Add pre-scheduling and retry handler cancellation support for activities and sub-orchestrators #595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…on in activities and sub-orchestrators Co-authored-by: YunchuWang <[email protected]>
Co-authored-by: YunchuWang <[email protected]>
…d fix TaskCompletionSource options Co-authored-by: YunchuWang <[email protected]>
There was a problem hiding this 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 implements cancellation support for activities, sub-orchestrators, and retry handlers by wiring up the previously unused CancellationToken parameter in the TaskOptions class. The implementation provides pre-scheduling cancellation checks and post-scheduling cancellation via a Task.WhenAny pattern, enabling cooperative cancellation of orchestrator tasks.
Key changes:
- Added
CancellationTokenproperty toTaskOptionswith comprehensive XML documentation and usage examples - Implemented cancellation logic in
TaskOrchestrationContextWrapperincluding pre-scheduling checks and theWaitForTaskWithCancellationhelper method - Added four integration tests covering activity cancellation, sub-orchestrator cancellation, and retry handler integration scenarios
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Abstractions/TaskOptions.cs | Added CancellationToken property with extensive documentation and examples, updated copy constructor to include the new property |
| src/Worker/Core/Shims/TaskOrchestrationContextWrapper.cs | Implemented cancellation logic for activities and sub-orchestrators with pre-scheduling checks, added WaitForTaskWithCancellation helper method, integrated cancellation token into retry handler flow |
| test/Grpc.IntegrationTests/CancellationTests.cs | New test file with comprehensive integration tests for activity cancellation, sub-orchestrator cancellation, and retry handler cancellation scenarios |
- Replace Chinese full-width comma with ASCII comma in TaskOrchestrationContextWrapper.cs - Fix documentation example to use TaskRetryOptions.FromRetryHandler instead of TaskOptions.FromRetryHandler Co-authored-by: YunchuWang <[email protected]>
|
@copilot address the comment |
|
@copilot why pr shows full file of src/Abstractions/TaskOptions.cs is changed, thats not right, fix |
The previous commits inadvertently changed all line endings from CRLF to LF, causing the entire file to show as modified. This commit restores CRLF line endings while keeping all the functional changes (CancellationToken property and documentation). Co-authored-by: YunchuWang <[email protected]>
Fixed in commit f68ff62. The issue was that the previous commits inadvertently changed line endings from CRLF to LF, causing git to show the entire file as modified. I've restored the CRLF line endings while keeping all functional changes. The PR now shows only 62 lines added (the actual CancellationToken property and documentation) instead of the full file replacement. |
|
@copilot does this really cancel an activity or suborch? when awaiting activity or suborch, the durable worker will yield the execution waiting for activity or suborch new workitem then resume execution. during that time if you try to cancel using the canceltoken, it wont cancel anything? |
|
@copilot WaitForTaskWithCancellation is useless then, // Wait for either the task to complete or cancellation |
You're absolutely correct. The The only meaningful cancellation happens at the pre-scheduling check (line 158). I'll remove |
yes make the change @copilot |
…umentation As correctly identified in review, WaitForTaskWithCancellation was ineffective because: - Once ScheduleTask is called, the orchestrator yields execution - During the yield, orchestrator code isn't running, so cancellation callbacks can't fire - Task.WhenAny would always return the activity/sub-orchestrator task, never the cancellation task Changes: - Removed WaitForTaskWithCancellation method entirely - Simplified CallActivityAsync and CallSubOrchestratorAsync to directly await scheduled tasks - Added clarifying comments about the pre-check being the only cancellation point - Updated CancellationToken documentation to accurately describe behavior and limitations - Emphasized that cancellation only works pre-scheduling and in retry handlers, not mid-execution Co-authored-by: YunchuWang <[email protected]>
Already completed in commit 37d523f. The |
|
@copilot add testcase to cancel the token outside the retryhandler |
|
@copilot add testcase for Multiple activities in a loop - The main use case for pre-check. once token is cancelled, the loop should exit instead of continuing calling remaining activities |
Added RetryHandlerReceivesCancelledTokenFromOutside test to verify behavior when: - A CancellationToken is provided to TaskOptions with retry logic - The token is cancelled OUTSIDE the retry handler (in orchestrator code) - The pre-scheduling check catches the cancelled token before scheduling This test demonstrates that when a token is cancelled before CallActivityAsync is invoked, the pre-scheduling cancellation check throws TaskCanceledException immediately, and the retry handler is never called. This is the expected behavior given the orchestrator execution model. Co-authored-by: YunchuWang <[email protected]>
|
@copilot update pr description to follow .github/PULL_REQUEST_TEMPLATE.md |
Added in commit 0e911a1. The new test |
| b.AddTasks(tasks => tasks | ||
| .AddOrchestratorFunc(orchestratorName, async ctx => | ||
| { | ||
| cts = new CancellationTokenSource(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added MultipleActivitiesInLoopWithCancellation test to demonstrate the main use case for pre-scheduling cancellation: - Orchestrator calls multiple activities in a loop - Token is cancelled after processing 3 items - Pre-scheduling check prevents remaining activities from being scheduled - Loop exits cleanly with cancellation message This test validates that cancellation in loops works as expected, allowing orchestrators to stop processing when a cancellation is requested. Co-authored-by: YunchuWang <[email protected]>
Added in commit a34d209. The new test
Updated PR description in commit a34d209 to follow the template at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Summary
What changed?
Added
CancellationTokensupport to theTaskOptionsclass for activities, sub-orchestrators, and retry handlers. The implementation provides pre-scheduling cancellation checks and passes cancellation tokens to retry handlers.Why is this change needed?
The
TaskOptionsclass had aCancellationTokenparameter that was not wired up. This change implements the cancellation functionality to allow orchestrators to cancel activities, sub-orchestrators, and stop retrying when requested.Important limitation: Due to the durable orchestrator execution model, cancellation only works at specific points (pre-scheduling and in retry handlers), not mid-execution while the orchestrator is yielded.
Project checklist
AI-assisted code disclosure (required)
Was an AI tool used? (select one)
If AI was used:
src/Abstractions/TaskOptions.cs- Added CancellationToken property with XML documentationsrc/Worker/Core/Shims/TaskOrchestrationContextWrapper.cs- Implemented cancellation logic in CallActivityAsync and CallSubOrchestratorAsynctest/Grpc.IntegrationTests/CancellationTests.cs- Created comprehensive integration testsWaitForTaskWithCancellationmethod after code review identified it wouldn't work with orchestrator execution modelAI verification (required if AI was used):
Testing
Automated tests
Test coverage:
ActivityCancellation- Pre-scheduling cancellation for activitiesSubOrchestratorCancellation- Pre-scheduling cancellation for sub-orchestratorsRetryHandlerReceivesCancellationToken- Token passed to retry handlersRetryHandlerCanStopOnCancellation- Retry handler can check token and stop retryingRetryHandlerReceivesCancelledTokenFromOutside- Token cancelled outside handler caught by pre-checkMultipleActivitiesInLoopWithCancellation- Main use case: loop exits when token cancelled (demonstrates pre-scheduling cancellation preventing remaining activities from being scheduled)Manual validation (only if runtime/behavior changed)
Notes for reviewers
Implementation details:
TaskCanceledExceptionif token is already cancelled before schedulingRetryContextso handlers can check cancellationKey changes after review:
WaitForTaskWithCancellationmethod - didn't work due to orchestrator yielding during awaitDesign considerations:
Main use case:
The primary use case for this feature is processing multiple items in a loop. When a cancellation token is cancelled, the orchestrator can exit the loop instead of continuing to schedule remaining activities. This is demonstrated in the
MultipleActivitiesInLoopWithCancellationtest.Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.