34. Redis: the ae.c event loop, sds dynamic strings, object encoding
What it is: an in-memory data-structure server, written by Salvatore
Sanfilippo (also kilo's author, Section 25) — single-threaded for all
command execution, which sidesteps Section 20's entire locking problem
by design.
Concept mapping:
ae.c, Redis's event loop, is Section 23.2'sselect()-based chat server, generalized: it abstracts overepoll(Linux),kqueue(macOS/BSD), andselect(fallback) behind one API (aeCreateFileEvent,aeMain), exactly the way SQLite's VFS abstracts over filesystems. This single-threaded event loop, not a thread pool, is why Redis commands are atomic without any mutex: only one command ever executes at a time, so Section 20's entire race-condition class simply cannot occur in command logic.sds(Simple Dynamic Strings),sds.h/sds.c, is Section 7's "a string is a convention" problem, solved once, everywhere in the codebase: ansdsis achar *(so it's still compatible with every libc string function), but the bytes before that pointer hold a header with the length and allocated capacity — the same "hidden header before the returned pointer" trick real allocators use internally (Section 39).
// simplified sketch of the real sds design
struct sdshdr {
uint32_t len; // used length — makes strlen() O(1) instead of O(n)
uint32_t alloc; // allocated capacity — makes "do we need to grow?" O(1)
char buf[]; // C99 flexible array member — the actual string bytes
};
// sdsnew() returns &hdr->buf[0], NOT &hdr — so callers use it exactly like char*,
// while sdslen(s) reaches backward: ((struct sdshdr*)(s - sizeof(struct sdshdr)))->len
This flexible-array-member technique (buf[] as the struct's last
member, with the actual allocation sized to sizeof(struct sdshdr) + needed_bytes) is standard C99 and appears constantly in real systems
code for exactly this "one allocation, header + variable-length
payload" shape.
- Object encoding: Redis's
robjuses Section 8.3's tagged-union pattern — atypefield plus anencodingfield that says whether a "list" is currently a compactziplist(few, small elements) or a full linked list (many elements), transparently upgrading as it grows. This is the real-world justification for tagged unions: pick the cheapest representation that fits the current data, dispatch on the tag everywhere you touch it.