Lucky Wirasakti
1 min read

Taming Campaign Traffic with Redis Locks

#Redis#Architecture#Backend

When a marketing campaign goes live nationwide, traffic does not ramp up politely. It spikes. For a voucher-distribution chatbot, that spike is the whole point — and also the thing most likely to hand the same voucher to two people at once.

The race condition

A voucher claim is a read-then-write: check that a code is still available, then mark it as taken. Under heavy concurrency, two requests can both read "available" before either writes "taken". Both succeed. The business loses money and trust.

A lock that expires

The fix is a short-lived distributed lock keyed on the resource. Redis SET with NX and a TTL gives you exactly that: only one caller acquires the key, and if a worker dies mid-claim the lock releases itself.

acquired = redis.set(f"lock:voucher:{code}", worker_id, nx=True, ex=5)
if not acquired:
    raise Busy("try again")
try:
    claim_voucher(code)
finally:
    release_if_owner(code, worker_id)

A lock without a TTL is just a future outage with extra steps.

Rate limiting at the edge

Locks protect correctness; rate limits protect the system. A per-user token bucket in Redis kept individual numbers from hammering the campaign endpoint, which smoothed the load enough that the locks rarely contended in the first place.

  • Lock on the smallest resource that needs protection, not the whole request.
  • Always set a TTL — assume every worker can crash.
  • Release only if you still own the lock, to avoid freeing someone else's.