]> git.hungrycats.org Git - bees/commit
borrower: make failed batch reacquires ordered, side-effect-free, quiet
authorZygo Blaxell <bees@furryterror.org>
Thu, 3 Sep 2026 01:35:11 +0000 (21:35 -0400)
committerZygo Blaxell <bees@furryterror.org>
Sat, 5 Sep 2026 04:04:15 +0000 (00:04 -0400)
commit46f7cb22d99eec7e7397526fb4c1ef93655ca8d5
treeb19e19e5032cf099a5f75e81ed5a627b01afee59
parentea87be81b8d0ab069153db11644353cf34462eb7
borrower: make failed batch reacquires ordered, side-effect-free, quiet

At endgame the only remaining work is a handful of highly-shared ref
clusters, and every planner task restart wakes the whole post-exec
waiter herd of the task that finished.  Each woken task re-ran the
batch reacquire, taking memoized locks one by one until one failed,
then releasing them all — knocking over the locks the other woken
tasks were about to succeed on.  Two tasks whose two-lock memos
overlap in opposite orders can ping-pong this way for minutes,
re-waking their full waiter sets on every iteration.  A full-corpus
run logged 4.36M "memo reacquire contention" lines (64% of the log)
in a 13-minute tail during which the dedupe rate collapsed and CPU
burned on retry churn and log formatting.

Three changes to the batch reacquire in the Borrower constructor:

- Sort the memoized set into canonical (pointer) order, so tasks with
  overlapping memos contend on their lowest common Exclusion first,
  forming one queue on one lock instead of a swap cycle.

- Peek the whole set with Exclusion::locked_by_another before
  acquiring anything.  A wakeup that cannot proceed now touches only
  the failing Exclusion (to establish the restart rendezvous) and
  disturbs no other locks.

- Rate-limit the contention log line to 10/s.  The
  borrower_dead_reacquire counter is unconditional as before; the
  pass-2 race path logs under its own limiter as "memo reacquire
  race" so the two causes stay distinguishable.

Assisted-by: Claude-Code:claude-fable-5
src/bees-borrower.cc