Skip to content

Commit 6aecc01

Browse files
committed
deepscholar: patched the Gemma startup hang
1 parent d4f5819 commit 6aecc01

2 files changed

Lines changed: 102 additions & 16 deletions

File tree

src/olmo_eval/common/beaker_status.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
import os
1515
import time
1616
from collections.abc import Callable
17+
from threading import Lock, Thread
1718

18-
from beaker import Beaker, BeakerWorkload
19+
from beaker import Beaker, BeakerExperiment, BeakerWorkload
1920
from beaker.exceptions import BeakerConfigurationError
2021

2122
DEFAULT_MIN_INTERVAL = 10.0
@@ -35,14 +36,23 @@ class BeakerStatusReporter:
3536
def __init__(self, min_interval: float = DEFAULT_MIN_INTERVAL) -> None:
3637
self.min_interval = min_interval
3738
self._git_suffix = _git_suffix()
38-
self._workload: BeakerWorkload | None = None
39+
workload_id = os.environ.get("BEAKER_WORKLOAD_ID")
40+
self._workload = (
41+
BeakerWorkload(experiment=BeakerExperiment(id=workload_id)) if workload_id else None
42+
)
43+
self._lock = Lock()
44+
self._update_in_flight = False
3945
self._last_update: float = float("-inf")
46+
self._client: Beaker | None = None
47+
if self._workload is None:
48+
return
49+
4050
try:
41-
self._client: Beaker | None = Beaker.from_env()
51+
self._client = Beaker.from_env()
4252
except BeakerConfigurationError:
43-
self._client = None
4453
return
45-
self._workload = self._client.workload.get(os.environ["BEAKER_WORKLOAD_ID"])
54+
except Exception as error:
55+
logger.warning("Beaker status reporting disabled during setup: %s", error)
4656

4757
def update(self, message: str, force: bool = False) -> None:
4858
"""Push a status message to the Beaker workload description.
@@ -54,12 +64,41 @@ def update(self, message: str, force: bool = False) -> None:
5464
return
5565

5666
now = time.monotonic()
57-
if not force and now - self._last_update < self.min_interval:
58-
return
67+
with self._lock:
68+
if not force and now - self._last_update < self.min_interval:
69+
return
70+
# Status is cosmetic. Never queue more work behind a slow Beaker API
71+
# request, and never let that request block model startup or evaluation.
72+
if self._update_in_flight:
73+
return
74+
self._update_in_flight = True
75+
self._last_update = now
5976

77+
client = self._client
78+
workload = self._workload
6079
full_message = f"{message} {self._git_suffix}"
61-
self._client.workload.update(self._workload, description=full_message)
62-
self._last_update = now
80+
81+
def send_update() -> None:
82+
try:
83+
client.workload.update(workload, description=full_message)
84+
except Exception as error:
85+
logger.warning("Beaker status reporting disabled after update failure: %s", error)
86+
self._client = None
87+
finally:
88+
with self._lock:
89+
self._update_in_flight = False
90+
91+
try:
92+
Thread(
93+
target=send_update,
94+
name="beaker-status-update",
95+
daemon=True,
96+
).start()
97+
except Exception as error:
98+
with self._lock:
99+
self._update_in_flight = False
100+
self._client = None
101+
logger.warning("Beaker status reporting disabled after thread failure: %s", error)
63102

64103
def progress_callback(self, label: str, units: str = "items/sec") -> Callable[..., None]:
65104
"""Return a ``(count, total, *, force=False)`` callback bound to a fresh start time.

tests/core/test_beaker_status.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
from olmo_eval.common import beaker_status
77

88

9+
class InlineThread:
10+
"""Run a thread target synchronously to keep status reporter tests deterministic."""
11+
12+
def __init__(self, *, target, name: str, daemon: bool) -> None:
13+
self.target = target
14+
self.name = name
15+
self.daemon = daemon
16+
17+
def start(self) -> None:
18+
self.target()
19+
20+
921
class BeakerStatusReporterTest(unittest.TestCase):
1022
def test_disabled_when_beaker_config_missing(self) -> None:
1123
with (
@@ -27,15 +39,15 @@ def test_throttles_updates_within_interval(self) -> None:
2739
"GIT_BRANCH": "main",
2840
}
2941
fake_client = mock.MagicMock()
30-
fake_workload = mock.MagicMock()
31-
fake_client.workload.get.return_value = fake_workload
3242
with (
3343
mock.patch.dict("os.environ", env, clear=True),
3444
mock.patch.object(beaker_status.Beaker, "from_env", return_value=fake_client),
45+
mock.patch.object(beaker_status, "Thread", InlineThread),
3546
):
3647
reporter = beaker_status.BeakerStatusReporter(min_interval=60.0)
3748

3849
self.assertIsNotNone(reporter._client)
50+
fake_client.workload.get.assert_not_called()
3951

4052
with mock.patch("time.monotonic", side_effect=[0.0, 1.0, 61.0]):
4153
reporter.update("first")
@@ -44,30 +56,36 @@ def test_throttles_updates_within_interval(self) -> None:
4456

4557
self.assertEqual(fake_client.workload.update.call_count, 2)
4658
suffix = "git_commit: abc123 git_branch: main"
47-
fake_client.workload.update.assert_any_call(fake_workload, description=f"first {suffix}")
48-
fake_client.workload.update.assert_any_call(fake_workload, description=f"third {suffix}")
59+
workload = beaker_status.BeakerWorkload(
60+
experiment=beaker_status.BeakerExperiment(id="wl_123")
61+
)
62+
fake_client.workload.update.assert_any_call(workload, description=f"first {suffix}")
63+
fake_client.workload.update.assert_any_call(workload, description=f"third {suffix}")
4964

5065
def test_git_suffix_uses_unknown_when_env_missing(self) -> None:
5166
env = {"BEAKER_WORKLOAD_ID": "wl_123"}
5267
fake_client = mock.MagicMock()
53-
fake_workload = mock.MagicMock()
54-
fake_client.workload.get.return_value = fake_workload
5568
with (
5669
mock.patch.dict("os.environ", env, clear=True),
5770
mock.patch.object(beaker_status.Beaker, "from_env", return_value=fake_client),
71+
mock.patch.object(beaker_status, "Thread", InlineThread),
5872
):
5973
reporter = beaker_status.BeakerStatusReporter(min_interval=0.0)
6074
reporter.update("hello")
6175

76+
workload = beaker_status.BeakerWorkload(
77+
experiment=beaker_status.BeakerExperiment(id="wl_123")
78+
)
6279
fake_client.workload.update.assert_called_once_with(
63-
fake_workload, description="hello git_commit: unknown git_branch: unknown"
80+
workload, description="hello git_commit: unknown git_branch: unknown"
6481
)
6582

6683
def test_force_bypasses_throttle(self) -> None:
6784
fake_client = mock.MagicMock()
6885
with (
6986
mock.patch.dict("os.environ", {"BEAKER_WORKLOAD_ID": "wl_xyz"}, clear=True),
7087
mock.patch.object(beaker_status.Beaker, "from_env", return_value=fake_client),
88+
mock.patch.object(beaker_status, "Thread", InlineThread),
7189
):
7290
reporter = beaker_status.BeakerStatusReporter(min_interval=60.0)
7391

@@ -77,6 +95,35 @@ def test_force_bypasses_throttle(self) -> None:
7795

7896
self.assertEqual(fake_client.workload.update.call_count, 2)
7997

98+
def test_update_failure_is_nonfatal_and_disables_reporting(self) -> None:
99+
fake_client = mock.MagicMock()
100+
fake_client.workload.update.side_effect = RuntimeError("API unavailable")
101+
with (
102+
mock.patch.dict("os.environ", {"BEAKER_WORKLOAD_ID": "wl_xyz"}, clear=True),
103+
mock.patch.object(beaker_status.Beaker, "from_env", return_value=fake_client),
104+
mock.patch.object(beaker_status, "Thread", InlineThread),
105+
):
106+
reporter = beaker_status.BeakerStatusReporter()
107+
reporter.update("starting")
108+
109+
self.assertIsNone(reporter._client)
110+
111+
def test_update_starts_a_daemon_thread(self) -> None:
112+
fake_client = mock.MagicMock()
113+
fake_thread = mock.MagicMock()
114+
with (
115+
mock.patch.dict("os.environ", {"BEAKER_WORKLOAD_ID": "wl_xyz"}, clear=True),
116+
mock.patch.object(beaker_status.Beaker, "from_env", return_value=fake_client),
117+
mock.patch.object(beaker_status, "Thread", return_value=fake_thread) as thread_class,
118+
):
119+
reporter = beaker_status.BeakerStatusReporter()
120+
reporter.update("starting")
121+
122+
thread_class.assert_called_once()
123+
self.assertEqual(thread_class.call_args.kwargs["name"], "beaker-status-update")
124+
self.assertTrue(thread_class.call_args.kwargs["daemon"])
125+
fake_thread.start.assert_called_once_with()
126+
80127

81128
if __name__ == "__main__":
82129
unittest.main()

0 commit comments

Comments
 (0)