Skip to main content

Module jobs

Module jobs 

Source
Expand description

Background jobs (Cloudflare Queues) and scheduled tasks (Cron Triggers).

Rails’ Active Job, on Cloudflare Queues (in the Workers Free plan since February 2026). The app’s Worker is both the producer and the consumer of its queues: <app>-jobs, bound as QUEUE_BINDING, plus one per named queue (<app>-jobs-urgent, bound as JOBS_URGENT, see queue). A job is a serde value, usually the app’s Job enum (src/jobs/mod.rs, written by ocre g job), and dispatch is a plain match in the app’s perform function, not a registry.

A handler enqueues with enqueue, enqueue_in or enqueue_all (generated jobs wrap it as job.perform_later(&ctx)) and returns as soon as Cloudflare stored the message; the Worker’s queue event runs it moments later through consume. Scheduled tasks run from the scheduled event through cron:

mod jobs {
    use ocre::{Ctx, Result};
    use serde::{Deserialize, Serialize};

    #[derive(Serialize, Deserialize)]
    #[serde(rename_all = "snake_case")]
    pub enum Job {
        SendWelcome { user_id: i64 },
    }

    pub async fn perform(_ctx: Ctx, job: Job) -> Result<()> {
        match job {
            Job::SendWelcome { user_id } => {
                Ok(())
            }
        }
    }
}

mod schedules {
    pub async fn run(_ctx: ocre::Ctx, cron: String) -> ocre::Result<()> {
        Ok(())
    }
}

// src/lib.rs
#[worker::event(queue)]
async fn queue(batch: worker::MessageBatch<String>, env: worker::Env, _ctx: worker::Context) -> worker::Result<()> {
    ocre::jobs::consume(batch, env, jobs::perform).await
}

#[worker::event(scheduled)]
async fn scheduled(event: worker::ScheduledEvent, env: worker::Env, _ctx: worker::ScheduleContext) {
    ocre::jobs::cron(event, env, schedules::run).await
}

// A handler
async fn create(axum::extract::State(ctx): axum::extract::State<ocre::Ctx>) -> ocre::Result<&'static str> {
    ocre::jobs::enqueue(&ctx, &jobs::Job::SendWelcome { user_id: 1 }).await?;
    Ok("created")
}

A message is JSON text, {"at": <due unix time>, "job": {"send_welcome": {"user_id": 1}}}. consume acknowledges a job that returns Ok; drops, with a log line, one that returns an error another try cannot fix (a 4xx Error such as NotFound: Rails’ discard_on); retries any other Err with a growing delay (30 s, 1 min, 3 min, 9 min, 27 min: twice the time since it was due); and drops a message it cannot decode (an unknown or changed job), so it is never retried forever. After maxRetries: 5 (cloudflare.config.ts) Cloudflare moves a failing message to the dead-letter queue <app>-jobs-failed, kept 24 hours. Delivery is at-least-once: write jobs to be safe to repeat. Every line Ocre logs starts with LOG_PREFIX or CRON_LOG_PREFIX.

§Free-plan budget (September 2026)

  • Queues operations: 10,000 a day. A message costs 3 (write, read, delete), each retry 1 more read, a dead-lettered message 1 more write. Ocre sends one message per job: about 3,300 jobs a day.
  • Retention: 24 hours on Free; the last retry comes after about 40 minutes.
  • Message size: 128 KB; enqueue refuses larger jobs (pass ids). enqueue_all sends 100 messages (256 KB) per call.
  • Delay: 24 hours at most, on send and on retry; enqueue_in refuses longer delays (MAX_DELAY).
  • Batches: up to 100 messages and 60 s wait; the generated max_batch_size = 10, max_batch_timeout = 5 make one consumer run (one Worker invocation) per 10 jobs.
  • CPU: 10 ms per invocation, consumer batches and cron runs included: jobs should be I/O (D1, mail, fetch); lower max_batch_size for CPU-heavy jobs.
  • Cron Triggers: 5 per account; run several tasks from one cron.

Structs§

Budget
How many calls an invocation may still make: D1 queries, fetches, KV and R2 operations, as the job counts them.
Queue
A job queue, from queue: enqueue on it like on the default queue.

Enums§

Step
What a step of run_steps did: more to do from the cursor, or done.

Constants§

CRON_LOG_PREFIX
Prefix of every line Ocre logs about Cron Triggers, e.g. [ocre cron] 0 3 * * * done.
DEFAULT_QUEUE
Name of the queue enqueue and enqueue_in use: default, bound as QUEUE_BINDING.
LOCKS_TABLE_SQL
The job_locks table of lock and unlock, created by the migration of ocre g job --lock.
LOG_PREFIX
Prefix of every line Ocre logs about jobs, e.g. [ocre jobs] send_welcome done.
MAX_DELAY
Longest delay Cloudflare Queues accepts: 24 hours, for enqueue_in and retries.
QUEUE_BINDING
Name of the queue producer binding every Ocre app sends jobs to.

Functions§

consume
Runs a batch of queue messages through the app’s perform; the Worker’s queue entry point.
cron
Runs the app’s task for the Cron Trigger that fired; the Worker’s scheduled entry point.
dev_routes
Development endpoint listing recent jobs, served by ocre dev only, for tests (Rails’ assert_enqueued_with and assert_performed_jobs).
enqueue
Sends job to the JOBS queue, to run in the background through consume.
enqueue_all
Sends every job of jobs to the JOBS queue in as few calls as possible, like Rails’ perform_all_later.
enqueue_in
Sends job to the JOBS queue like enqueue, to run after delay.
lock
Takes the lock key for owner until ttl seconds from now; false when another owner holds it and it has not expired. The owner holding it takes it again (extending it), so a job that continues in several queue messages keeps its lock from step to step. One D1 write.
queue
A named queue, for jobs that must not wait behind others: ocre::jobs::queue(&ctx, "urgent").enqueue(&job).
run_steps
Runs step from cursor while budget covers its cost (the calls one step makes), for work too large for one invocation: a page of rows per step, the next page’s cursor between them (Active Job’s continuations).
unlock
Releases the lock key if owner holds it. One D1 write.