feat(core): operationResource request deadline and disconnect signal (#78) - #109
Draft
tnramalho wants to merge 1 commit into
Draft
feat(core): operationResource request deadline and disconnect signal (#78)#109tnramalho wants to merge 1 commit into
tnramalho wants to merge 1 commit into
Conversation
tnramalho
marked this pull request as draft
August 27, 2026 17:47
…78) Every operation carries `ctx.signal: AbortSignal`, exposed through an optional `deadlineMs` on the operation descriptor. Elapsing the deadline aborts the signal and resolves `504 Gateway Timeout`; a client disconnecting before the handler settles aborts it too, without writing anything back and without surfacing as an application error. A handler that never reads `ctx.signal` keeps running after the timeout response is sent — the deadline bounds what the client waits for, not the work itself. `op.sse()` exposes no `deadlineMs`, and `ctx.signal` is inert there: the handler returns its Observable before the guard is torn down, so an SSE client going away is observed through the Observable's own unsubscription. Documented on the field, in the changelog, and in CONFIGURATION.md §6f. Type-level breaking change: `OperationContext` gained a required `signal` field. A hand-constructed context (a unit test mocking one instead of reading it from a route) needs to add it; the framework-constructed context every route receives already does. Reimplemented on top of #105 rather than rebased: the original #78/#86 branch predates the schema engine, and its file-storage half is dropped here so core keeps one storage abstraction instead of two.
tnramalho
force-pushed
the
feat/operation-deadline-78
branch
from
August 27, 2026 22:41
f659b8f to
f72140c
Compare
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.
Pull Request
Summary
Adds an optional
deadlineMstooperationResourceoperations and anAbortSignalon every operation context. Closes #78.Replaces #94, which bundled this with a file-storage seam. That half is
dropped:
@concepta/rockets-storage(#107) is the storage abstraction, andcore should not carry a second one. #94 will be closed.
Based on
feat/schema-engine-104(#105), notmain.#105 reshaped theoperation compiler (
inputDto→inputSchema,applyOutputDto→applyOutputSchema) and renumberedCONFIGURATION.md§6b/§6c. Rebasing #94'scommits produced conflicts through the middle of both functions, so this is
reimplemented against the current code rather than replayed. Retarget to
mainonce #105 lands.
Changes
ctx.signal: AbortSignalon every operation context. Pass it to whateveractually waits — a
fetch, a driver call — so a request nobody is waitingfor stops burning work.
deadlineMson the operation descriptor and on theop.read/op.write/op.deletebuilders. Elapsing it aborts the signal and resolves504 Gateway Timeout.Nothing is written back (the socket is gone) and it never reaches the
exceptions filter — no 5xx, no error log.
CONFIGURATION.md§6f, plus changelog and core README.Deliberate boundaries
that ignores
ctx.signalkeeps running in the background after the 504 issent. Stopping the work is cooperative, by design — core cannot kill an
arbitrary handler mid-flight.
operationResourceonly. CRUD command handlers have the same gap (along-running create, e.g. PDF generation) and are not covered here.
op.sse()exposes nodeadlineMs, and itsctx.signalis inert. Thehandler returns its Observable immediately, so a deadline would race the
setup call rather than the stream, and cutting a long-lived SSE connection is
the opposite of what SSE is for. The guard is torn down before the first
event, so nothing is left to abort the signal — an SSE client going away is
observed through the Observable's own unsubscription (
finalize/takeUntil). Stated on the field, in the changelog, and in §6f rather thanleft for someone to discover.
Breaking change
Type-level only:
OperationContextgained a requiredsignalfield. Ahand-constructed context — a unit test mocking one instead of reading it from a
route — needs to add it. The framework-constructed context every route receives
already does.
Two edge cases the implementation handles
Both were found by reproduction, and both have a comment at the site:
while
routeHandleris still awaiting DI resolution, beforerace()isever called. Under Node's default
--unhandled-rejections=throwthat takesdown the process on a single slow request. A permanent no-op
catchkeepsit handled;
race()still observes the same rejection.Promise.raceresolving by array order. When both inputs are alreadysettled,
Promise.racepicks by argument position, not by which settledfirst — so an already-fulfilled handler result beat an earlier-fired
deadline and returned
200well pastdeadlineMs.race()now rejectsimmediately when the signal is already aborted.
Type of Change
Verification
Run against this branch, on the #105 base:
yarn buildyarn api:report:update— 9 lines, onlydeadlineMsandsignalyarn typecheck:specyarn test— 106 files, 1112 testsyarn test:e2e(packages/rockets-core) — 29 files, 315 tests,including 6 new deadline cases
yarn lint:allChecklist