MindForge
MindForge
DashboardHelp & Support
Dashboard
MindForge

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Concurrency in Low-Level Design
Course contents

Interview Preparation Bootcamp

Course progress

0%
0 of 177 modules complete
Start Here0/1
  • How to Use This Course
Data Structures & Algorithms0/27
  • Arrays and Hashing
  • Two Pointers
  • Sliding Window
  • Sliding Window: Harder Problems
  • Binary Search
  • Stacks
  • Practice Test: Arrays, Pointers, Windows, Search and Stacks
  • Trees: Traversals and Core Patterns
  • Binary Search Trees and Rebuilding a Tree
  • Tries (Prefix Trees)
  • Heaps and Priority Queues
  • Graphs: BFS and DFS
  • Topological Sort and Cycle Detection
  • Union-Find (Disjoint Set Union)
  • Practice Test: Trees, Heaps and Graphs
  • Dynamic Programming Basics
  • Dynamic Programming: Two Dimensions
  • Dynamic Programming: Hard String Matching
  • Backtracking
  • Backtracking: Constraint Satisfaction
  • Greedy Algorithms
  • Interval Problems
  • Bit Manipulation
  • Math and Geometry
  • String Manipulation
  • Pattern Recall and Mock Coding Rounds
  • Practice Test: DP, Backtracking, Greedy and More

System Design

Foundations0/16
  • The System Design Interview Framework
  • Back-of-the-Envelope Estimation
  • Networking and Protocols for Design
  • API Design and Idempotency
  • Load Balancing, Proxies, and Scaling Out
  • Caching and CDNs
  • Choosing a Database: Storage, Indexes and Transactions
  • Replication, Partitioning, and Sharding
  • CAP, PACELC, and Levels of Consistency
  • Messaging, Queues, and Event-Driven Architecture
  • Distributed Transactions and Data Integrity
  • Coordination, Consensus, and Distributed Locks
  • Reliability, Resilience, and Rate Limiting
  • Observability, Security, and Cost
  • System Design Cheat Sheet
  • Practice Test: System Design Foundations
Case Studies0/23
  • Design a URL Shortener
  • Design a Rate Limiter
  • Design a Notification Service
  • Design a Job Queue System
  • Design a Chat App (WhatsApp)
  • Design an Analytics Pipeline
  • Design a Multi-tenant SaaS Architecture
  • Design a Social Media Feed (Twitter/X)
  • Design a Search Autocomplete System
  • Design a File Storage Service (Dropbox)
  • Design Google Docs
  • Design a Code Review System
  • Design Netflix
  • Design YouTube
  • Design Spotify
  • Design Uber/Lyft
  • Design Airbnb
  • Design Ticketmaster (Eventbrite)
  • Design an E-commerce Cart, Checkout, and Catalog
  • Design a Payment System
  • Design a Logging and Monitoring System
  • Mock Design Rounds
  • Practice Test: System Design Case Studies
Low-Level Design0/19
  • The LLD Interview Framework and UML
  • OOP Foundations for Design
  • SOLID Principles
  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns I — Strategy, Observer, Command, State, Template Method
  • Behavioral Patterns II and Anti-Patterns
  • Concurrency in Low-Level Design
  • Design a Parking Lot
  • Design an Elevator System
  • Design a Movie Ticket Booking System
  • Design an Expense-Sharing App (Splitwise)
  • Design a Vending Machine and an ATM
  • Design a Rate Limiter and a Logging Framework
  • Design a Notification Service and an In-Memory Cache
  • Design Games — Tic-Tac-Toe, Snake and Ladder, Chess
  • Database Design for LLD
  • LLD Cheat Sheet and Recall Drill
  • Practice Test: Low-Level Design

Backend

Python0/5
  • How Python Runs Your Code
  • Functions Deep Dive: Closures, Generators, Context Managers, Scope
  • Classes Deep Dive: Method Types, MRO, Metaclasses
  • Standard Library and Serialization
  • Practice Test: Python
Django0/6
  • Django Request Lifecycle
  • Lab: Django Middleware — Request ID & Response Time
  • Django ORM and Querysets
  • Django's Cache Framework
  • Practice Test: Django
  • Django in Production: Debugging Labs
FastAPI0/5
  • FastAPI, ASGI, and Async
  • FastAPI Dependency Injection
  • FastAPI Background Tasks
  • Lab: FastAPI Async — Concurrent Aggregation
  • Practice Test: FastAPI
Databases (PostgreSQL)0/6
  • Indexing
  • Query Optimization
  • Transactions and Isolation
  • Migrations and Schema Changes
  • Hierarchical Data and Safe Concurrent Updates
  • Practice Test: Databases
Caching, Queues & Distributed Systems0/8
  • Redis Caching
  • Redis Advanced Patterns
  • Celery and Background Workers
  • Kafka and Message Queues
  • Async Pipelines
  • Distributed Systems Basics
  • Distributed Locks
  • Practice Test: Caching, Queues and Distributed Systems
APIs, Security & Deployment0/9
  • REST API Design
  • API Versioning and Error Handling
  • API Security and Authentication
  • WebSockets and Real-Time Systems
  • Docker and Containers
  • File Storage with S3
  • Testing Strategies
  • Mock Backend Round
  • Practice Test: APIs, Security and Deployment
Frontend0/38
  • HTML Document Structure and Semantics
  • CSS Box Model, Flexbox and Grid
  • CSS Architecture and Theming
  • CSS and Rendering Performance
  • Variables, Scope, Hoisting, and Truthiness
  • Functions, this, and Binding
  • Objects and Prototypal Inheritance
  • JavaScript Utility Patterns: Cloning, Caching, and Memory
  • Promises From Scratch
  • The Event Loop and Browser Rendering Pipeline
  • The DOM: Nodes, Events, and Traversal
  • Practice Test: HTML, CSS and JavaScript
  • React Rendering Fundamentals
  • React Hooks Internals
  • React Performance
  • Lab: React — Stateful Counter Component
  • State Management
  • State Machines in React
  • React Custom Hooks and Composition Patterns
  • TypeScript for React
  • React Testing
  • Practice Test: React
  • HTTP and Caching
  • Network Performance and Web Vitals
  • Code Splitting and Bundles
  • Virtualization
  • GraphQL and Data Fetching
  • Web Security
  • WebSockets and Real-time
  • SSR and Next.js
  • Micro-frontends
  • Build Tools and Bundling
  • Accessibility
  • Error Handling
  • Internationalization
  • Animation
  • Rapid Recall and Mock Frontend Rounds
  • Practice Test: Performance, Architecture and Production
Behavioral & Interview Day0/14
  • Resume and Project Walkthroughs
  • The STAR Method and Refining Your Stories
  • Conflict and Difficult Coworkers
  • Failures, Incidents, and Obstacles
  • Leadership, Mentorship, and Ownership
  • Teamwork, Communication, and Feedback
  • Growth and Handling Ambiguity
  • Career Goals, Why This Company, and Negotiation
  • Situational Questions
  • Company Research
  • Mock Behavioral Rounds
  • Interview Day
  • After the Interview
  • Practice Test: Behavioral and Interview Day
Browsing without an account — progress is saved in this browser only.Sign in to save it

Concurrency in Low-Level Design

Reading50 min

"Two users try to book the last seat at the exact same moment — what happens?" That question shows up as a follow-up in almost every LLD interview, and it's where otherwise solid designs quietly fall apart. This lesson gives you the vocabulary, the three tools you'll actually use, and the specific patterns for the problems coming up later in this section.

Here's the one idea that keeps you out of trouble: find the piece of data that's shared and changeable, then either stop sharing it, stop letting it change, or protect access to it. Every correct answer boils down to one of those three moves.

Race conditions and shared, changeable data

A race condition happens whenever the result depends on exactly how two things happening at once get interleaved. The classic shape is check, then act:

Python
import threading

class UnsafeCounter:
    def __init__(self): self.count = 0
    def increment(self):
        current = self.count      # read
        self.count = current + 1  # write — another thread might have written in between

class SafeCounter:
    def __init__(self):
        self.count = 0
        self._lock = threading.Lock()
    def increment(self):
        with self._lock:          # read-then-write happens as one atomic step now
            self.count += 1


def hammer(counter, times=50_000):
    for _ in range(times):
        counter.increment()

def run(counter):
    threads = [threading.Thread(target=hammer, args=(counter,)) for _ in range(4)]
    for t in threads: t.start()
    for t in threads: t.join()
    return counter.count

safe = run(SafeCounter())
assert safe == 200_000                      # always exactly right, every time
unsafe = run(UnsafeCounter())
print(f"safe={safe} (always correct), unsafe={unsafe} (may be < 200000)")

There are three shapes of race worth being able to name on sight:

Race What it looks like Example
Check-then-act Test something, then act on it — but it could change in between if seat.is_free: seat.book()
Read-modify-write Load a value, do some math, save it back count = count + 1, balance -= amount
Publish-before-init Another thread sees an object that's only half built A naive lazy singleton, with no lock at all

Two questions to answer out loud in every LLD interview, before anyone even asks:

  1. What's the shared, changeable data here? — the set of free spots, the seat map, the inventory count, the account balance.
  2. What's protecting it? — a lock, an atomic operation, the fact that it never changes, or a database rule.

Say both, unprompted, and you've already answered the concurrency follow-up before it lands.

Remember: thread-safe pieces don't add up to a thread-safe sequence. The whole check-then-act step needs one lock around it, not two separately-safe halves.

Sign in to try this knowledge check.

The three tools

1. Locks — mutual exclusion. The general-purpose tool: only one thread is allowed inside the protected section at a time.

Python
import threading

class SeatMap:
    def __init__(self, seats: list[str]):
        self._free = set(seats)
        self._lock = threading.Lock()

    def book(self, seat: str, user: str) -> bool:
        with self._lock:                     # check and act happen as one atomic step
            if seat not in self._free:
                return False
            self._free.remove(seat)
            return True

    @property
    def free_count(self) -> int:
        with self._lock:
            return len(self._free)


seats = SeatMap([f"A{i}" for i in range(3)])
results = []
threads = [threading.Thread(target=lambda: results.append(seats.book("A1", "u")))
           for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()

assert results.count(True) == 1              # exactly one winner, every single time
assert seats.free_count == 2
print("bookings succeeded:", results.count(True), "of", len(results))

Rules for locks, and all of them get asked about:

  • Hold the lock for the shortest time possible — never across a network call, a disk read, or a callback into code you don't control.
  • Never call unfamiliar code while holding a lock. It might call back into your code and deadlock against itself.
  • Prefer many small locks over one big one: one lock per seat, spot, or account, so unrelated operations can run at the same time.
  • Use a read-write lock when reads vastly outnumber writes — many readers can run together, with just one writer at a time.

2. Atomic operations. For a single value, an atomic compare-and-swap beats a lock outright: no blocking, no possibility of deadlock, less overhead. Java's AtomicInteger.incrementAndGet(), Go's atomic.AddInt64, and Redis's INCR are all this same idea. The limit is that atomics only protect one variable — the moment two things need to change together, you're back to needing a lock or a transaction.

3. Making it unchangeable — needs no protection at all. An object that never changes after it's built is safe to hand to any number of threads, with no lock, no cost, and literally no way for it to go wrong. This is exactly why value objects like Money, TimeSlot, and Point should never change after they're created, and why "just make it unchangeable" is the cheapest concurrency fix you'll ever get to use. When something genuinely has to change, replace the whole object instead of mutating it in place — this is called copy-on-write.

Remember: the three tools, from cheapest to most general: make it unchangeable (free), use an atomic operation (one value), or take a lock (anything else, held as briefly as possible).

Sign in to try this knowledge check.

Deadlock, livelock, and starvation

Deadlock needs all four of the Coffman conditions to hold at once — break just one of them, and it can't happen:

Condition What it means How to break it
Mutual exclusion Something can only be held by one thread at a time Make it unchangeable, or use a lock-free structure
Hold and wait A thread holds one lock while waiting on another Grab every lock you need at once, or none at all
No pre-emption A lock can't be taken away from whoever's holding it Use timeouts (tryLock)
Circular wait Thread A waits on B, and B waits on A A fixed global lock order — the usual fix
Python
import threading

class Account:
    def __init__(self, aid: int, paise: int):
        self.id, self.balance = aid, paise
        self.lock = threading.Lock()

def transfer(src: Account, dst: Account, paise: int) -> bool:
    # ALWAYS lock in a fixed order (by id) — this is exactly what removes the circular wait.
    first, second = (src, dst) if src.id < dst.id else (dst, src)
    with first.lock:
        with second.lock:
            if src.balance < paise:
                return False
            src.balance -= paise
            dst.balance += paise
            return True


a, b = Account(1, 100_000), Account(2, 50_000)
# Concurrent transfers in OPPOSITE directions — the classic setup for a deadlock.
t1 = threading.Thread(target=lambda: [transfer(a, b, 100) for _ in range(1_000)])
t2 = threading.Thread(target=lambda: [transfer(b, a, 100) for _ in range(1_000)])
t1.start(); t2.start(); t1.join(); t2.join()

assert a.balance + b.balance == 150_000       # the money is all still there, and nothing froze
print("balances:", a.balance, b.balance)

The bank-transfer deadlock gets asked about by name, and locking accounts in a fixed order by id is exactly the answer interviewers are listening for. The fallback, when no stable ordering exists, is tryLock with a timeout plus a randomised retry — that breaks the "no pre-emption" condition instead. Mention it as a backup option.

Two related problems worth telling apart from deadlock:

  • Livelock: two threads keep reacting to each other and changing state, but neither one ever makes progress — like two people stepping side to side in a hallway, both trying to let the other pass. Fixed with random backoff before retrying.
  • Starvation: one thread never gets its turn because others keep winning first. Fixed with fair locks or a proper queue.

Remember: lock accounts (or any pair of things) in a fixed order by id, and the circular-wait condition simply can't happen.

Sign in to try this knowledge check.

The patterns you'll actually use

Producer-consumer with a bounded queue. This is the standard shape whenever requests arrive faster than they can be handled — and the "bounded" part is what gives you backpressure instead of memory growing without limit.

Python
import queue, threading

def worker(jobs: "queue.Queue", done: list, lock: threading.Lock):
    while True:
        job = jobs.get()
        if job is None:                      # a signal to shut down cleanly
            jobs.task_done()
            return
        with lock:
            done.append(job * 2)
        jobs.task_done()


jobs: "queue.Queue" = queue.Queue(maxsize=10)     # bounded → producers wait once it's full
done, lock = [], threading.Lock()
workers = [threading.Thread(target=worker, args=(jobs, done, lock)) for _ in range(3)]
for w in workers: w.start()

for i in range(20):
    jobs.put(i)
for _ in workers:
    jobs.put(None)
jobs.join()
for w in workers: w.join()

assert sorted(done) == [i * 2 for i in range(20)]
print("processed", len(done), "jobs across", len(workers), "workers")

Optimistic locking (a version check). No lock held during the operation at all: read a version number, and only allow the write if that version hasn't changed since. This is right for low contention — like a user editing their own profile, where clashes are rare.

Python
class VersionedRecord:
    def __init__(self, value: str):
        self.value, self.version = value, 0

    def update(self, new_value: str, expected_version: int) -> bool:
        if self.version != expected_version:
            return False                     # someone else wrote first — caller should retry
        self.value, self.version = new_value, self.version + 1
        return True


rec = VersionedRecord("draft")
v = rec.version
assert rec.update("edited by A", v) is True
assert rec.update("edited by B", v) is False     # B's outdated write gets rejected
assert rec.value == "edited by A" and rec.version == 1
print("optimistic lock rejected the stale write; value =", rec.value)

Pessimistic locking. Take the lock (or run SELECT ... FOR UPDATE) before you even read. This is right for high contention — the very last seat of a sold-out show, where optimistic retries would mostly just fail over and over.

Reservation with a TTL. This is the pattern behind every seat-booking flow you've ever used: hold the resource for a limited time, then either confirm it or let it expire. It avoids holding a database lock for the several minutes a real human spends typing in their card details, and it's exactly why an expiry timestamp beats a plain is_locked boolean — a crashed browser tab releases the seat back automatically.

And here's the rule that matters the most in an interview: in a multi-process or multi-server system, an in-process lock protects absolutely nothing. Whatever invariant you need has to be enforced wherever the actual data lives:

UPDATE seats SET status = 'booked', booked_by = $1
WHERE seat_id = $2 AND status = 'available';     -- 0 rows affected = someone else already won

A conditional UPDATE, or a unique constraint, is atomic across every server at once. A threading.Lock is atomic across exactly one process, full stop. Saying that sentence out loud is what separates a design that works on your laptop from one that survives production — and it connects directly to the fencing-token discussion in the HLD coordination lesson.

Remember: in-process locks are invisible across servers. Only the database — through a conditional write or a unique constraint — actually enforces anything across all of them.

Sign in to try this knowledge check.

Quick recap

Find the SHARED, CHANGEABLE data → stop sharing it, stop letting it change, or protect access.
Races: check-then-act · read-modify-write · publish-before-init
  Thread-safe methods do NOT add up: the whole sequence needs one lock, not each half.

Tools, cheapest first:
  Immutability — no protection needed at all. Value objects should never change.
  Atomic       — one variable, no blocking (AtomicInteger, INCR, compare-and-swap)
  Lock         — general purpose; hold briefly, never across I/O or callbacks; small > big

Deadlock = all 4 Coffman conditions at once; break circular wait with a GLOBAL LOCK ORDER (by id).
  Fallback: tryLock + timeout + randomised retry.  Livelock → backoff.  Starvation → fair locks.

Patterns:
  Producer-consumer with a BOUNDED queue (backpressure) + a shutdown signal
  Optimistic (version check + retry) → LOW contention
  Pessimistic (lock / SELECT FOR UPDATE) → HIGH contention
  Reservation with a TTL → long, human-paced flows (seat holds, checkout)

MULTI-PROCESS: an in-process lock protects nothing.
  Enforce it at the shared resource instead: conditional UPDATE ... WHERE status='available',
  or a UNIQUE constraint. Zero rows affected means you lost the race.
  • Say the concurrency answer before you're asked for it. Naming the shared, changeable data and how it's protected, unprompted, is one of the highest-value moves in an LLD interview.
  • Match your locking strategy to how much contention there really is: optimistic for rare conflicts, pessimistic for the very last seat, reservations for anything a human takes several minutes to finish.
  • Lock ordering is the deadlock answer, and the bank-transfer example is the one worth having ready.
  • The database is the only lock that works across every server. Every problem later in this section — seat booking, inventory, wallets — comes down to a conditional write or a unique constraint at the storage layer.
PreviousBehavioral Patterns II and Anti-PatternsNextDesign a Parking Lot

Course progress

0%
0 of 177 modules complete

On this page

  • Race conditions and shared, changeable data
  • The three tools
  • Deadlock, livelock, and starvation
  • The patterns you'll actually use
  • Quick recap