Skip to content

Unique Jobs

When the same work might be enqueued multiple times — for example, sending a welcome email triggered by multiple webhook deliveries, or a cron job firing while a previous run is still in progress — unique jobs let you deduplicate at enqueue time using an idempotency key.

Every job can optionally carry a unique_key. When you enqueue a job with a unique_key, the store checks for any existing job with the same key that is still in a non-terminal state (available, executing, scheduled, or retryable). Jobs that are completed, discarded, or cancelled are ignored during this check.

If a match is found, the behavior depends on the unique_strategy.

pub const UniqueStrategy = enum {
ignore_new,
cancel_existing,
};
StrategyBehavior
ignore_newThe new job is silently dropped. The existing job is returned from enqueue.
cancel_existingThe existing job is moved to discarded, and the new job is inserted.

The most common case: you want to ensure only one instance of a job is pending at a time. If a duplicate is enqueued, it is silently ignored.

const zzz_jobs = @import("zzz_jobs");
// First enqueue creates the job
const job1 = try supervisor.enqueue("email_worker", "{\"to\": \"user@example.com\"}", .{
.unique_key = "welcome-email-user-123",
.unique_strategy = .ignore_new,
});
// Second enqueue with the same key returns the existing job (no duplicate created)
const job2 = try supervisor.enqueue("email_worker", "{\"to\": \"user@example.com\"}", .{
.unique_key = "welcome-email-user-123",
.unique_strategy = .ignore_new,
});
// job1.id == job2.id

When you want new data to supersede an older pending job. The existing job is discarded and a fresh job takes its place.

// First enqueue
const job1 = try supervisor.enqueue("sync_worker", "{\"version\": 1}", .{
.unique_key = "sync-account-42",
.unique_strategy = .cancel_existing,
});
// Second enqueue discards job1 and creates a new job
const job2 = try supervisor.enqueue("sync_worker", "{\"version\": 2}", .{
.unique_key = "sync-account-42",
.unique_strategy = .cancel_existing,
});
// job1 is now discarded, job2 is the active job
// job1.id != job2.id

The unique key is an arbitrary string (up to 128 characters in DbStore). Good keys include enough context to identify the logical unit of work:

PatternExampleUse case
{action}-{entity}-{id}"welcome-email-user-123"One welcome email per user
{worker}-{resource}"sync-account-42"One sync per account
{schedule-name}"hourly-cleanup"One cleanup job at a time
{action}-{hash}"process-order-abc123"One processing per order

The uniqueness constraint is active only while the job is in a non-terminal state:

StateBlocks new jobs with same key?
availableYes
executingYes
scheduledYes
retryableYes (transitions to available)
completedNo
discardedNo
cancelledNo

Once a job completes, is discarded, or is cancelled, its unique key is freed and a new job with the same key can be enqueued.

Unique keys are particularly useful with cron-scheduled jobs to prevent overlap when a job runs longer than the cron interval:

try supervisor.registerCron(
"hourly_report",
"0 * * * *", // every hour
"report_worker",
"{}",
.{
.unique_key = "hourly-report",
.unique_strategy = .ignore_new,
},
);

If the report takes 90 minutes, the 1-hour cron tick will try to enqueue a duplicate, but ignore_new silently skips it because the previous job is still executing.

OptionDefault
unique_keynull (no uniqueness constraint)
unique_strategy.ignore_new

When unique_key is null, no uniqueness check is performed and the job is always enqueued.