Distributed Key-Value Store with Replication
Build a replicated key-value store in Rust: one primary node accepts writes and streams them to follower nodes over TCP, followers serve reads from their own copy, and a follower that disconnects (or starts fresh) catches up on everything it missed, from the primary's write log or a snapshot. Messages are encoded with Serde, all networking is async with Tokio, and you'll test the tricky cases on purpose: followers falling behind, connections dropping mid-stream and followers restarting. You'll learn primary-follower replication, log sequence numbers, snapshots and catch-up, async networking in Rust, framing messages on TCP, and testing distributed behavior.
- Estimate
- ~12h
- Steps
- 6
- Completed by
- 0
- Proposed by
- codeseed.app
Tokio · serde
Project roadmap
- 01
Build a single-node store
~2hCreate a project with tokio, serde, bincode (or serde_json) and tokio-util. Define request and response message types with Serde (Get, Set, Delete and their results), sent as length-prefixed frames (with LengthDelimitedCodec). Build a server that keeps data in a HashMap behind a lock and handles many clients at once, plus a small client command. Done when: several clients can set and get keys on one node.
- 02
Stream writes to followers
~2.5hGive every write on the primary a sequence number and append it to an in-memory log (and optionally a file). Followers connect to a separate replication port and send the last sequence number they have; the primary sends the log entries after it and then streams new writes as they happen, using a broadcast channel. Followers apply entries in order. Done when: writes on the primary appear on connected followers within milliseconds.
- 03
Catch up after reconnecting
~2.5hKeep the log bounded: when it grows too large, take a snapshot of the data at a sequence number and drop older log entries. A follower asking for entries older than the log's start receives the snapshot first, then the log after it. Followers reconnect with increasing waits after a disconnect and save their data and last sequence number to disk so a restart continues from there. Done when: a follower stopped during many writes catches up completely when it returns, whether from the log or a snapshot.
- 04
Route reads and writes
~1.5hFollowers reject writes with a message pointing to the primary's address, and serve reads from their copy; responses include the node's last applied sequence number, so clients can tell how up to date the answer is. Add an option for clients to require reads at least as recent as a given sequence number (waiting briefly or failing). Done when: writes to followers are redirected, and reads report their freshness.
- 05
Test failure scenarios
~2.5hWrite integration tests that start a primary and two followers on random ports inside the test: replication of many writes, a follower disconnected mid-stream and reconnected, a follower restarted from its saved state, catch-up through a snapshot, a slow follower that falls behind, and redirected writes. After each scenario, check every node has exactly the same data. Done when: cargo test passes reliably when run several times.
- 06
Add observability
~1hLog (with tracing) each follower's connection, catch-up method and lag in sequence numbers, and add a STATUS command showing the node's role, last sequence number, and for the primary each follower's lag. Document the design and its limits in README.md (for example, what happens if the primary crashes, which this project doesn't handle automatically). Done when: STATUS shows each follower's lag, and README.md explains the design and limits.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack