The Invariants I Didn't Hold
An honest accounting of a write-ahead log: what it guarantees, where the documentation outran the code, and the pattern in every gap.
Technical learnings, projects, and things worth writing about.
Technical learnings, side projects, and things I find worth writing about.
An honest accounting of a write-ahead log: what it guarantees, where the documentation outran the code, and the pattern in every gap.
You can't unit test a power cut. What I built instead, and the gap I left in the middle of it.
A 2.55x speedup that was real, produced by a mechanism that doesn't exist, hiding a bug that resurrects deleted keys.
Recovery isn't about salvaging as much as possible. It's about knowing exactly where you stopped believing the file.
Splitting the log into files makes almost everything easier. The seam between writing and rotating is where a durability bug was hiding.
A bounded queue forces you to decide who waits. Cancellation forces you to decide what an error means.
Getting from 255 writes a second to nearly ten thousand, without weakening a single durability guarantee.
The LSN is the identity of every record in the log. It isn't stored on disk, and three problems follow from that.
A length prefix tells you where a record ends. A checksum tells you whether to believe the length prefix.
The durability primitive underneath every database, and the one number that shapes every decision in it.
An honest accounting of what this job queue guarantees, what it doesn't, and the config settings that do nothing at all.
Parent and child is a claim about containment. Queued work isn't contained by anything, so it needs a different relationship.
173,000 requests with zero errors told me nothing useful. The test that broke its own thresholds told me everything.
A status column records what a job was doing. It cannot record whether anyone is still doing it.
Deadlines protect the worker, not the job. Once blocking code is involved, the difference stops being academic.
Check-then-insert looks correct and isn't. The only reliable fix is to stop checking and let a constraint decide.
Retrying is easy. Deciding when to retry, and making sure everyone doesn't decide the same thing, is the actual work.
A single Redis sorted set gives strict priority and FIFO inside each tier. Then I checked the arithmetic.
Splitting durable truth from fast dispatch buys a lot. It also creates a dual write I never made atomic.
A job queue is a promise that work you accepted will eventually happen. Making that promise honestly is the hard part.
Looking back at a collaborative canvas app, the decisions that held up, the ones that didn't, and what's still broken.
Ctrl+Z is trivial in a single user app. Add one collaborator and the obvious implementation starts erasing other people's work.
Rooms live in memory and get thrown away. The client is the persistence layer, which changes what reconnecting means.
Every broadcast loop contains an assumption that send() works. Backpressure is what you build when it doesn't.
Presence looks like the easiest feature in a collaborative app. It has the tightest constraints of anything in the system.
What actually goes over the wire in a collaborative canvas, and why the ready made library didn't fit.
Two people editing the same canvas is not a networking problem. It's an agreement problem.
Freehand input looks simple, but the amount of data it generates can quickly overwhelm your rendering system.
Rendering isn’t just about drawing shapes. It’s about controlling how much work happens per frame.
Performance issues don’t show up immediately, they build up quietly until your assumptions stop working.
What this blog is, what I write about, and why.