codecrafters-redis-rust: The Redis Clone That Starts by Pretending to Be a Client

A Rust server that teaches Redis from the wire up, with RESP parsing, passive expiration, and a replication handshake that does just enough to look real.

8 min read • View on GitHub • More from jnsahaj

A small server node wearing a client badge approaches a larger master node across a protocol checkpoint. Four stamped message cards move between them in order, and a sealed crate sits behind the smaller node. The scene explains that replication begins as a performance of protocol compliance before the node can settle into replica mode.PING, REPLCONF, REPLCONF, PSYNC. Behind the smaller node sits a sealed crate on a dolly, hinting at an empty database payload waiting to be handed over. The artist uses tightly packed crosshatching lines layered at different angles to build up shadow and form, with clean open areas of white for highlights. The line work has the quality of a classic metal engraving, precise and deliberate, with varied line weights where bold contour lines define shapes and finer interior lines create tonal depth. The overall style evokes vintage newspaper editorial illustrations from The Economist or Wall Street Journal. No color, no gradients, no grey fills. Only black lines on white. The background MUST be pure white #FFFFFF. No paper texture, no cream, no off-white, no noise, no grain. Perfectly clean flat white background." loading="lazy">
The clone does not start by storing data. It starts by negotiating its role.
Key Takeaways

The moment the clone stops being a clone

The most interesting thing in jnsahaj/codecrafters-redis-rust is not `SET` or `GET`. It is the moment the server has to speak Redis before it can act like Redis. A replica does not just connect. It performs a sequence of protocol moves, `PING`, `REPLCONF`, `REPLCONF`, `PSYNC`, and only then does it earn the right to listen for live updates.

Replication here is protocol theater, and the diagram makes each cue visible.

That sequence is the article in miniature. The clone is not proving feature breadth first. It is proving that it can negotiate state correctly, and that is a better lesson for distributed systems than a pile of commands with no conversation.

Why this repo exists at all

This is a CodeCrafters project, which changes how you should read the code. The goal is not Redis parity. The goal is to internalize the protocol shape, the event loop, the data flow through `main.rs`, `resp/parser.rs`, `store.rs`, and `redis.rs`, and the way a small Rust program can hold those pieces together without a lot of ceremony.

In this challenge, you'll build a toy Redis clone that's capable of handling basic commands like `PING`, `SET` and `GET`. Along the way we'll learn about event loops, the Redis protocol and more.

That README sentence is the right frame. The repository is not trying to hide its educational purpose. It is trying to make the protocol legible, and that constraint explains why the implementation makes a few sharp, deliberate shortcuts.

RESP becomes Rust types

The parser does not treat RESP as magic text. It turns bytes into a Rust `DataType`, then hands that structured value to a command evaluator. That is the elegant part of the repo: the wire format and the language type system line up cleanly, so the protocol can become code without losing its shape.

pub enum DataType {
    SimpleString(String),
    BulkString(Option<String>),
    Array(Vec<DataType>),
}

fn eval_dt(dt: DataType) -> Command {
    match dt {
        DataType::Array(items) => Command::from(items),
        _ => Command::Unknown,
    }
}

That mapping matters because it makes the parser, the dispatcher, and the response serializer feel like one pipeline instead of three unrelated subsystems. In Rust, a tagged protocol like RESP is a natural fit for enums, traits, and a small amount of glue.

Expiration happens only when you ask for the key

The expiration policy is one of the smartest tradeoffs in the codebase. There is no background janitor thread. Instead, `store.rs` checks whether a key has expired when the key is read, and removes it at that moment if needed. That keeps the implementation small and makes the cost of expiration visible where it matters.

A close-up of a drawer of key cards with small time tags attached. A hand pulls one card forward, and the expired cards crumble only as they are touched. The scene explains passive expiration, where cleanup happens on read instead of in the background.
The store does not sweep expired keys in the background. It discovers them at the moment of access.
if let Some(record) = self.records.get(key) {
    if record.is_expired(now) {
        self.records.remove(key);
        return None;
    }
    return Some(record.value.clone());
}

That is a clean engineering compromise for a learning server. Read latency absorbs the work, but the system avoids the complexity of a sweeper, a scheduler, or a second moving part that students would have to understand before they understand the protocol itself.

The empty RDB shortcut is the real flex

The most delightful trick in the repo is the hardcoded empty RDB payload during `PSYNC`. In production Redis, replication needs real persistence machinery behind it. Here, the code satisfies the protocol requirement with the smallest possible valid artifact. That is not a fake system. It is a disciplined one.

let empty_db = EMPTY_RDB_BYTES;
stream.write_all(b"+FULLRESYNC ...\r\n").await?;
stream.write_all(&empty_db).await?;
stream.write_all(live_replication_stream).await?;
A split scene shows a sprawling machine of pipes, tanks, and modules on the left, and a compact scaffold with only a few essential beams on the right. The contrast explains the difference between production Redis and a learning clone that keeps only the parts needed for protocol and replication milestones.
The point is not to reproduce everything. The point is to preserve the parts that teach the system.

What this repo teaches that Redis itself cannot

Redis proper is the benchmark, but this repo is a better teacher in one narrow sense. It strips the system down to the parts that create understanding: the protocol contract, the replication handshake, the state transition from client to replica, and the expiration policy. What disappears is scale, not meaning.

Dimensioncodecrafters-redis-rustRedis proper
PurposeLearn Redis by rebuilding the wire protocol and replication pathServe as a production in-memory data store
Protocol handlingManual RESP parsing into Rust enums and commandsIndustrial networking, many commands, broad compatibility
ReplicationHandshake-driven, with a small protocol sequence and an empty RDB shortcutFull replication behavior with mature persistence backing it
ExpirationPassive, on readMore complete lifecycle management across the system
PersistenceMinimal scaffolding for the exerciseReal persistence formats and operational guarantees
AudienceLearners who want to understand Redis from the wire upUsers who need a production database

That is why the project lands. It does not compete with Redis by imitation. It competes by focus. If you want to understand why a distributed system feels alive at the wire, this repo gives you just enough machinery to see the joints.