fix(cron): prevent script pipe timeouts - #60490
noamsiegel wants to merge 1 commit into
Conversation
Capture script output through temp files and clean POSIX process groups so orphaned children cannot keep stdout/stderr pipes open after the parent script exits.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the inherited-pipe failure in cron scripts. The premise remains valid on current main: cron/scheduler.py:2101-2109 still uses subprocess.run(..., capture_output=True) for script execution.
Problems
- Current main added
_run_job_script_with_claim_heartbeat()incron/scheduler.py:2139-2201viacd5371876; no-agent and pre-run jobs now route through it atcron/scheduler.py:2611and2758. The conflicting patch needs to preserve this one-shot claim protection. - The new
job.get("script_timeout_seconds")calls do not match a current public cron-job field. The documented timeout contract is global config/environment only (website/docs/user-guide/features/cron.md:404-414), and the heartbeat wrapper currently calls_run_job_script()without this argument (cron/scheduler.py:2162,2193,2196). - The diff adds no regression test.
tests/cron/test_cron_script.py:184-196covers a direct timeout, not the parent-exits/child-keeps-fd-open case.
Suggested changes
- Reapply the process-group/temp-file fix inside current
_run_job_script()while retaining the heartbeat wrapper. - Add a POSIX regression for an inherited stdout/stderr descriptor and child cleanup.
- Either remove the per-job timeout change or wire and document it consistently.
Automated hermes-sweeper review.
| return False, f"Script path is not a file: {path}" | ||
|
|
||
| script_timeout = _get_script_timeout() | ||
| script_timeout = int(timeout_seconds) if timeout_seconds else _get_script_timeout() |
There was a problem hiding this comment.
Current cron jobs do not expose script_timeout_seconds as a stored or schema-supported per-job field, and current main routes real script runs through _run_job_script_with_claim_heartbeat(), whose calls do not pass this argument. Please either remove this dead override or wire it through the public job API, documentation, and heartbeat wrapper.
|
Closing per #85125 Phase 4a: cron scripts now run in their own process group and are tree-killed on timeout (PR #93795, merged). The post-kill pipe drain is bounded by _drain_script_pipes (5s communicate → kill → 5s wait → abandon). @noamsiegel's temp-file capture approach was an alternative path; the functional requirements (bounded drain, group kill, child cleanup) are met by the tree-kill + bounded-drain implementation. |
Fixes #60485.
Summary
script_timeout_seconds, redaction, and error formattingValidation
python3 -m py_compile cron/scheduler.py_run_job_script("fd-leak-probe.sh", 5)returns immediately withparent-doneand leaves nosleep 30process