This repository contains a fully functional, Redis-compatible in-memory data structure store built from scratch in Go. It was developed following the CodeCrafters "Build Your Own Redis" challenge, which provides an excellent hands-on approach to understanding the internal mechanics of networked services, distributed systems, and databases.
This project supports a wide array of standard Redis commands and concepts, handling raw TCP connections and parsing the RESP (REdis Serialization Protocol) directly.
-
Core Data Structures & Commands:
- Strings:
SET(withEX/PXTTLs),GET,INCR,TYPE,ECHO,PING. - Lists:
LPUSH,RPUSH,LRANGE,LLEN,LPOP,RPOP, and blocking operations (BLPOP). - Sorted Sets:
ZADD,ZRANGE,ZRANK,ZCARD,ZSCORE,ZREM.
- Strings:
-
Replication (Master/Replica):
- Full handshake sequence (
PING,REPLCONF,PSYNC). - Initial synchronization via empty RDB file transfer.
- Asynchronous command propagation from Master to Replicas.
- Replication offset tracking and acknowledgment (
REPLCONF GETACK). - Synchronous replication synchronization via the
WAITcommand.
- Full handshake sequence (
-
Persistence:
- RDB (Redis Database): Parsing binary
.rdbsnapshot files to load initial state and expiries. - AOF (Append-Only File): Command logging and sequential replaying on startup with manifest file tracking.
- RDB (Redis Database): Parsing binary
-
Transactions (Optimistic Locking):
- Support for
MULTI,EXEC, andDISCARDblock queues. WATCHcommand to monitor keys for modifications, aborting transactions if changes occur (CAS - Compare and Swap logic).
- Support for
-
Streams:
XADD(with auto-generating IDs*),XRANGE, andXREAD(with blocking supportBLOCKand dynamic ID resolution$).
-
Geospatial (GEO):
- Geohash encoding/decoding using bit interleaving.
- Haversine formula for distance calculation.
GEOADD,GEOPOS,GEODIST, andGEOSEARCH(by radius).
-
Pub/Sub:
SUBSCRIBEandPUBLISHusing observer patterns.
-
Authentication & ACL:
requirepassconfiguration support.AUTHcommand for connection unlocking.- Basic
ACL SETUSERandACL GETUSERreturning SHA-256 hashed passwords.
Building Redis is an excellent exercise in Data Structures and Algorithms. Here is a comparison of how features are implemented in this repository versus how a production-grade Redis does it. This is a great roadmap for further DSA exploration.
-
Current: Uses Go's native
map[string]anyguarded by async.RWMutex. -
Redis Internals: Uses custom hash tables that implement incremental resizing. When the map gets full, Redis creates a new table and gradually moves buckets over during normal command execution to avoid blocking the main thread.
-
Current: Backed by a Go
map[string]float64. Range queries (ZRANGE) extract all nodes and use Go'ssort.Slice($O(N \log N)$ time complexity). -
Redis Internals: Uses a combination of a Hash Map (for
$O(1)$ member lookups) and a Skip List (a probabilistic linked list). A Skip List allows for$O(\log N)$ insertions, deletions, and range queries. Building a Skip List from scratch is a fantastic follow-up DSA project.
-
Current: Implemented as a flat Go slice (
[]StreamEntry). Fetching the last ID is$O(1)$ , but searching requires linear scanning. -
Redis Internals: Uses a Radix Tree (Trie) where each node contains a "listpack" (a tightly packed array of data). This minimizes memory overhead significantly and makes looking up specific IDs incredibly fast.
-
Current: Converts coordinates to a 52-bit integer via bit interleaving (Geohashing) and stores them inside the Sorted Set map.
-
Redis Internals: Does exactly this! Redis
GEOcommands are fundamentally justZSETcommands under the hood. The 52-bit geohash becomes thescore. Because Geohashes preserve spatial locality (points close to each other have similar integer prefixes), Redis can do radius searches by querying integer ranges in the underlying Skip List.
-
Current: Uses lazy evaluation (checking if expired when
GETis called) combined with an active Goroutine that sleeps and cleans up. -
Redis Internals: Also uses lazy evaluation. However, instead of spawning a thread per key, Redis periodically samples a random batch of keys with TTLs. If a high percentage of the batch is expired, it aggressively samples again (Probabilistic Active Expiration).
If you're looking to expand this project further, consider implementing:
-
Skip Lists for ZSETs: Refactor the current
map+sortimplementation into a proper Skip List to achieve true$O(\log N)$ performance forZRANGE. -
RDB Serialization: Currently, the server parses RDB files. You could implement the reverse: dumping the in-memory state back into a properly formatted binary
.rdbfile. -
Eviction Policies: Implement memory limits (
maxmemory) and algorithms like LRU (Least Recently Used) or LFU (Least Frequently Used) to evict keys when the cache is full. -
RESP3 Support: Upgrade the RESP parser to support Redis 6+ RESP3 features (maps, sets, booleans, floating-point numbers).
-
Cluster Mode / Sentinels: Add support for highly available setups involving auto-failover, cluster routing (
MOVEDresponses), and hash slots.

