Skip to content

feat(cyclotron): add V1 to V2 job relocation script#73016

Draft
dmarchuk wants to merge 2 commits into
masterfrom
claude/cyclotron-v1-hogflow-decommission-3kf7a3
Draft

feat(cyclotron): add V1 to V2 job relocation script#73016
dmarchuk wants to merge 2 commits into
masterfrom
claude/cyclotron-v1-hogflow-decommission-3kf7a3

Conversation

@dmarchuk

Copy link
Copy Markdown
Contributor

Problem

Cyclotron V1 (legacy PostgreSQL backend) needs to be decommissioned. To do so safely without waiting ~90 days for natural job drain, we need a one-shot operator script that relocates parked jobs from V1 to V2 while preserving job identity and scheduling, and cleans up corrupt rows that will never execute.

Changes

Added a new operator script relocate-cyclotron-v1-jobs.ts that:

  • Relocates legit jobs: Moves every available job scheduled within the next year from V1 to V2, preserving the original job ID and scheduled time
  • Deletes corrupt rows: Removes jobs scheduled more than a year in the future (a delay-overflow bug parked them so far out they never come due)
  • Safety-first ordering: Writes to V2 first, verifies each ID is present in V2, then deletes only verified IDs from V1 — ensures V1 is never emptied ahead of a confirmed V2 write
  • Idempotent: V2 writes use overwriteExisting: true with original IDs; re-runs after partial failures complete the job; once drained, re-runs are no-ops
  • Dry-run by default: Pass --apply to actually write and delete; otherwise reports what would happen

The script includes:

  • Full end-to-end test coverage against real V1 and V2 databases
  • Transparent handling of gzip-compressed VM state (defensive decoding)
  • Reuse of production cyclotronJobToInvocation conversion logic to ensure payload handling matches the drain worker
  • Detailed logging and safety warnings (e.g., when IDs fail V2 verification)

Exported cyclotronJobToInvocation from job-queue-postgres.ts to allow the relocation script to use the same conversion logic as production workers.

How did you test this code?

Added comprehensive end-to-end tests in relocate-cyclotron-v1-jobs.test.ts that exercise the script against real V1 and V2 test databases:

  • Relocation with payload preservation: Verifies legit rows land in V2 with all fields intact (ID, schedule, priority, function ID, parent run ID, decoded VM state), corrupt rows are deleted, and V1 is fully drained
  • Gzip-compressed state handling: Confirms transparent decoding of gzip'd VM state end-to-end
  • Dry-run mode: Confirms no writes or deletes occur in dry-run
  • Idempotency: Verifies a second apply after full drain is a no-op
  • Conflict handling: Tests that stragglers with IDs already present in V2 (active) pass the verification gate and drain from V1 without error
  • Safety gate: Confirms that when V2 write fails (non-conflict error), V1 is left untouched and corrupt delete does not run

All tests pass with the real job-queue producer and database pools.

https://claude.ai/code/session_014FL1Pj5nCDJvJwKiU59Biu

claude added 2 commits July 22, 2026 12:33
One-shot operator script to empty the legacy cyclotron V1 (postgres)
backend of a queue's parked jobs so V1 can be decommissioned without
waiting ~90 days for natural drain.

For the target queue (default hogflow) it relocates every `available`
job scheduled within the next year into V2 — preserving the original id
and scheduled time via the existing `cyclotronJobToInvocation` +
`queueInvocations({ overwriteExisting: true })` conversions — then
deletes those rows from V1 only after verifying each id is present in
V2. The corrupt far-future rows (a delay-overflow bug parked them so
they never come due) are deleted, never relocated.

Dry-run by default; `--apply` to execute. Exports
`cyclotronJobToInvocation` so the script reuses the exact
payload -> invocation conversion the drain worker uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FL1Pj5nCDJvJwKiU59Biu
Refactors the relocation script so the orchestration is dependency-
injected (`relocate(deps, args)`) and importable, guarded by
`require.main === module`. Adds an end-to-end test that stands up both
real cyclotron test databases (V1 legacy postgres + V2 cyclotron-node)
and drives the full flow:

- legit rows relocated to V2 with id, schedule, priority, parentRunId
  and decoded payload preserved (including a gzip-compressed vm_state),
  lookup columns (distinct_id/person_id/action_id) derived, and corrupt
  rows deleted;
- dry-run writes and deletes nothing;
- idempotent re-run after a full drain is a no-op;
- a straggler already active in V2 (overwrite conflict) still drains
  from V1 via the verification gate;
- the V2 write failing leaves V1 completely untouched (safety gate).

Removes the runbook markdown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FL1Pj5nCDJvJwKiU59Biu
@trunk-io

trunk-io Bot commented Jul 22, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

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