Skip to content

Respawn overdue segmented national children and fail before the parent timeout - #795

Draft
anth-volk wants to merge 2 commits into
mainfrom
fix/segmented-national-child-deadline
Draft

anth-volk wants to merge 2 commits into
mainfrom
fix/segmented-national-child-deadline

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #794

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Summary

Segmented US national runs had no deadline per regional child. A child that never finished held the parent until Modal's 3,600 s timeout killed it, which also left the children running. Both 2023 national reports on 9 October 2026 failed this way.

Changes to src/modal/segmented_national.py:

  • Deadline per child: each child records when it was dispatched and its attempt number. A child with no result 900 s after dispatch is cancelled, and its region group is respawned once. The respawn's deadline is limited to the parent's remaining budget. Successful children in the week took a median of 124 s and at most 302 s, apart from one 2,672 s outlier.
  • Cancelling replaced attempts: an overdue attempt that a respawn replaces stays tracked. It is never polled again, so a late result from it is ignored. Its first cancel can fail, and Modal keeps re-running a stuck input until a cancel succeeds, so every later failure path cancels it again, and so does the end of collection when the run succeeds.
  • Clear failure: the run fails with a message naming the region group when:
    • the respawn is also overdue;
    • the respawn fails;
    • the parent's remaining time can't fit a retry (at least 300 s is needed).
  • Fail in code before the Modal timeout: when the parent's budget (the 3,600 s run_simulation timeout minus a 300 s completion margin) runs out, the run cancels every child and then fails.
  • Telemetry:
    • segmented_national_child_respawned (warning) and segmented_child_retry_count are recorded only after the respawn spawns. A respawn that fails to spawn is not counted.
    • segmented_national_child_timed_out (error) is emitted when a child misses its deadline and is not respawned. Attributes: region_group, child_attempt, waited_seconds, deadline_seconds and remaining_budget_seconds.
    • segmented_national_budget_exhausted (error) is emitted when the parent budget runs out. Attributes: region_groups and child_attempts (in the same order), waited_seconds since the runner started, and parent_budget_seconds.
    • An outright child failure is still recorded through log_and_redact_exception, with region_group and child_attempt.
  • Configuration: the deadline, budget and clock can be set on the constructor. No Modal function settings or environment variables change.

Not included

  • Why the 2023 children's containers kept being terminated: this needs Modal container logs. With this change those reports fail after about 30 minutes, with a named region group, instead of being killed at 60 minutes.
  • Trace linkage: respawned children start new traces without a parent span.

Testing

  • New tests in tests/test_segmented_national.py (16 cases):
    • An overdue child is cancelled and respawned once.
    • A respawn that is also overdue fails the run.
    • A respawn failure is reported as a respawn failure.
    • A respawn that fails to spawn cancels every child and is not recorded as a respawn.
    • A late result from the replaced original is ignored, and the original is not polled again.
    • When the first cancel of the replaced original fails, a later failure cancels it again (2 cases: the respawn fails, or is also overdue).
    • When the first cancel of the replaced original fails and the run succeeds, it is cancelled again after collection.
    • There is no respawn when the parent budget can't fit a retry.
    • A respawn's deadline is limited to the remaining parent budget.
    • An exhausted parent budget cancels the children before the Modal timeout.
    • A respawn is recorded in telemetry.
    • A child timeout is recorded in telemetry (2 cases: the respawn is also overdue, or there is no budget for a respawn).
    • An exhausted parent budget is recorded in telemetry.
    • The parent budget matches the run_simulation timeout in src/modal/app.py.
  • Updated test: one existing test in tests/test_canonical_spm.py builds a runner without its constructor and calls _collect. It now uses the new internal handle shape and sets the empty list of replaced attempts.
  • Suites, on Python 3.13 with uv run --frozen: projects/policyengine-simulation-executor 749 passed, 23 skipped, 3 deselected.
  • Formatting and lint:
    • ruff format --check and git diff --check pass.
    • ruff check reports no new findings. The two in segmented_national.py are the existing pattern that ignores errors from best-effort child cancellation, now in its new location.

🤖 Generated with Claude Code

anth-volk and others added 2 commits October 13, 2026 23:16
…t timeout

A segmented US national run waited on each regional child with no deadline
of its own. When Modal kept re-running a child whose containers were
terminated, the parent waited until its 3,600 s Modal timeout killed it,
which also left the children running with nobody to cancel them.

On 9 October 2026 both 2023 national reports (7fa9408d and 765eefcf) hit
this: their 20 children were re-executed 180 and 131 times, none finished,
and the parents ended at 3,599.8 s (timeout) and 3,167 s ("Server has lost
track of input" from a child poll).

The runner now:
- cancels a child with no result 900 s after dispatch and respawns its
  region group once, recording a segmented_national_child_respawned event
  and segmented_child_retry_count;
- fails with a message naming the group when the respawn is also overdue,
  fails, or the parent budget is too short to fit a retry;
- fails in-band and cancels every child when the parent budget (the
  3,600 s function timeout minus a 300 s completion margin) runs out.

A unit test pins the budget constant to the run_simulation timeout in
src/modal/app.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ures

A respawned child's original attempt was cancelled once, with errors
ignored, and then dropped from tracking, so later failure paths never
retried the cancel. Superseded attempts now stay tracked: they are never
polled again, but every failure path and the end of collection cancel
them again.

The final deadline failure and parent budget exhaustion now emit
structured events (segmented_national_child_timed_out and
segmented_national_budget_exhausted). The respawn event and
segmented_child_retry_count are recorded only after the respawn spawns.

Adds tests for retrying a failed cancel, ignoring a late result from the
superseded original, a failed respawn spawn, clamping a respawn's
deadline to the remaining budget, and the new events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Segmented US national runs wait on stuck regional children until the parent times out

1 participant