A small Redis-like server written in Go. It implements a subset of Redis commands It communicates using the RESP protocol and stores data in memory with optional per-key expiry.
This project is meant for learning and experimentation.
Why Redis?
- Quick retrieval and fast communication are the cornerstone of modern storage and communication.
- Redis is lightweight, it is in-memory and honestly quite robust. (And I needed something to build that is not related to any of my courses :D)
- Build and run the server:
go run cmd/server/main.go --port 6379- Test with redis-cli (recommended):
redis-cli -p 6379 PING
# PONG
redis-cli -p 6379 SET mykey hello
redis-cli -p 6379 GET mykey
# helloIf you don't have redis-cli, you can use nc to send raw RESP messages:
printf '*1\r\n$4\r\nPING\r\n' | nc -w 1 localhost 6379
# +PONGcmd/server/main.go- CLI entry point; loads config and starts the server.internal/server- TCP listener, connection handling, and cleanup loop.internal/protocol- RESP parser and writer (parsing client requests and writing replies).internal/command- Command dispatch and command handler implementations (SET, GET, PING, etc.).internal/store- In-memory key-value store with optional expiry and safe concurrent access.pkg/config- Default configuration and optional config file loading.
- Unit tests for the store:
go test ./internal/store -v- Integration script (starts server, runs commands and checks replies):
bash scripts/integration_test.sh 6381- Add a new file in
internal/commandimplementing theHandlerinterface:
type MyCmdHandler struct{}
func (h *MyCmdHandler) Execute(s *store.Store, args []string) Response { ... }- Register it in the
handlersmap ininternal/command/command.gowith the uppercase command name.
- Add support for additional data types: hashes (HSET/HGET), lists (LPUSH/LRANGE), sets (SADD/SMEMBERS), and sorted sets (ZADD/ZRANGE).
- Add command handlers and store methods for each data type and enforce type checks (return an error when a command is used on the wrong type).
- Extend the integration script to exercise the new data types and to assert type-error cases.
- Improve RESP parser robustness and add tests for malformed input and large bulk strings.
- Implement persistence (AOF or RDB-style snapshot) and wire
pkg/configpersistence settings into the server. - Apply per-connection read/write timeouts (use
ReadTimeout/WriteTimeoutfrom config) and add tests for timeout behavior. - Improve
KEYSpattern matching or addSCANto avoid blocking on large datasets. - Add server-level integration tests (Go tests that start the server on an ephemeral port and assert RESP replies).