$npx -y skills add vibeeval/vibecosystem --skill concurrency-securityTOCTOU prevention, distributed locking, idempotency keys, race condition detection for Node.js and serverless environments.
| 1 | # Concurrency Security |
| 2 | |
| 3 | Patterns for preventing race conditions, double-execution, and state corruption in concurrent systems. |
| 4 | |
| 5 | ## TOCTOU Prevention |
| 6 | |
| 7 | Time-of-Check to Time-of-Use: the gap between reading state and acting on it. |
| 8 | |
| 9 | ```typescript |
| 10 | // WRONG: check then act - another process can change state between lines |
| 11 | const balance = await db.accounts.findUnique({ where: { id } }) |
| 12 | if (balance.amount >= amount) { |
| 13 | await db.accounts.update({ where: { id }, data: { amount: balance.amount - amount } }) |
| 14 | } |
| 15 | |
| 16 | // CORRECT: atomic check-and-update in a single statement |
| 17 | const updated = await db.$executeRaw` |
| 18 | UPDATE accounts |
| 19 | SET amount = amount - ${amount} |
| 20 | WHERE id = ${id} AND amount >= ${amount} |
| 21 | RETURNING * |
| 22 | ` |
| 23 | if (updated.count === 0) throw new Error('Insufficient funds or concurrent update') |
| 24 | ``` |
| 25 | |
| 26 | ```typescript |
| 27 | // File system TOCTOU (Node.js) |
| 28 | // WRONG |
| 29 | if (fs.existsSync(filePath)) { |
| 30 | fs.writeFileSync(filePath, data) // another process may have deleted it |
| 31 | } |
| 32 | |
| 33 | // CORRECT: use O_EXCL flag for exclusive creation |
| 34 | import { open } from 'fs/promises' |
| 35 | try { |
| 36 | const fh = await open(filePath, 'wx') // fail if file exists |
| 37 | await fh.writeFile(data) |
| 38 | await fh.close() |
| 39 | } catch (err: any) { |
| 40 | if (err.code === 'EEXIST') { /* already exists, handle */ } |
| 41 | throw err |
| 42 | } |
| 43 | ``` |
| 44 | |
| 45 | ## Distributed Locking with Redis |
| 46 | |
| 47 | ```typescript |
| 48 | import Redis from 'ioredis' |
| 49 | |
| 50 | const redis = new Redis(process.env.REDIS_URL!) |
| 51 | |
| 52 | // Simple SETNX + TTL lock |
| 53 | async function acquireLock(key: string, ttlMs: number): Promise<string | null> { |
| 54 | const token = crypto.randomUUID() |
| 55 | // SET key token NX PX ttlMs — atomic, returns OK or null |
| 56 | const result = await redis.set(`lock:${key}`, token, 'NX', 'PX', ttlMs) |
| 57 | return result === 'OK' ? token : null |
| 58 | } |
| 59 | |
| 60 | async function releaseLock(key: string, token: string): Promise<void> { |
| 61 | // Lua script: only delete if we own the lock (atomic compare-and-delete) |
| 62 | const script = ` |
| 63 | if redis.call("GET", KEYS[1]) == ARGV[1] then |
| 64 | return redis.call("DEL", KEYS[1]) |
| 65 | else |
| 66 | return 0 |
| 67 | end |
| 68 | ` |
| 69 | await redis.eval(script, 1, `lock:${key}`, token) |
| 70 | } |
| 71 | |
| 72 | // Usage |
| 73 | async function processPayment(paymentId: string) { |
| 74 | const token = await acquireLock(paymentId, 30_000) // 30s TTL |
| 75 | if (!token) throw new Error('Payment already being processed') |
| 76 | |
| 77 | try { |
| 78 | await doPaymentWork(paymentId) |
| 79 | } finally { |
| 80 | await releaseLock(paymentId, token) |
| 81 | } |
| 82 | } |
| 83 | ``` |
| 84 | |
| 85 | ### Redlock Algorithm (multi-node) |
| 86 | |
| 87 | ```typescript |
| 88 | import Redlock from 'redlock' |
| 89 | import Redis from 'ioredis' |
| 90 | |
| 91 | // Connect to 3+ independent Redis nodes for Redlock quorum |
| 92 | const clients = [ |
| 93 | new Redis('redis://redis1:6379'), |
| 94 | new Redis('redis://redis2:6379'), |
| 95 | new Redis('redis://redis3:6379'), |
| 96 | ] |
| 97 | |
| 98 | const redlock = new Redlock(clients, { |
| 99 | retryCount: 3, |
| 100 | retryDelay: 200, |
| 101 | retryJitter: 100, |
| 102 | }) |
| 103 | |
| 104 | async function criticalSection(resourceId: string) { |
| 105 | await redlock.using([`resource:${resourceId}`], 10_000, async (signal) => { |
| 106 | if (signal.aborted) throw signal.error |
| 107 | |
| 108 | await performAtomicOperation(resourceId) |
| 109 | |
| 110 | if (signal.aborted) throw signal.error // check after long operations |
| 111 | }) |
| 112 | } |
| 113 | ``` |
| 114 | |
| 115 | ## PostgreSQL Advisory Locks |
| 116 | |
| 117 | ```typescript |
| 118 | import { Pool } from 'pg' |
| 119 | const pool = new Pool() |
| 120 | |
| 121 | // Session-level advisory lock (held until released or connection closes) |
| 122 | async function withAdvisoryLock<T>(lockId: number, fn: () => Promise<T>): Promise<T> { |
| 123 | const client = await pool.connect() |
| 124 | try { |
| 125 | await client.query('SELECT pg_advisory_lock($1)', [lockId]) |
| 126 | return await fn() |
| 127 | } finally { |
| 128 | await client.query('SELECT pg_advisory_unlock($1)', [lockId]) |
| 129 | client.release() |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | // Non-blocking try variant — returns false if lock is already held |
| 134 | async function tryAdvisoryLock(lockId: number): Promise<boolean> { |
| 135 | const client = await pool.connect() |
| 136 | try { |
| 137 | const { rows } = await client.query('SELECT pg_try_advisory_lock($1) AS acquired', [lockId]) |
| 138 | return rows[0].acquired |
| 139 | } finally { |
| 140 | client.release() |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | // Usage |
| 145 | const PAYMENT_PROCESSOR_LOCK = 12345 // stable integer per operation type |
| 146 | await withAdvisoryLock(PAYMENT_PROCESSOR_LOCK, async () => { |
| 147 | await processQueue() |
| 148 | }) |
| 149 | ``` |
| 150 | |
| 151 | ## Idempotency Key Implementation |
| 152 | |
| 153 | ```typescript |
| 154 | // Middleware: extract idempotency key from header and dedup in DB |
| 155 | import { Request, Response, NextFunction } from 'express' |
| 156 | import { db } from './db' |
| 157 | |
| 158 | export async function idempotencyMiddleware(req: Request, res: Response, next: NextFunction) { |
| 159 | const idempotencyKey = req.headers['idempotency-key'] as string | undefined |
| 160 | |
| 161 | if (!idempotencyKey || req.method === 'GET') return next() |
| 162 | |
| 163 | // Look up existing result |
| 164 | const existing = await db.idempotencyKeys.findUnique({ |
| 165 | where: { key: idempotencyKey }, |
| 166 | }) |
| 167 | |
| 168 | if (existing) { |
| 169 | // Return cached response — same status and body |
| 170 | return res.status(existing.statusCode).json(existing.responseBody) |
| 171 | } |
| 172 | |
| 173 | // Capture response to s |