electric-sql / electric-sql/electric-circuits
/v1/shape HTTP caching: cache-control tiers, ETag/304, cursor collapsing, 10MB chunking
- Dominant language
- Rust
- Stars
- 30
- Forks
- 6
- PR merge metrics
- No merged PRs in 30d
Description
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 `"::"`; 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](https://claude.com/claude-code)
Contributor guide
Research direction
Read apps/engine/src/electric.rs at lines 652 and 698, then compare upstream shapes/api/response.ex and utils.ex:59-93; account for dependency #8 before changing cache behavior. Done means the shape endpoint implements the specified cache tiers, ETag/304 handling, cursor bucketing, 10 MB offsets, cacheable long-poll responses, and the send_cache_headers off-switch.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100