Skip to content

Record completed_at for ContinuedAsNew executions - #32

Open
Thom Chiovoloni (thomcc-work) wants to merge 9 commits into
mainfrom
thomcc/continue_as_new_completed_at
Open

Thom Chiovoloni (thomcc-work) wants to merge 9 commits into
mainfrom
thomcc/continue_as_new_completed_at

Conversation

@thomcc-work

@thomcc-work Thom Chiovoloni (thomcc-work) commented Oct 5, 2026 •

Copy link
Copy Markdown

Summary

Record completed_at for ContinuedAsNew executions so age-based retention can remove old generations of long-running workflows. Previously, those timestamps remained NULL, leaving continued executions ineligible for age-based pruning.

This also adds a paginated SQL pruning API and coordinates locking across pruning and deletion operations to protect live executions during concurrent maintenance.

Changes

Completion timestamps — migration 0027

  • Set completion timestamps when an execution continues as new.
  • Backfill missing timestamps from the last persisted OrchestrationContinuedAsNew history event, using its original timestamp rather than the upgrade time.
  • Preserve existing timestamps and other execution statuses. Rows without a recoverable terminal event remain NULL and produce a warning.
  • Retain the acknowledgement lease safeguards introduced in fix: make orchestration lock renewal and acknowledge safe under concurrency #44.
  • Fail closed on malformed history JSONB, preserving SQLSTATE and reporting the schema and full history key without committing a partial backfill.

Batched pruning — migration 0028

  • Add prune_executions_batch, which scans a primary-key-ordered page of 1–1,000 execution keys and returns a continuation cursor and scanned/deleted counts.
  • Advance the cursor across protected executions, even when nothing is deleted.
  • Preserve current/future executions, Running executions, and executions with NULL or insufficiently old completion timestamps.
  • Precheck batch eligibility without locking, then lock only instances with deletion candidates. Both pruning APIs check eligibility under the same instance-row lock, preventing stale decisions during deletion/recreation or execution updates.
  • Give atomic deletion a consistent instance-ID lock order to avoid multi-instance deadlocks with pruning.
  • Remove the new function during schema cleanup, including cleanup of public.

Each batch is atomic. The limit bounds scanned execution keys, not the number of history events deleted.

Bulk deletion and compatibility

  • Keep pending continuations out of age-filtered bulk instance deletion now that their completion timestamps are populated.
  • Preserve existing unfiltered bulk-deletion behavior, retention options, and provider trait APIs.
  • Include companion migration diffs, documentation, and regression coverage.

Upgrade notes

This requires a fully drained upgrade, not a rolling upgrade with old and new provider binaries together.

  1. Pause all delete and prune jobs.
  2. Stop and drain every process using the provider: workers, clients, retention and maintenance jobs, and direct SQL callers. Wait for in-flight calls and transactions to finish.
  3. Apply the migrations with provider traffic stopped, then restart only new binaries containing these fixes and updated direct SQL callers.
  4. Review retention cutoffs before resuming delete and prune jobs.

Stopping only workers is insufficient: old age-based deletion code can delete pending continuations after their completion timestamps are populated.

Expect a one-time wave of age-based deletions of old continue-as-new generations when retention resumes. Backfill time and WAL volume grow with the number of continue-as-new rows to repair; large histories can further increase backfill time. The backfill itself does not delete executions or history.

If the upgrade was not fully drained, an old acknowledgement can write a NULL completion timestamp after the backfill. Drain all callers first, then use the explicit timestamp repair procedure. Re-running the backfill while old calls remain in flight does not close that race; do not delete migration ledger rows to force a rerun.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The production data backfill and replacement of a critical acknowledgement function warrant the planned manual testing and human review.

Review effort: Balanced
Findings: None

What changed in this PR

Records completion timestamps for continued-as-new executions, enabling age-based pruning of older generations.

Changes:

  • Updates acknowledgement logic and backfills recoverable timestamps.
  • Adds regression coverage for migration and pruning behavior.
  • Documents migration behavior and operational guidance.
File Description
migrations/​0024_continue_as_new_completed_at.sql Updates and backfills completion timestamps.
migrations/​0024_diff.md Documents the migration delta.
migrations/​README.md Adds upgrade and pruning guidance.
src/​migrations.rs Refreshes the migration embedding marker.
tests/​regression_tests.rs Tests fresh installs, upgrades, and pruning.
CHANGELOG.md Records the fix under Unreleased.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@waldemort-auto waldemort-auto Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads-up from #44, now tracked in #46.

#44 also adds migration 0026 and replaces ack_orchestration_item. It adds an advisory lock, a token recheck and a row-count check, to fix the double processing in #29. This PR's 0024 rebuilds ack_orchestration_item from the 0020 body. If this PR merges after #44 as it is, it silently reverts that fix.

Whichever PR merges second needs to:

  1. renumber its migration;
  2. carry both changes in its copy of ack_orchestration_item;
  3. run cargo test --test deadlocks --test orchestration_ack_lease_tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rom age-filtered bulk deletion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A delete/recreate race can let pruning erase a replacement instance’s current execution and history, while regression tests do not independently verify key eligibility safeguards.

2 open issues

Review effort: Balanced

Comment thread tests/execution_pruning_tests.rs Resolved

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Batched pruning and atomic deletion acquire instance locks in inconsistent orders, risking deadlocks during concurrent multi-instance operations that existing single-instance tests cannot expose.

1 open issue
2 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Concurrent batch and whole-instance pruning can deadlock without a shared instance-row lock, and schema cleanup leaves the new batch-pruning function behind.

2 open issues
1 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The retention backfill and destructive-operation lock-order changes require PostgreSQL-backed upgrade, replay/pruning, and concurrency validation that was unavailable in this review.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

Precheck eligibility without changing scanned-key bounds or cursors, then retain the locked eligibility recheck. Exercise the hostile search_path with an eligible row and verify protected instances do not require locks.

Ref: #32 (32-S1, 32-S3)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the backfill set-based on success and preserve SQLSTATE while adding the failing history key on JSONB decode errors. Verify atomic rollback, valid upgrades, and a schema built entirely from the pre-0027 migration prefix.

Ref: #32 (backfill diagnostics and upgrade-fixture review notes)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Require draining all provider callers, pause retention during upgrades, and describe the one-time deletion wave and backfill/WAL cost. Document an explicit timestamp-only repair after draining, preserve the migration ledger, and classify existing-procedure fixes under Fixed.

Ref: #32 (32-M1, 32-S2, changelog review note)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Start one new ApplyAll process first and wait for provider initialization to complete before restarting the remaining upgraded callers. Keep migrations numbered 0027/0028 pending merge-order coordination.

Ref: #32 (upgrade-startup review nit)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants