fix(cloud): keep the directory projection fresh during startup

Boot continues for minutes after the first directory snapshot is committed:
platform ports reconnect, knowledge bases and pipelines reload, and a cold
shared Runtime restore may still be running. The refresh loop only started at
the end of that stage, so any boot slower than the projection staleness window
failed closed while restoring knowledge bases, and Core then exited during
startup - every retry hit the same window.

Start the single refresh task as soon as the first snapshot is committed and
keep the later start as a guarded fallback so the loop is never created twice.

Observed on the production Cloud instance: a boot that took ~120s to reach
knowledge base restore raised 'Cloud directory projection is stale' and the
process exited, while the same image had booted earlier with a faster start.

Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
RockChinQ
2026-10-21 17:20:36 +00:00
co-authored by multica-agent
parent af013eb077
commit 5c0236ac3e
+15 -4
View File
@@ -173,6 +173,17 @@ class BuildAppStage(stage.BootingStage):
)
await directory_projection_service.initialize()
ap.directory_projection_service = directory_projection_service
# Startup continues for a while after the first snapshot: platform
# ports reconnect, knowledge bases and pipelines reload, and a cold
# shared Runtime restore may still be running. Workspace admission
# fails closed once the projection is older than its staleness
# window, so keep refreshing it from the moment the first snapshot
# is committed instead of after the rest of this stage.
ap.directory_projection_task = ap.task_mgr.create_task(
directory_projection_service.run(),
name='cloud-directory-projection',
scopes=[core_entities.LifecycleControlScope.APPLICATION],
)
workspace_policy = deployment.workspace_policy
workspace_service_inst = workspace_service_module.WorkspaceService(
@@ -313,10 +324,10 @@ class BuildAppStage(stage.BootingStage):
async def runtime_disconnect_callback(connector: plugin_connector.PluginRuntimeConnector) -> None:
connector.schedule_reconnect()
if ap.directory_projection_service is not None:
# Keep the projection fresh while shared Runtime cold restore runs.
# BuildApp initializes the connector before Application.run() starts
# its long-lived tasks, so start the single refresh task here.
if ap.directory_projection_service is not None and getattr(ap, 'directory_projection_task', None) is None:
# Fallback for a deployment that reached this point without the
# startup task (see the projection start right after the first
# snapshot is committed).
ap.directory_projection_task = ap.task_mgr.create_task(
ap.directory_projection_service.run(),
name='cloud-directory-projection',