Skip to content

/v1/shape HTTP caching: cache-control tiers, ETag/304, cursor collapsing, 10MB chunking #10

Description

@balegas

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions