Skip to content

Overview — Project 2: kvlite

In Project 1 · logwise you built a program that reads data, runs to completion, and exits. kvlite is the opposite shape: a program that holds data, stays running, and is talked to by many clients at once. That single change — long-lived shared state — is what pulls in the next tier of Rust: traits and generics to make the store reusable, Arc/RwLock to share it across threads without a data race, an append-only log so a restart doesn’t lose everything, and a TCP server to put it on the network. You’ll meet each one exactly when the build demands it.

A small key-value store — a mini-Redis. By the end it speaks a plain-text line protocol you can drive with nc:

$ nc 127.0.0.1 6380
SET name ada lovelace
OK
GET name
VALUE ada lovelace
DEL name
DELETED
GET name
NIL

Under that one-line protocol sits a layered design, and each layer is one day’s lesson:

TCP server day 7 std::net, thread-per-connection, the line protocol
protocol day 7 parse a line -> Request -> Response
Db day 6 thread-safe + durable: SharedStore + append-only log
╱ ╲
store.rs log.rs day 4/5: Store trait + MemStore + SharedStore / day 6: the WAL
DayPageYou buildThe Rust it forces
4Traits & genericsa single-threaded store + a REPLtraits, generics, lifetimes-in-context, HashMap
5Concurrencyconcurrent access from many threadsArc, Mutex/RwLock, mpsc, Send/Sync
6Persistence & errorssurvive a restartappend-only log, iterators, thiserror
7TCP servera networked kvlitestd::net, thread-per-connection, the protocol

Each day teaches its concept, then has you build that slice into the companion crate at rust/kvlite/. Type the code yourself, run it, and break it on purpose — the compiler’s objection is the lesson.

Why a key-value store is the right teacher

Section titled “Why a key-value store is the right teacher”

A KV store is the smallest program that is simultaneously shared, mutable, long-lived, and durable — the four properties that make real systems hard. Hold all four at once and every big Rust idea has a concrete reason to exist:

  • Shared + mutable → the borrow checker’s “shared XOR mutable” rule (from The Rust Mindset) now has to hold across threads. That’s Arc<RwLock<T>>, and the compiler enforces it with the Send/Sync traits.
  • Reusable → you don’t want to rewrite the engine for every key/value type, so the store is a trait with a generic implementation.
  • Durable → RAM is wiped on exit, so writes go to an append-only log you replay on startup — a job tailor-made for iterators.

Hold the playbook’s recurring question on every page: what does building this force you to understand — and what is Rust’s compiler protecting you from? For kvlite the answer sharpens to its most famous form. The moment two threads touch one HashMap, C would let you race; Rust simply won’t compile the shared-mutable access until you put it behind a lock — fearless concurrency, checked at compile time. By Day 7 you’ll have a networked store where the scariest class of bug in systems programming was made unwritable by the type system.

Begin with Day 4 · Traits & Generics →