Repository navigation
Show cancelling workflow state immediately - #7528
aidevelopers2 wants to merge 1 commit into
Conversation
|
PR author is not in the allowed authors list. |
There was a problem hiding this comment.
Pull request overview
Adds an intermediate Cancelling workflow sub-status so cancellation requests are reflected immediately (before background cancellation completes), improving UI/API consistency during bulk cancellation operations.
Changes:
- Introduce
WorkflowSubStatus.Cancellingin core and API client enums. - Persist non-finished workflow instances as
Running/Cancelling(includingWorkflowState) before dispatching cancellation work. - Add unit tests covering the save-before-dispatch behavior and the “instance not found” path.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/Elsa.Workflows.Runtime.UnitTests/Services/WorkflowCancellationServiceTests.cs | Verifies instances are persisted as Cancelling before dispatch and that missing instances do not dispatch. |
| src/modules/Elsa.Workflows.Runtime/Services/WorkflowCancellationService.cs | Marks cancellable instances as Running/Cancelling and saves them before dispatching cancellation requests. |
| src/modules/Elsa.Workflows.Core/Enums/WorkflowSubStatus.cs | Adds the Cancelling sub-status to the core enum. |
| src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs | Maps Cancelling to main Running status for execution-context status derivation. |
| src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Enums/WorkflowSubStatus.cs | Adds Cancelling to the API client enum to keep it in sync with server responses. |
There was a problem hiding this comment.
Elsa 3 Code Review: REQUEST_CHANGES + HIGH @ 3608864
Code Review, Round 1/6
Thank you for taking on #5187. Showing an intermediate Cancelling state is a good goal, and appending the enum value at the end keeps the existing numeric values stable. I can't approve this yet, for three reasons. The branch can't merge into current main. The way the state is persisted can overwrite a running workflow. And an instance can get stuck in Cancelling with nothing to recover it.
Blocking
-
Rebase onto current main (it is conflicted). The branch is 1 commit ahead and about 4,300 behind, based on main from 2026-05-22, and GitHub reports it as
dirty. Since then the repository moved undercore/:core/src/modules/Elsa.Workflows.Runtime/Services/WorkflowCancellationService.cscore/src/modules/Elsa.Workflows.Core/Enums/WorkflowSubStatus.csandcore/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cscore/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Enums/WorkflowSubStatus.cs- the tests under
core/test/unit/Elsa.Workflows.Runtime.UnitTests/
The service's logic hasn't changed on main, so the port should be mechanical.
-
Saving the whole instance snapshot can overwrite a running workflow.
MarkWorkflowInstancesAsCancellingAsynccallsIWorkflowInstanceStore.SaveManyAsyncwith the fullWorkflowInstance, including theWorkflowStateread a moment earlier. It does this for every non-finished instance, including ones inExecuting.- Lost update. If an execution cycle owns the instance at that moment, this write replaces the newer state that the cycle has committed, or is about to commit, with the stale snapshot. That can lose activity progress and bookmarks.
- It skips the guard main uses. The cancel path on main goes through
LocalWorkflowClient.CancelAsync, which first callsDemandUnownedAsync. That guard exists to stop writes to an owned instance, and this write doesn't go through it. - What to change. Please don't persist the snapshot. Update only
SubStatusandUpdatedAt, and only for instances that are not currently owned or executing. Instances that are executing can keep their real sub-status until the cancel runs.
-
An instance can get stuck in
Cancelling. The instance is saved asCancellingbefore dispatch, but nothing undoes that if the cancellation never completes:dispatcher.DispatchAsyncthrows: the instances are already saved asCancelling, and nothing reverts them.- The background
CancelWorkflowsCommandHandlerfails: for example,DemandUnownedAsyncrejects an owned instance, or the canceler throws. The state staysCancelling. - Recovery doesn't cover it.
InterruptedRecoveryScanneronly scansInterrupted, and crash recovery looks atExecutingwith a stale liveness timestamp, so aCancellinginstance is never picked up again. A formerlySuspendedinstance would also drop out of "suspended" filters and views for good.
Please add one of these: restore the previous sub-status when dispatch or the cancel fails, or give the cancel path a recovery or retry for
Cancelling. Either way, add a test for it.
The rest checks out
- Final transition is intact.
LocalWorkflowClient.CancelAsyncreturns early only whenStatus != Running. BecauseCancellingmaps toRunning, both in the PR'sGetMainStatuschange and in the persisted instance, the canceler still runs and moves the instance toCancelled.ValidateStatusTransitionallows leavingCancelling, because its main status isn'tFinished. - Enum is in sync within this repository. The server enum and
Elsa.Api.Clientboth getCancellingappended last. elsa-core has no other copy of this enum, and there is no studio code under core to update. Elsa Studio lives in a separate repository and will need to renderCancelling; the maintainers can coordinate that. Note that an older client that deserializes the enum by name will reject the new value, so this belongs in a minor release, not a patch. - Other code that switches on the sub-status.
BulkDispatchWorkflowsandDrainOrchestratorfall through to a default for values they don't know, so they are safe.GetMainStatusis the onlyswitchthat throws on an unknown value, and the PR updates it. - Tests. The save-before-dispatch ordering test and the not-found test are good. After the rebase, please add tests for items 2 and 3: an executing or owned instance is not overwritten, and a failed dispatch does not leave
Cancellingbehind.
Housekeeping
- CLA. The
license/clacheck is still pending. Please reply to the CLA bot with@dotnet-policy-service agree. - CI. The only CI on this head ran in May against the old base, so it says nothing about current main. Pushing the rebase will start fresh runs.
- Maintainer edits. "Allow edits by maintainers" is on. If you can't rebase, the maintainers may take this over and finish it on top of your commit, keeping your authorship.
There are no advisory bot threads to answer. Copilot left an overview with no inline comments, and Greptile didn't run for this author.
— Code Review (Elsa 3), via Elsa Review
|
Hey @aidevelopers2! Are you still working on this? If not, I'd be happy to carry it forward with full credit to you. I'd bring it up to date with main, fix the review points around state saving and failed cancellations, and add tests. I'll be using some AI help and will say so in the PR. |
|
Thanks for offering, @JohnyKid123. Let's give @aidevelopers2 a few days to reply here. If there's no answer by then, feel free to carry it forward. A few pointers so it lands smoothly:
We'll close this one in favour of yours once it's up. |
|
Hi maintainers! I'm planning to carry forward @aidevelopers2's work from #7528 with full credit, focusing first on the existing review blockers. For the initial change, I'll use conditional IWorkflowInstanceStore updates (following TryMarkInterruptedAsync), avoid touching execution snapshots, and restore the previous status when an observed cancellation failure is terminal. Executing instances will keep their current sub-status, and I'll leave transactional outbox/crash recovery for a follow-up. The one point I'd appreciate confirmation on is whether this narrower scope is acceptable for #5187, given the remaining crash-before-dispatch window. If you'd prefer atomic outbox recovery as part of the initial fix, please let me know. I'll include tests and disclose AI assistance as before. Thanks! |
|
Thanks, @JohnyKid123. @aidevelopers2 hasn't replied since the 22nd, so please go ahead and carry it forward. Yes, that narrower scope is fine for #5187. It covers what Code Review asked for in item 3 ("restore the previous sub-status when dispatch or the cancel fails"), so atomic outbox recovery doesn't need to be part of the first PR. A few things to keep it safe:
Housekeeping: open the new PR against |
|
Follow-up for #5187: opened #9115 against current |
|
Thanks, @JohnyKid123. One open PR is easier to track, so review continues on #9115 (milestone 3.11, since it adds a new |
Summary
Cancellingworkflow sub-status for accepted cancellation requestsRunning/Cancellingbefore dispatching background cancellation workCancelledFixes #5187
Tests
.nuke/temp/dotnet-unix/dotnet test test/unit/Elsa.Workflows.Runtime.UnitTests/Elsa.Workflows.Runtime.UnitTests.csproj --filter WorkflowCancellationServiceTests