Implement durable module recovery operations

This commit is contained in:
2026-08-03 03:07:42 +02:00
parent 21c1fa49b6
commit bca6a7c8aa
6 changed files with 744 additions and 11 deletions
@@ -12,6 +12,7 @@ from govoplan_core.db.bootstrap import bootstrap_dev_data, create_all_tables
from govoplan_core.db.session import get_database
from govoplan_core.server.config import GovoplanServerConfig
from govoplan_core.server.runtime_agent import RuntimeNodeAgent
from govoplan_core.core.runtime_coordination import RuntimeIdentity, runtime_identity
from govoplan_core.settings import Settings, settings
@@ -49,11 +50,19 @@ async def lifespan(app: FastAPI):
if registry is not None
else ()
)
configured_identity = getattr(
app.state,
"govoplan_runtime_identity",
None,
)
runtime_agent = RuntimeNodeAgent(
settings=settings,
software_version=app.version,
module_ids=module_ids,
metadata={"process": "api"},
identity=configured_identity
if isinstance(configured_identity, RuntimeIdentity)
else None,
)
await runtime_agent.start()
app.state.govoplan_runtime_agent = runtime_agent
@@ -73,6 +82,16 @@ def register_health_details(
active_settings = (
config_settings if isinstance(config_settings, Settings) else settings
)
module_ids = tuple(manifest.id for manifest in registry.manifests())
if not isinstance(
getattr(app.state, "govoplan_runtime_identity", None),
RuntimeIdentity,
):
app.state.govoplan_runtime_identity = runtime_identity(
active_settings,
software_version=app.version,
module_ids=module_ids,
)
@app.get("/health/details")
def health_details(
+18 -2
View File
@@ -17,6 +17,21 @@ from govoplan_core.db.session import get_database
logger = logging.getLogger("govoplan.runtime")
def application_runtime_identity(app: object) -> RuntimeIdentity:
"""Return the registered identity used to fence request-owned effects."""
state = getattr(app, "state", None)
agent = getattr(state, "govoplan_runtime_agent", None)
identity = getattr(agent, "identity", None) or getattr(
state,
"govoplan_runtime_identity",
None,
)
if not isinstance(identity, RuntimeIdentity):
raise RuntimeError("The application runtime identity is not available")
return identity
class RuntimeNodeAgent:
"""Register one process in the shared runtime directory and heartbeat it."""
@@ -30,9 +45,10 @@ class RuntimeNodeAgent:
node_id: str | None = None,
queues: tuple[str, ...] | None = None,
metadata: dict[str, Any] | None = None,
identity: RuntimeIdentity | None = None,
) -> None:
self.settings = settings
self.identity: RuntimeIdentity = runtime_identity(
self.identity: RuntimeIdentity = identity or runtime_identity(
settings,
software_version=software_version,
module_ids=module_ids,
@@ -126,4 +142,4 @@ class RuntimeNodeAgent:
return True
__all__ = ["RuntimeNodeAgent"]
__all__ = ["RuntimeNodeAgent", "application_runtime_identity"]