WB-186: fix sse N+1 Pattern#5
Merged
Merged
Conversation
3a49950 to
698d7ba
Compare
refactor(backend): wire N+1 SSE fix into the actually-used endpoint The first pass at WB-186 introduced fetchEventsAfter + drainEventsSince but only wired them into a new GET /:id handler. That endpoint isn't called by the frontend — use-backend-execution.ts opens EventSource on the streamUrl returned by POST /execute, which is /:id/stream. Result: drainEventsSince was unit-tested and dead-pathed, while the hot SSE endpoint kept the original full-history-fetch-then-filter pattern. The N+1 bug was unchanged in production traffic. Helper-only tests masked the drift because the route had no integration coverage. This commit: - Restores GET /:id as a JSON metadata endpoint (REST convention, reverses the accidental co-opting into SSE). - Rewrites GET /:id/stream to use drainEventsSince + fetchEventsAfter for both the snapshot and live drains — one query shape across the route. Heartbeat + abort cleanup preserved. - Drops the now-unused executionEvents Drizzle import the in-route query previously needed; the route reaches the table only through fetchEventsAfter. Cleanups: - drain-events.ts: EventWriter and DrainResult demoted from export type to module-local types. knip flagged them as unused exports; consumer code reaches them only by inference from drainEventsSince's signature. - drain-events.test.ts: extract an inline fixedFetcher helper to satisfy unicorn/consistent-function-scoping. The earlier deletion of fetch-events-after.test.ts is preserved — the helper now carries zero testing scaffolding in its signature; SQL coverage moves to a separate integration test path when wanted. Refs: WB-186 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
698d7ba to
a5ef265
Compare
The serialize-coalesce concurrency logic lived inline in the /:id/stream route and could only be exercised by re-implementing it inside a test, so the test guarded a copy rather than the shipped code. Extract it into createSerializedDrainer and test the real function. - serialized-drainer.ts: owns the concurrency policy (one drain at a time, coalesce a burst into a single follow-up pass, track done/stop). The route wires fetchEventsAfter + drainEventsSince through it. - Close the snapshot->subscribe lost-wakeup window: a terminal event inserted between the snapshot read and subscribe fired its NOTIFY into the void and the stream hung in "running". A catch-up drain after subscribe replays anything missed from the snapshot cursor. - Log SSE write failures at the route boundary instead of swallowing them silently; document that DrainResult.writeFailed means the client disconnected, not data corruption. - drain-events.test.ts: drop the duplicated harness test, now covered against the real createSerializedDrainer. Refs: WB-186 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
piotrblaszczyk
approved these changes
May 27, 2026
# Conflicts: # apps/backend/src/routes/executions.ts
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.
refactor(backend): wire N+1 SSE fix into the actually-used endpoint
The first pass at WB-186 introduced fetchEventsAfter + drainEventsSince but only wired them into a new GET /:id handler. That endpoint isn't called by the frontend — use-backend-execution.ts opens EventSource on the streamUrl returned by POST /execute, which is /:id/stream. Result: drainEventsSince was unit-tested and dead-pathed, while the hot SSE endpoint kept the original full-history-fetch-then-filter pattern. The N+1 bug was unchanged in production traffic. Helper-only tests masked the drift because the route had no integration coverage.
This commit:
Cleanups:
The earlier deletion of fetch-events-after.test.ts is preserved — the helper now carries zero testing scaffolding in its signature; SQL coverage moves to a separate integration test path when wanted.