fix(api-websockets): deliver server->client pushes on SQL deployments (recency filter) - #5553
Open
adrians5j wants to merge 10 commits into
Open
fix(api-websockets): deliver server->client pushes on SQL deployments (recency filter)#5553adrians5j wants to merge 10 commits into
adrians5j wants to merge 10 commits into
Conversation
Watching all apps at once isn't supported yet for the self-hosted hosting type, but the post-create message told users to run a single `webiny watch` command. Update the message to run the API and Admin apps separately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…placeholder
oxfmt reformatted the admin index.tsx `{GLOBAL_CSS}` placeholder into a block
statement, so the literal find-replace in ServerBuildAppWorkspaceService no
longer matched and the bare `GLOBAL_CSS;` shipped to the workspace (TS2304).
Exclude the server template's appTemplates folder from oxfmt (mirroring the
AWS template) and restore the placeholder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…config The 6.5.0 stale-branch merge (#5396) clobbered createRsbuildConfig.js back to a pre-#5453 state, dropping the `assetPrefix: "auto"` config and the isServer externals gating. Without assetPrefix, the self-hosted bg-tasks worker chunk (spawned via `new Worker(new URL(..., import.meta.url))`) resolved to an absolute filesystem-root URL and failed with "Cannot find module". The referencing comments in WorkerTaskService/BreeSchedulerService survived the revert, masking the loss. Restore the #5453 version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SendToIdentity silently matched zero connections on SQL-backed (self-hosted) deployments, so server->client pushes (e.g. the file-manager AI enrichment notification) never arrived even though the connection was registered under the correct identity. ListConnectionsUseCase filtered stale connections with a lexicographic string comparison (`connectedOn >= <iso cutoff>`). But `connectedOn` is a SQL `datetime` column, and the driver returns it in a shape that doesn't compare against an ISO string: mysql2 hands back a `Date` (coerces to "Wed Aug 04 2026 ...", sorts below "2026-...") and other drivers return a `T`/`Z`-less "2026-08-04 17:02:06" (space sorts before `T`). Either way every live connection read as expired and was dropped. Compare by parsed UTC epoch instead, normalizing the space form back to UTC (safe — the stored wall-clock is UTC), so it works regardless of driver format. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the connectedOn epoch parsing and recency predicate out of execute() into module-level helpers (connectedOnToEpoch, isRecentConnection) plus a named RECENT_CONNECTION_WINDOW_MS constant. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🚓 Slop Cop ✅ Nothing worth flagging. The diff looks consistent with the PR's stated intent and the code-style rules. The PR is a small, coherent fix matching its stated intent (normalizing connectedOn at the SQL boundary and comparing ISO strings), with no integrity red flags and no clear style-rule violations in the added lines. Automated, non-blocking heads-up from an LLM. It can be wrong — use your judgment. Regenerates on every push. |
Member
Author
|
/e2e |
|
Cypress E2E tests have been initiated (for more information, click here). ✨
|
Member
Author
|
/vitest |
|
Vitest tests have been initiated (for more information, click here). ✨
❌ Failed packagesPGlite
|
…ndary Root-cause fix for server->client pushes matching zero connections on SQL deployments. `connectedOn` is declared `string` and written as a UTC ISO string, but the `datetime` column is read back driver-specifically — a `Date` (node-postgres, some sqlite clients) or a `T`/`Z`-less "2026-08-04 17:02:06". That violated the type everywhere it flows (the recency filter dropped every connection; the GraphQL `connectedOn: DateTime!` field could serialize inconsistently; a test asserting `expect.any(String)` failed on PGlite). Normalize the value to a canonical UTC ISO string once, in the SQL registry's `toData()` boundary, so every consumer gets the declared `string`. With that guarantee, ListConnectionsUseCase goes back to a plain ISO string comparison (ISO-8601 UTC strings sort chronologically) — no per-consumer parsing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
On SQL-backed (self-hosted / server flavour) deployments,
SendToIdentitysilently matched zero connections, so server→client WebSocket pushes never arrived — even though the connection was registered under the correct identity. Symptom: file-manager AI image enrichment completes and updates the file, but no "Image enriched" notification appears in Admin.Root cause
connectedOnis declaredstringand written as a UTC ISO string, but it lives in a SQLdatetimecolumn. On read the driver hands it back in a shape that violates the declared type: aDate(node-postgres / some sqlite clients) or aT/Z-less"2026-08-04 17:02:06".ListConnectionsUseCasethen filtered stale connections with a lexicographic string compare against an ISO cutoff — aDatecoerces to"Wed Aug 04 2026 …"(sorts below"2026-…"), and the space form's space (0x20) sorts beforeT(0x54). Either way every live connection read as expired and was dropped.SendToIdentitysent to nobody, threw nothing, and the task still reported success.The type violation was wider than the filter: the SQL registry's
toData()passed the raw driver value straight through, so it also reachedWebsocketsGraphQLFactory'sconnectedOn: DateTime!field, and a unit test assertingexpect.any(String)failed on the PGlite lane.Fix — normalize at the SQL boundary
Normalize
connectedOnto a canonical UTC ISO string once, inWebsocketsConnectionRegistry.toData()(@webiny/api-websockets-sql) — aDatemaps throughtoISOString(), the space form is read as UTC (safe: the stored wall-clock is UTC). Every consumer now gets the declaredstring. With that guarantee,ListConnectionsUseCasegoes back to a plain ISO string comparison (ISO-8601 UTC strings sort chronologically) — no per-consumer date parsing.Verification
yarn test:pglite packages/api-websockets— 25/25 pass (was 1 failing: "should properly list connections")yarn test packages/api-websockets(ddb) — 25/25 passyarn test:pglite packages/api-websockets-sql— 10/10 passNot a WS-wiring regression
The recent 6.5.0 merges did not revert any WebSocket source; the wiring (register → sql registry → SendToIdentity → transport → admin handler) is intact. Latent bug in a new code path. AWS/DynamoDB never hit it — ddb stores
connectedOnas a plain string attribute that round-trips as ISO.Follow-up (out of scope here) — recency filter keys off the wrong field
ListConnectionsUseCasejudges "recent" byconnectedOn(first-connect time), butlistStalecorrectly useslastSeen(heartbeat). So an Admin session open > 3 hours — alive and heartbeating,lastSeenfresh — has an oldconnectedOnand gets filtered out, silently losing pushes. The heartbeat/lastSeenmachinery exists for exactly this. Worth a separate PR: filter onlastSeen(fall back toconnectedOnwhen never heard from). Independent of the type-normalization fixed here.🤖 Generated with Claude Code