fix: handle entity message actions in InMemoryOrchestrationBackend - #327
Open
YunchuWang wants to merge 5 commits into
Open
fix: handle entity message actions in InMemoryOrchestrationBackend#327YunchuWang wants to merge 5 commits into
YunchuWang wants to merge 5 commits into
Conversation
…TYMESSAGE and TERMINATEORCHESTRATION action types Add handling for SENDENTITYMESSAGE (case 8) and TERMINATEORCHESTRATION (case 7) action types in InMemoryOrchestrationBackend.processAction(). Previously, orchestrations using entity signals or terminate-orchestration actions would crash with 'Unexpected action type' when run against the in-memory testing backend. - SENDENTITYMESSAGE: No-op (entity signals are fire-and-forget; in-memory backend does not simulate entity workers) - TERMINATEORCHESTRATION: Extracts target instance ID and delegates to existing terminate() method - Adds 3 unit tests verifying entity signal orchestrations complete without crashing Fixes #209 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes crashes in the in-memory testing backend by handling additional orchestrator action types that can be produced by entity interactions and orchestration termination.
Changes:
- Handle
SENDENTITYMESSAGEactions inInMemoryOrchestrationBackend.processAction()(acknowledged/no-op). - Handle
TERMINATEORCHESTRATIONactions by terminating the targeted instance. - Add unit tests to verify entity signal (fire-and-forget) orchestrations don’t crash when run against the in-memory backend.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/durabletask-js/src/testing/in-memory-backend.ts | Adds handling for SENDENTITYMESSAGE and TERMINATEORCHESTRATION orchestrator actions. |
| packages/durabletask-js/test/in-memory-backend.spec.ts | Adds regression tests ensuring entity signaling doesn’t crash the in-memory backend. |
Comments suppressed due to low confidence (1)
packages/durabletask-js/src/testing/in-memory-backend.ts:471
- TERMINATEORCHESTRATION handling is newly added but isn't covered by a unit test (the existing termination test exercises external termination via the test client, not an OrchestratorAction of type TERMINATEORCHESTRATION). Adding a regression test would help ensure this action type doesn't reintroduce crashes.
case pb.OrchestratorAction.OrchestratoractiontypeCase.TERMINATEORCHESTRATION:
// Terminate-orchestration actions are used for recursive termination of
// sub-orchestrations. Process by terminating the target instance.
this.processTerminateOrchestrationAction(action);
break;
Comment on lines
+654
to
+658
| try { | ||
| this.terminate(targetInstanceId, output); | ||
| } catch { | ||
| // Target instance may not exist or already terminated - ignore | ||
| } |
Comment on lines
+467
to
+471
| case pb.OrchestratorAction.OrchestratoractiontypeCase.TERMINATEORCHESTRATION: | ||
| // Terminate-orchestration actions are used for recursive termination of | ||
| // sub-orchestrations. Process by terminating the target instance. | ||
| this.processTerminateOrchestrationAction(action); | ||
| break; |
…tity-action-handling Conflicts came from main adding REWINDORCHESTRATION handling (#296) to the same processAction switch and the same method region, plus independent additive tests in the spec. Kept all three action cases (SENDENTITYMESSAGE, TERMINATEORCHESTRATION, REWINDORCHESTRATION), both new private methods, and both sets of tests.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
packages/durabletask-js/src/testing/in-memory-backend.ts:755
- processTerminateOrchestrationAction wraps terminate() in a broad try/catch, but terminate() only throws on a missing instance. Catching everything can hide unexpected errors if terminate() changes in the future; it’s safer to check existence up-front and call terminate() without swallowing exceptions.
private processTerminateOrchestrationAction(action: pb.OrchestratorAction): void {
const terminateAction = action.getTerminateorchestration();
if (!terminateAction) {
return;
}
const targetInstanceId = terminateAction.getInstanceid();
if (!targetInstanceId) {
return;
}
const output = terminateAction.getReason()?.getValue();
try {
this.terminate(targetInstanceId, output);
} catch {
// Target instance may not exist or already terminated - ignore
}
}
Comment on lines
+551
to
+555
| case pb.OrchestratorAction.OrchestratoractiontypeCase.TERMINATEORCHESTRATION: | ||
| // Terminate-orchestration actions are used for recursive termination of | ||
| // sub-orchestrations. Process by terminating the target instance. | ||
| this.processTerminateOrchestrationAction(action); | ||
| break; |
SENDENTITYMESSAGE is a four-way oneof, but it was handled with a single blanket no-op. signalEntity and the critical-section unlock are fire-and-forget, so dropping them is correct. callEntity and lockEntities await an EntityOperationCompleted / EntityLockGranted response that this backend has no entity worker to produce, so the no-op turned a fast 'unknown action type' failure into a silent hang until the caller's timeout. Now branches on EntitymessagetypeCase: signal and unlock stay no-ops, call and lock throw an explanatory error naming the operation and pointing at a real backend or the emulator, and an unrecognized case throws like the surrounding action switch does. The worker already converts a throw here into a FAILED orchestration with the message in failureDetails. Added tests for both paths; without the fix they hang until the Jest timeout (5002 ms), with it they fail in 6 ms.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
packages/durabletask-js/src/testing/in-memory-backend.ts:549
- TERMINATEORCHESTRATION handling was added here, but the test suite changes only cover SENDENTITYMESSAGE (entity signaling/call/lock). Consider adding a regression test that exercises a TERMINATEORCHESTRATION action end-to-end (or via a small backend-level simulation) to ensure terminating a target instance works and doesn’t regress.
case pb.OrchestratorAction.OrchestratoractiontypeCase.TERMINATEORCHESTRATION:
// Terminate-orchestration actions are used for recursive termination of
// sub-orchestrations. Process by terminating the target instance.
this.processTerminateOrchestrationAction(action);
break;
The JS SDK never emits a TerminateOrchestrationAction, so the handler added
alongside the entity-message support could not be reached:
- No construction site exists anywhere in src/ (outside generated proto).
pb-helper.util.ts has builders for complete, timer, scheduleTask,
subOrchestration, sendEvent and the four entity messages, but none for
terminate.
- OrchestrationContext exposes no terminate API; termination is client-side.
- The only non-proto reference is getActionSummary() in worker/index.ts,
which merely maps action types to display names for logging.
By contrast REWINDORCHESTRATION, which the backend also handles, is genuinely
constructed at worker/rewind.ts:123 - so handling exactly what is reachable is
the existing convention here.
The removed handler was also untested and swallowed all errors from terminate().
Full unit suite is unchanged at 1181 passing across 67 suites, confirming
nothing exercised this path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5aaa70df-fae3-4570-aabe-0fc96cb869bc
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.
Fixes #209.
InMemoryOrchestrationBackendis public API (exported fromsrc/index.ts) — it is whatconsumers use to unit-test orchestrations without a sidecar. Its
processActionswitchends in
default: throw new Error("Unknown orchestrator action type ..."), and none ofthe four entity message actions were handled.
Taking the inventory of action builders in
pb-helper.util.ts:newCompleteOrchestrationActionnewCreateTimerActionnewScheduleTaskActionnewCreateSubOrchestrationActionnewSendEventActionnewSendEntityMessageSignalActionnewSendEntityMessageCallActionnewSendEntityMessageLockActionnewSendEntityMessageUnlockActionBefore
Any orchestration calling
ctx.entities.signalEntity(...)fails on this backend with:A raw enum ordinal, with nothing indicating entities are involved. In practice
signalEntityis untestable with the shipped test harness.After
SENDENTITYMESSAGEdispatches on its four-way oneof, because the sub-types are notequivalent:
signalEntity, unlock — fire-and-forget, no reply expected → no-op, orchestrationproceeds normally.
callEntity,lockEntities— block awaiting a response that no entity worker existsto send here → throw an explanatory error naming the operation and pointing at the
emulator/a real backend.
A blanket no-op across all four would be worse than the status quo: it converts today's
fast failure into a permanent hang for
callEntity/lockEntities. The two fail-fast testscover exactly that — with a blanket no-op they hang to Jest's 5000 ms timeout; with the
dispatch they fail in ~6 ms.
Scope note
An earlier revision of this PR also handled
TERMINATEORCHESTRATION. That has been removedas unreachable:
TerminateOrchestrationActionis constructed anywhere insrc/outside the generatedproto, and
pb-helper.util.tshas no builder for it.OrchestrationContextexposes no terminate API — termination is client-side.getActionSummary()inworker/index.ts, a logginghelper that maps action types to display names.
By contrast
REWINDORCHESTRATION, which this backend also handles, is constructed atworker/rewind.ts:123. Handling exactly what is reachable is the existing convention.Verification
signal tests report
FAILEDinstead ofCOMPLETED, and the fail-fast tests surface theUnknown orchestrator action type '8'message.in-memory-backend.spec.ts.removing the terminate handler, confirming nothing exercised that path.
npm run buildandeslintboth clean.