electric-sql / electric-sql/electric-circuits

Shape id reuse after engine restart can append onto stale shape/<id> streams

Open
#3 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
30
Forks
6
PR merge metrics
No merged PRs in 30d

Description

Found during the production-readiness investigation (vs upstream sync-service).

**Symptom / failure scenario:** shape ids come from an in-memory counter that resets to 1 on restart (`apps/engine/src/engine.rs:291`, `473-475`), while `shape/*` streams from the previous run survive on the durable-streams server. `ensure_stream` is an idempotent PUT (`apps/engine/src/ds.rs:75-92`), so the first post-restart `create_shape` reuses `shape/s1` and appends a fresh backfill **on top of the old run's rows**. A client reading that stream from `offset=-1` sees stale rows merged with the new shape's contents — silent data corruption, no error anywhere.

**Fix direction:** make shape ids collision-free across restarts (persist the counter, or random/uuid ids), and garbage-collect orphaned `shape/*` streams at boot. Both fall out of the shape-catalog/recovery epic, but the id collision is a standalone corruption hazard worth closing first.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Contributor guide

Open the contributing guide

Research direction

Start with the counter and create_shape paths in apps/engine/src/engine.rs:291, 473-475, then inspect ensure_stream in apps/engine/src/ds.rs:75-92. Reproduce a restart with surviving shape/* streams and verify that a new shape cannot append to stale rows; boot recovery should also address orphaned streams if that remains in scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.