From b7c586cebb79edb0f432d4502f4b814564a41059 Mon Sep 17 00:00:00 2001 From: kronecker <2026692687@qq.com> Date: Wed, 21 Oct 2026 10:00:59 +0800 Subject: [PATCH] fix(delivery): bound parallel startup checks --- backend/tests/test_delivery_io.py | 13 +++++++++++++ deploy/release/README.md | 8 +++++--- scripts/release/lifecycle.py | 8 +++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/tests/test_delivery_io.py b/backend/tests/test_delivery_io.py index 1ee5931..577b2f6 100644 --- a/backend/tests/test_delivery_io.py +++ b/backend/tests/test_delivery_io.py @@ -626,6 +626,19 @@ def ordinary_lifecycle_model(monkeypatch): return maintenance, states, trace, clock +def test_ordinary_start_groups_fit_parent_cpu_budget(): + # This checks scheduling policy; actual startup timing remains VM-only. + node = Path(__file__).resolve().parents[2] / "deploy/release/kfonlinejudge.slice" + quotas = [line for line in node.read_text().splitlines() if line.startswith("CPUQuota=")] + assert len(quotas) == 1 + node_cpu = int(quotas[0].partition("=")[2].removesuffix("%")) + profiles = lifecycle.services.PROFILES + dispatched = [name for group in lifecycle.START_GROUPS for name in group if name in profiles] + assert sorted(dispatched) == sorted(profiles) + for group in lifecycle.START_GROUPS: + assert sum(profiles[name][3] for name in group if name in profiles) <= node_cpu + + def test_start_group_dispatches_independent_units_before_confirming_each(ordinary_lifecycle_model): maintenance, _, trace, _ = ordinary_lifecycle_model names = lifecycle.START_GROUPS[0] diff --git a/deploy/release/README.md b/deploy/release/README.md index 860907f..86b1cf8 100644 --- a/deploy/release/README.md +++ b/deploy/release/README.md @@ -126,9 +126,11 @@ volumes, then follow this order: The tools use offline materials and do not contact a package index. -Startup dispatches PostgreSQL, RabbitMQ and the Root factory together, then the -five ordinary services, then HTTPS after API startup. Each stage waits for every -member's actual manager state and resource/identity checks before continuing. +Startup dispatches PostgreSQL, RabbitMQ and the Root factory together. Ordinary +services follow in batches of at most two, limiting concurrent full-bundle +startup checks within the two-CPU node budget; HTTPS follows those batches. +Each stage waits for every member's actual manager state and resource/identity +checks before continuing. Every service retains its complete startup verification and its original unit and observation deadlines, measured from that service's dispatch. Admission stays closed until both normal readiness checks confirm the complete node. Drain and diff --git a/scripts/release/lifecycle.py b/scripts/release/lifecycle.py index 7350db6..78d89b3 100644 --- a/scripts/release/lifecycle.py +++ b/scripts/release/lifecycle.py @@ -49,11 +49,13 @@ START_ORDER = ( "worker", "proxy", ) -# Root has no DB/MQ dependency. Ordinary units require the two dependencies; -# the HTTPS unit additionally orders itself after API startup. +# Root has no DB/MQ dependency. Pair ordinary full-bundle startup checks under +# the two-CPU node cap; every unit keeps its original startup deadline. START_GROUPS = ( (*DEPENDENCIES, "sandbox-factory"), - ("coordinator", "api", "outbox", "problem-import", "worker"), + ("coordinator", "api"), + ("outbox", "problem-import"), + ("worker",), ("proxy",), ) DRAIN_SECONDS = 360