Commit dfadadd
fix(otel): use asyncio.Lock per loop in LiveOTelExporter to avoid event-loop deadlock
The rollout stage runs all OTelTracedSession instances in a single
event loop (`asyncio.run` -> `rollout.execute`). Holding a sync
`threading.Lock` across the inner `await invoke_callable(...)` in
`OTelTracedSession.run_turn` blocked the entire loop: when
`rollout.concurrency > 1`, the second coroutine could not acquire the
lock because the first was awaiting an HTTP call on the same loop, and
the first could never resume because the loop thread was parked on the
sync lock.
Reproducer (pre-fix): `rollout.concurrency: 4` with the LangGraph
travel-planner callable target deadlocked indefinitely after the first
batch of concurrent rollouts. Heartbeat froze and no transcripts were
produced.
Fix:
- `LiveOTelExporter._lock` becomes an `asyncio.Lock` created lazily
per running event loop via `LiveOTelExporter.get_lock()`. This
serializes the clear-invoke-export cycle within one loop while
yielding the loop to other coroutines while waiting.
- `OTelTracedSession.run_turn` switches from `lock.acquire()` /
`finally lock.release()` to `async with lock_ctx`, using
`contextlib.nullcontext()` when `live_otel` is False.
- A class-level `asyncio.Lock()` would bind to the first loop on use
and raise on subsequent `asyncio.run` calls; the per-loop cache
keeps repeated runs and tests safe.
Verification:
- `uv run pytest -q` -> 505 passed, 14 skipped.
- LangGraph travel-planner config patched to `rollout.concurrency: 4`
with 20 seeds end-to-end: PASS in 375s, 20 transcripts, 20 scores.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent b7f063e commit dfadadd
2 files changed
Lines changed: 31 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | | - | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
451 | 451 | | |
452 | 452 | | |
453 | 453 | | |
454 | | - | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
455 | 475 | | |
456 | 476 | | |
457 | 477 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | 22 | | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
144 | | - | |
145 | | - | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
146 | 150 | | |
147 | | - | |
148 | | - | |
149 | 151 | | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
| 152 | + | |
| 153 | + | |
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
| |||
188 | 188 | | |
189 | 189 | | |
190 | 190 | | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | 191 | | |
195 | 192 | | |
196 | 193 | | |
| |||
0 commit comments