fix(server): a WS board subscriber bootstraps the current board - #97
Merged
Conversation
wsSubscribe replayed the journal only for a run-trace topic; a board room (project/{key}/issues) fell through and returned having sent nothing, so a late subscriber saw an empty board until an issue next changed — broadcastBoards diffs against a server-global cursor, not per-connection, so an unchanged issue is never re-sent. Now a board subscribe dumps the current board to that socket, the same first-tick dump the SSE stream does per connection. Subscribe stays first so nothing published mid-dump is lost; the client keys by issue id. Verified live with a WS client (this surface has no unit tests; it needs the server extension). The residual stale-snapshot-vs-concurrent-change ordering is TODO 1d's per-message cursor.
EdmondDantes
added a commit
that referenced
this pull request
Jul 27, 2026
1a: an idle WS connection is NOT torn down at 15s — measured against a running server, the keepalive PING arrives at ~30s and the connection stays open past 75s, so setReadTimeout(15) does not apply after the upgrade. 1b: the board-bootstrap defect is fixed; what remains is the ordering half, which is 1d's per-message cursor.
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.
What
TODO item 1b. Over the WebSocket transport, a client that subscribes to a board room (
project/{key}/issues) received nothing — it could not arrive at a correct board fromsubscribealone.wsSubscribe()replayed the journal only for a run-trace topic (project/{key}/run/{id}/trace); a board topic fell through the regex and returned having sent nothing.broadcastBoards()diffs against a$sentcursor that is server-global, not per-connection — so an issue unchanged since the server last published it is never re-sent, and a room a client joins later never receives it.The SSE stream doesn't have this bug because its
$sentSnapshotscursor is a per-connection local, so its first tick dumps the whole board to each new subscriber.Fix
On a board
subscribe, dump the current board to that socket — the same first-tick dump SSE does, now available to WS.subscribestays first so nothing published mid-dump is lost; the client keys by issue id, so the overlap withbroadcastBoards' next tick is idempotent.Verified live
No unit test — this surface has none and needs the server extension (
TrueAsync\HttpServer+ WebSocket). Verified with a minimal WS client against a running server: subscribing to a project's board room now delivers one{"kind":"issue","data":{…}}frame per current issue immediately, where before it delivered nothing until an issue changed.Left for TODO 1d
The residual ordering — a stale snapshot value racing a concurrent change delivered on the same socket during the sub-millisecond dump — is what 1d's per-message cursor/version closes (boards have no seq to de-dupe by, unlike traces). This change is strictly better than the current "nothing sent" and introduces no new bug; the board self-corrects on the issue's next change.
Checks
composer qagreen — cs, PHPStan level 8, Testo (463 tests, unchanged: no test covers this WS path).