mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-23 01:54:52 +02:00
[eric] router: adopt a healthy 9Router instead of evicting the one other backends are using
This commit is contained in:
@@ -497,7 +497,19 @@ async def p_ensure_running_impl():
|
||||
p_is_packaged = os.environ.get("OPENSWARM_PACKAGED") == "1"
|
||||
|
||||
if is_running():
|
||||
# In dev mode, kill stale standalone servers (from previous builds) so we can start `next dev` which always uses latest source code
|
||||
# ADOPT a healthy router instead of evicting it. is_running() has just confirmed HTTP 200 on
|
||||
# /v1/models, so whatever is on the port is serving; the only thing killing it establishes is
|
||||
# who owns the process. That eviction is a thrash engine on a shared machine: every dev
|
||||
# backend that boots kills the router the others are using, their watchdogs revive it, and
|
||||
# the next boot kills it again. Measured with 8 backends on one box: a coverage sweep scored
|
||||
# 1/9 with zero code change, wall times went 30s -> 202s, and one run logged 0 kills of its
|
||||
# own against 4 revives. The original reason to kill ("stale build") does not apply to a
|
||||
# component we pin by npm version rather than build from source. OPENSWARM_ROUTER_TAKEOVER=1
|
||||
# restores the old behaviour for the rare case someone must force a fresh spawn.
|
||||
if not p_is_packaged and os.environ.get("OPENSWARM_ROUTER_TAKEOVER") != "1":
|
||||
logger.info("9Router already healthy on port %d; adopting it (set "
|
||||
"OPENSWARM_ROUTER_TAKEOVER=1 to force a fresh spawn)", NINE_ROUTER_PORT)
|
||||
return
|
||||
if not p_is_packaged:
|
||||
# But never kill the instance WE already started: a second ensure call (another sub-app's lifespan races settings') would pkill our fresh next-server, leaving a dead window the boot key-sync fails into, so the cp-openai node never registers and gpt-5.* own-key dies.
|
||||
if p_process is not None and p_process.poll() is None:
|
||||
|
||||
@@ -204,3 +204,34 @@ def test_detection_revival_gated_on_evidence():
|
||||
assert ensures, "sub-only users must get a revival attempt"
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_a_healthy_router_is_adopted_not_evicted():
|
||||
"""The dev-machine thrash engine, removed.
|
||||
|
||||
Every dev backend used to kill any 9Router it had not personally spawned, on a "stale build"
|
||||
rationale that does not apply to a component we pin by npm version instead of building from
|
||||
source. On a shared box that is a loop: each boot evicts the router the others are using, their
|
||||
watchdogs revive it, the next boot evicts it again. Measured with 8 backends on one machine, a
|
||||
coverage sweep scored 1/9 with zero code change, wall times went 30s -> 202s, and one backend
|
||||
logged 0 kills of its own against 4 revives.
|
||||
|
||||
is_running() has already confirmed HTTP 200 on /v1/models before this point, so the router is
|
||||
serving by definition; killing it settles ownership and nothing else.
|
||||
"""
|
||||
killed = []
|
||||
with patch.object(proc, "is_running", return_value=True), \
|
||||
patch.object(proc, "p_process", None), \
|
||||
patch("subprocess.run", side_effect=lambda *a, **k: killed.append(a)), \
|
||||
patch.dict("os.environ", {"OPENSWARM_PACKAGED": "0"}, clear=False):
|
||||
asyncio.run(proc.p_ensure_running_impl())
|
||||
assert killed == [], "a healthy router another process owns must be adopted, never pkill'd"
|
||||
|
||||
|
||||
def test_takeover_escape_hatch_still_exists():
|
||||
"""Adoption must not become a trap: someone genuinely needing a fresh spawn keeps a way to get
|
||||
one, and it is explicit rather than the silent default."""
|
||||
import inspect
|
||||
src = inspect.getsource(proc.p_ensure_running_impl)
|
||||
assert "OPENSWARM_ROUTER_TAKEOVER" in src
|
||||
assert 'os.environ.get("OPENSWARM_ROUTER_TAKEOVER") != "1"' in src
|
||||
|
||||
Reference in New Issue
Block a user