Avoid restarting OpenCode on transient health misses

This commit is contained in:
Nick Sweeting 2026-09-02 23:49:58 -07:00
parent 3327edba24
commit 7f1767a2db
No known key found for this signature in database
2 changed files with 32 additions and 0 deletions

View File

@ -37,6 +37,7 @@ _PROCESS: subprocess.Popen | None = None
_PROCESS_LOCK = threading.Lock()
_SESSION_LOCK = threading.Lock()
_LOGGER = logging.getLogger(__name__)
_PROCESS_HEALTH_GRACE = 5
_PROXY_PREFIX = "/admin/agent/opencode"
_PROXY_PREFIX_REGEX = _PROXY_PREFIX.replace("/", r"\/")
_PROXY_PREFIX_NO_SLASH_REGEX = _PROXY_PREFIX.lstrip("/").replace("/", r"\/")
@ -401,6 +402,11 @@ def _ensure_opencode(settings: dict) -> tuple[bool, str]:
if _health(settings):
return True, ""
if _PROCESS is not None and _PROCESS.poll() is None:
deadline = time.monotonic() + min(_PROCESS_HEALTH_GRACE, settings["timeout"])
while time.monotonic() < deadline and _PROCESS.poll() is None:
if _health(settings):
return True, ""
time.sleep(0.25)
_stop_owned_process(_PROCESS)
try:

View File

@ -1,7 +1,9 @@
import asyncio
import os
import signal
import socket
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from types import SimpleNamespace
@ -301,6 +303,30 @@ def test_opencode_restarts_an_unhealthy_owned_process(live_opencode):
assert views._health(settings)
def test_opencode_preserves_a_transiently_unhealthy_owned_process(live_opencode):
from archivebox.opencode import views
process = views._PROCESS
assert process is not None
views._signal_owned_process(process, signal.SIGSTOP)
def resume_process():
time.sleep(2.5)
views._signal_owned_process(process, signal.SIGCONT)
try:
with ThreadPoolExecutor(max_workers=1) as executor:
resumed = executor.submit(resume_process)
ok, error = views._ensure_opencode(live_opencode.settings)
resumed.result()
finally:
views._signal_owned_process(process, signal.SIGCONT)
assert ok, error
assert views._PROCESS is process
assert process.poll() is None
def test_opencode_proxy_sse_response_is_unbuffered(admin_client, live_opencode):
response = admin_client.get(
"/admin/agent/opencode/global/event",