Today every /v1/shape response is cache-control: no-store (apps/engine/src/electric.rs:652,698), the client's cursor is ignored, there is no ETag, and snapshot bodies are unbounded. Zero CDN/edge offload. Depends on #8 (handles/offsets must survive restarts, or cached URLs point at dead handles).
Adopt upstream sync-service's scheme (values encode real CDN tuning — copy verbatim, refs into shapes/api/response.ex):
- Cache-control tiers: snapshot
public, max-age=604800, s-maxage=3600, stale-while-revalidate=2629746; catch-up public, max-age=60, stale-while-revalidate=300 (configurable); live public, max-age=5, stale-while-revalidate=5; 409 must-refetch public, max-age=1 (no handle) / 60 (with handle) + must-revalidate; other errors no-store + surrogate-control: no-store.
- ETag + If-None-Match → 304: etag
"<handle>:<request offset>:<chunk end offset>"; empty live responses get a monotonic suffix so CDNs never serve them from cache (forces origin collapsing).
electric-cursor time-bucketing: bucket = long-poll window, with jitter on rollover, so concurrent live pollers share a URL and CDNs collapse them to one origin request (upstream utils.ex:59-93).
- Response chunking at 10 MB: cap each response's read size and return
electric-offset at the cut; client re-requests from there (fresh cacheable URL). Our DS offsets are byte-positioned so no storage change is needed.
- Long-poll timeout → 200 +
[up-to-date] control (+ electric-has-data: false) instead of today's 204 — the 200 is the cacheable/collapsible response, and it matches upstream exactly (conformance win).
send_cache_headers off-switch for deployments without a CDN.
Non-goal here: the extended API read path (durable-streams server) — separate issue, different surface; investigating whether the DS implementation supports per-stream/response cache headers.
🤖 Generated with Claude Code
Today every /v1/shape response is
cache-control: no-store(apps/engine/src/electric.rs:652,698), the client'scursoris ignored, there is no ETag, and snapshot bodies are unbounded. Zero CDN/edge offload. Depends on #8 (handles/offsets must survive restarts, or cached URLs point at dead handles).Adopt upstream sync-service's scheme (values encode real CDN tuning — copy verbatim, refs into
shapes/api/response.ex):public, max-age=604800, s-maxage=3600, stale-while-revalidate=2629746; catch-uppublic, max-age=60, stale-while-revalidate=300(configurable); livepublic, max-age=5, stale-while-revalidate=5; 409 must-refetchpublic, max-age=1(no handle) /60(with handle) +must-revalidate; other errorsno-store+surrogate-control: no-store."<handle>:<request offset>:<chunk end offset>"; empty live responses get a monotonic suffix so CDNs never serve them from cache (forces origin collapsing).electric-cursortime-bucketing: bucket = long-poll window, with jitter on rollover, so concurrent live pollers share a URL and CDNs collapse them to one origin request (upstreamutils.ex:59-93).electric-offsetat the cut; client re-requests from there (fresh cacheable URL). Our DS offsets are byte-positioned so no storage change is needed.[up-to-date]control (+electric-has-data: false) instead of today's 204 — the 200 is the cacheable/collapsible response, and it matches upstream exactly (conformance win).send_cache_headersoff-switch for deployments without a CDN.Non-goal here: the extended API read path (durable-streams server) — separate issue, different surface; investigating whether the DS implementation supports per-stream/response cache headers.
🤖 Generated with Claude Code