---
title: "Keep locks and counters in a shared store · Agent Wranglers"
url: https://agentwranglers.se/playbook/scaling/stateless-app-servers/shared-locks-and-counters
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Keep locks and counters in a shared store

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Stateless app servers](https://agentwranglers.se/playbook/scaling/stateless-app-servers.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: SCL-8
- Level: Required
- Chapter: [Stateless app servers](https://agentwranglers.se/playbook/scaling/stateless-app-servers)
- Rule: 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.
- Risk: Money lost or taken twice
- Applies when: The project runs its own servers, virtual machines or containers.
- How: 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](https://agentwranglers.se/playbook/data/transactions-and-concurrency/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](https://agentwranglers.se/playbook/website/security/rate-limits) says.
- 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.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
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

```python
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
```

## Sources

- [Redis SET command, locking pattern](https://redis.io/docs/latest/commands/set/)
- [PostgreSQL advisory locks](https://www.postgresql.org/docs/current/explicit-locking.html)

## Related rules

- [Make conditional writes](https://agentwranglers.se/playbook/data/transactions-and-concurrency/conditional-writes) in Data
- [Limit how often anyone can call your APIs](https://agentwranglers.se/playbook/website/security/rate-limits) in Website
- [Let one caller rebuild an expired entry](https://agentwranglers.se/playbook/scaling/caching/stampede-protection) in Scaling

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/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.

- Previous: [Pinning each visitor to one server](https://agentwranglers.se/playbook/scaling/stateless-app-servers/no-sticky-sessions)
- Next: [Start fast and warm up before taking traffic](https://agentwranglers.se/playbook/scaling/stateless-app-servers/fast-warm-start)
- [Stateless app servers](https://agentwranglers.se/playbook/scaling/stateless-app-servers)
