fix: preserve live tasks during daemon recovery
This commit is contained in:
1 parent
079d7b1e28
commit
0355af7833
11 files changed
+327
-17
No files matched your search
@@ -24,6 +24,7 @@ import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
from types import FrameType
|
||||
|
||||
@@ -421,6 +422,14 @@ queue_goals = state.queue_goals
|
||||
class ArtemisUvicornServer(uvicorn.Server):
|
||||
"""Notify application streams before Uvicorn waits for them to close."""
|
||||
|
||||
async def startup(self, sockets: list[socket.socket] | None = None) -> None:
|
||||
# Reload workers start in a fresh interpreter; restore the actual bind
|
||||
# address there before lifespan publishes runtime metadata.
|
||||
state.host = self.config.host
|
||||
state.port = self.config.port
|
||||
app.state.uvicorn_server = self
|
||||
await super().startup(sockets=sockets)
|
||||
|
||||
@staticmethod
|
||||
def _task_is_active() -> bool:
|
||||
"""Check task state without doing process I/O from a signal handler."""
|
||||
@@ -470,31 +479,40 @@ class ArtemisUvicornServer(uvicorn.Server):
|
||||
|
||||
def run_ui_server(host: str, port: int, reload: bool = False) -> None:
|
||||
"""Run the UI server with bounded, signal-aware graceful shutdown."""
|
||||
# Reserve a listening socket before publishing metadata or running lifespan.
|
||||
# Uvicorn normally runs application startup *before* binding its port; a
|
||||
# duplicate daemon would otherwise recover jobs owned by the real server.
|
||||
family = socket.AF_INET6 if ":" in host else socket.AF_INET
|
||||
try:
|
||||
listener = socket.create_server((host, port), family=family)
|
||||
except OSError as exc:
|
||||
print(f"无法启动服务:{host}:{port} 端口不可用({exc})。现有服务和任务保持不变。")
|
||||
raise SystemExit(1) from exc
|
||||
listener.set_inheritable(True)
|
||||
port = listener.getsockname()[1]
|
||||
state.host = host
|
||||
state.port = port
|
||||
write_server_info(port=port, host=host, lifecycle_token=LIFECYCLE_TOKEN)
|
||||
|
||||
try:
|
||||
if reload:
|
||||
uvicorn.run(
|
||||
"apps.admin_console.server:app",
|
||||
host=host,
|
||||
port=port,
|
||||
reload=True,
|
||||
timeout_graceful_shutdown=5,
|
||||
)
|
||||
return
|
||||
|
||||
config = uvicorn.Config(
|
||||
app,
|
||||
"apps.admin_console.server:app" if reload else app,
|
||||
host=host,
|
||||
port=port,
|
||||
reload=reload,
|
||||
timeout_graceful_shutdown=5,
|
||||
)
|
||||
if reload:
|
||||
from uvicorn.supervisors import ChangeReload
|
||||
|
||||
server = ArtemisUvicornServer(config)
|
||||
ChangeReload(config, target=server.run, sockets=[listener]).run()
|
||||
return
|
||||
|
||||
server = ArtemisUvicornServer(config)
|
||||
app.state.uvicorn_server = server
|
||||
server.run()
|
||||
server.run(sockets=[listener])
|
||||
finally:
|
||||
listener.close()
|
||||
app.state.uvicorn_server = None
|
||||
clear_server_info(port=port, lifecycle_token=LIFECYCLE_TOKEN)
|
||||
|
||||
|
||||
Reference in new issue
Block a user