generated from coulomb/repo-seed
feat(workflows): implement RunActivityWorkflow — T18
workflows.py — RunActivityWorkflow:
1. load_activity_definition(activity_id)
2. resolve_context(context_sources)
3. evaluate_templates (pure, called in-workflow)
4. log_run({run_id, ...}) — run_id = uuid5(NAMESPACE_URL, activity_id:trigger_key)
5. start_child_workflow(TaskExecutorWorkflow, ...) per task spec
ABANDON parent-close policy (fire-and-forget)
Returns {"run_id": str, "tasks_spawned": int}
activities.py — log_run updated:
- now accepts run_id in run_payload (deterministic, passed from workflow)
- uses pg INSERT ... ON CONFLICT (run_id) DO NOTHING for idempotency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
from temporalio import activity
|
||||
from temporalio.exceptions import ApplicationError
|
||||
@@ -120,7 +121,11 @@ async def resolve_context(context_sources: list[dict]) -> dict:
|
||||
async def log_run(run_payload: dict) -> str:
|
||||
"""Persist an ActivityRun record to Postgres and return its run_id.
|
||||
|
||||
Idempotent: uses INSERT … ON CONFLICT (run_id) DO NOTHING so Temporal
|
||||
activity retries do not produce duplicate rows.
|
||||
|
||||
Expected keys in run_payload:
|
||||
run_id (str UUID — computed deterministically in workflow)
|
||||
activity_id (str UUID)
|
||||
scheduled_for (ISO-8601 str or None)
|
||||
context_snapshot (dict)
|
||||
@@ -132,21 +137,28 @@ async def log_run(run_payload: dict) -> str:
|
||||
"""
|
||||
Session = _get_session_factory()
|
||||
|
||||
run_id = uuid.UUID(run_payload["run_id"])
|
||||
|
||||
scheduled_for: datetime | None = None
|
||||
if run_payload.get("scheduled_for"):
|
||||
scheduled_for = datetime.fromisoformat(run_payload["scheduled_for"])
|
||||
|
||||
row = ActivityRun(
|
||||
activity_id=uuid.UUID(run_payload["activity_id"]),
|
||||
scheduled_for=scheduled_for,
|
||||
fired_at=datetime.now(tz=timezone.utc),
|
||||
context_snapshot=run_payload["context_snapshot"],
|
||||
tasks_spawned=run_payload["tasks_spawned"],
|
||||
version_used=run_payload["version_used"],
|
||||
stmt = (
|
||||
pg_insert(ActivityRun)
|
||||
.values(
|
||||
run_id=run_id,
|
||||
activity_id=uuid.UUID(run_payload["activity_id"]),
|
||||
scheduled_for=scheduled_for,
|
||||
fired_at=datetime.now(tz=timezone.utc),
|
||||
context_snapshot=run_payload["context_snapshot"],
|
||||
tasks_spawned=run_payload["tasks_spawned"],
|
||||
version_used=run_payload["version_used"],
|
||||
)
|
||||
.on_conflict_do_nothing(index_elements=["run_id"])
|
||||
)
|
||||
|
||||
async with Session() as session:
|
||||
async with session.begin():
|
||||
session.add(row)
|
||||
await session.execute(stmt)
|
||||
|
||||
return str(row.run_id)
|
||||
return str(run_id)
|
||||
|
||||
Reference in New Issue
Block a user