System Design · India Scale

Design IRCTC Tatkal Booking: Handling a 10 AM Traffic Spike

Lakhs of people try to book a few hundred berths in the same minute. This is how you design for that: a virtual waiting room, segment-aware berth allocation, fair admission, bot defense and payment holds, plus an honest look at why Tatkal feels broken.

✍️ Pranjal Jain, Ex-Microsoft · IIT Kanpur 📅 10 October 2026 ⏱ 20 min read
A train at a platform under a large clock at 10, with an orderly queue flowing through a gateway
TL;DR
  • Tatkal is a contention and fairness problem, not just a scaling problem: a few hundred berths per popular train, demand many times larger, and nearly all of it in the first minute.
  • Put a virtual waiting room at the edge (CDN-served, signed queue tickets, randomised order for everyone who arrived before opening) and admit users at the rate the booking engine can sustain.
  • Model berths over route legs (bitmasks), keep inventory per (train, date, class, quota) in a single-writer partition, and allocate in memory with a durable log.
  • Hold berths for a short payment window; treat payment as an asynchronous state machine with automatic refunds and reconciliation.
  • Layer bot defenses (identity verification, rate limits, behavioural signals, per-account caps, agent restrictions); bots are the main fairness threat.

Every Indian engineer knows the Tatkal ritual: log in at 9:55, keep the passenger details ready, refresh at 9:59:58, click "Book Now" at 10:00:00, watch the spinner, and see "Waiting List" or "Not Available" by 10:02. "Design IRCTC Tatkal" is a brilliant interview question precisely because everyone has felt the pain, and because the honest answer contains an uncomfortable truth: no architecture can give 2 lakh people the 400 berths that exist. What good design can do is keep the system up, allocate correctly, and make the outcome fair rather than a contest of bots and refresh speed.

Note on facts: IRCTC's front end and CRIS's Passenger Reservation System (PRS) are real systems whose internals are mostly not public. This post designs a Tatkal system from first principles and is explicit when it refers to publicly announced rules. Numbers are interview assumptions.

Anatomy of a Tatkal Morning

Tatkal opens one day before the journey date: 10:00 AM for AC classes and 11:00 AM for non-AC classes (as per IRCTC's published rules; verify the current rules). Here is what the load looks like around opening, which shapes everything else:

TimeUser behaviourSystem load
09:45–09:58Users log in, open the booking form, pre-fill passengersLogin and session creation surge; static assets
09:58–09:59:59Refreshing availability, waiting for the clockRead storm on availability; many idle open sessions
10:00:00–10:00:30Everyone submits at oncePeak write burst on a few hundred popular train-classes
10:00:30–10:05Payment pages, retries, "money deducted?" panicPayment gateway load, status checks, retries
10:05–10:30Waitlist bookings, cancellations, alternate trainsTapering load

The demand is also hugely skewed: a few hundred trains (Delhi–Patna, Mumbai–Gorakhpur, Bengaluru–Guwahati routes around festivals) attract most of the contention, while most trains are fine.

Requirements

Functional

  • Users search trains between stations for a date; see availability per class and quota (General, Tatkal, Premium Tatkal, Ladies, Senior Citizen, etc.).
  • Book up to the allowed number of passengers per ticket with berth preferences.
  • Pay via UPI, cards, net banking or wallet; get a PNR with confirmed (CNF), RAC or waitlist (WL) status.
  • Tatkal opens at fixed times; Tatkal-specific rules (fare, cancellation, ID requirements) apply.
  • Cancellations release berths; waitlisted tickets get upgraded through charting rules.

Non-functional

  • Correctness above all: never sell a berth-segment twice; never confirm a ticket without payment; never keep money without a ticket.
  • Survive the spike: the site must stay up at 10:00; degraded but orderly beats crashed.
  • Fairness: genuine users should have a fair chance relative to bots, agents and people with faster connections.
  • Latency: once admitted, booking should complete in seconds, not minutes.
  • Auditability for a public-sector system: who got what, when, and why.

Back-of-the-Envelope Estimates

Interview assumptions (not IRCTC's real figures):

  • 2M users attempt Tatkal bookings in the 10:00 window across all trains.
  • Each user generates ~30 requests in the window (login, search, refreshes, form submit, payment redirects, status polls) → 60M requests. If half arrive in the first 2 minutes: 30M / 120 s ≈ 250k requests/s, with second-level peaks well above that.
  • Tatkal inventory: assume ~150,000 Tatkal berths across all trains for a day, of which the 500 hottest train-classes have ~200 each = 100,000 berths with perhaps 10x oversubscription.
  • Bookings: even if every berth sells in 5 minutes, that is 150,000 / 300 s = 500 bookings/s. The write throughput needed is modest. The problem is that 250k requests/s are fighting over 500 writes/s, concentrated on a few hundred inventory partitions.
  • Per hot partition: a popular AC 3-tier Tatkal quota of ~100 berths receives tens of thousands of attempts in a minute. Any design that takes a database row lock per attempt on that partition collapses.
~250k/s
Peak requests
~500/s
Successful bookings
10x+
Oversubscription on hot trains
~60 s
Window that matters

Why Tatkal Feels Broken (an Honest Analysis)

Interviewers often ask "why does IRCTC struggle?" A thoughtful answer avoids mocking the engineers and identifies structural causes:

  1. Scarcity is the root cause. When demand is 10x supply, 90% of users fail no matter what. Users experience failure as "the site is broken" even when it worked perfectly.
  2. Synchronised demand. A fixed opening time turns a day's demand into a single-minute spike. Systems sized for average load (and even 10x peaks) are overwhelmed by 100x synchronous bursts.
  3. Contention on tiny inventory. Read-heavy spikes are easy to cache. Writes to the same 100-berth pool cannot be spread across machines; they serialise somewhere.
  4. Legacy coupling. Reservation inventory has historically lived in PRS, which also serves station counters across the country. The web front end can scale horizontally; the system of record cannot be replaced overnight.
  5. Multi-hop payments. Each booking redirects through banks, UPI apps and gateways with their own latency and failure rates. A slow bank at 10:01 holds berths in limbo and generates "money deducted, ticket not booked" cases.
  6. Bots and touts. Automated tools fill forms and submit faster than any human, so genuine users lose to scripts. Indian Railways has repeatedly tightened rules, including Aadhaar-based authentication for Tatkal booking announced in 2025 and restrictions on authorised agents booking in the first 30 minutes of the Tatkal window.

The design goal follows: shape demand (waiting room), make contention cheap (in-memory single-writer allocation), decouple slow dependencies (holds and async payment), and enforce fairness (bot defense and randomised admission).

High-Level Architecture

Users ──► CDN / edge: static app, waiting-room page, queue ticket check │ (only requests with a valid admission pass pass through) ▼ WAF + bot management ──► API gateway (rate limits per user/IP) │ ┌──────────┼──────────────┬───────────────────┬─────────────────┐ ▼ ▼ ▼ ▼ ▼ Auth / Search & Booking service Payment Waiting-room identity availability (orchestrator, orchestrator controller (Aadhaar (cached state machine) (gateways, UPI, (admission verified) snapshots) │ wallet, recon) rate) ▼ Inventory service: one partition per (train, date, class, quota) → single writer, in-memory berth bitmasks + durable log │ ▼ System of record (PRS / reservation DB), PNR generation, charting, counters

The key boundary: everything above the inventory service scales horizontally and is cheap to add capacity to. The inventory service is the scarce, serialised resource, so the architecture's job is to send it only well-formed, authenticated, deduplicated, rate-shaped booking attempts.

Deep Dive 1: The Virtual Waiting Room

A waiting room is the most effective tool for synchronised spikes. Ticketing companies, sneaker drops and government portals use them. It converts an uncontrolled stampede into an orderly queue that drains at a known rate.

How it works

  1. Activation: from 09:55, requests to booking pages for Tatkal train-classes are redirected to a lightweight waiting page served from the CDN. It is static HTML plus a small script, so it costs almost nothing to serve to millions.
  2. Queue ticket: each logged-in user gets one signed ticket (user id, position, issue time). One per user, not per tab: duplicate tabs get the same ticket.
  3. Randomised start: everyone who joins before 10:00:00 is assigned a random position among early arrivals at 10:00 (a lottery), instead of first-come-first-served by milliseconds. Arrivals after 10:00 queue behind in FIFO order. This removes the incentive to hammer refresh and neutralises sub-second bot advantages.
  4. Admission rate: a controller advances an "admitted up to position N" counter based on the booking engine's real-time health: e.g. admit 2,000 users per 10 seconds, slowing if latency rises.
  5. Polling: the waiting page polls a CDN-cacheable endpoint for the current admitted position every few seconds (with jitter) and shows "You are number 4,200; about 3 minutes".
  6. Admission pass: once admitted, the user exchanges the ticket for a short-lived pass (say 10 minutes) required by booking APIs. Expired passes go back to the queue.
Python: signed queue tickets and admission passes
import base64
import hashlib
import hmac
import json
import secrets
import time

KEY = secrets.token_bytes(32)          # shared by waiting-room edge and booking service

def _sign(payload: dict) -> str:
    body = base64.urlsafe_b64encode(json.dumps(payload, separators=(",", ":")).encode())
    sig = hmac.new(KEY, body, hashlib.sha256).hexdigest()
    return body.decode() + "." + sig

def _verify(token: str) -> dict:
    body, sig = token.rsplit(".", 1)
    if not hmac.compare_digest(sig, hmac.new(KEY, body.encode(), hashlib.sha256).hexdigest()):
        raise PermissionError("tampered token")
    return json.loads(base64.urlsafe_b64decode(body))

def queue_ticket(user_id: str, position: int) -> str:
    return _sign({"u": user_id, "pos": position, "iat": int(time.time())})

def admission_pass(ticket: str, admitted_upto: int, ttl_s: int = 600) -> str:
    t = _verify(ticket)
    if t["pos"] > admitted_upto:
        raise PermissionError(f"still waiting: {t['pos'] - admitted_upto} ahead of you")
    return _sign({"u": t["u"], "adm": True, "exp": int(time.time()) + ttl_s})

def check_pass(token: str, user_id: str) -> bool:
    p = _verify(token)
    return p.get("adm") is True and p["u"] == user_id and p["exp"] > time.time()

Because tickets and passes are HMAC-signed, the edge and booking servers verify them without a database lookup, and users cannot forge a better position. Binding the pass to the user id stops pass sharing. The admitted-position counter is one tiny value, easily served from cache.

Why not just autoscale?

Autoscaling helps stateless tiers, but the inventory for one hot train-class is a single serial resource; extra web servers only increase contention on it. And autoscaling reacts in minutes while the spike lasts seconds. Pre-scaling stateless tiers and shaping demand is the correct combination.

Search and Availability Without Melting the Inventory

The pre-10:00 refresh storm is reads. Never serve them from the inventory partitions:

  • The inventory service publishes availability snapshots (counts per train/date/class/quota) every second or two into a cache (Redis or an in-memory replicated store); see distributed cache design.
  • Search endpoints read only snapshots. Displayed availability can be a second stale; it is advisory. The authoritative check happens at allocation.
  • Train schedules, routes, fares and station lists are static and cached at the CDN.
  • Before opening, Tatkal availability is a known constant; serve it statically and avoid any backend call.
  • Per-user rate limits on availability refreshes (e.g. one per 2 seconds) stop scripted polling from costing anything; see rate limiter design.

Deep Dive 2: Berth Inventory and Allocation

Berths are sold per segment, not per train

Unlike a cinema seat, a train berth can be sold several times on one run: Delhi→Agra to one passenger and Agra→Bhopal to another. Model the route as legs between consecutive stops; a booking from stop a to stop b occupies legs a through b−1. Each berth stores a bitmask of occupied legs. A request fits a berth if occupied & mask(a, b) == 0.

Python: segment-aware berth allocation
class CoachInventory:
    """Berth occupancy per leg of the route. Leg i = stop i -> stop i+1.
    A booking from stop a to stop b (a < b) occupies legs a..b-1."""

    def __init__(self, berths, num_stops, preferences=None):
        self.occupied = [0] * berths          # bitmask of occupied legs per berth
        self.legs = num_stops - 1
        self.pref = preferences or {}         # berth -> type ("LB", "UB", ...)

    @staticmethod
    def _mask(a, b):
        return ((1 << (b - a)) - 1) << a      # bits a..b-1

    def available(self, a, b):
        m = self._mask(a, b)
        return sum(1 for occ in self.occupied if occ & m == 0)

    def allocate(self, a, b, want=None):
        """Pick a free berth for legs a..b-1. Prefer the requested berth type, then
        the berth whose existing bookings touch our range (less fragmentation)."""
        m = self._mask(a, b)
        best, best_score = None, None
        for i, occ in enumerate(self.occupied):
            if occ & m:
                continue
            touch = (((occ >> b) & 1) if b < self.legs else 1) + \
                    (((occ >> (a - 1)) & 1) if a > 0 else 1)
            score = (want is not None and self.pref.get(i) == want, touch)
            if best_score is None or score > best_score:
                best, best_score = i, score
        if best is not None:
            self.occupied[best] |= m
        return best

# Route: 0 NDLS - 1 AGC - 2 BPL - 3 NGP - 4 SC  (4 legs), coach with 3 berths
c = CoachInventory(berths=3, num_stops=5, preferences={0: "LB", 1: "UB", 2: "LB"})
print(c.allocate(0, 2))          # NDLS->BPL     -> 0
print(c.allocate(2, 4))          # BPL->SC       -> 0 (fits snugly after the first booking)
print(c.allocate(1, 3, "LB"))    # AGC->NGP, LB  -> 2
print(c.available(0, 4))         # end-to-end berths still free -> 1

Dry run: the first booking marks legs 0 and 1 on berth 0 (0b0011). For BPL→SC (legs 2 and 3), berth 0 is free on those legs and its existing booking touches the requested range on the left, so it scores higher than empty berths: berth 0 becomes 0b1111, fully used end to end. That "snug fit" heuristic matters: without it, you would split berth 1 and lose the ability to sell a full-route NDLS→SC berth later, which is the most common request type. Real allocation rules are richer (quota-specific berth ranges, senior citizen and ladies' lower-berth rules, keeping families together, RAC side berths), but they are all scoring rules on top of this check.

C++: the same core check and allocation
#include <cstdint>
#include <iostream>
#include <vector>

// Occupancy of each berth as a bitmask over route legs (max 64 legs).
class CoachInventory {
public:
    CoachInventory(int berths, int numStops) : occ_(berths, 0), legs_(numStops - 1) {}

    int available(int a, int b) const {
        const uint64_t m = mask(a, b);
        int n = 0;
        for (uint64_t o : occ_) n += (o & m) == 0;
        return n;
    }

    // Free berth for legs a..b-1, preferring berths whose bookings touch [a, b).
    int allocate(int a, int b) {
        const uint64_t m = mask(a, b);
        int best = -1, bestTouch = -1;
        for (int i = 0; i < static_cast<int>(occ_.size()); ++i) {
            const uint64_t o = occ_[i];
            if (o & m) continue;
            const int touch = (b < legs_ ? static_cast<int>((o >> b) & 1) : 1) +
                              (a > 0 ? static_cast<int>((o >> (a - 1)) & 1) : 1);
            if (touch > bestTouch) { best = i; bestTouch = touch; }
        }
        if (best < 0) return -1;                    // no berth free on this range
        occ_[best] |= m;
        return best;
    }

private:
    static uint64_t mask(int a, int b) { return ((uint64_t{1} << (b - a)) - 1) << a; }
    std::vector<uint64_t> occ_;
    int legs_;
};

int main() {
    CoachInventory c(3, 5);                 // 3 berths, stops 0..4 (4 legs)
    int x = c.allocate(0, 2);               // 0
    int y = c.allocate(2, 4);               // 0: berth 0 is reused for the second half
    int z = c.allocate(1, 3);               // 1
    std::cout << x << " " << y << " " << z << " " << c.available(0, 4) << '\n';  // 0 0 1 1
}

Complexity: O(B) per allocation for B berths in the quota pool (a few hundred), each check a single AND instruction. Microseconds. The allocator is never the bottleneck; coordination around it is.

Quotas are separate pools

Each train-date-class has inventory split into quotas: General, Tatkal, Premium Tatkal, Ladies, Senior Citizen/Lower Berth, defence, and others, some released or merged at charting time. Model each quota as its own pool with its own berth set and rules. Tatkal requests touch only the Tatkal pool, which is why a "partition per (train, date, class, quota)" key falls out naturally.

Concurrency: single writer per partition

ApproachBehaviour under 10 AM contention
DB row lock per berth (SELECT … FOR UPDATE)Thousands of transactions queue on the same rows; lock waits, deadlocks, timeouts
SKIP LOCKED on berth rowsBetter: concurrent transactions grab different free berths; still DB-bound and hard with segment logic
Optimistic version on the pool rowMassive retry storms; most attempts fail and retry
Single-writer partition (actor) in memory + durable logRequests for one pool are queued and processed sequentially at microseconds each; no locks; easily thousands of allocations per second per pool

The single-writer model is how exchanges and high-contention systems work (compare the matching engine in stock exchange design). Each inventory partition is owned by exactly one node (assigned via a coordination service with leases), holds its bitmasks in memory, appends every allocation and release to a replicated log before acknowledging, and rebuilds state from snapshot plus log on failover. Hot partitions get dedicated nodes before 10:00.

Bounded queues per partition

If a 100-berth pool has 30,000 attempts queued, processing them all just to say "sold out" 29,900 times wastes time. Bound each partition's queue (e.g. 3x remaining berths plus holds), reject the overflow immediately with "currently unavailable, try waitlist/alternatives", and publish "sold out" to the availability cache the moment it happens so new attempts stop at the edge.

Deep Dive 3: Holds, Payment and PNR Generation

Payment is slow and uncertain. Allocating a berth only after payment would let 50 users pay for the last berth. Allocating permanently before payment would lose berths to abandoned payments. The answer is a hold with a timeout, like BookMyShow seat locks.

StateEntered whenExits to
HELDInventory partition allocates berths; hold expiry set (e.g. 5 to 10 minutes)PAYMENT_PENDING, EXPIRED
PAYMENT_PENDINGUser redirected to gateway/UPI with booking id as referenceCONFIRMED, PAYMENT_FAILED, UNKNOWN
UNKNOWNTimeout or missing callbackResolved by status polling and reconciliation
CONFIRMEDPayment verified; PNR issued; berths committedCANCELLED (later)
EXPIRED / PAYMENT_FAILEDHold timeout or explicit failureBerths released to pool; refund if money was captured

Critical rules

  • Hold expiry is enforced by the inventory partition itself (a timer wheel), so released berths go back immediately and appear in the next availability snapshot.
  • Late payment success: if the bank confirms after the hold expired and the berth was resold, the booking cannot be honoured: trigger an automatic refund. Size the hold window to cover typical UPI/bank latency to make this rare.
  • Idempotency: the booking id is the idempotency key with the gateway; duplicate callbacks and user retries cannot create two tickets or two charges.
  • Reconciliation against gateway settlement reports catches "money deducted, ticket not booked" cases and refunds them automatically; see payment system design.
  • Prepaid balance option: a stored-value wallet (IRCTC has offered one) removes the external payment hop from the critical minute; the debit is a local ledger operation inside the booking transaction.
  • PNR generation happens once at confirmation, using a unique, non-guessable id scheme; the ticket is committed to the system of record asynchronously through a durable queue if that system is slower than the allocator.

Deep Dive 4: Bot Defense

Tatkal's real competitor for a genuine user is not another user; it is a script. Defenses must be layered because each one can be bypassed alone.

LayerControlWhat it defeats
IdentityVerified accounts (Aadhaar-based authentication for Tatkal was mandated in 2025), one active session per accountFarms of throwaway accounts
Account limitsMonthly ticket caps per user id; one in-flight Tatkal booking per account; limits on passengers per ticketTouts booking at volume
Agent rulesAuthorised agents blocked during the opening windowProfessional bulk booking
ChallengeCAPTCHA at login and at booking, ideally risk-based (harder for suspicious sessions)Simple scripts
BehaviourForm-fill time, keystroke/mouse cadence, navigation path, time from page load to submitAutofill extensions and headless browsers
Device and networkFingerprinting, app attestation for mobile, IP reputation, data-center IP blocking, per-IP rate limitsBot farms on cloud servers
Post-hocPattern analysis on PNRs (same payment instrument, same passenger names across accounts), cancellation of fraudulent tickets, account bansOrganised resale

Two design principles: make defenses risk-based (low-friction for normal users, heavy for suspicious sessions), and move checks earlier (verify identity and solve challenges before 10:00 while in the waiting room, so the critical minute is not spent on CAPTCHAs).

Fairness Policies (Product Decisions with System Consequences)

  • Randomised admission for early arrivals removes the millisecond race. Everyone who is ready by 10:00 has an equal chance.
  • Pre-submitted intent: a more radical design lets users submit a complete booking request (train, class, passengers, payment mandate) between 09:00 and 09:59, then allocates by lottery at 10:00. This turns a stampede into a batch job: no spike at all, perfectly fair, trivially scalable. The trade-off is user expectation and the need for pre-authorised payment (e.g. UPI mandates).
  • One attempt per user per train-class in the opening window, so persistent retries do not improve odds.
  • Transparent outcomes: showing queue position and "sold out" status immediately reduces anxiety-driven refresh load, which is itself a capacity win.
💡
Mention the lottery design in interviewsProposing "collect requests before opening, allocate by lottery" shows you understand that the best way to handle a spike is sometimes to remove it. Then discuss why a real-time model might still be preferred (policy, expectations), and how the waiting room approximates the same fairness.

Failure Modes and Operational Readiness

FailureMitigation
Login storm at 09:55 overloads authPre-scaled auth, long-lived sessions (log in earlier in the day), JWT validation at the edge
Inventory node dies at 10:00:05Standby replica tails the log; lease-based failover in seconds; waiting room pauses admission during failover
Payment gateway degradedMultiple gateways with health-based routing; longer holds for in-flight payments; wallet path
Downstream system of record slowAllocator confirms from its own durable log; async commit to the record system through a queue
Retry storms from clientsIdempotent booking ids; client backoff with jitter; per-user rate limits
Cache stampede on availabilitySnapshots pushed (not pulled), request coalescing, static responses for sold-out pools

Readiness practices

  • Pre-warm every stateless tier and the CDN at 09:30; do not trust autoscaling for a 60-second spike.
  • Place hot partitions deliberately: the busiest train-classes (predictable from history and festival calendars) get dedicated inventory nodes.
  • Load test with realistic replay of last week's 10:00 traffic shape, including bots and retries.
  • Game days that kill an inventory node at 10:00:10 in staging.
  • Freeze deploys around the window; feature flags to switch on stricter bot rules and slower admission if needed.

Interview Playbook

  1. Clarify and frame (3 min): Tatkal only or general booking? Mention that demand exceeds supply, so fairness is a requirement.
  2. Estimate (4 min): requests/s vs successful bookings/s; per-partition contention.
  3. Architecture (8 min): CDN/waiting room, gateway, services, inventory partitions, system of record.
  4. Waiting room (7 min): tickets, randomisation, admission rate, passes.
  5. Inventory (10 min): segments and bitmasks, quotas, single-writer partitions, bounded queues.
  6. Payment and holds (6 min): state machine, expiry, refunds, reconciliation.
  7. Bots, fairness, failures (7 min).

Common mistakes

  • "Add more servers and a load balancer" as the main answer; it ignores contention on the inventory.
  • Treating a berth like a cinema seat and missing segment-based reuse.
  • Locking berths in the database for every attempt at 10:00.
  • No hold timeout, or holding berths until payment with no expiry.
  • Ignoring bots, which is the biggest real-world fairness issue.
  • Calling the system "broken" without acknowledging that scarcity guarantees most users will not get a berth.

This question pairs naturally with the Flipkart flash sale design, which faces the same "huge demand, tiny inventory, fixed start time" shape with different domain rules. For a broader list of India-flavoured questions, see system design interview questions for India.

Frequently Asked Questions

What time does Tatkal booking open on IRCTC?▾
Tatkal booking opens one day before the journey date (excluding the journey date), at 10:00 AM for AC classes and 11:00 AM for non-AC classes such as Sleeper. Rules have been updated over the years, including Aadhaar authentication requirements announced in 2025, so check the current IRCTC rules before booking.
Why does IRCTC Tatkal booking fail or slow down at 10 AM?▾
Demand for a popular train's Tatkal quota can exceed supply many times over, and almost all of it arrives in the same minute. Every request competes for the same small inventory, legacy reservation systems and payment gateways add bottlenecks, and bots and agents compete with genuine users. Even a perfectly scaled system cannot give confirmed berths to more people than there are berths.
What is a virtual waiting room in system design?▾
A virtual waiting room holds excess users in a lightweight queue, usually served from a CDN or edge, and admits them to the real application at a rate it can handle. Each user gets a signed ticket with their position, and the backend accepts only requests carrying a valid admission pass, which protects the system and makes access orderly.
How does train berth allocation work for passengers travelling different segments?▾
A berth can be sold to multiple passengers if their journeys do not overlap. The route is split into legs between stations, and each berth tracks which legs are occupied, for example as a bitmask. A request from station A to station C is allocatable on a berth only if all legs between A and C are free.
How do ticket booking systems stop bots?▾
They layer defenses: login and identity verification, CAPTCHAs, per-user and per-IP rate limits, device and browser fingerprinting, behavioural signals such as impossibly fast form filling, limits on bookings per account, and restrictions on agents during the opening window. No single control is sufficient.
What happens if money is deducted but the Tatkal ticket is not booked?▾
In a well-designed system the booking is a state machine: if payment succeeds but the ticket cannot be issued, the transaction is marked for automatic refund, and reconciliation with the payment gateway catches cases where callbacks were lost. Refunds typically take a few working days to reach the original payment method.
Is IRCTC Tatkal a good system design interview question?▾
Yes. It combines extreme read and write spikes, contention on a tiny inventory, fairness, bot abuse, payments and legacy integration, all with a context Indian interviewers and candidates understand well. It is closely related to flash sale and BookMyShow-style designs.

Practise India-Scale System Design

Prepflix's system design course works through IRCTC, Flipkart sale and UPI-scale designs with mock interviews that push on contention, fairness and failure handling.

Explore the Prepflix Course →
Pranjal Jain
Pranjal Jain

Ex-Microsoft SDE · IIT Kanpur · Founder of Prepflix. Helps engineers crack startup and product company interviews across India.