Every database query cost us four round-trips for six months September 15, 2026
On the evening of 14 September a guest tried to book Mara Hilltop, the lodge I started and run in the Maasai Mara, and every time he chose dates and pressed Continue the page died. That night we fixed the crash and found a separate database configuration mistake we had been living with since March in KaribuKit, the hotel software we build and run the lodge on. Correcting one setting cut the availability check behind the booking page from 8.06 seconds to 1.88. We had measured the cost of that mistake four days after launch, done the arithmetic correctly, and blamed geography.
The crash and the slowness turned out to be unrelated. The crash is the short story; the slowness is the one worth writing down.
The crash
7:13 PM. He apologised for the screenshot being in Spanish.
Chrome's "page could not be opened" screen is what iOS shows when the browser's content process has been killed twice in a row on the same page, and on a phone that almost always means memory. Our booking page was loading the original camera files for every room photo, and a preloader pulled the full gallery of every room type the moment availability arrived. I downloaded the same 36 files the page fetches and measured them: 47 MB over the wire, 3.9 GB once decoded. The largest, one of the luxury tent interiors, is 8171 by 5957, which is 186 MB on its own. A laptop has the RAM to hide that, which is why I couldn't reproduce it on mine. An iPhone does not, and the tab badge in his original screenshot says 81.
The uncomfortable part is that the fix already existed. Claude Code, Anthropic's command-line coding agent, had written it on 26 August after the same crash on a different page: serve resized copies through the Next.js image optimizer and delete the preloader. It sat in an open pull request for nineteen days because my notes said "PR to dev" and I read that as done. It merged that night and the page went from 36 raw fetches to zero. He had a message saying so at three minutes past midnight. I still don't know whether he booked.
The number that didn't fit
While checking the fixed page I timed the availability request behind it, the call that asks the API which rooms are free for the guest's dates. From the API server itself, on localhost with no network in the way: 9.9 seconds that night, and 8.1 seconds at the median over the previous three days of logs, at 03:00 as much as at 17:00.
The health check was the better clue. /health runs one SELECT 1 against Postgres and pings Redis, and from localhost it took 240 milliseconds the night I measured it, 285 at the median over the three days before. A trivial query should cost about one network round-trip to the database. Our API is a Hetzner box in Helsinki talking to Supabase in Dublin, and that round-trip, measured with a TCP connect from the box, is 47 milliseconds. 240 is four of those with change left over. At that point I didn't know what the four were.
... continue reading