Skip to content
Bitween AI is on the way
Scheduling

Build integrations

Scheduling

Schedules, jobs, run history and schedule health

Scheduled jobs and aggregations run on schedules. Bitween uses Quartz, with its job store in the Bitween database, through SimplyWorks.Scheduler.

Schedules#

A subscription can have several schedules. Each has a recurrence and a time, always in UTC.

Recurrence Time fields Quartz cron
Hourly minute 0 {m} * * * ?
Daily hour, minute 0 {m} {h} * * ?
Weekly day from 0 (Sunday) to 6, hour, minute 0 {m} {h} ? * {day + 1}
Monthly day from 1 to 27, hour, minute 0 {m} {h} {day} * ?

In the API a schedule looks like this.

{ "recurrence": "Daily", "days": 0, "hours": 2, "minutes": 30, "backwards": false }

The backwards flag, shown as count from the end, is stored and is part of the job key, but the cron conversion ignores it.

Saving a subscription replaces its Quartz triggers, and deactivating it removes them. The UI gives new scheduled jobs a daily schedule at 00:00 UTC.

Jobs#

Job When What it does
Receiving job Each scheduled job's schedules Runs the receiver. See Scheduled jobs.
Aggregation job Each aggregation's schedules Rolls up finished exchanges. See Aggregation.
Retry job The Retry poll schedule setting, every minute by default Runs due automatic retries, dropping any whose exchange was already retried
Receive attempt cleanup Bitween:ReceiveAttemptCleanupCron, 03:00 UTC daily by default Deletes receive attempts older than Bitween:ReceiveAttemptRetentionDays, 30 days by default
Inbound message prune Bitween:InboundMessagePruneCron, 03:30 UTC daily by default Deletes broker deduplication keys older than each data source's window

A job never runs concurrently with itself on one scheduler, and missed fires are skipped rather than caught up.

At startup, a background service registers the retry and cleanup jobs, then the triggers of every active scheduled subscription. Registration is idempotent.

Running flag#

Before a scheduled job runs, it sets a running flag on the subscription with a conditional database update, and it clears the flag when it ends. If the flag is already set, the run is skipped. This stops two instances receiving for the same job at once.

If a run is killed before it finishes, the flag stays set and every later run is skipped. Schedule health reports this as Stuck. No API clears the flag, so it has to be reset in the database. Aggregations do not use the flag.

Manual runs#

Receive now and Roll up now schedule a one-off run immediately. Both need subscriptions.operate.

Health and history#

Information Shown on Source
Consecutive failures, last error Subscription overview and lists Updated after each run, reset by a successful run
Receive attempts Overview of scheduled jobs and aggregations Bitween's own record of each run
Recent runs Overview of other scheduled subscriptions The scheduler's execution history
Last run, reliability Scheduled jobs and Aggregations lists The newest run, and successes among the last 20 finished runs
Schedule health Status column of those lists The Quartz triggers themselves

Receive attempts say what the receiver found: data, with the exchanges it created; nothing new; or a failure and its message. The scheduler's own history only knows whether the job threw, and the receiving job catches its own errors, so receive attempts are the more useful record.

Schedule health reports the worst state across a subscription's triggers.

State UI label Meaning
Normal The triggers exist and will fire
Missing Not scheduled There are fewer triggers than schedules, so a schedule will never fire
Paused Trigger paused A Quartz trigger is paused
Blocked Blocked A Quartz trigger is blocked
Error Trigger error A Quartz trigger is in error
Complete Schedule ended Every trigger has finished and will not fire again
Stuck Stuck The running flag is set but nothing is executing

Saving the subscription again recreates missing triggers.

Bitween never pauses or deactivates a subscription because of repeated failures. The failure count is for information only.

Caveats#

  • Deleting a subscription does not remove its Quartz triggers. They keep firing, and the job exits because the subscription is gone.
  • The startup code states that Quartz clustering is not guaranteed with the pinned scheduler packages. With several replicas, only the running flag prevents duplicate receiving, and aggregations have no such flag.