mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 10:11:30 +08:00
feat(companion): Promote companions from STARTING to RUNNING after startsecs
Add promote_running to CompanionManager: scans STARTING companions and moves any that have stayed alive at least their startsecs window to RUNNING, logging the pid and returning the promoted ones. Companions that die inside the window are left to reaping. Add tests for promotion after the window, too-early no-op, and non-STARTING. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bd8a91f656
commit
84d69c46fd
@ -675,7 +675,7 @@ No per-companion logic in Arbiter.
|
||||
- [x] Apply `cwd` and `env` before target.
|
||||
- [x] Redirect `stdout` and `stderr`.
|
||||
- [x] Reap exited companion processes.
|
||||
- [ ] Implement `STARTING -> RUNNING` using `startsecs`.
|
||||
- [x] Implement `STARTING -> RUNNING` using `startsecs`.
|
||||
- [ ] Implement `BACKOFF` with fixed `companion_restart_delay`.
|
||||
- [ ] Implement `start_process`.
|
||||
- [ ] Implement `stop_process`.
|
||||
|
||||
@ -84,6 +84,24 @@ class CompanionManager:
|
||||
reaped.append(proc)
|
||||
return reaped
|
||||
|
||||
def promote_running(self, now: float = None) -> list:
|
||||
"""Move companions that survived ``startsecs`` from STARTING to RUNNING.
|
||||
|
||||
A freshly spawned companion starts in STARTING. If it stays alive for
|
||||
its ``startsecs`` window it is considered up and becomes RUNNING; if it
|
||||
dies first, reaping handles it instead. Returns the promoted ones.
|
||||
"""
|
||||
now = now or time.time()
|
||||
promoted = []
|
||||
for proc in self.processes.values():
|
||||
if proc.state != State.STARTING or proc.started_at is None:
|
||||
continue
|
||||
if now - proc.started_at >= proc.config.startsecs:
|
||||
proc.state = State.RUNNING
|
||||
self.log.info("companion %s running (pid %s)", proc.name, proc.pid)
|
||||
promoted.append(proc)
|
||||
return promoted
|
||||
|
||||
def _process_by_pid(self, pid: int):
|
||||
for proc in self.processes.values():
|
||||
if proc.pid == pid:
|
||||
|
||||
@ -125,6 +125,36 @@ def test_reap_no_children():
|
||||
assert mgr.reap_processes() == []
|
||||
|
||||
|
||||
def test_promote_running_after_startsecs():
|
||||
mgr = make_manager("rq")
|
||||
proc = mgr.processes["rq"]
|
||||
proc.config.startsecs = 1
|
||||
proc.state = State.STARTING
|
||||
proc.started_at = 100.0
|
||||
promoted = mgr.promote_running(now=101.5)
|
||||
assert promoted == [proc]
|
||||
assert proc.state == State.RUNNING
|
||||
|
||||
|
||||
def test_promote_running_too_early():
|
||||
mgr = make_manager("rq")
|
||||
proc = mgr.processes["rq"]
|
||||
proc.config.startsecs = 5
|
||||
proc.state = State.STARTING
|
||||
proc.started_at = 100.0
|
||||
assert mgr.promote_running(now=102.0) == []
|
||||
assert proc.state == State.STARTING
|
||||
|
||||
|
||||
def test_promote_running_ignores_non_starting():
|
||||
mgr = make_manager("rq")
|
||||
proc = mgr.processes["rq"]
|
||||
proc.state = State.BACKOFF
|
||||
proc.started_at = 100.0
|
||||
assert mgr.promote_running(now=999.0) == []
|
||||
assert proc.state == State.BACKOFF
|
||||
|
||||
|
||||
def test_spawn_parent_records_pid_and_starting():
|
||||
mgr = make_manager("rq")
|
||||
proc = mgr.processes["rq"]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user