Skip to content

Commit 857e9e4

Browse files
authored
Merge pull request #9 from spacedock-dev/fix/dab-mongo-stability-and-common-scaffold
fix(dab): mongo crash recovery + vendor common_scaffold for per-query validators
2 parents 37d2f91 + ec1720c commit 857e9e4

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/compose.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ def generate_compose(
133133
if mongo_dbs:
134134
services["dab-mongo"] = {
135135
"image": MONGO_IMAGE,
136+
# mongo:8 intermittently SIGSEGVs (exit 139) on startup, and its
137+
# WiredTiger cache auto-sizes to ~half of host RAM (≈7GB on a 15GB
138+
# box), starving the agent. Cap the cache and let docker restart a
139+
# crashed mongod: the data dir is already populated so the restart
140+
# comes straight back up with data and the healthcheck recovers.
141+
# Without this, a single crash bricks the trial — main's healthcheck
142+
# fails for its whole retry window and the trial is cancelled.
143+
"command": ["--wiredTigerCacheSizeGB", "1"],
144+
"restart": "on-failure",
136145
"healthcheck": {
137146
"test": ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand({ping:1})"],
138147
"interval": "5s",

packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/prepare.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ def _materialize_task_dir(
262262
dataset=dataset_meta.name,
263263
query_id=query_id,
264264
)
265+
# Some upstream validators do `from common_scaffold.validate.levenshtein
266+
# import levenshtein`. verify.py exec_module's validate.py inside the
267+
# dab-agent container, which has no common_scaffold installed, so the
268+
# import raises, verify.py exits non-zero under `set -eu`, no reward.json
269+
# is written, and harbor reports RewardFileNotFoundError (verifier appears
270+
# to never run). Vendor common_scaffold next to verify.py — /tests is
271+
# sys.path[0] — so the import resolves. The batch path already does this.
272+
_install_common_scaffold(tests_dir=tests_dir, data_root=dataset_dir.parent)
265273

266274
write_stratum_file(
267275
tests_dir=tests_dir,

packages/razorback-plugin-dab/tests/unit/test_compose_mongo.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ def test_mongo_service_emitted(tmp_path: Path):
3232
assert "mongosh" in services["dab-mongo"]["healthcheck"]["test"]
3333

3434

35+
def test_mongo_has_restart_and_cache_cap(tmp_path: Path):
36+
# mongo:8 intermittently SIGSEGVs on startup; on a constrained host its
37+
# default WiredTiger cache (~half of RAM) also starves the agent. The
38+
# service must cap the cache and restart on failure so a single crash
39+
# does not brick the trial via main's healthcheck retry window.
40+
text = generate_compose(db_config=_AGNEWS_LIKE, dataset_name="agnews", data_root=tmp_path)
41+
mongo = yaml.safe_load(text)["services"]["dab-mongo"]
42+
assert mongo["restart"] == "on-failure"
43+
assert mongo["command"] == ["--wiredTigerCacheSizeGB", "1"]
44+
45+
3546
def test_main_depends_on_mongo(tmp_path: Path):
3647
text = generate_compose(db_config=_AGNEWS_LIKE, dataset_name="agnews", data_root=tmp_path)
3748
compose = yaml.safe_load(text)

packages/razorback-plugin-dab/tests/unit/test_prepare_per_query.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# ABOUTME: AC-3 — task-dir shape for one (dataset, query) emission.
22
# ABOUTME: Asserts forbidden files (ground_truth.csv, validate.py) never reach workdir.
33

4+
import importlib.util
45
from pathlib import Path
56

67
import pytest
@@ -74,6 +75,65 @@ def test_task_dir_layout(tmp_path: Path):
7475
assert (task_dir / "steps" / "main" / "workdir" / "README.md").exists()
7576

7677

78+
def _build_common_scaffold_data_root(root: Path) -> Path:
79+
"""Single-query data root whose validator imports common_scaffold."""
80+
data_root = root / "data"
81+
scaffold_validate = data_root / "common_scaffold" / "validate"
82+
scaffold_validate.mkdir(parents=True)
83+
(scaffold_validate / "levenshtein.py").write_text(
84+
"def levenshtein(left, right):\n"
85+
" return 0 if left == right else 1\n"
86+
)
87+
(scaffold_validate / "__pycache__").mkdir()
88+
(scaffold_validate / "__pycache__" / "ignored.pyc").write_bytes(b"cached")
89+
90+
qdir = data_root / "query_PATENTS"
91+
qdir.mkdir(parents=True)
92+
(qdir / "db_description.txt").write_text("Synthetic affected DAB dataset.")
93+
q1 = qdir / "query1"
94+
q1.mkdir()
95+
(q1 / "query.json").write_text('{"question": "Return abc."}')
96+
(q1 / "validate.py").write_text(
97+
"from common_scaffold.validate.levenshtein import levenshtein\n\n"
98+
"def validate(answer):\n"
99+
" distance = levenshtein(answer, 'abc')\n"
100+
" return (distance == 0, f'distance={distance}')\n"
101+
)
102+
return data_root
103+
104+
105+
def test_per_query_materializes_common_scaffold_for_upstream_validators(
106+
tmp_path: Path, monkeypatch
107+
) -> None:
108+
# Regression: 9 of 54 DAB validators do `from common_scaffold.validate
109+
# .levenshtein import levenshtein`. verify.py exec_module's validate.py in
110+
# the dab-agent container (no common_scaffold installed), so without the
111+
# vendored package the import raises, no reward.json is written, and harbor
112+
# reports RewardFileNotFoundError (the verifier appears never to run). The
113+
# batch path already vendored it; the per-query path must too.
114+
data_root = _build_common_scaffold_data_root(tmp_path)
115+
manifest = prepare_dataset_tasks(
116+
data_root=data_root,
117+
dataset="PATENTS",
118+
tasks_root=tmp_path / "tasks",
119+
)
120+
tests_dir = manifest[0]["task_dir"] / "tests"
121+
122+
assert (tests_dir / "common_scaffold" / "validate" / "levenshtein.py").exists()
123+
assert not (tests_dir / "common_scaffold" / "validate" / "__pycache__").exists()
124+
125+
# The copied validator must import and run with /tests on sys.path
126+
# (verify.py runs as `python /tests/verify.py`, so /tests is sys.path[0]).
127+
monkeypatch.syspath_prepend(str(tests_dir))
128+
spec = importlib.util.spec_from_file_location(
129+
"_generated_validate_per_query", tests_dir / "validate.py"
130+
)
131+
assert spec is not None and spec.loader is not None
132+
module = importlib.util.module_from_spec(spec)
133+
spec.loader.exec_module(module)
134+
assert module.validate("abc") == (True, "distance=0")
135+
136+
77137
def test_compose_bind_mount_sources_resolve_to_real_files(tmp_path: Path):
78138
"""PKG-13 T1 / AC-4 + PKG-14 AC-1: bind-mount sources in the generated
79139
compose must point at existing files. Under PKG-14 bind mode (the default)

0 commit comments

Comments
 (0)