fivenines

Theory / 10 min

track Redis-Like System

Building a Redis-like system is not about cloning every command. It is about proving that the central ideas fit together in one running machine.

The system you are aiming for has a shape like this:

RESP server
  -> evented client lifecycle
  -> command table
  -> typed in-memory objects
  -> expiration and memory policy
  -> persistence and recovery
  -> optional replication or sharding layer

Each layer should be understandable on its own, but the real value appears when they compose.

The Core Server

The first complete version should accept multiple TCP clients, parse RESP arrays, dispatch commands through a table, and return serialized replies.

The minimum command surface can stay small:

PING
GET, SET, DEL, EXISTS
EXPIRE, TTL, PERSIST
LPUSH, RPOP, LRANGE
HSET, HGET, HGETALL
MULTI, EXEC, DISCARD

This is enough to bring the main architecture into contact with reality: strings, lists, hashes, expiration metadata, transaction state, command validation, wrong-type errors, input buffering, output buffering, and ordered replies.

Boundaries Matter More Than Feature Count

A clean Redis-like server is not one large file that knows everything. The boundaries carry the design.

One useful layout is:

src/
  server.ts
  event-loop.ts
  client.ts
  resp.ts
  command-table.ts
  db.ts
  object.ts
  commands/
    string.ts
    list.ts
    hash.ts
    expire.ts
    transaction.ts
  persistence/
    rdb.ts
    aof.ts
    recovery.ts
  replication/
    primary.ts
    replica.ts

The exact names are less important than the separations. RESP parsing should not know command semantics. Command handlers should not perform socket writes. Persistence should not be tangled into every handler. Client state should not be confused with database state.

The Execution Path

The defining path of the system remains:

socket becomes readable
bytes enter client input buffer
RESP parser extracts commands
dispatcher validates command metadata
handler reads or mutates typed objects
reply is serialized into client output buffer
event loop flushes bytes when socket is writable

Every feature should be placed somewhere on that path or deliberately outside it.

Expiration modifies lookup. Eviction guards writes under memory pressure. Transactions alter dispatch for one client. Pub/Sub changes client mode and output fanout. AOF observes write propagation. RDB serializes the keyspace. Replication streams writes after local execution.

Once those placements are clear, the system stops feeling like a pile of features.

Persistence Closes The Loop

A Redis-like server that cannot restart is only half alive.

RDB-style snapshotting proves that the in-memory object model can be serialized as state. AOF-style logging proves that write commands can reconstruct state. Startup recovery proves that persisted data can become a coherent live keyspace again.

The important recovery question is always:

after restart, what should memory contain?

Expired keys should stay dead. Corrupt files should not be silently trusted. AOF replay should rebuild data without pretending to be a live client conversation.

One Distributed Feature Is Enough

A track does not need full Redis Cluster to be meaningful. One distributed slice can demonstrate the same principles.

A primary-replica full sync shows the relationship between snapshots and command streams. Partial sync adds replication offsets and backlog. A small hash-slot router shows how keys map to nodes. A failover toy demonstrates why topology changes require agreement and epochs.

Any one of these features forces the local server model to interact with a larger system.

What The Finished System Should Teach

A successful Redis-like implementation makes the following ideas concrete:

RESP separates bytes from command meaning
the event loop separates readiness from completion
the command table separates syntax from policy
the object model separates logical type from encoding
expiration and eviction delete keys for different reasons
transactions queue work on a client, not globally
persistence records state or mutations
replication copies state and streams future changes
cluster mode routes keys by slot

The finished system will be smaller than Redis by several orders of magnitude. That is fine. The point is not to reproduce Redis' age, optimizations, or production hardening. The point is to build a machine where Redis' design choices become inevitable.

When that happens, Redis stops being a bag of commands and becomes a set of interlocking ideas: memory, protocol, eventing, data structures, durability, and distribution all arranged around a fast command path.