This is a starting point for C++ solutions to the "Build Your Own Redis" Challenge.
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.
Note: If you're viewing this repo on GitHub, head over to codecrafters.io to try the challenge.
Note: This section is for stages 2 and beyond.
- Ensure you have
cmakeinstalled locally - Run
./your_program.shto run your Redis server, which is implemented insrc/Server.cpp. - Commit your changes and run
git push origin masterto submit your solution to CodeCrafters. Test output will be streamed to your terminal.
This repository is a minimal C++ implementation scaffold for a Redis-compatible server. Core pieces:
src/Server.cpp: Program entry point. Responsible for:- Creating and binding a TCP listening socket (per challenge instructions)
- Accepting client connections (blocking for early stages; later you'll add evented I/O)
- Reading raw bytes from the socket and delegating to the RESP parser / command handler
src/handle_command.cpp&include/handle_command.h: Dispatch layer turning parsed RESP Arrays into command responses (e.g. PING, ECHO, SET, GET). Extend this as new commands are required by later stages.src/resp_parser.cpp&include/resp_parser.h: Implements a minimal RESP (Redis Serialization Protocol) decoder (and possibly helpers for encoding). It should incrementally parse incoming buffers into strongly-typed RESP values.include/resp_datatypes.h: Data structures (enums / structs / variants) representing RESP types (Simple Strings, Bulk Strings, Integers, Arrays, Errors, Nulls). Central to type-safe command handling.CMakeLists.txt: Build configuration (targets, C++ standard, include paths, dependency linkage through vcpkg if needed).vcpkg.json/vcpkg-configuration.json: Declares external C/C++ dependencies resolved via vcpkg (currently likely empty or minimal for early stages).your_program.sh: Wrapper script executed by the CodeCrafters platform. It configures & builds (via CMake) then launches the compiled server.tests/: JavaScript end-to-end tests (Node +redis-clistyle interactions) executed by the platform to validate protocol behavior. Not compiled into your binary; they exercise the running server.
- Process starts in
main()insidesrc/Server.cpp(or equivalent initialization code there). - Server sets up a listening socket, then waits for a client connection.
- Upon receiving bytes, data is fed into the RESP parser producing a
RespValue(or similar). - Parsed command array is passed to
handle_command(...)which:- Validates arity
- Executes logic (e.g. respond with
+PONG\r\nfor PING) - Returns an encoded RESP reply string/bytes.
- Reply bytes are written back to the client socket.
Later stages introduce:
- In-memory key-value store (map) with expiry metadata
- Concurrency / multiplexing via
select,poll, orepoll-like abstractions (macOS:kqueueorselectfor simplicity) - Persistence / replication features (depending on challenge scope)
.
├── CMakeLists.txt # Build configuration
├── src/ # C++ source files
│ ├── Server.cpp
│ ├── handle_command.cpp
│ └── resp_parser.cpp
├── include/ # Public headers
│ ├── handle_command.h
│ ├── resp_datatypes.h
│ └── resp_parser.h
├── tests/ # E2E tests (JS)
├── your_program.sh # Build & run wrapper used by platform
├── vcpkg.json # Dependency manifest
└── README.md
Prerequisites:
- CMake (>=3.16 recommended)
- A C++17 (or later) compiler (clang on macOS is fine)
vcpkg(automatically bootstrapped by CodeCrafters scripts; local optional if you add dependencies)
Quick start (macOS / Linux):
./your_program.sh
This script will:
- Configure a build directory (usually
build/) - Invoke
cmake --buildproducing the executable (often namedredis_serveror similar) - Launch the server listening on the required port (default from challenge spec, typically 6379 or a provided env var)
Manual build (if you prefer explicit steps):
mkdir -p build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
./build/redis_server
This project does NOT use a hand-written Makefile or make build target. Instead it relies on:
- CMake (project generator & build orchestration)
- vcpkg (manifest mode) for dependency acquisition via
vcpkg.json - Wrapper script
./your_program.shthat encapsulates: configure + build + run
What ./your_program.sh conceptually does:
- (Optional) Bootstraps vcpkg if not present (handled by the CodeCrafters environment)
- Runs
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmakeso CMake knows to integrate vcpkg - Invokes
cmake --build build(which internally may callninjaormakedepending on generator chosen automatically by CMake) - Executes the produced binary
So while an underlying tool like make or ninja might still execute, you don't manually call it—CMake abstracts that away.
- Add it to
vcpkg.jsonunder thedependenciesarray. - Re-run
./your_program.sh(CMake + vcpkg will install & integrate it automatically). - Include headers / link usage normally; CMake target usage is typically automatic in manifest mode if you use
find_packageor provided config packages.
If you need a clean slate:
rm -rf build
./your_program.sh
This forces a full re-configure.
If you want to force Ninja (faster incremental builds):
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
You can still run the binary directly afterward.
Then connect from another terminal:
nc localhost 6379
*1\r\n$4\r\nPING\r\n
You should receive:
+PONG
To add a command:
- Update
handle_command.cppswitch/if chain to recognize the command name (uppercase) from the parsed array. - Validate argument count; return a RESP Error (
-ERR ...\r\n) on mismatch. - Implement logic, possibly updating in-memory state (add a global/store singleton or pass a state object).
- Encode response using RESP conventions (Simple String, Bulk String, Integer, etc.).
RESP forms you'll commonly handle early:
- Simple String:
+OK\r\n - Error:
-ERR msg\r\n - Integer:
:1000\r\n - Bulk String:
$5\r\nhello\r\n - Array:
*2\r\n$4\r\nECHO\r\n$5\r\nhello\r\n
Parser strategy tips:
- Accumulate incoming bytes; only finalize when complete terminators (
\r\n) or length-specified payloads are fully present. - Represent partial state if you later move to non-blocking sockets.
The platform runs the JS tests in tests/. For ad-hoc checks you can use redis-cli (if installed) or nc:
redis-cli -p 6379 PING
You have two easy options if redis-cli isn't already installed:
-
Install Redis locally (provides
redis-cli):- macOS (Homebrew):
brew install redis - Linux (Debian/Ubuntu):
sudo apt-get update && sudo apt-get install -y redis-tools - Then run:
redis-cli -p 6379 PINGor start an interactive session:redis-cli -p 6379
- macOS (Homebrew):
-
Use a temporary CLI via npx (no local install needed, requires Node.js):
- One-off command:
npx redis-cli -p 6379 PING - Interactive:
npx redis-cli -p 6379
- One-off command:
Inside the interactive prompt you can try:
PING
SET mykey hello
GET mykey
ECHO hi
Expected responses (once those commands are implemented) resemble:
PONG
OK
"hello"
"hi"
- Introduce a
KeyValueStoremodule for SET/GET - Add expiration handling (store expiry timestamps, prune on access)
- Switch to non-blocking sockets +
select()loop - Implement concurrency-safe data structures if you add threads (optional; single-thread event loop suffices initially)
- Port already in use: change or free it (
lsof -i :6379). - Build errors about missing headers: ensure
cmakepicked up the correct compiler and run a clean build (rm -rf build). - Parser returning unexpected types: log raw incoming buffer & parsed token sequence.
Feel free to adapt the structure as the challenge advances; keep the README updated so newcomers (and your future self) can navigate quickly.