Commit 3ce26f0
fix(dagster-aws): guarantee ECS pipes interruption subprocess is reaped (#25004)
## Summary & Motivation
On master build
[153444](https://buildkite.com/dagster/internal/builds/153444), the
`dagster-aws-3-12` step hung: every `pipes_tests` test from
`test_ecs_pipes_task_failed_to_start` onward took *exactly* 240.0s — the
global `pytest-timeout` fallback (`timeout = "240"`, `method: signal`)
killing each hung test and limping to the next. Every pipes test
*before* that point ran in seconds, which localizes the problem to
mid-module state corruption rather than a slow agent or network issue.
The corruption is introduced by
`test_ecs_pipes_interruption_forwarding`, which #24924 rewrote. Its
happy path was a single `p.terminate()` + `p.join(timeout=30)` + `assert
not p.is_alive()`, with `p.kill()` escalation present *only* in the
"task never launched within 60s" error branch. The subprocess under test
runs `materialize`, which installs Dagster's
SIGTERM→graceful-interruption handler — the whole point of the test.
When that graceful shutdown outlives the 30s join (or any of the
assertions fails), the happy path returns without ever calling
`p.kill()`, leaving the child **alive**.
That child is `fork()`ed from the test process and holds the inherited
moto server socket on `localhost:5193` (the shared, function-scoped
`moto_server` fixture port). A leaked orphan wedges that port, so every
subsequent moto-dependent pipes test blocks until the 240s timeout
fires. The build's 32.7s runtime for
`test_ecs_pipes_interruption_forwarding` (≈ startup + the full 30s join
cap) is consistent with the join expiring and the child surviving
SIGTERM.
## Changes
- **Always reap the child.** Wrap the test body in `try/finally` and
unconditionally `p.kill()` + `p.join()` the subprocess on every exit
path (assertion failure, join timeout, or the "didn't launch" branch),
so it can never be leaked.
- **Don't inherit the moto socket.** Start the subprocess from a `spawn`
multiprocessing context (`multiprocessing.get_context("spawn")`, used
for the `Manager`, `Event`, and `Process`). A spawned child gets a fresh
interpreter and does not inherit this process's file descriptors —
including the moto listening socket — and reaches moto over the network
as a client instead. Even a leaked child can then no longer wedge port
5193. Linux CI defaults to `fork`; macOS already defaults to `spawn`.
- **Gate on a real completion signal.** Add a
`materialization_done_event` that the subprocess sets in its `finally`
immediately after writing `return_dict`. The parent waits on this event
after `p.terminate()` instead of asserting `not p.is_alive()` within a
fixed window. Under spawn, instance/multiprocessing teardown after the
interruption can outrun a process-death check even when the
materialization data the test cares about is already ready — that timing
mismatch was the residual flake #24924 was originally chasing.
Together these turn what was a module-wide hang into a robust,
deterministic test.
## Test Plan
- `ruff format --check` / `ruff check` pass on the changed file.
- `test_ecs_pipes_interruption_forwarding` in isolation: passes.
- Full `test_ecs.py` (4 tests including the previously-wedged
`test_ecs_pipes_task_failed_to_start` and
`test_ecs_pipes_waiter_config`): **5/5 consecutive local runs pass
cleanly at ~67s each**, no 240s timeouts, no flakes.
- Watch the `dagster-aws` steps on this PR's Buildkite run.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Internal-RevId: 8e83e4f0d0a8915c241262bcd52beac7c777376c1 parent 45c0d68 commit 3ce26f0
1 file changed
Lines changed: 39 additions & 13 deletions
Lines changed: 39 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
176 | 181 | | |
177 | 182 | | |
178 | 183 | | |
179 | | - | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
180 | 192 | | |
181 | | - | |
| 193 | + | |
| 194 | + | |
182 | 195 | | |
183 | | - | |
| 196 | + | |
184 | 197 | | |
185 | 198 | | |
186 | 199 | | |
187 | 200 | | |
188 | 201 | | |
| 202 | + | |
189 | 203 | | |
190 | 204 | | |
191 | 205 | | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
192 | 209 | | |
193 | | - | |
194 | 210 | | |
195 | | - | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
196 | 229 | | |
197 | 230 | | |
198 | 231 | | |
199 | | - | |
200 | | - | |
201 | | - | |
202 | | - | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | 232 | | |
207 | 233 | | |
208 | 234 | | |
| |||
0 commit comments