KarmSakha
Help

Interview & Soft Skills

System Design Interview Guide: Reservations, Concurrency and Retries

Reviewed: 9 October 2026.

A system design interview asks you to reason about a service: what it promises, where it stores state, how requests interact and what happens when dependencies fail. A list of databases, queues and caches is incomplete without an explanation of the behaviour those components are intended to protect.

This guide uses an original fictional reservation service. It is an interview practice design, not deployed software or a verified architecture. The example is deliberately small so that concurrency, retries and failure handling can be examined precisely.

Clarify the service before drawing components

Suppose a centre offers bookable demonstration sessions. Each session has a fixed capacity. A visitor may create a reservation, read its status and cancel it. Payments, timed holds, waiting lists and automatic expiry are outside this first exercise.

Ask whether a visitor may reserve multiple places, whether cancellation is allowed after a session starts and how access is checked. For this fictional version, each reservation consumes one place, cancellation is allowed before the session starts and every operation checks the authenticated visitor's authority. Capacity is fixed for the session. Do not quietly assume these rules apply to an actual employer's product.

The principal invariant is that active reservations for a session must never exceed capacity. Another is that a successful cancellation releases exactly one place once. Define those promises before suggesting a fast read path or a second region.

State the operations and responses

OperationContract
CreateTakes visitor identity, session ID and request token; returns a confirmed reservation ID, full-session result or explicit failure
ReadTakes reservation ID and authorised visitor identity; returns current stored status
CancelTakes reservation ID, visitor identity and cancellation token; returns cancelled, already cancelled or explicit failure

A request timeout means the caller does not know the outcome. It is not proof that creation failed. A full-session response differs from an infrastructure error because the client may handle them differently.

Use named states such as ACTIVE and CANCELLED. In this model, a successful create returns only after the database transaction commits. Do not promise confirmation before committing and then treat a rollback as if the reservation still exists.

Sketch a minimal storage model

One session row stores its ID, capacity and occupied count. Each reservation row stores a unique reservation ID, session ID, visitor ID and status. A request-record table associates a non-null request token with the authenticated visitor, operation, validated payload and committed result.

For this proposed design, a composite uniqueness rule on visitor, operation and token prevents two stored request records with that combination. A primary key identifies each reservation. These constraints do not independently enforce the occupied-count invariant across several rows. PostgreSQL's constraints documentation describes unique combinations and primary keys; use explicit non-null tokens rather than assuming ordinary nullable uniqueness has the same semantics.

The occupied count is a chosen implementation detail requiring consistent updates. Every writer that creates or cancels a reservation must use the guarded path. A manual insert bypassing that path could invalidate the relationship between count and active reservation rows.

Trace the unsafe version with capacity one

Session S has capacity 1 and occupied count 0. Two different authorised visitors submit different create requests.

  1. Request A reads occupied count 0 and decides a place is available.
  2. Request B also reads occupied count 0 before A's change commits.
  3. A inserts an active reservation and writes occupied count 1.
  4. B inserts another active reservation and also writes occupied count 1 from its earlier calculation.

There are now two active reservation rows for capacity one, even though the counter says one. This is a possible failure trace for the specified unprotected read-then-write algorithm, not a database experiment performed for this article.

Wrapping a vague sequence in “a transaction” does not explain why this race is prevented. PostgreSQL's isolation documentation distinguishes query snapshots at Read Committed from Serializable behaviour, which can require whole-transaction retries. State the actual locking or update rule used by your proposal.

Propose one guarded create path

For the exercise, choose a single authoritative PostgreSQL database and require all relevant writers to lock the same session row before changing its reservations or counter. Keep the lock until the transaction ends. Use a consistent lock order and a transaction-scoped uniqueness mechanism to arbitrate concurrent use of the same request token. Any rejected conflicting operation must roll back its tentative effects.

Inside the transaction, resolve whether the token already has a committed result for the same authenticated visitor, operation and payload. If so, return that result without creating another reservation. Reusing the token with a different session or other material payload is an error in this proposed contract.

For a new request, acquire the session-row lock and inspect the current occupied count. If it equals capacity, store the full-session result under the request contract or return it according to the explicitly chosen retry policy. If a place is available, insert one ACTIVE reservation, increase the count by one and store the successful token result atomically. Commit before reporting confirmation.

With the same two different create requests, A obtains the session lock first and commits one active reservation with count one. B then obtains the lock and sees the updated count. B returns full without inserting an active reservation. Capacity remains one with one active reservation. This reasoning depends on every competing create and cancel following the same lock discipline; it is not a complete proof for arbitrary additional writers.

A conditional counter update can be another design, but explain its condition, affected-row check, reservation insertion and rollback scope. Do not casually combine two alternatives and assume their failure behaviour is identical.

Handle cancellation and cancellation retries

An authorised cancellation locks the relevant session row and reservation through the prescribed consistent order, then checks the current reservation status. ACTIVE changes to CANCELLED and the occupied count decreases by one in the same transaction. An already CANCELLED reservation leaves the count unchanged.

Starting from one active reservation and occupied count one, the first successful cancellation produces zero active reservations and count zero. A second cancellation of that same reservation still leaves count zero. It must not subtract another place and produce a negative count.

Define whether a repeated cancellation token returns the stored first result. If the create token is later retried after cancellation, this exercise's contract returns the original create result and directs the caller to Read for current status; it does not reactivate the reservation. Explain that historical operation outcome and current state can differ.

Keep external notifications outside the database promise

If the database commits but an email service times out, the reservation can exist even though delivery is unknown. Sending an email inside application code does not make the external provider part of the database transaction.

An optional design can store a notification job atomically with the reservation and let a worker attempt delivery later. Describe retry and duplicate-delivery handling explicitly. Recording a request token is not a guarantee that an external message will be delivered exactly once.

AWS's engineering discussion of idempotent APIs explains why retry identity and intended request meaning need a coherent contract. In this exercise, the token applies within a stated retention period and scope. Once a token record expires, the client cannot assume the old deduplication promise still holds.

Discuss reads, scale and availability

A session-list cache can display a stale available-place count. Label that read as advisory and perform creation against authoritative state. A cached “one place remaining” message cannot authorise a reservation by itself.

For a small service, a single authoritative database may be a reasonable first proposal. It creates a dependency: if safe creation cannot reach the database, this exercise rejects or defers the request rather than claiming confirmation from a stale cache. Recovery, backups and failover need separate design and testing.

Ask about the actual load before inventing capacity numbers. In a fictional observation window with 12 HTTP create attempts, four of which repeat previous tokens, there are eight distinct tokens. That is not necessarily eight unique users or eight accepted reservations. Track accepted creations, full responses, retries and failures separately.

For a busy individual session, its row lock can become a bottleneck. Measure transaction latency and contention before choosing partitioning or a different capacity-allocation approach. Multiple database shards introduce further questions about where the session's authoritative invariant is enforced.

Explain what you would verify

Tests should exercise concurrent requests for the last place, repeated identical tokens, changed payloads under the same token, cancellation retries, rollback after a failed insert and client timeouts after commit. Inspect both reservation rows and counters, rather than verifying only the displayed count.

Monitor transaction failures, lock waits, accepted reservations, full responses and notification backlog. An operational alert needs a defined threshold and response owner; naming a dashboard is insufficient.

Practise defending the invariant and the failure boundaries aloud. For explaining your own project contribution, use teamwork interview questions. For related technical practice, continue with machine learning interview questions.

Frequently asked questions

Should I start with microservices?

Start with the required behaviour and constraints. Propose separate services when their responsibilities or operating needs justify the complexity.

Does Serializable remove all application work?

No. Correct transaction logic and whole-transaction retry handling still matter. External side effects require their own contracts.

Has this architecture been tested?

No deployed system or concurrency run is claimed here. The traces are original reasoning exercises under stated assumptions; production acceptance would require implementation, testing and operational review.

Related guides

Ask KarmSakha AI