nodejs/src/cdp/services/cyclotron-v2/README.md
TypeScript job queue backed by PostgreSQL, using pg directly.
Lives in a separate database (cyclotron_node) with its own migrations
in rust/cyclotron-node-migrations/.
Run them with rust/bin/migrate-cyclotron-node.
Single table cyclotron_jobs with a single state BYTEA column
for all job payload data (mirrors how the Kafka backend serializes everything into one blob).
Creates jobs via createJob() / bulkCreateJobs().
The bulk path uses UNNEST for efficient batch inserts.
Poll-based consumer using FOR UPDATE SKIP LOCKED to dequeue batches.
Each dequeued job exposes an ack interface:
ack() — mark completedfail() — mark failedretry({ delayMs?, state? }) — re-queue with optional delay and updated statecancel() — mark canceledheartbeat() — extend the lock to prevent the janitor from reclaiming the jobRuns on a timer interval as its own service (PLUGIN_SERVER_MODE=cdp-cyclotron-v2-janitor).
All operations use FOR UPDATE SKIP LOCKED so multiple janitor instances are safe.
Responsibilities:
DELETE of terminal jobs older than a grace periodavailablejanitor_touch_count)The CyclotronJobQueuePostgresV2 wrapper in job-queue/ bridges this SDK with the existing
CyclotronJobInvocation types used by CDP consumers.
It's enabled via CDP_CYCLOTRON_NODE_ENABLED=true and routed alongside the existing backends
(kafka, delay) in CyclotronJobQueue.
Failed jobs are deleted by the janitor along with completed and canceled jobs. There is no dead-letter queue — a DLQ would fill the database exponentially since failed jobs often produce more failed jobs on retry. Errors are captured via logs and metrics before the job reaches terminal status.
action_id column for workflows —
an additional indexed column so scheduled workflow actions
can be queried and displayed more effectively
(e.g. "show me all pending actions for this workflow step").function_id, parent_run_id, or action_id).
Especially useful for workflows where a user cancels a run
and all its pending scheduled jobs need to be cleaned up.