I took down my own search 🔥 A developer behind the film and TV recommendation site Because I Liked traced a production search outage to a forgotten batch job that was recomputing nearest-neighbour lists for all 14,000 titles, hammering Postgres with the same vector queries users were waiting on and tripping the statement timeout. After adding per-request deadlines and a resumable --before flag, they deleted 7,971 parked 'lead character' vectors from the shared item_embeddings table, cutting filtered searches from over 5 seconds to 0.9–3.8 seconds cold and about 0.3 seconds warm, and added a hard format filter so searches for 'animated' no longer return live-action films. Because I liked https://becauseiliked.com/?utm source=devto&utm campaign=part3 asks what you loved and why , then finds films, shows, books and games that match the why. Part 1 https://dev.to/pushpendra2011/i-built-a-recommender-that-asks-why-you-loved-something-heres-what-broke-1ma4 was how it works; part 2 https://dev.to/pushpendra2011/i-measured-my-ai-search-the-numbers-were-humbling-4lkc was measuring it. This one is shorter on cleverness and longer on mistakes. In one week I broke search for real users, found the cause was an experiment I'd forgotten was still running, taught it that "animated" doesn't always mean cartoon, and added 1,810 films for 38 cents. Every title page shows "More like this": its 12 nearest neighbours, precomputed into a table so a page view is one indexed read instead of a vector search. After adding a batch of films, I started a job to recompute those lists for all 14,000 titles. Six titles at a time, each one a vector search. Then I tried the site: Search is having trouble right now. Please try again. The vector search behind every query was hitting Postgres's statement timeout; with retries, a search took 26 seconds to fail. The database is small, and my batch job was hammering it with the same kind of query users were waiting on. Stopping the job brought search back. Two things went wrong at once, and the second one hid behind the first. The job could stall forever. Before the outage, the job had already gone quiet: the process was alive, but nothing had been written for six minutes. One request to the database had never come back, and nothing told it to give up. Every call now gets a deadline: // a hung request otherwise stalls the whole run return await Promise.race fn , timeout 30 000 It couldn't resume. A full recompute rewrote everything, so killing it meant starting over. It now takes a --before