Nexus
Documentation / Reliability & Operations

Scheduling Guarantees

Nexus cron schedules fire exactly once across replicas via a compare-and-swap claim plus cross-replica advisory locks, on wall-clock-aligned ticks. Why that is a firing guarantee, how it contrasts with at-least-once queue dispatch, and why there is no end-to-end exactly-once execution.

This page is for engineers who need the precise delivery semantics of the Nexus scheduler — the kind of precision you need before you trust a cron to drive a financial close or a nightly reconciliation. It draws a sharp line between two different guarantees that are easy to conflate: exactly-once firing (which Nexus provides) and exactly-once execution (which it does not).

Exactly-once firing

Nexus runs a built-in cron scheduler on every replica. In a multi-replica deployment that raises the obvious hazard: if three replicas each hold the same schedule, a nightly job could fire three times. Nexus prevents that. A given cron occurrence fires exactly once across the whole tenant, no matter how many replicas are running, using two coordinated mechanisms in your per-tenant Postgres:

  • Compare-and-swap claim. When a scheduled occurrence comes due, a replica attempts to atomically claim that specific tick — a conditional update that succeeds for exactly one replica and fails for the others. Only the winner enqueues the run.
  • Cross-replica advisory locks. Postgres advisory locks serialize the scheduler's claim path across replicas, so the compare-and-swap is evaluated without a race. Two replicas evaluating the same due tick at the same instant cannot both win.

The result: for a schedule like 0 2 * * * (02:00 daily), exactly one run is enqueued for the 02:00 occurrence, tenant-wide, on a machine cluster of any size. Adding replicas for capacity never multiplies your scheduled runs.

Wall-clock-aligned ticks

Scheduler ticks are aligned to the wall clock, so a 0 * * * * schedule fires at the top of the hour rather than drifting by whatever offset the process happened to start with. Alignment is what makes "hourly" mean :00 every hour and lets multiple schedules co-fire predictably.

The critical distinction: firing vs. execution

Here is the line to hold precisely:

Exactly-once applies to cron firing — the decision to enqueue a run. It does not extend to execution. Once a run is enqueued, it enters the durable run queue, whose delivery contract is at-least-once at the claim boundary.

Why at-least-once on the queue side? Because that is the correct, recoverable behavior for a durable queue. If a worker claims a run and then dies before recording a terminal state, the heartbeat/orphan reaper returns the run to a claimable state so a healthy worker finishes the job. That recovery is a feature — it is why a node eviction doesn't silently drop your nightly sync — but its consequence is that a run can, in a worker-death scenario, execute more than once.

Put the two together and the honest end-to-end statement is:

Guarantee Provided? Mechanism
Cron fires exactly once per occurrence Yes Compare-and-swap claim + advisory locks
Queue delivers a claimed run at least once Yes FOR UPDATE SKIP LOCKED claim + reaper recovery
Run executes exactly once, end to end No Not built — a reaped run can re-execute

There is no end-to-end exactly-once execution guarantee. Anyone who needs "this side effect happens exactly once" gets there by making the side effect idempotent, not by relying on the scheduler.

What this means for your design

The takeaway is not "the scheduler is unreliable" — it is very reliable, and it will not spuriously multiply firings. The takeaway is that exactly-once firing plus at-least-once execution is a strong, standard, well-understood contract, and the correct way to build on it is idempotency:

  1. Trust that a cron occurrence enqueues exactly one run. You will not get accidental duplicate firings from replica count.
  2. Design the run's effects to be idempotent — keyed upserts, workspace checkpoints — so that the rare at-least-once re-execution (from worker death) converges rather than double-applies. See Retries & Idempotency.
  3. If you need retry-on-failure (which the platform does not do automatically), drive it from an external scheduler that reads the terminal status — again, safe only because the run is idempotent.

Scheduling ergonomics

Schedules are declared when you save a pipeline, or inline with a directive at the top of the module:

# nexus:trigger scheduled 0 2 * * *     # 02:00 daily, fires exactly once tenant-wide
# nexus:timeout 1800

async def run(ctx):
    rows = await ctx.adapters.netsuite.read({"collection": "invoices"})
    await ctx.adapters.postgres.write(rows, op="upsert")   # idempotent by design
    return {"synced": len(rows)}

See Scheduling Pipelines for triggers, parameters, and the write-approval gate, and Pipelines Overview for the # nexus: directive surface.

Limits & guarantees

  • Guaranteed: each cron occurrence fires exactly once across all replicas; ticks are wall-clock-aligned; a claimed run survives worker death via reaper recovery.
  • Not guaranteed: exactly-once execution end to end (queue dispatch is at-least-once; a reaped run can re-execute — design idempotently); automatic retry of a run that failed on its own (drive retries externally).

See also