fix(gateway): budget restart observer for full stop envelope
(cherry picked from commit e9b2123af68ce955dd66b027404687a0a375def3)
This commit is contained in:
@@ -358,6 +358,9 @@ def resolve_systemd_timeout_stop_sec(
|
||||
def resolve_restart_exit_wait_budget(
|
||||
drain_timeout: float, after_turn_timeout: float, cron_drain_timeout: float = 0.0, *, headroom: float = 15.0,
|
||||
) -> float:
|
||||
"""Seconds a CLI should wait for the gateway PID to exit after SIGUSR1: in-band restart may
|
||||
defer ``stop()`` until turns finish, then spend the longer chat or cron drain inside it."""
|
||||
return max(_seconds(drain_timeout), _seconds(cron_drain_timeout)) + _seconds(after_turn_timeout) + _seconds(headroom)
|
||||
"""Observer budget for in-band deferral, the full stop envelope, and replacement startup.
|
||||
|
||||
The stop envelope includes cron cleanup reserve, supervisor headroom and the floor;
|
||||
deferral precedes it, while observer headroom follows it. Cron zero opts out.
|
||||
"""
|
||||
return _seconds(after_turn_timeout) + resolve_systemd_timeout_stop_sec(drain_timeout, cron_drain_timeout) + _seconds(headroom)
|
||||
|
||||
@@ -4,6 +4,7 @@ from gateway.restart import (
|
||||
DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT,
|
||||
parse_restart_after_turn_timeout,
|
||||
resolve_restart_exit_wait_budget,
|
||||
resolve_systemd_timeout_stop_sec,
|
||||
)
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
@@ -20,12 +21,15 @@ def test_parse_restart_after_turn_timeout_defaults_and_clamps():
|
||||
|
||||
|
||||
def test_resolve_restart_exit_wait_budget_covers_both_phases():
|
||||
assert resolve_restart_exit_wait_budget(0, 0, headroom=15) == 15.0
|
||||
assert resolve_restart_exit_wait_budget(180, 21600, headroom=15) == 180 + 21600 + 15
|
||||
assert resolve_restart_exit_wait_budget(2, 3, 80, headroom=15) == 3 + 80 + 15
|
||||
assert resolve_restart_exit_wait_budget(80, 3, 2, headroom=15) == 3 + 80 + 15
|
||||
assert resolve_restart_exit_wait_budget(2, 3, 0, headroom=15) == 3 + 2 + 15
|
||||
assert resolve_restart_exit_wait_budget("bad", "bad", headroom="x") == 0.0
|
||||
assert resolve_restart_exit_wait_budget(0, 0, 0, headroom=15) == resolve_systemd_timeout_stop_sec(0, 0) + 15
|
||||
assert resolve_restart_exit_wait_budget(180, 21600, 0, headroom=15) == 21600 + resolve_systemd_timeout_stop_sec(180, 0) + 15
|
||||
for chat, cron in ((2, 80), (80, 2), (2, 0), (0, 0)):
|
||||
stop_envelope = resolve_systemd_timeout_stop_sec(chat, cron)
|
||||
assert resolve_restart_exit_wait_budget(chat, 3, cron, headroom=15) == 3 + stop_envelope + 15
|
||||
# A bounded observer uses the longer stop path, not the sum of independent drains.
|
||||
assert resolve_restart_exit_wait_budget(80, 3, 80, headroom=15) == 3 + resolve_systemd_timeout_stop_sec(80, 80) + 15
|
||||
assert resolve_restart_exit_wait_budget(2, 3, 0, headroom=15) < resolve_restart_exit_wait_budget(2, 3, 80, headroom=15)
|
||||
assert resolve_restart_exit_wait_budget("bad", "bad", 0, headroom="x") == 60.0
|
||||
|
||||
|
||||
def test_cli_restart_wait_covers_configured_cron_drain(tmp_path, monkeypatch):
|
||||
@@ -38,7 +42,12 @@ def test_cli_restart_wait_covers_configured_cron_drain(tmp_path, monkeypatch):
|
||||
"agent:\n restart_drain_timeout: 2\n restart_after_turn_timeout: 3\n cron_drain_timeout: 80\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert gateway_cli._get_restart_exit_wait_budget() >= 3 + 80 + 15
|
||||
assert gateway_cli._get_restart_exit_wait_budget() == 3 + resolve_systemd_timeout_stop_sec(2, 80) + 15
|
||||
(tmp_path / "config.yaml").write_text(
|
||||
"agent:\n restart_drain_timeout: 2\n restart_after_turn_timeout: 3\n cron_drain_timeout: 0\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert gateway_cli._get_restart_exit_wait_budget() == 3 + resolve_systemd_timeout_stop_sec(2, 0) + 15
|
||||
|
||||
|
||||
def test_load_restart_after_turn_timeout_preserves_zero(tmp_path, monkeypatch):
|
||||
|
||||
Reference in New Issue
Block a user