From 68b5f36c9b2cb229ea17c16c05db0c8819182ee4 Mon Sep 17 00:00:00 2001 From: kronecker <2026692687@qq.com> Date: Mon, 12 Oct 2026 21:50:10 +0800 Subject: [PATCH] fix(delivery): share one node budget across services and tasks --- deploy/release/README.md | 7 +++++++ deploy/release/contract.v1.json | 2 +- deploy/release/kfonlinejudge.slice | 8 ++++++++ scripts/release/configuration.py | 6 +++--- scripts/release/install.py | 2 ++ scripts/release/policy.py | 2 +- 6 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 deploy/release/kfonlinejudge.slice diff --git a/deploy/release/README.md b/deploy/release/README.md index 8586f01..b9a6fff 100644 --- a/deploy/release/README.md +++ b/deploy/release/README.md @@ -34,6 +34,13 @@ an explicitly installed version. Private operator/service configuration lives in `/var/lib/kf-judge`. The delegated task cgroup is separate from the Root factory's own supervision cgroup. Application processes do not initialize tables on start; Backend Alembic remains the only migration entry point. +All application services and the separate delegated task subtree live beneath +`kfonlinejudge.slice`: 3840 MiB memory, no swap, 384 processes/threads and two CPU +equivalents. The remaining physical RAM is reserved for the kernel/minimal OS. +The task subtree is `/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks`, a sibling +of the Root factory supervision unit under that same total node budget. Its +installation template does not prove the effective limits; actual service and +task limits are read back in the independent acceptance environment. Database login identities are separate for migration, API/outbox, Coordinator, problem import and local identity operations. Rust Worker and Root factory do not diff --git a/deploy/release/contract.v1.json b/deploy/release/contract.v1.json index c578b79..e3b1ad4 100644 --- a/deploy/release/contract.v1.json +++ b/deploy/release/contract.v1.json @@ -11,7 +11,7 @@ "data": "/var/lib/kf-onlinejudge", "judge_configuration": "/etc/kf-judge", "judge_state": "/var/lib/kf-judge", - "task_cgroup": "/sys/fs/cgroup/kf-judge-tasks" + "task_cgroup": "/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks" }, "services": { "api": "platform", diff --git a/deploy/release/kfonlinejudge.slice b/deploy/release/kfonlinejudge.slice new file mode 100644 index 0000000..7f33c93 --- /dev/null +++ b/deploy/release/kfonlinejudge.slice @@ -0,0 +1,8 @@ +[Unit] +Description=KF application and delegated execution node budget + +[Slice] +MemoryMax=3840M +MemorySwapMax=0 +TasksMax=384 +CPUQuota=200% diff --git a/scripts/release/configuration.py b/scripts/release/configuration.py index 394e111..2367cc9 100644 --- a/scripts/release/configuration.py +++ b/scripts/release/configuration.py @@ -20,7 +20,7 @@ PROFILES = { "identity": "kf_identity_operator", "migration": "kf_migrator", } -IMPORT_CGROUP = "/sys/fs/cgroup/system.slice/kf-problem-import.service" +IMPORT_CGROUP = "/sys/fs/cgroup/kfonlinejudge.slice/kf-problem-import.service" CONFIG_FILES = { "api.env", "outbox.env", @@ -170,7 +170,7 @@ def measured_hardware() -> dict: value["node_scheduler"] = {} for label, group in ( ("root", Path("/sys/fs/cgroup")), - ("system", Path("/sys/fs/cgroup/system.slice")), + ("node", Path("/sys/fs/cgroup/kfonlinejudge.slice")), ): value["node_scheduler"][label] = { name: (group / name).read_text().strip() if (group / name).exists() else None @@ -192,7 +192,7 @@ def admission_runtime(payload: Path, data: dict) -> tuple[dict, dict]: admission = AdmissionRegistry.model_validate(admission).model_dump(mode="json") state = "/var/lib/kf-judge/images/" + data["release_id"] root["image_root"] = state + "/legacy-gnu" - root["delegated_cgroup_root"] = "/sys/fs/cgroup/kf-judge-tasks" + root["delegated_cgroup_root"] = "/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks" root["identities"] = { "worker_uid": preflight.ACCOUNTS["kf-worker"], "worker_gid": preflight.ACCOUNTS["kf-worker"], diff --git a/scripts/release/install.py b/scripts/release/install.py index 7b60a21..b57c6e7 100644 --- a/scripts/release/install.py +++ b/scripts/release/install.py @@ -205,6 +205,8 @@ def files(bundle: Path, expected: str) -> dict: for name in ("artifacts", "imports"): data_directory(Path("/var/lib/kf-onlinejudge") / name, preflight.ACCOUNTS["kf-oj"]) data_directory(Path("/var/lib/kf-onlinejudge/worker"), preflight.ACCOUNTS["kf-worker"]) + node_unit = bundle / "payload/deploy/release/kfonlinejudge.slice" + exclusive_file(Path("/etc/systemd/system/kfonlinejudge.slice"), node_unit.read_bytes(), 0o644) release = RELEASES / data["release_id"] if release.exists(): preflight.protected(release, directory=True) diff --git a/scripts/release/policy.py b/scripts/release/policy.py index b79ffed..b8ad78c 100644 --- a/scripts/release/policy.py +++ b/scripts/release/policy.py @@ -162,7 +162,7 @@ def contract(path: Path) -> dict: "data": "/var/lib/kf-onlinejudge", "judge_configuration": "/etc/kf-judge", "judge_state": "/var/lib/kf-judge", - "task_cgroup": "/sys/fs/cgroup/kf-judge-tasks", + "task_cgroup": "/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks", }: raise DeliveryError("delivery layout mismatch") expected_services = {