Skip to content

Commit 734c7b4

Browse files
committed
Default scheduler queue event log from env
1 parent cda095e commit 734c7b4

5 files changed

Lines changed: 76 additions & 6 deletions

File tree

configs/tensorcore-scheduler.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ TC_MESH_ARBITER_CMD=/opt/computer_mesh/tsotchke/bin/tsotchke-arbiter
99
# TC_REMOTE_ARBITER_KEY=/etc/tensorcore/arbiter_ssh_key
1010
TC_SCHEDULER_ROOT=/var/lib/tensorcore
1111
TC_SCHEDULER_JOBS_JSON=/var/lib/tensorcore/mesh_resource_jobs.json
12+
TC_SCHEDULER_EVENT_LOG_JSONL=/var/lib/tensorcore/mesh_resource_queue_events.jsonl
1213
TC_SCHEDULER_INVENTORY_JSON=/opt/tensorcore/configs/mesh_resources.json
1314
TC_SCHEDULER_STATE_JSON=/var/lib/tensorcore/mesh_resource_state.json
1415
TC_SCHEDULER_INTERVAL_SEC=30

docs/ci_and_scripts.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,9 @@ rows until the unit or equivalent launcher is installed from a git checkout.
786786
Real `submit` and `cancel` queue mutations require `--event-log-jsonl` and
787787
take an advisory lock beside the queue file before updating jobs or appending
788788
the queue event. Scheduler-VM clients should use those commands instead of
789-
editing `mesh_resource_jobs.json` directly.
789+
editing `mesh_resource_jobs.json` directly. Set
790+
`TC_SCHEDULER_EVENT_LOG_JSONL` in the scheduler environment so clients inherit
791+
the canonical event-log path.
790792

791793
Run one dry pass:
792794

docs/mesh_resource_scheduler.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ append-only audit trail. Mutating commands take an advisory lock beside the
4646
queue file, named like `.mesh_resource_jobs.json.lock`, before the
4747
load-modify-write cycle and before appending the queue event. Scheduler-VM
4848
clients should call the submit/cancel CLI instead of editing the queue file
49-
directly.
49+
directly. On the scheduler VM, set `TC_SCHEDULER_EVENT_LOG_JSONL` so submit and
50+
cancel clients inherit the canonical queue event log path.
5051

5152
Example scheduler loop:
5253

scripts/mesh_resource_scheduler.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2422,7 +2422,7 @@ def add_output_flags(p: argparse.ArgumentParser) -> None:
24222422
submit.add_argument("--job-json", required=True)
24232423
submit.add_argument("--jobs-json", required=True)
24242424
submit.add_argument("--inventory-json", required=True)
2425-
submit.add_argument("--event-log-jsonl")
2425+
submit.add_argument("--event-log-jsonl", default=os.environ.get("TC_SCHEDULER_EVENT_LOG_JSONL"))
24262426
submit.add_argument("--replace", action="store_true")
24272427
submit.add_argument("--dry-run", action="store_true")
24282428
add_output_flags(submit)
@@ -2439,7 +2439,7 @@ def add_output_flags(p: argparse.ArgumentParser) -> None:
24392439

24402440
cancel = sub.add_parser("cancel", help="Disable a queued job without releasing leases")
24412441
cancel.add_argument("--jobs-json", required=True)
2442-
cancel.add_argument("--event-log-jsonl")
2442+
cancel.add_argument("--event-log-jsonl", default=os.environ.get("TC_SCHEDULER_EVENT_LOG_JSONL"))
24432443
cancel.add_argument("--job-id", required=True)
24442444
cancel.add_argument("--reason", default="operator_cancelled")
24452445
cancel.add_argument("--dry-run", action="store_true")
@@ -2468,7 +2468,13 @@ def add_output_flags(p: argparse.ArgumentParser) -> None:
24682468
audit.add_argument("--worker-reconciliation-dir", action="append", default=[])
24692469
add_output_flags(audit)
24702470

2471-
return parser.parse_args(argv)
2471+
args = parser.parse_args(argv)
2472+
if (
2473+
args.control_command == "status"
2474+
and args.gpu_reconciliation_max_age_sec <= 0
2475+
):
2476+
parser.error("--gpu-reconciliation-max-age-sec must be > 0")
2477+
return args
24722478

24732479
parser = argparse.ArgumentParser(description=__doc__)
24742480
parser.add_argument("--arbiter-cmd", default=DEFAULT_ARBITER_CMD)

scripts/mesh_resource_scheduler_selftest.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
import importlib.util
99
import io
1010
import json
11+
import os
1112
import pathlib
1213
import subprocess
1314
import tempfile
14-
from contextlib import redirect_stdout
15+
from contextlib import redirect_stderr, redirect_stdout
1516
from types import ModuleType
1617
from typing import Any
1718

@@ -1792,6 +1793,40 @@ def test_submit_and_cancel_require_event_log_for_queue_mutation() -> None:
17921793
raise AssertionError("cancel without event log was accepted")
17931794

17941795

1796+
def test_submit_and_cancel_parse_event_log_default_from_env() -> None:
1797+
scheduler = load_scheduler()
1798+
previous = os.environ.get("TC_SCHEDULER_EVENT_LOG_JSONL")
1799+
os.environ["TC_SCHEDULER_EVENT_LOG_JSONL"] = "/var/lib/tensorcore/events.jsonl"
1800+
try:
1801+
submit_args = scheduler.parse_args(
1802+
[
1803+
"submit",
1804+
"--job-json",
1805+
"job.json",
1806+
"--jobs-json",
1807+
"queue.json",
1808+
"--inventory-json",
1809+
"inventory.json",
1810+
]
1811+
)
1812+
cancel_args = scheduler.parse_args(
1813+
[
1814+
"cancel",
1815+
"--jobs-json",
1816+
"queue.json",
1817+
"--job-id",
1818+
"job-1",
1819+
]
1820+
)
1821+
finally:
1822+
if previous is None:
1823+
os.environ.pop("TC_SCHEDULER_EVENT_LOG_JSONL", None)
1824+
else:
1825+
os.environ["TC_SCHEDULER_EVENT_LOG_JSONL"] = previous
1826+
assert submit_args.event_log_jsonl == "/var/lib/tensorcore/events.jsonl"
1827+
assert cancel_args.event_log_jsonl == "/var/lib/tensorcore/events.jsonl"
1828+
1829+
17951830
def test_cancel_accepts_expanded_pool_job_id() -> None:
17961831
scheduler = load_scheduler()
17971832
with tempfile.TemporaryDirectory() as tmp:
@@ -1941,6 +1976,29 @@ def test_status_reports_gpu_reconciliation_audit_gate() -> None:
19411976
assert gate["cuda_job_count"] == 1
19421977

19431978

1979+
def test_status_rejects_nonpositive_gpu_reconciliation_max_age() -> None:
1980+
scheduler = load_scheduler()
1981+
stderr = io.StringIO()
1982+
with redirect_stderr(stderr):
1983+
try:
1984+
scheduler.parse_args(
1985+
[
1986+
"status",
1987+
"--inventory-json",
1988+
"inventory.json",
1989+
"--gpu-reconciliation-audit-json",
1990+
"audit.json",
1991+
"--gpu-reconciliation-max-age-sec",
1992+
"0",
1993+
]
1994+
)
1995+
except SystemExit as exc:
1996+
assert exc.code == 2
1997+
else:
1998+
raise AssertionError("nonpositive status GPU reconciliation max age was accepted")
1999+
assert "--gpu-reconciliation-max-age-sec must be > 0" in stderr.getvalue()
2000+
2001+
19442002
def test_scheduler_dry_run_includes_launch_plan() -> None:
19452003
candidate = job("qllm-phase1", priority=50)
19462004
candidate["cwd"] = "scripts"
@@ -2287,10 +2345,12 @@ def main() -> int:
22872345
test_submit_dry_run_expands_tensorcore_job_v1()
22882346
test_submit_writes_queue_and_cancel_pauses_job()
22892347
test_submit_and_cancel_require_event_log_for_queue_mutation()
2348+
test_submit_and_cancel_parse_event_log_default_from_env()
22902349
test_cancel_accepts_expanded_pool_job_id()
22912350
test_submit_rejects_malformed_tensorcore_job_v1_fields()
22922351
test_status_offline_reports_inventory_jobs_and_drain_updates_copy()
22932352
test_status_reports_gpu_reconciliation_audit_gate()
2353+
test_status_rejects_nonpositive_gpu_reconciliation_max_age()
22942354
test_scheduler_dry_run_includes_launch_plan()
22952355
test_scheduler_blocks_cuda_when_gpu_reconciliation_audit_missing()
22962356
test_scheduler_keeps_non_cuda_placement_when_gpu_reconciliation_audit_missing()

0 commit comments

Comments
 (0)