A high-performance, Redis-compatible key-value store implementation in Go with support for multiple data structures, replication, pub/sub, and transactions.
- 🗂️ Multiple Data Structures: Strings, Lists, Sorted Sets, Streams, and Geospatial data
- 📡 Pub/Sub Messaging: Publish/subscribe pattern with channel-based messaging
- 🔄 Master-Replica Replication: Asynchronous data synchronization across replicas
- 💾 Transaction Support: ACID-compliant transactions with MULTI/EXEC/DISCARD
- ⏱️ Expiration & TTL: Key expiration with millisecond precision
- 🌍 Geospatial Operations: GEOADD, GEOPOS, GEODIST, GEORADIUS with geohash encoding
- 📊 Stream Data: Time-series data with XADD, XRANGE, XREAD
- 🔒 Blocking Operations: BLPOP with configurable timeout
- 🐳 Docker Ready: Multi-stage Docker builds with compose support
- ⚡ High Performance: Written in Go with goroutines and efficient data structures
Start a local server:
./spawn_redis_server.shThis starts a Redis server on port 6379 by default.
Start a replica server:
./spawn_redis_server.sh --port 3000 --replicaof localhost 6379Test the server:
$ redis-cli ping
PONGCheck server connectivity.
PING
# Returns: PONGEcho the given string.
ECHO "Hello World"
# Returns: "Hello World"Set key to hold the string value with optional expiration.
SET mykey "value"
SET session "data" PX 5000 # Expire in 5000msGet the value of a key.
GET mykey
# Returns: "value"Increment the integer value of a key by one.
INCR counter
# Returns: 1Lists are ordered collections of strings. Elements can be added/removed from both ends.
Insert elements at the head of a list.
LPUSH mylist "element1" "element2"
# Returns: 2Append elements to the tail of a list.
RPUSH mylist "element3" "element4"
# Returns: 4Remove and return the first element of a list.
LPOP mylist
# Returns: "element2"Remove and return the last element of a list.
RPOP mylist
# Returns: "element4"Get the length of a list.
LLEN mylist
# Returns: 2Get a range of elements from a list.
LRANGE mylist 0 -1 # Get all elements
# Returns: ["element1", "element3"]Blocking version of LPOP. Waits for an element to become available.
BLPOP mylist 5 # Wait up to 5 seconds
# Returns: ["mylist", "element1"]Sorted sets store unique members with associated scores, automatically sorted by score.
Add members with scores to a sorted set.
ZADD leaderboard 100 "player1" 200 "player2"
# Returns: 2Get the number of members in a sorted set.
ZCARD leaderboard
# Returns: 2Get the rank (index) of a member in a sorted set.
ZRANK leaderboard "player1"
# Returns: 0Get the score of a member in a sorted set.
ZSCORE leaderboard "player1"
# Returns: "100"Get members in a score range.
ZRANGE leaderboard 0 -1 WITHSCORES
# Returns: ["player1", "100", "player2", "200"]Remove members from a sorted set.
ZREM leaderboard "player1"
# Returns: 1Geospatial indexes store locations with latitude and longitude coordinates, enabling radius queries and distance calculations.
Add geospatial locations (longitude, latitude, member) to a key.
GEOADD locations 13.361389 38.115556 "Palermo"
# Returns: 1
GEOADD locations 15.087269 37.502669 "Catania" 12.5 37.8 "Agrigento"
# Returns: 2Validation:
- Longitude: -180.0 to 180.0
- Latitude: -85.05112878 to 85.05112878
Get the coordinates (longitude, latitude) of members.
GEOPOS locations "Palermo" "Catania"
# Returns: [[13.361389, 38.115556], [15.087269, 37.502669]]
GEOPOS locations "NonExistent"
# Returns: [nil]Calculate the distance between two members.
GEODIST locations "Palermo" "Catania"
# Returns: "166274.1516" (meters by default)
GEODIST locations "Palermo" "Catania" km
# Returns: "166.2741516"
GEODIST locations "Palermo" "Catania" mi
# Returns: "103.3182"Supported units:
m- meters (default)km- kilometersmi- milesft- feet
Query members within a radius from coordinates.
GEORADIUS locations 15.0 37.0 200 km
# Returns: ["Palermo", "Catania"]
GEORADIUS locations 15.0 37.0 100 km
# Returns: ["Catania"]Optional flags:
# Include distances
GEORADIUS locations 15.0 37.0 200 km WITHDIST
# Returns: [["Catania", "56.4413"], ["Palermo", "190.4424"]]
# Include coordinates
GEORADIUS locations 15.0 37.0 200 km WITHCOORD
# Returns: [["Palermo", [13.361389, 38.115556]], ...]
# Include geohash
GEORADIUS locations 15.0 37.0 200 km WITHHASH
# Returns: [["Palermo", 3479099956230698], ...]
# Limit results
GEORADIUS locations 15.0 37.0 200 km COUNT 1
# Returns: ["Catania"]
# Sort by distance
GEORADIUS locations 15.0 37.0 200 km ASC
# Returns: ["Catania", "Palermo"] (closest first)
GEORADIUS locations 15.0 37.0 200 km DESC
# Returns: ["Palermo", "Catania"] (farthest first)
# Combine options
GEORADIUS locations 15.0 37.0 200 km WITHDIST WITHCOORD ASC COUNT 2Implementation details:
- Uses 52-bit geohash encoding (26-bit latitude + 26-bit longitude)
- Haversine formula for distance calculations
- Earth radius: 6372797.560856 meters
- Stored in sorted sets with geohash as score
Streams are append-only log data structures for event streaming.
Append a new entry to a stream.
XADD mystream * field1 value1 field2 value2
# Returns: "1640000000000-0"Query a range of entries in a stream.
XRANGE mystream - +
# Returns all entriesRead entries from one or more streams.
XREAD STREAMS mystream 0
# Returns new entries since ID 0Publish/Subscribe messaging pattern for real-time communication.
Post a message to a channel.
PUBLISH news "Breaking news!"
# Returns: 1 (number of subscribers)Subscribe to channels.
SUBSCRIBE news sports
# Receives messages from news and sports channelsUnsubscribe from channels.
UNSUBSCRIBE news
# Stops receiving messages from news channelExecute multiple commands atomically.
Mark the start of a transaction block.
MULTIExecute all queued commands in the transaction.
EXEC
# Executes all commands queued since MULTIDiscard all commands in the transaction queue.
DISCARD
# Cancels the transactionMaster-replica replication for high availability and read scaling.
Get server information including replication status.
INFO replication
# Returns: role:master, connected_slaves:1, ...Internal command used by replicas to configure replication.
REPLCONF listening-port 6380Internal command used by replicas to synchronize with master.
PSYNC ? -1
# Initiates full synchronization- RESP Protocol: Redis Serialization Protocol for client-server communication
- Skip List: Efficient sorted set implementation with O(log n) operations
- Geospatial Index: 52-bit geohash encoding with Haversine distance calculations
- Pub/Sub: In-memory message broker with channel subscriptions
- Replication: Asynchronous master-replica data synchronization
- Transactions: ACID-compliant transaction support with command queuing
Build the Docker image:
docker build -t kv-store .Run a single instance:
docker run -p 6379:6379 kv-storeRun as a replica:
docker run -p 6380:6380 kv-store --port 6380 --replicaof <master-host> 6379Start the full stack (1 master + 2 replicas):
docker compose up --buildThis will start:
- Master on port 6379
- Replica 1 on port 6380
- Replica 2 on port 6381
Connect to master:
redis-cli -p 6379 pingConnect to replica:
redis-cli -p 6380 pingStop all containers:
docker compose downView logs:
docker compose logs -f kv-store-master
docker compose logs -f kv-store-replica-1Run all tests:
go test ./tests/...Run specific test:
go test ./tests/zadd_test.go -v- Written in Go for performance and concurrency
- Custom RESP protocol parser
- Skip list data structure for sorted sets
- Non-blocking pub/sub with goroutines
- Polling-based blocking operations (BLPOP)
- Thread-safe operations with mutex locks