fix(tasks): isolate worker execution scopes

Persist a required Server-owned execution scope and enforce it for listing, claiming, lease recovery and SCADA verification. Backfill existing tasks explicitly through migration 034 so local development workers no longer claim production tasks from the shared database.
This commit is contained in:
2026-11-08 01:54:48 +08:00
parent 2a7b96f813
commit 33df25f22b
14 changed files with 156 additions and 16 deletions
+9 -4
View File
@@ -264,6 +264,7 @@ class OperationalTaskRepository:
async def list_runs(
self, *, status: str | None, limit: int, cursor_created_at=None, cursor_run_id=None,
task_type: str | None = None, task_created_from: datetime | None = None,
execution_scope: str = "production",
) -> list[dict]:
result = await self.connection.execute(
"""SELECT run_id,task_id,generation,execution_request_id,status,revision,
@@ -282,18 +283,20 @@ class OperationalTaskRepository:
AND t.task_id=operational_task_runs.task_id
AND (%s::text IS NULL OR t.task_type=%s::text)
AND (%s::timestamptz IS NULL OR t.created_at>=%s::timestamptz)
AND t.document->>'execution_scope'=%s
)
ORDER BY created_at,run_id LIMIT %s""",
(
self.project_id, status, status, cursor_created_at,
cursor_created_at, cursor_run_id,
task_type, task_type, task_created_from, task_created_from, limit,
task_type, task_type, task_created_from, task_created_from, execution_scope, limit,
),
)
return [dict(row) for row in await result.fetchall()]
async def claim_expired_runs(
self, limit: int, *, task_type: str | None = None, task_created_from: datetime | None = None,
execution_scope: str = "production",
) -> list[dict]:
cursor = await self.connection.execute(
"""SELECT run_id,task_id,generation,execution_request_id,status,revision,
@@ -310,9 +313,10 @@ class OperationalTaskRepository:
AND t.task_id=operational_task_runs.task_id
AND (%s::text IS NULL OR t.task_type=%s::text)
AND (%s::timestamptz IS NULL OR t.created_at>=%s::timestamptz)
AND t.document->>'execution_scope'=%s
)
ORDER BY lease_expires_at,run_id FOR UPDATE SKIP LOCKED LIMIT %s""",
(self.project_id, task_type, task_type, task_created_from, task_created_from, limit),
(self.project_id, task_type, task_type, task_created_from, task_created_from, execution_scope, limit),
)
return [dict(row) for row in await cursor.fetchall()]
@@ -711,15 +715,16 @@ class OperationalTaskRepository:
row = await cursor.fetchone()
return dict(row) if row else None
async def recovery_candidates(self, limit: int, created_from=None) -> list[dict]:
async def recovery_candidates(self, limit: int, created_from=None, execution_scope: str = "production") -> list[dict]:
cursor = await self.connection.execute(
"""SELECT document FROM workbench.operational_tasks
WHERE project_id=%s AND task_type='event_investigation'
AND status NOT IN ('cancelled','failed')
AND document#>>'{handling_context,phase}'='waiting_scada'
AND document->>'execution_scope'=%s
AND (%s::timestamptz IS NULL OR created_at>=%s)
ORDER BY COALESCE((document#>>'{handling_context,last_checked_at}')::timestamptz,created_at),task_id LIMIT %s""",
(self.project_id, created_from, created_from, limit),
(self.project_id, execution_scope, created_from, created_from, limit),
)
return [row["document"] for row in await cursor.fetchall()]