every restart left the fleet it had already paid for (v0.3.103)
Adoption existed, but only ON DEMAND: it runs inside `acquire()`, when a request needs a pod. Nothing reconciled the registry at boot. So after every restart this deployment's surviving pods sat unowned until either a request happened to want that pool or the reaper's 20-minute grace expired — and an hourly unattended upgrade could pay for a fleet twice, once for the pods it abandoned and again for the ones it rented beside them. Observed 2026-10-16. A forced upgrade at 00:01 orphaned three pods. At 00:04 the surya pool rented a NEW pod while a healthy surya pod from before the restart was still running and still in the registry. The three were reaped at 00:21 having billed $1.11 between them: "not tracked by any pool and its owner stopped reporting". THREE FIXES. 1. A POD THAT WAS BOOTING COULD NEVER BE ADOPTED. register_pod writes the url only once the pod is ready — `# url once ready` — and find_shared_pod skipped a url-less entry in SILENCE. These images take 6-14 minutes to boot, so a restart has a good chance of landing inside one, and the pod it lands on is unadoptable for the rest of its paid life. The pod id is enough to ask RunPod where it went, so an entry with no address now recovers one. 2. TWO OF THE THREE REFUSALS SAID NOTHING. No url and a failed health probe both `continue`d without a word, so "rented beside a pod it could have adopted" and "had nothing to adopt" produced identical logs. That is why tonight's case cannot be pinned down: the evidence was never written. Every refusal now names the pod and the reason. 3. NOTHING RECLAIMED AT STARTUP. `reclaim_registered_pods()` walks the registry when the scaler starts and takes this deployment's pods back into their pools — before `reap_orphans`, deliberately: a pod we are already paying for should rejoin its pool, not be terminated and rented again a minute later. It takes back EVERY pod of a pool, not one; the first version of this reclaimed one of sixteen and left fifteen billing, which is the same bug with extra steps. Bounded by how many the registry holds for that key, since adoption removes nothing by itself and the loop must end whatever it answers. A `pool:` key is left to the on-demand path: its shape comes from whichever model loads first, and guessing would be worse. Also: `scale_up_max_parallel_boots` is now editable in the admin model form (and survives the save). It decides how many GPUs start BILLING together when a pool grows — a floor of 16 fills in waves of 4 at the default — which is exactly the kind of thing an operator should not have to edit models.json for. 17 tests. Suite: 3183 passed, the same 13 known-stale failures. Co-Authored-By:Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KnfkxBpcZ1v6ypRFFyhrVM
Showing
Please
register
or
sign in
to comment