feat(postgres): GetStepProgress, EnqueueTx, UpdateRunSpec - #41
Conversation
Three additive methods on *postgres.Store that unblock consumers migrating off hand-rolled Postgres adapters onto the experimental store. None of them touch the worker package, the runquery interface, or existing method signatures. - GetStepProgress(ctx, executionID) reads the step_progress rows back out. Without it, consumers whose runs come through runquery.Store.GetRun have no API to render per-step status in their UI. Ordered by started_at NULLS LAST, step_name, branch_id. Lives on *Store, not runquery.Store, to keep the backend-neutral read interface minimal. - EnqueueTx(ctx, tx, run) takes a caller-owned pgx.Tx so the run insert can be atomic with writes to adjacent tables (credit ledger debits, idempotency keys, audit rows) that live outside the library's schema. Enqueue is now a one-line wrapper around EnqueueTx with its own transaction. pgx.Tx is the right type here — experimental/store/postgres already exposes *pgxpool.Pool publicly, so this does not leak a new dependency. - UpdateRunSpec(ctx, claim, spec) replaces the spec on a running claim with the same (claim_id, worker_id, attempt, status=running) fencing as Heartbeat and Complete, returning ErrLeaseLost on fence failure. For long-running activities that mutate the spec incrementally (e.g. a KB-apply loop persisting progress between steps) and need it durable without waiting for the next checkpoint. Tests exercise ordering and detail round-trip for GetStepProgress, rollback+commit semantics and cross-table atomicity for EnqueueTx, and all three fence failure modes for UpdateRunSpec (wrong worker, wrong attempt, completed run). Nil-argument guards on EnqueueTx and UpdateRunSpec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThe changes introduce transaction-based enqueueing for workflow runs with a new delegated method ( Changes
Sequence DiagramssequenceDiagram
participant Client
participant Store
participant PgxPool as PgxPool<br/>(Begin)
participant PgxTx as PgxTx<br/>(Insert)
participant Database
Client->>Store: Enqueue(ctx, run)
Store->>PgxPool: Begin(ctx)
PgxPool->>Database: Start Transaction
Database-->>PgxTx: Transaction Handle
Store->>Store: EnqueueTx(ctx, tx, run)
Store->>PgxTx: Exec(insert into workflow_runs)
PgxTx->>Database: INSERT workflow_runs
Database-->>PgxTx: Success
PgxTx-->>Store: RowsAffected
Store->>PgxTx: Commit(ctx)
PgxTx->>Database: Commit Transaction
Database-->>Client: Success
sequenceDiagram
participant Client
participant Store
participant Database
Client->>Store: UpdateRunSpec(ctx, claim, spec)
Note over Store: Validate claim fields<br/>(WorkerID, Attempt)
Store->>Database: UPDATE workflow_runs SET spec = ?<br/>WHERE id = ? AND claimed_by = ?<br/>AND attempt = ? AND status = 'running'
Database-->>Store: RowsAffected
alt RowsAffected > 0
Store-->>Client: Success (spec updated)
else RowsAffected == 0
Store-->>Client: worker.ErrLeaseLost<br/>(lease fence violated)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Summary
Three additive methods on
*postgres.Storethat unblock consumers migrating off hand-rolled Postgres adapters onto the experimental store. None touch the worker package, the runquery interface, or existing method signatures.GetStepProgress(ctx, executionID) ([]workflow.StepProgress, error)— reads step_progress rows back out. Ordered bystarted_at NULLS LAST, thenstep_name, thenbranch_id. Lives on*Store, notrunquery.Store, to keep the backend-neutral read interface minimal. Consumers whose runs come back fromrunquery.Store.GetRunpreviously had no API to render per-step status in their UI.EnqueueTx(ctx, tx pgx.Tx, run) error— transaction-aware enqueue so the run insert can be atomic with writes to adjacent tables (credit ledger debits, idempotency keys, audit rows) that live outside the library's schema but in the same database.Enqueueis now a one-line wrapper aroundEnqueueTxwith its own transaction.pgx.Txis the right type here —experimental/store/postgresalready exposes*pgxpool.Poolpublicly, so this does not leak a new dependency.UpdateRunSpec(ctx, claim, spec) error— replaces the spec on a running claim with the same(claim_id, worker_id, attempt, status=running)fencing asHeartbeat/Complete, returningErrLeaseLoston fence failure. For long-running activities that mutate the spec incrementally (e.g. a KB-apply loop persisting progress between steps) and need the update durable without waiting for the next checkpoint.Test plan
TestStore_GetStepProgress— ordering (started_at asc, NULLS last), detail round-trip, per-execution isolation, status parsingTestStore_EnqueueTxRollbackAndCommit— tx rollback leaves no row; commit with cross-table ledger write is atomic; nil tx rejectedTestStore_UpdateRunSpecFencing— happy path updates spec; wrong worker →ErrLeaseLost; wrong attempt →ErrLeaseLost; completed run →ErrLeaseLost; nil claim rejectedmake test-allgreen (root module + worker + postgres store + sqlite store)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Testing