You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(client): add EXPERIMENTAL_LIVE_SSE_QUERY_PARAM to protocol params list
The deprecated `experimental_live_sse` query param was missing from
ELECTRIC_PROTOCOL_QUERY_PARAMS, causing canonicalShapeKey to produce
different keys for the same shape depending on whether the SSE code
path added the param to the URL. This caused:
- expiredShapesCache entries written during SSE to be invisible when
the stream fell back to long polling
- upToDateTracker entries from SSE sessions to be lost on page refresh
- fast-loop cache clearing to target the wrong key during SSE
Also adds a static analysis test that verifies all internal protocol
query param constants are included in the protocol params list, and
updates SPEC.md with the unconditional 409 cache buster invariant.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| L1 |`#requestShape` → `#requestShape`| 940 | Normal completion after `#fetchShape()`| Offset advances from response headers |`#checkFastLoop` (non-live)|
352
-
| L2 |`#requestShape` catch → `#requestShape`| 874 | Abort with `FORCE_DISCONNECT_AND_REFRESH` or `SYSTEM_WAKE`|`isRefreshing` flag changes `canLongPoll`, affecting `live` param | Abort signals are discrete events|
353
-
| L3 |`#requestShape` catch → `#requestShape`| 886 |`StaleCacheError` thrown by `#onInitialResponse`|`StaleRetryState` adds `cache_buster` param |`maxStaleCacheRetries` counter in state machine|
354
-
| L4 |`#requestShape` catch → `#requestShape`| 924 | HTTP 409 (shape rotation) |`#reset()` sets offset=-1 + new handle; or request-scoped cache buster if no handle| New handle from 409 response or unique retry URL |
355
-
| L5 |`#start` catch → `#start`| 782 | Exception + `onError` returns retry opts | Params/headers merged from `retryOpts`| User-controlled; `#checkFastLoop` on next iteration|
356
-
| L6 |`fetchSnapshot` catch → `fetchSnapshot`| 1975 | HTTP 409 on snapshot fetch | New handle via `withHandle()`; or local retry cache buster if same/no handle|`#maxSnapshotRetries` (5) + cache buster on same handle|
361
+
| # | Site | Line | Trigger | URL changes because | Guard |
|`#checkFastLoop`| Non-live `#requestShape` only | Detects N requests at same offset within a time window. First: clears caches + resets. Persistent: exponential backoff → throws FetchError(502). |
363
375
|`maxStaleCacheRetries`| Stale response path (L3) | State machine counts stale retries. Throws FetchError(502) after 3 consecutive stale responses. |
364
-
|`#maxSnapshotRetries`| Snapshot 409 path (L6) | Counts consecutive snapshot 409s. Adds cache buster when handle unchanged. Throws FetchError(502) after 5.|
376
+
|`#maxSnapshotRetries`| Snapshot 409 path (L6) | Counts consecutive snapshot 409s. Unconditional cache buster on every retry. Throws FetchError(502) after 5. |
365
377
| Pause lock |`#requestShape` entry | Returns immediately if paused. Prevents fetches during snapshots. |
366
378
| Up-to-date exit |`#requestShape` entry | Returns if `!subscribe` and `isUpToDate`. Breaks loop for one-shot syncs. |
0 commit comments