This project is a Redis-style key/value server written in modern C++ with nonblocking TCP, epoll, and RESP parsing. It keeps a simple in-memory store with optional expirations, a command dispatcher (SET/GET/DEL/EXISTS/EXPIRE/TTL/PING/ECHO), and a small Python load generator to measure throughput/latency. Optimizations were guided by perf and timing data.
- Network: Nonblocking
accept4+ epoll; per-connection read/write buffers; backpressure by togglingEPOLLOUTonly when writes are queued. - Protocol: Streaming RESP array-of-bulk parser; RESP encoder helpers for status, bulk strings, integers, arrays.
- Store:
unordered_mapfor keys, optional expirations usingsteady_clock; lazy expiry on access plus sweep hook. - Commands: Dispatcher maps argv → handlers; minimal allocations via
string_viewplumbing. - Client load: Python driver sends a pipelined mix of SET/GET/DEL/EXISTS/PING over TCP to a fixed keyspace, records throughput and p50/p95/p99 latencies.
redis_engine
├── makefile # builds kvserv
├── src
│ ├── main.cpp # epoll loop, accept/read/write wiring
│ ├── commands/dispatcher. # command handlers
│ ├── db/store.* # in-memory KV + expirations
│ ├── net/{socket,epoll,connection}. # sockets/epoll/per-connection buffers
│ ├── protocol/{resp,resp_parser}. # RESP encoder/parser
│ └── util/{error,time}.hpp # helpers
├── client/runner.py # load generator (pipelined RESP client)
├── utils/redis.sh # build+run server
├── utils/client.sh # run client load
└── data, plots # experiment outputs
From repo root:
# server (hardcoded to listen on port 9000)
./utils/redis.sh
# client load (hardcoded host 192.168.37.1, port 9000)
./utils/client.sh
- on_write (~67% of CPU): dominated by kernel
__send(); not much to squeeze here beyond batching and avoiding extra wakeups. - on_read (~19%): half in
recv(syscall), but inside it:- handle_get (~3.8%): most time in
db::store::get, with ~1.6% insteady_clock::nowand the rest in hash lookup and also saw RESP string encoding overhead. Fixes: only callnow()when the key has an expiry; makegetreturnstd::optional<string_view>to avoid copies alongisde swapping RESPto_stringforto_charsto stay on-stack. - handle_set (~2.2%): split between
db::store::setandfreefrom allocations. Fixes: move semantics on inserts and passstring_viewthrough dispatcher→store to avoid extra allocations. - parse (~1.8%): two-pass integer parsing (find terminator then
from_chars). Fix: single-pass parsing over the buffer rather than the double pass and avoid transientstring_viewconstruction.
- handle_get (~3.8%): most time in
- CPU pinning: pin server threads during tests to reduce cpu-migration to 0 (shown via perf stats).
Client uses pipelined requests (depth 16) over TCP with a keyspace=200, Averages exclude the single 200M run as that was only run once compared to the others being run three times. Errors were 0 in all runs.
| Workload | Avg elapsed (s) | Avg throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|---|---|
| 2M ops (avg of 3) | 14.163 | 141,218 | 0.31 | 0.59 | 0.76 |
| 20M ops (avg of 3) | 129.372 | 154,662 | 0.28 | 0.53 | 0.69 |
| Workload | Avg elapsed (s) | Avg throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) |
|---|---|---|---|---|---|
| 2M ops (avg of 3) | 13.154 | 152,059 | 0.29 | 0.54 | 0.69 |
| 20M ops (avg of 3) | 128.969 | 155,077 | 0.28 | 0.52 | 0.66 |
| 200M ops (single) | 1278.375 | 156,449 | 0.28 | 0.52 | 0.66 |
Also note that the server scales very well under load.
| Workload | Throughput speedup | p50 delta | p95 delta | p99 delta |
|---|---|---|---|---|
| 2M ops | 1.08x | -0.02 ms | -0.05 ms | -0.07 ms |
| 20M ops | 1.01x | 0.00 ms | -0.01 ms | -0.03 ms |
- Listener on
AF_INETTCP, nonblocking;EPOLLINfor reads,EPOLLOUTtoggled when write buffer non-empty. - Per-connection buffers + RESP parser to extract argv; dispatcher writes RESP replies; backpressure via bounded buffers.
- Store with optional expirations; lazy cleanup on access; TTL/EXPIRE commands mapped directly to deadlines.
- Client load emphasizes realistic pressure: fixed keyspace to maintain hit rate, pipelining to simulate in-flight ops, client-perceived latency as primary SLO.
Jason Majoros




