fix(kvb-org-queue): an idle queue no longer waits on a head the host load alone keeps out
evaluate() counted a non-hotfix entry that passed every queue check but was held back by the live host load as granted for every entry after it: its slot, the exclusive-kind drain and its CPU/RAM share. With RAM held outside the queue the head never started and nothing behind it did either (2026-11-12 03:50 UTC: 27 entries, no job active, host idle). While no queue job is active, such an entry now reserves nothing and holds nothing back; with jobs active it keeps its reservation, and a load-held hotfix still holds back every entry after it. Replayed on the live queue, the fix grants test-nostack-1035 at once. 🎯 Fix: 93 % 💥 Break: 4 % 💸 Cost: 100 % ⏱️ Time: 100 % 🛡️ Security: 100 % 🏆 Score: 89 🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ab13b6e65
commit
256fe2c327
2 files changed
+36
-9
No files matched your search
@@ -406,7 +406,10 @@ def evaluate(entries: list[dict], free_gb: float, floor: float, stage: str, memo
|
||||
holds it, so a lower entry never takes a higher entry's slot; one
|
||||
blocked by disk or budget reserves its need against the entries after it.
|
||||
A hotfix entry that passes every check but the host load holds back
|
||||
every entry after it, so lighter jobs cannot keep the load up. An
|
||||
every entry after it, so lighter jobs cannot keep the load up. While no
|
||||
queue job is active, any other entry the host load alone keeps out
|
||||
reserves nothing and holds nothing back: that load lies outside the
|
||||
queue, so holding the queue frees none of it. An
|
||||
exclusive entry that only running jobs of other kinds keep out holds
|
||||
back every entry after it but light ones, so none overtakes it. A waiting
|
||||
entry whose holder has not polled for CLAIM_SECONDS reserves nothing and
|
||||
@@ -435,13 +438,17 @@ def evaluate(entries: list[dict], free_gb: float, floor: float, stage: str, memo
|
||||
if reason is not None and kind in exclusive:
|
||||
drain = entry["id"]
|
||||
if reason is None:
|
||||
reason, used, available = resources(need, share, used, available, floor, budget, taken)
|
||||
if reason is None and entry["hotfix"] and entry["id"] in loaded:
|
||||
reason, held = loaded[entry["id"]], entry["id"]
|
||||
elif reason is None:
|
||||
taken += 1
|
||||
counts[kind] += 1
|
||||
busy.add(workdir)
|
||||
reason, after, left = resources(need, share, used, available, floor, budget, taken)
|
||||
if reason is None and entry["id"] in loaded and (entry["hotfix"] or not active):
|
||||
reason = loaded[entry["id"]]
|
||||
if entry["hotfix"]:
|
||||
held = entry["id"]
|
||||
else:
|
||||
used, available = after, left
|
||||
if reason is None:
|
||||
taken += 1
|
||||
counts[kind] += 1
|
||||
busy.add(workdir)
|
||||
verdicts[entry["id"]] = reason
|
||||
for entry in waiting:
|
||||
if silent(entry, now):
|
||||
|
||||
+21
-1
@@ -296,9 +296,29 @@ class TestEvaluate(unittest.TestCase):
|
||||
self.decide([hotfix, light], loaded={"j1": load}),
|
||||
{"j1": load, "j2": "waits behind hotfix j1, which the host load holds back"},
|
||||
)
|
||||
self.assertEqual(self.decide([job(1), light], loaded={"j1": load}), {"j1": None, "j2": None})
|
||||
self.assertEqual(self.decide([job(1), light], loaded={"j1": load}), {"j1": load, "j2": None})
|
||||
self.assertEqual(self.decide([hotfix, light]), {"j1": None, "j2": None})
|
||||
|
||||
def test_with_no_job_active_a_head_the_host_load_alone_keeps_out_reserves_nothing_and_with_one_active_it_does(self):
|
||||
load = "RAM 55 % used + 41 % still to come reach the limit 90 %"
|
||||
head, twin = job(1, kind="quality-high"), job(2, kind="quality-high", task="work/b")
|
||||
gate, light = job(3, task="work/b"), job(4, kind="test-nostack", task="work/c")
|
||||
shares = {"j1": {"ram": 41, "cpu": 62}, "j2": {"ram": 41, "cpu": 62}, "j3": {"ram": 20, "cpu": 40}, "j4": {"ram": 6, "cpu": 40}}
|
||||
rules = {"slot_limits": {"quality-high": 1, "test": 5, "test-nostack": 6}, "exclusive": {"quality-high", "test"}, "light": {"test-nostack"}}
|
||||
loaded = {"j1": load, "j2": load}
|
||||
self.assertEqual(
|
||||
self.decide([head, twin, gate, light], loaded=loaded, footprints=shares, **rules),
|
||||
{"j1": load, "j2": load, "j3": None, "j4": None},
|
||||
)
|
||||
running = job(5, kind="test-nostack", task="work/c", status="active")
|
||||
self.assertEqual(
|
||||
self.decide([running, head, twin, gate, light], loaded=loaded, footprints=shares | {"j5": {"ram": 6, "cpu": 11}}, **rules),
|
||||
{
|
||||
"j1": None, "j2": "all 1 quality-high slots taken", "j3": "test runs only beside test-nostack, but quality-high runs",
|
||||
"j4": "CPU 73 % in use or reserved + 40 % > budget 100 %; reserved",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class TestHistory(unittest.TestCase):
|
||||
@classmethod
|
||||
|
||||
Reference in new issue
Block a user