Job Uniqueness Patterns — Complete Guide
In this tutorial, you will learn about Job Uniqueness Patterns. We cover key concepts, practical examples, and best practices to help you master this topic.
Ensure exactly-once job processing with uniqueness constraints, idempotency keys, database unique constraints, and Redis-based deduplication for reliable queues.
What You Learn
You will learn how to enforce job uniqueness at the queue level, use database constraints for uniqueness, implement time-window uniqueness, and combine deduplication with idempotency.
Why It Matters
Duplicate jobs cause duplicate emails, double payments, and redundant processing. Uniqueness guarantees that the same logical operation executes only once, even if submitted multiple times.
Real-World Use
DodaTech's payment system enforces uniqueness with database-level constraints on transaction IDs. Webhook handlers use idempotency keys with Redis SETNX. Together they prevent duplicate payments.
Uniqueness Architecture
flowchart LR
A[Submit Job] --> B{Unique Check}
B -->|New| C[Enqueue]
B -->|Duplicate| D[Reject]
C --> E[Process]
E --> F{Idempotent?}
F -->|Yes| G[Safe to retry]
F -->|No| H[Exactly once]
D --> I[Return existing result]
Redis SETNX Uniqueness
import redis
import json
import time
import uuid
r = redis.Redis()
class UniqueJobQueue:
def __init__(self, ttl=86400):
self.ttl = ttl
def enqueue_unique(self, queue, uniqueness_key, job_data):
lock_key = f'unique:{queue}:{uniqueness_key}'
acquired = r.setnx(lock_key, '1')
if not acquired:
print(f"Duplicate blocked: {uniqueness_key}")
return None
r.expire(lock_key, self.ttl)
job_id = str(uuid.uuid4())
job_data['_unique_key'] = uniqueness_key
job_data['_job_id'] = job_id
r.lpush(queue, json.dumps(job_data))
print(f"Enqueued unique job: {uniqueness_key}")
return job_id
def is_processed(self, queue, uniqueness_key):
return r.exists(f'unique:{queue}:{uniqueness_key}')
def release(self, queue, uniqueness_key):
r.delete(f'unique:{queue}:{uniqueness_key}')
uq = UniqueJobQueue()
uq.enqueue_unique('email_queue', 'welcome-user-123',
{'to': 'user@example.com', 'template': 'welcome'})
uq.enqueue_unique('email_queue', 'welcome-user-123',
{'to': 'user@example.com', 'template': 'welcome'})
Expected output:
Enqueued unique job: welcome-user-123
Duplicate blocked: welcome-user-123
Database Unique Constraint
import sqlite3
import time
import json
class DBUniqueQueue:
def __init__(self, db_path=':memory:'):
self.conn = sqlite3.connect(db_path)
self.conn.execute('''
CREATE TABLE IF NOT EXISTS unique_jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
job_type TEXT NOT NULL,
uniqueness_key TEXT NOT NULL,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(job_type, uniqueness_key)
)
''')
self.conn.commit()
def submit_unique(self, job_type, uniqueness_key, payload):
try:
self.conn.execute(
'INSERT INTO unique_jobs (job_type, uniqueness_key) VALUES (?, ?)',
(job_type, uniqueness_key)
)
self.conn.commit()
job_id = self.conn.execute(
'SELECT id FROM unique_jobs WHERE job_type = ? AND uniqueness_key = ?',
(job_type, uniqueness_key)
).fetchone()[0]
print(f"Created unique job: {job_type}/{uniqueness_key} (id={job_id})")
return job_id
except sqlite3.IntegrityError:
existing = self.conn.execute(
'SELECT id, status FROM unique_jobs WHERE job_type = ? AND uniqueness_key = ?',
(job_type, uniqueness_key)
).fetchone()
print(f"Duplicate rejected: {job_type}/{uniqueness_key} (status={existing[1]})")
return None
def mark_completed(self, job_id):
self.conn.execute(
'UPDATE unique_jobs SET status = ? WHERE id = ?',
('completed', job_id)
)
self.conn.commit()
def get_status(self, job_type, uniqueness_key):
cursor = self.conn.execute(
'SELECT status FROM unique_jobs WHERE job_type = ? AND uniqueness_key = ?',
(job_type, uniqueness_key)
)
row = cursor.fetchone()
return row[0] if row else 'not_found'
dbq = DBUniqueQueue()
dbq.submit_unique('payment', 'txn-20260628-001', {'amount': 100})
dbq.submit_unique('payment', 'txn-20260628-001', {'amount': 100})
Expected output:
Created unique job: payment/txn-20260628-001 (id=1)
Duplicate rejected: payment/txn-20260628-001 (status=pending)
Time-Window Uniqueness
import redis
import json
import time
r = redis.Redis()
class TimeWindowUniqueQueue:
def __init__(self, window_seconds=3600):
self.window = window_seconds
def enqueue_in_window(self, queue, namespace, resource_id, job_data):
key = f'window_unique:{namespace}:{resource_id}'
exists = r.exists(key)
if exists:
ttl = r.ttl(key)
print(f"Duplicate in window: {namespace}/{resource_id} (TTL: {ttl}s)")
return False
r.setex(key, self.window, '1')
r.lpush(queue, json.dumps(job_data))
print(f"Enqueued: {namespace}/{resource_id}")
return True
def queue_depth(self, queue):
return r.llen(queue)
twq = TimeWindowUniqueQueue(window_seconds=60)
twq.enqueue_in_window('scans', 'file_scan', 'doc-123',
{'file': 'doc-123', 'scanner': 'av'})
twq.enqueue_in_window('scans', 'file_scan', 'doc-123',
{'file': 'doc-123', 'scanner': 'av'})
time.sleep(61)
twq.enqueue_in_window('scans', 'file_scan', 'doc-123',
{'file': 'doc-123', 'scanner': 'av'})
Expected output:
Enqueued: file_scan/doc-123
Duplicate in window: file_scan/doc-123 (TTL: 58s)
Enqueued: file_scan/doc-123
Hash-Based Uniqueness
import hashlib
import json
import redis
r = redis.Redis()
class HashBasedUniqueness:
def __init__(self, ttl=86400):
self.ttl = ttl
def compute_hash(self, job_data):
serialized = json.dumps(job_data, sort_keys=True)
return hashlib.sha256(serialized.encode()).hexdigest()
def enqueue_if_unique(self, queue, job_data, namespace='job'):
content_hash = self.compute_hash(job_data)
dedup_key = f'hash_unique:{namespace}:{content_hash}'
acquired = r.setnx(dedup_key, '1')
if not acquired:
print(f"Duplicate hash: {content_hash[:16]}...")
return None
r.expire(dedup_key, self.ttl)
job_data['_hash'] = content_hash
r.lpush(queue, json.dumps(job_data))
print(f"Enqueued hash: {content_hash[:16]}...")
return content_hash
hbu = HashBasedUniqueness()
job = {'type': 'report', 'params': {'date': '2026-06-28', 'format': 'pdf'}}
hbu.enqueue_if_unique('reports', job)
hbu.enqueue_if_unique('reports', job)
Expected output:
Enqueued hash: a1b2c3d4e5f6a7b8...
Duplicate hash: a1b2c3d4e5f6a7b8...
Common Mistakes
1. Uniqueness Without Expiration
Unique keys without TTL grow forever and leak memory. Always set TTL based on the maximum expected retry window.
2. Key Too Narrow
Uniqueness on job ID alone lets different jobs with same ID be blocked. Include queue name, job type, and content fingerprint in the key.
3. Assuming Queue-Level Uniqueness
Queues do not enforce uniqueness by default. You must implement it at the application layer using Redis, database, or distributed locks.
4. Ignoring Race Conditions
Two concurrent submissions may both pass the uniqueness check. Use atomic operations (SETNX, INSERT with unique constraint) not check-then-set patterns.
5. No Fallback on Store Failure
If Redis is down, uniqueness checks fail open (all jobs accepted) or fail closed (all jobs rejected). Design based on your safety requirements.
Practice Questions
1. What is the difference between uniqueness and idempotency?
Uniqueness prevents duplicate submissions. Idempotency ensures safe processing of duplicates. Uniqueness is proactive, idempotency is defensive.
2. How does SETNX guarantee atomic uniqueness checks?
SETNX only sets a key if it does not exist. The atomic check-and-set prevents race conditions between concurrent submissions.
3. When should you use time-window uniqueness?
When the same operation should not repeat within a window but should be allowed after (e.g., rate-limited email sending, periodic cache refresh).
4. Why include namespace in uniqueness keys?
Different job types may share the same key (e.g., user ID 123). Namespacing prevents false duplicates across different job types.
Challenge
Build a uniqueness system for a payment processor that supports: unique transaction IDs with database constraints, retry idempotency within 24 hours, concurrent submission safety, and status reporting for duplicate queries.
FAQ
Mini Project: Uniqueness System
import redis
import json
import time
import hashlib
r = redis.Redis()
class UniquenessSystem:
def __init__(self, default_ttl=86400):
self.default_ttl = default_ttl
def submit(self, queue, job_type, identifier, job_data):
unique_key = f"unique:{queue}:{job_type}:{identifier}"
acquired = r.setnx(unique_key, json.dumps({'status': 'pending', 'created': time.time()}))
if not acquired:
existing = r.get(unique_key)
print(f"Duplicate: {job_type}/{identifier} - {existing.decode()}")
return None
r.expire(unique_key, self.default_ttl)
r.lpush(queue, json.dumps({**job_data, '_uid': identifier}))
return identifier
def complete(self, queue, job_type, identifier):
key = f"unique:{queue}:{job_type}:{identifier}"
r.setex(key, self.default_ttl,
json.dumps({'status': 'completed', 'completed_at': time.time()}))
def status(self, queue, job_type, identifier):
key = f"unique:{queue}:{job_type}:{identifier}"
data = r.get(key)
if data:
return json.loads(data)
return None
us = UniquenessSystem()
us.submit('payments', 'payment', 'txn_001', {'amount': 50})
us.submit('payments', 'payment', 'txn_001', {'amount': 50})
us.complete('payments', 'payment', 'txn_001')
print(us.status('payments', 'payment', 'txn_001'))
Expected output:
Duplicate: payment/txn_001 - {"status": "pending", "created": ...}
{'status': 'completed', 'completed_at': ...}
What's Next
Now that you understand job uniqueness, explore job chaining for sequential execution, then learn about job DAG workflows for complex Orchestration.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro