When Good Logging Takes Down Your Whole System
The Forensic Engineer
SubscribeSign in
When Good Logging Takes Down Your Whole System<br>Sometimes doing it "right" isn't enough.
Manpreet Kaur Sasan<br>Jul 08, 2026
Share
Ever been so responsible it backfired?<br>Bluesky’s engineering team knows the feeling. They did the “right” thing - logged every error, exactly like you’re taught to - and it took their whole system down for a weekend.<br>Thanks for reading The Forensic Engineer ! Subscribe for free to receive new posts and support my work.
Subscribe
A blocking syscall, a thread explosion, and a sharp bit of thinking under pressure. Let’s get into it.<br>The Byte
Observability, monitoring, logging - they’re meant to catch failures, not cause them. Or so I thought.<br>Turns out they can take your whole system down too.<br>In April 2026, Bluesky quietly shipped a new internal service. Nothing alarming about it - low traffic, nothing flagged in review.<br>But one endpoint was missing a limit that every other endpoint in the system had. For a week, that went unnoticed.<br>Then, on Saturday, April 4, alerts started trickling in. The team’s first instinct was a network issue - reasonable, but wrong. The system limped along through the weekend.<br>By Monday, April 6, it gave way completely. Half their users were affected for eight hours.<br>The twist: the system’s own error logging was the actual cause of the collapse. Every failed operation was being logged, exactly as good practice says it should be.<br>But in Go, writing a log entry is a “blocking” action - the program stops everything else until that one line is fully written. Normally, a tiny pause nobody notices.<br>But thousands of these pauses, every second, add up faster than a system can clear them. The runtime kept compensating, and the compensation made things worse. A system built to catch failures ended up feeding one.<br>The Breakdown
Here’s why it happened, step by step:<br>The Spark - One endpoint had no limit on concurrent requests. A burst of 20,000 came in at once, and the system tried to open 20,000 connections simultaneously.
The Lock-Up - Closed connections don’t free up instantly; they stay reserved briefly before reuse. With that many opening at once, the system ran out of available connections. New attempts failed outright.
The Block - Every failure got logged. Writing a log line uses a blocking write syscall (true in Go, common in many languages) - it pauses the program until done. Thousands of failures meant thousands of tiny pauses, stacking up fast.
The Overload - With so many tasks stuck waiting, the runtime spun up new, heavier real threads so other work wouldn’t be stuck behind them. This holds true in almost any modern language - lightweight tasks run cheaply on top of a few real threads, until one blocks, and the system compensates by creating more. Do that for thousands of tasks in seconds, and a lean handful of threads becomes an overcrowded mess.
The Freeze - Those extra threads spiked memory pressure. Cleanup had to pause everything just to catch up.
The Loop - The moment things unfroze, every stalled task woke up at once, rushed the same exhausted connections, and triggered the same failure again. A fire the system kept re-lighting.
The Workaround - To buy time, engineers stopped routing every connection through one address. They spread connections across hundreds instead. Slots multiplied, and the cycle broke - for now.
Each step was the correct behavior of one layer, triggering the correct behavior of the next - until the sum of “correct” became a collapse.<br>The team didn’t do the wrong thing by logging failures. What was missing was rigor: a cap on how much could run at once, and separation between the logging path and the system it watched.
That same gap - assuming something is safe at any volume - shows up everywhere: input validation, retries, rate limits, anything on the unhappy path nobody stress-tests.<br>It matters even more in AI-augmented systems, where we’re logging every model call and treating that pipeline as free.<br>Bluesky’s outage wasn’t really about logging. It was about an assumption nobody checked. Somewhere in your codebase, something’s making the same one. Might be worth finding it before production does.<br>Sources: Bluesky’s postmortem on pckt.blog, with additional analysis from Lorin Hochstein on Surfing Complexity.<br>Thanks for reading The Forensic Engineer ! Subscribe for free to receive new posts and support my work.
Subscribe
Share
Discussion about this post<br>CommentsRestacks
Ready for more?
Subscribe
© 2026 Manpreet Kaur Sasan · Privacy ∙ Terms ∙ Collection notice<br>Start your SubstackGet the app<br>Substack is the home for great culture
This site requires JavaScript to run correctly. Please turn on JavaScript or unblock scripts