engine.c and its dependencies. How results reach a client — whether through a terminal or a TCP socket — is the concern of the frontend layer alone.
This separation means the same command semantics power both the interactive REPL and the TCP server, with zero duplication of logic.
Module layout
main.c startup routine initializes the hash table, opens and replays the AOF, and then hands control to whichever frontend was compiled in.
The protocol-agnostic engine
engine.c exposes a single entry point:
engine.c
SET, GET, DEL, TTL, etc.), and returns a Result struct. It never reads from or writes to any file descriptor. There is no printf, no fgets, no socket call anywhere in engine.c.
This makes the engine trivially testable and reusable: any caller that can produce a string and consume a Result can drive it.
Result type system
Commands communicate outcomes back to frontends through a tagged union:engine.h
print_result(fp, &r) to serialize the result to its output stream — stdout for the REPL, the client socket for the TCP server. After printing, it calls free_result(&r) to release any heap-allocated string.
RES_CLEAN is returned specifically by the CLEAR command to signal the REPL to clear the terminal screen (system("clear")). The TCP server ignores this result type. RES_EXIT is returned by EXIT and QUIT to signal the frontend to shut down.Two frontends, one engine
- REPL (repl.c)
- TCP server (server.c)
The REPL frontend reads lines from Use the REPL for local development, scripting, and exploration.
stdin and writes results to stdout. It displays a >>> prompt before each command and runs the active expiry sweep on every iteration.repl.c
Single-threaded design
RadishDB is deliberately single-threaded. One OS thread handles everything: the event loop, command execution, expiry sweeping, and I/O.Simplicity
No mutexes, no lock ordering, no data races. The entire state of the database is owned by a single thread at all times.
Predictability
Command latency is uniform. There is no contention-induced jitter from concurrent readers or writers.
Correctness
The hash table, AOF, and expiry sweeper share state freely without synchronization primitives — and that is safe by design.
Auditability
The execution path for any command can be traced linearly through the source. There are no concurrency-related control flows to reason about.
Uptime tracking
The engine records its start time inengine_start_time at initialization. The INFO command subtracts the current time from this value to compute and report uptime in seconds. No external clock service or global state is required.
Intentional omissions
The following are out of scope by design:- No threads — all state is single-owner, no synchronization
- No replication — there is no leader/follower or primary/replica protocol
- No clustering — data is not sharded or distributed
- No authentication — the TCP server accepts all connections
- No pipelining — commands are processed one at a time per connection
RadishDB is an educational database that implements real storage engine internals. For production multi-client workloads, consider Redis, KeyDB, or Dragonfly.