Loading...
Loading...
Review distributed systems patterns, concurrency, and resilience. Analyzes retry policies, idempotency, timeouts, circuit breakers, and race conditions. Use when reviewing async code, workers, queues, or distributed transactions.
npx skill4agent add xinbenlv/codereview-skills codereview-concurrency// 🚨 No retry
const result = await callExternalService()
// ✅ With retry
const result = await retry(
() => callExternalService(),
{ maxAttempts: 3, backoff: 'exponential' }
)// 🚨 Immediate retry storm
while (!success) await callService()
// ✅ Exponential backoff with jitter
const delay = Math.min(baseDelay * 2 ** attempt + jitter, maxDelay)// ✅ Random jitter
const jitter = Math.random() * 1000
await sleep(baseDelay + jitter)// 🚨 Retrying non-retryable error
catch (e) { retry() } // retries 400 Bad Request
// ✅ Check error type
if (isRetryable(e)) retry() // only 429, 503, network errors| Semantic | Use Case | Implementation |
|---|---|---|
| At-most-once | Logging, metrics | Fire and forget |
| At-least-once | Most operations | Retry + idempotency |
| Exactly-once | Payments | Dedup + transactions |
// ✅ Idempotency key prevents double processing
async function processPayment(payment, idempotencyKey) {
if (await alreadyProcessed(idempotencyKey)) {
return getExistingResult(idempotencyKey)
}
// ... process
}// 🚨 Not idempotent
async function handleEvent(event) {
await incrementCounter() // multiple calls = multiple increments
}
// ✅ Idempotent
async function handleEvent(event) {
await setCounter(event.value) // same result regardless of calls
}// 🚨 No timeout - can hang forever
await fetch(url)
// ✅ Timeout configured
await fetch(url, { timeout: 5000 })// ✅ Context with deadline
async function process(ctx) {
await serviceA.call(ctx) // inherits deadline
await serviceB.call(ctx) // inherits remaining deadline
}// ✅ Circuit breaker pattern
const breaker = new CircuitBreaker(callService, {
failureThreshold: 5,
resetTimeout: 30000
})
await breaker.call()// ✅ Graceful degradation
try { return await breaker.call() }
catch { return cachedValue || defaultValue }// 🚨 Partial failure leaves inconsistent state
await chargeCard(amount)
await createOrder() // if this fails, card charged but no order
// ✅ Saga pattern
try {
const chargeId = await chargeCard(amount)
await createOrder()
} catch {
await refundCharge(chargeId) // compensating action
}// ✅ Outbox pattern
await db.transaction(async tx => {
await createOrder(tx)
await insertOutboxEvent(tx, orderCreatedEvent)
})
// Separate process publishes events from outbox// 🚨 Deadlock potential
// Thread A: lock(resource1), lock(resource2)
// Thread B: lock(resource2), lock(resource1)
// ✅ Consistent order
// All threads: lock(resource1), lock(resource2)// ✅ Lock with TTL
const lock = await redlock.acquire('resource', 30000)
try { await process() }
finally { await lock.release() }// 🚨 Race condition
if (await getBalance() >= amount) {
await withdraw(amount) // balance may have changed
}
// ✅ Atomic operation
await withdrawIfSufficient(amount) // atomic check-and-update// ✅ Optimistic locking
const updated = await db.update(
{ id, version }, // condition includes version
{ ...changes, version: version + 1 }
)
if (!updated) throw new ConcurrentModificationError()## Concurrency Review Findings
### Critical Issues 🔴
| Issue | Location | Impact | Fix |
|-------|----------|--------|-----|
| No retry logic | `PaymentService.ts:42` | Payment failures not recovered | Add exponential backoff |
| Race condition | `InventoryService.ts:15` | Overselling possible | Use optimistic locking |
### Resilience Gaps 🟡
| Gap | Component | Recommendation |
|-----|-----------|----------------|
| Missing circuit breaker | External API calls | Add circuit breaker with fallback |
| No timeout | `fetchUserData` | Add 5s timeout |
### Recommendations 💡
- Add jitter to retry delays to prevent thundering herd
- Consider saga pattern for multi-step order process
- Add idempotency keys to payment processing□ Retry Policy
□ Retries implemented?
□ Exponential backoff?
□ Jitter added?
□ Only retryable errors retried?
□ Delivery Semantics
□ Semantics clear?
□ Dedup keys present?
□ Handlers idempotent?
□ Timeouts
□ All external calls have timeout?
□ Timeouts propagated?
□ Values reasonable?
□ Circuit Breakers
□ Present for dependencies?
□ Fallback defined?
□ Health check exists?
□ Partial Failure
□ Compensating actions exist?
□ Safe rollback possible?
□ Outbox pattern for events?
□ Locking
□ Consistent lock order?
□ Locks expire?
□ Leader election correct?
□ Race Conditions
□ Check-then-act protected?
□ Concurrent mods handled?async function retryWithBackoff(fn, maxAttempts = 3) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn()
} catch (e) {
if (!isRetryable(e) || attempt === maxAttempts - 1) throw e
const delay = Math.min(1000 * 2 ** attempt + Math.random() * 1000, 30000)
await sleep(delay)
}
}
}async function processWithIdempotency(key, fn) {
const existing = await cache.get(key)
if (existing) return existing
const result = await fn()
await cache.set(key, result, { ttl: 86400 })
return result
}