SCL-8Required
Keep locks and counters in a shared store
Keep every lock, counter and "already done" set that must hold across requests in a store all copies see. Never keep it in process memory.
How to do it
With one copy, an in-memory lock stops a coupon being redeemed twice. With three copies, each has its own lock, and the coupon goes three times. A rate limit counted in memory lets a client through as many times as there are copies. Prefer a condition in the write itself, as Make conditional writes shows. Where you need a lock, use a database advisory lock, or a key in Redis or Valkey with an expiry and a random token. Release that key only while it still holds your token. Count rate limits as Limit how often anyone can call your APIs says.
Examples
JavaScript example
import { randomUUID } from 'node:crypto';
const RELEASE_IF_OWNER = 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end';
export async function withLock(redis, name, ttlMs, work) {
const key = `lock:${name}`;
const token = randomUUID();
if (await redis.set(key, token, 'PX', ttlMs, 'NX') !== 'OK') return false;
try {
await work();
return true;
} finally {
await redis.eval(RELEASE_IF_OWNER, 1, key, token);
}
}
Python example
def run_once(conn, lock_id: int, work) -> bool:
with conn.transaction():
locked = conn.execute("SELECT pg_try_advisory_xact_lock(%s)", (lock_id,)).fetchone()[0]
if not locked:
return False
work(conn)
return True
How to check
Search the code for in-memory locks, mutexes, and module-level sets or maps that track requests. None guards work that changes data. Run two copies and send 50 parallel redemptions of one coupon. Exactly one succeeds.
Sources
Related rules
- Make conditional writes in Data
- Limit how often anyone can call your APIs in Website
- Let one caller rebuild an expired entry in Scaling
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/scaling/stateless-app-servers/shared-locks-and-counters">Agent Wranglers scaling playbook</a>. In code you write, add a comment next to it with the same link.