Content · Web Development · PHP

Designing Idempotent PHP Background Jobs

An idempotent job can be retried without applying the same side effect twice.

1 views Published Oct 17, 2026 Updated Oct 17, 2026 Admin

An idempotent job can be retried without applying the same side effect twice.

What It Does and When to Use It


A unique database key provides atomic deduplication across worker retries. A producer publishes work, a consumer executes it, and the broker or scheduler controls delivery and retries.

Requirements, Inputs and Expected Behavior


Confirm the runtime, extension, environment variables, and permissions before running the examples in production. Enforce idempotency with unique database keys or transactions, not a race-prone SELECT alone.

Example 1: Basic Operation


A unique database key provides atomic deduplication across worker retries.

<?php
$pdo=new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TABLE job_receipts (job_id TEXT PRIMARY KEY)');
$insert=$pdo->prepare('INSERT OR IGNORE INTO job_receipts (job_id) VALUES (?)');
foreach (['invoice:42','invoice:42'] as $jobId) { $insert->execute([$jobId]); echo $insert->rowCount()?'First delivery':'Duplicate delivery',PHP_EOL; }

Example 2: Behavior and Validation


Derive a stable, tenant-scoped key; hashing itself does not replace an atomic claim operation.

<?php
function idempotencyKey(string $tenant,int $invoiceId): string {
  return hash('sha256', $tenant.':invoice:'.$invoiceId);
}
echo idempotencyKey('shop-a',42);

Practical Example: Application Use


Illustrate the state transition; real workers must persist state in a transactional shared store.

<?php
$attempts=0;
function processOnce(callable $work, int &$attempts): void {
  if (++$attempts!==1) return;
  $work();
}
processOnce(static fn()=>print('Delivered once'),$attempts);
processOnce(static fn()=>print('Would be duplicate'),$attempts);

Important Behavior and Edge Cases


The second example highlights: Derive a stable, tenant-scoped key; hashing itself does not replace an atomic claim operation. The practical example adds: Illustrate the state transition; real workers must persist state in a transactional shared store.

Comparison with Related Approaches


ApproachWhat changes
Producer, consumer and schedulerA producer publishes work, a consumer executes it, and the broker or scheduler controls delivery and retries.
Choosing safelyAcknowledge completed work and use idempotency to make retries safe.

Common Beginner Mistakes


MistakeCorrect approach
Acknowledging a job before it finishesMark delivery successful only after durable processing succeeds.
Treating retries as harmlessUse idempotency keys and a bounded retry policy with dead-letter handling.
Skipping operational constraintsEnforce idempotency with unique database keys or transactions, not a race-prone SELECT alone.

Key Takeaways


  • An idempotent job can be retried without applying the same side effect twice.
  • Illustrate the state transition; real workers must persist state in a transactional shared store.
  • Acknowledge completed work and use idempotency to make retries safe.
Share this resourceSend this page to someone who may find it useful.