A payment API receives duplicate POST /charge requests because the client retries on a 504 gateway timeout. Charges must never be double-applied. The request carries a client-generated `Idempotency-Key`. Which server implementation actually guarantees exactly-once effect under concurrent retries?
- A. On each request, SELECT the key; if absent, process the charge and then INSERT the key with the result
- B. Wrap the charge in a transaction and rely on the DB's default READ COMMITTED isolation to serialize the two requests
- C. INSERT the idempotency key with a UNIQUE constraint FIRST in the same transaction as the charge; on duplicate-key violation, return the stored prior response ✓
- D. Hash the request body and skip processing if an identical body was seen in the last 60 seconds
Correct answer: C. Inserting the unique key first makes the database's uniqueness constraint the concurrency gate, so exactly one transaction commits the charge and retries hit the duplicate-key path, whereas check-then-act (option 0) has a race window between SELECT and INSERT.
A hot cache key holding a homepage product list expires. Within milliseconds thousands of concurrent requests miss and all hit the database, which collapses. Besides the immediate outage, which mitigation prevents recurrence WITHOUT weakening correctness the way plain 'never expire' would?
- A. Increase the connection pool size so the database can absorb the simultaneous rebuild queries
- B. Use a per-key distributed lock/single-flight so only one request rebuilds the value while others briefly wait or serve the stale value ✓
- C. Switch the cache eviction policy from LRU to LFU so hot keys are evicted less often
- D. Shorten the TTL so the key refreshes more frequently and is never deeply stale
Correct answer: B. Single-flight (mutex/lease per key) collapses the stampede to a single database rebuild while other callers wait or serve stale, directly eliminating the thundering herd; a bigger pool just lets more queries pile on and a shorter TTL makes stampedes more frequent.
Two transactions run concurrently under a database using MVCC snapshot isolation (e.g. Postgres REPEATABLE READ). Both read the current seat count (0 booked of 1), both see a seat available, and both INSERT a booking. What actually happens, and why is this the classic gap in snapshot isolation?
- A. One INSERT blocks until the other commits, then sees the updated count and aborts itself
- B. Both commit successfully; snapshot isolation does not detect this write-skew because the two writes touch different rows than the ones read ✓
- C. The second commit fails automatically with a serialization error because MVCC guarantees serializability
- D. Both are rolled back because a phantom row was inserted by a concurrent transaction
Correct answer: B. This is write skew: each transaction reads a snapshot and writes rows the other did not read, so snapshot isolation (short of true SERIALIZABLE / SSI) allows both to commit, violating the invariant, which is why you need SELECT ... FOR UPDATE, a serializable level, or a materialized conflict row.
A Kafka consumer group processes orders. Each poll returns a batch of 500; the consumer commits offsets AFTER the whole batch is processed. A consumer instance crashes after processing 300 of 500 records but before committing. Assuming default at-least-once configuration, what happens on rebalance?
- A. The 300 processed records are skipped and processing resumes at record 301, giving exactly-once
- B. All 500 records are redelivered to another consumer, so the first 300 are processed twice ✓
- C. The uncommitted 300 records are lost permanently because their offsets were fetched
- D. Kafka replays only the 200 unprocessed records because it tracks per-record acks like a traditional queue
Correct answer: B. Kafka commits offsets per partition, not per record, so with no commit for the batch the new owner re-reads from the last committed offset and reprocesses all 500 — hence at-least-once delivery requires idempotent processing to tolerate the 300 duplicates.
Two transactions deadlock repeatedly in production: T1 updates row A then row B; T2 updates row B then row A. The database detects the cycle and aborts one victim, but throughput still suffers from frequent retries. Which fix eliminates the deadlock cycle itself rather than just handling its symptom?
- A. Raise the deadlock detection timeout so the database waits longer before choosing a victim
- B. Enforce a consistent global lock-acquisition order (always lock A before B) in all code paths ✓
- C. Lower the isolation level to READ UNCOMMITTED so the updates no longer take row locks
- D. Wrap each transaction in an application-level retry loop with exponential backoff
Correct answer: B. A deadlock requires a circular wait; acquiring resources in a consistent global order makes a cycle impossible, whereas retries and longer timeouts only cope with deadlocks after they form and READ UNCOMMITTED still takes write locks.
A write-heavy 'user activity feed' table is sharded by `shard = hash(user_id) % N`. A handful of celebrity users generate 40% of all writes, saturating a few shards. You must rebalance without downtime and stop the hot partitions. What is the soundest change?
- A. Switch the shard key to `hash(timestamp)` so writes spread evenly across shards over time
- B. Increase N and rehash everything at once during a maintenance window to rebalance load
- C. Use a composite/compound shard key such as hash(user_id, bucket) that splits a hot user's writes across several sub-partitions ✓
- D. Add read replicas to the hot shards so the write load is distributed across the replicas
Correct answer: C. Salting the key with a per-user bucket spreads a single hot user's writes across multiple partitions, curing the hotspot; timestamp keys create a moving write hotspot, and replicas do not absorb write load.
In a Saga using choreography (services react to each other's events with no central coordinator), the flow is Order → Payment → Inventory → Shipping. Inventory fails and emits a compensation event. Six months later you must add a Fraud-check step between Payment and Inventory. Which drawback of choreography does this expose most sharply?
- A. Choreography cannot implement compensating transactions, so partial rollback is impossible
- B. The distributed control flow is implicit across many services, making it hard to reason about, add steps to, and debug the end-to-end sequence ✓
- C. Choreography forces synchronous calls, so adding a step increases end-to-end latency linearly
- D. Events in choreography cannot carry a correlation ID, so tracing the saga is impossible
Correct answer: B. Choreography scatters the workflow logic across event handlers with no single place that describes the sequence, so understanding and modifying the flow is hard — the readability/maintainability tradeoff orchestration addresses by centralizing control.
An HTTP/1.1 backend service opens a new TCP connection per outbound call to a downstream dependency under high load and you observe rising latency and TIME_WAIT socket exhaustion. Migrating the client to HTTP/2 with a connection pool helps most because of which specific mechanism?
- A. HTTP/2 compresses response bodies, reducing bytes transferred per call
- B. HTTP/2 multiplexes many concurrent requests over a single long-lived TCP connection, avoiding per-request connect and TIME_WAIT churn ✓
- C. HTTP/2 uses UDP instead of TCP, eliminating the TCP handshake entirely
- D. HTTP/2 removes the need for TLS, cutting the handshake round-trips
Correct answer: B. HTTP/2 stream multiplexing lets many concurrent requests share one persistent connection, removing per-request TCP/TLS handshakes and the TIME_WAIT accumulation from connection churn; it does not use UDP (that's HTTP/3) nor drop TLS.
A service protects a slow downstream with a circuit breaker plus retries: 3 retries with exponential backoff on every failed call. During a downstream brownout, the breaker is configured to open at 50% error rate, but the outage still cascades and amplifies. What is the most likely root cause of the amplification?
- A. The retries multiply load onto the already-struggling downstream (a retry storm) faster than the breaker trips, so aggregate traffic spikes 4x ✓
- B. The exponential backoff jitter makes retries too slow, so the breaker never sees enough failures to open
- C. Circuit breakers are incompatible with retries and must never be combined
- D. The 50% threshold is too low, so the breaker opens prematurely and blocks healthy traffic
Correct answer: A. Unbounded retries stacked on a failing dependency multiply request volume (a retry storm) and amplify the overload, which is why retries need a shared retry budget, jitter, and to be suppressed while the breaker is open.
You need optimistic concurrency for a `wallets` table to prevent lost updates on balance edits. Which SQL pattern correctly detects a concurrent modification and lets the application retry, rather than silently overwriting?
- A. UPDATE wallets SET balance = balance - 10 WHERE id = 1
- B. UPDATE wallets SET balance = :new, version = version + 1 WHERE id = 1 AND version = :read_version, then check affected-rows = 1 ✓
- C. SELECT balance FROM wallets WHERE id = 1 FOR UPDATE, then UPDATE with the new balance
- D. UPDATE wallets SET balance = :new WHERE id = 1 AND balance = balance
Correct answer: B. The version-guarded UPDATE affects zero rows if another writer bumped the version since your read, signaling a conflict to retry — that compare-and-set is the essence of optimistic locking, while option 2 is pessimistic and option 0/3 can silently lose the concurrent update.