Database Management Systems

Unit 6: Transaction Processing

ACID Properties, Concurrency Control, Locking Protocols, Deadlocks, Isolation Levels & Recovery — how databases guarantee data correctness when everything goes wrong.

🏢 Oracle & PostgreSQL  |  📝 15 MCQs (Bloom's)  |  🔬 5 Lab Exercises  |  💼 Interview Prep

Section 1

Why This Chapter Pays Your Salary

Every ₹1 that moves through India's banking system — UPI transfers, ATM withdrawals, online payments — relies on transactions. A transaction is a promise: either ALL operations succeed, or NONE do. Without this guarantee, ₹10,000 could vanish from your account without reaching the recipient. Transaction processing is the foundation of database reliability, and it's the most tested DBMS topic in GATE CS (5-8 marks every year).

🏢 Industry Snapshot

PhonePe/Google Pay — Every UPI transaction is an ACID transaction: (1) Debit sender's bank, (2) Credit receiver's bank, (3) Log to NPCI, (4) Send confirmation. If step 3 fails, steps 1 and 2 are automatically rolled back. PhonePe processes 600 crore transactions/month — every one of them ACID-compliant.

SBI Core Banking — When 50,000 ATMs simultaneously process withdrawals, the database uses Two-Phase Locking (2PL) to ensure no two transactions corrupt the same account balance. If two ATMs try to debit the same account simultaneously, one is blocked until the other commits.

Razorpay — Payment gateway uses SERIALIZABLE isolation for critical payment reconciliation. Lower isolation levels (READ COMMITTED) for dashboard queries — a deliberate performance-vs-correctness trade-off.

🇮🇳 PhonePe🇮🇳 SBI🇮🇳 Razorpay🇮🇳 Paytm🇮🇳 IRCTCGATE CS
Section 2

Learning Outcomes — Bloom's Taxonomy

Bloom's LevelOutcome Statement
L1 — RememberDefine ACID properties; list transaction states; recall lock types (Shared/Exclusive); name the four SQL isolation levels
L2 — UnderstandExplain how dirty reads, non-repeatable reads, and phantom reads occur; describe why 2PL guarantees serializability; explain WAL (Write-Ahead Logging) for recovery
L3 — ApplyWrite SQL transactions with COMMIT, ROLLBACK, SAVEPOINT; construct precedence graphs and test for conflict serializability; apply 2PL to a schedule
L4 — AnalyzeAnalyze a concurrent schedule for conflicts; detect deadlocks using wait-for graphs; determine the isolation level that prevents a given anomaly
L5 — EvaluateEvaluate trade-offs between isolation levels (performance vs correctness); justify when to use optimistic vs pessimistic concurrency control
L6 — CreateDesign a transaction processing strategy for a payment system with proper locking, isolation levels, error recovery, and deadlock handling
Section 3

Concept Explanations

3.1 ACID Properties — The Four Guarantees

📌 ACID — The Contract Every Database Must Honour

🌍 REAL-WORLD ANALOGY

A bank transfer of ₹10,000 from Rahul to Priya:

PropertyMeaningBank Transfer ExampleGuaranteed By
AtomicityAll or nothing — either ALL operations complete, or NONE doIf debiting Rahul succeeds but crediting Priya fails → both are rolled back. Rahul's ₹10,000 is restored. No money vanishes.Transaction Manager (COMMIT/ROLLBACK)
ConsistencyDatabase moves from one valid state to another. All constraints are satisfied before and after.Total money in the system before = after. If Rahul had ₹50,000 and Priya ₹30,000, after transfer: Rahul=₹40,000, Priya=₹40,000. Total=₹80,000 (unchanged).Integrity Constraints + Application Logic
IsolationConcurrent transactions don't interfere with each other — each appears to run alone.If Priya checks her balance WHILE the transfer is in progress, she sees either ₹30,000 (before) or ₹40,000 (after) — never an intermediate state.Concurrency Control (Locks, MVCC)
DurabilityOnce committed, data survives any failure — power outage, crash, disk error.After COMMIT, even if the server crashes 1ms later, the transfer is permanently recorded. On restart, the recovery system replays the committed transaction from the log.Write-Ahead Logging (WAL) + Recovery Manager
SQL — Transaction in Action
-- ═══════════════════════════════════════
-- Bank Transfer: ₹10,000 from Rahul (A/C 1001) to Priya (A/C 1002)
-- ═══════════════════════════════════════

-- Oracle: transactions start implicitly with the first DML
-- PostgreSQL: explicit BEGIN required
BEGIN;  -- PostgreSQL; Oracle: not needed (auto-begin)

SAVEPOINT before_transfer;  -- Bookmark in case partial rollback needed

-- Step 1: Debit Rahul
UPDATE accounts SET balance = balance - 10000
WHERE account_id = 1001 AND balance >= 10000;  -- Balance check in WHERE

-- Step 2: Verify debit succeeded (row was actually updated)
-- In PL/SQL: IF SQL%ROWCOUNT = 0 THEN ROLLBACK; END IF;

-- Step 3: Credit Priya
UPDATE accounts SET balance = balance + 10000
WHERE account_id = 1002;

-- Step 4: Log the transaction
INSERT INTO transactions (from_acc, to_acc, amount, txn_date, status)
VALUES (1001, 1002, 10000, CURRENT_TIMESTAMP, 'SUCCESS');

COMMIT;  -- All 3 operations are now permanent (DURABLE)

-- If ANY step fails:
-- ROLLBACK TO before_transfer;  -- Undo to savepoint
-- OR
-- ROLLBACK;  -- Undo everything since BEGIN

3.2 Transaction States

Transaction State Diagram
                    ┌─────────────┐
                    │   ACTIVE    │ ← Transaction executing operations
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              │ (success)               │ (failure)
              ▼                         ▼
     ┌────────────────┐       ┌────────────────┐
     │ PARTIALLY      │       │   FAILED       │ ← Error occurred
     │ COMMITTED      │       └───────┬────────┘
     └───────┬────────┘               │
             │                        │ ROLLBACK
             │ COMMIT written         ▼
             │ to disk         ┌────────────────┐
             ▼                 │   ABORTED      │ ← All changes undone
     ┌────────────────┐       └────────────────┘
     │   COMMITTED    │            │
     └────────────────┘            │ (restart?)
                                   ▼
                             Re-execute or
                             terminate
StateDescriptionCan Transition To
ActiveTransaction is executing read/write operationsPartially Committed or Failed
Partially CommittedLast operation executed; COMMIT issued but not yet flushed to diskCommitted or Failed
CommittedAll changes are permanently written to disk. Cannot be undone.Terminal state
FailedAn error occurred (constraint violation, system crash, deadlock)Aborted
AbortedAll changes rolled back. Database restored to pre-transaction state.Restart or terminate

3.3 Schedules & Serializability

📌 Schedules — How Concurrent Transactions Interleave

📌 WHAT IT IS

A schedule is a sequence of read/write operations from multiple concurrent transactions. A serial schedule runs transactions one after another (no interleaving). A serializable schedule is a concurrent schedule that produces the same result as SOME serial schedule — the gold standard for correctness.

Schedules — Example
T1: Transfer ₹100 from A to B     T2: Add 10% interest to A and B
    R(A), A=A-100, W(A),              R(A), A=A*1.1, W(A),
    R(B), B=B+100, W(B)               R(B), B=B*1.1, W(B)

Initial: A=1000, B=500

Serial Schedule S1 (T1 then T2):
  T1: A=1000-100=900, B=500+100=600
  T2: A=900*1.1=990, B=600*1.1=660
  Final: A=990, B=660

Serial Schedule S2 (T2 then T1):
  T2: A=1000*1.1=1100, B=500*1.1=550
  T1: A=1100-100=1000, B=550+100=650
  Final: A=1000, B=650

Both are correct! Any concurrent schedule equivalent to S1 or S2 is SERIALIZABLE.

Conflict Serializability — Precedence Graph Test

Conflict Serializability Test
Two operations CONFLICT if: (1) they belong to DIFFERENT transactions,
(2) they access the SAME data item, (3) at least one is a WRITE.

Conflict pairs: R-W, W-R, W-W (NOT R-R — both reads, no conflict)

Test: Build a PRECEDENCE GRAPH (directed graph)
  - Node for each transaction
  - Edge Ti → Tj if Ti has a conflicting operation BEFORE Tj's conflicting operation
  - If graph has NO CYCLE → schedule is CONFLICT SERIALIZABLE
  - If graph has a CYCLE → NOT conflict serializable

Example Schedule:
  T1: R(A)  W(A)            R(B)  W(B)
  T2:            R(A)  W(A)            R(B)  W(B)

Conflicts:
  T1.W(A) before T2.R(A) → T1 → T2
  T1.W(A) before T2.W(A) → T1 → T2
  T1.R(B) before T2.W(B) → T1 → T2
  T1.W(B) before T2.W(B) → T1 → T2

Precedence Graph: T1 → T2 (all edges same direction)
No cycle! ✓ CONFLICT SERIALIZABLE (equivalent to serial T1,T2)
View Serializability is a weaker (more permissive) form: a schedule is view serializable if it has the same initial reads, final writes, and read-from relationships as some serial schedule. Every conflict-serializable schedule is view-serializable, but not vice versa. View serializability is NP-complete to test, so databases use conflict serializability (polynomial time via precedence graphs).

3.4 Concurrency Problems — What Goes Wrong

ProblemDescriptionExamplePrevented By
Dirty ReadT2 reads data WRITTEN by T1 that hasn't committed yet. If T1 rolls back, T2 has read "ghost" data.T1 updates salary to ₹1,00,000 (uncommitted). T2 reads ₹1,00,000 for a report. T1 rolls back. T2's report is wrong — salary was never ₹1,00,000.READ COMMITTED and above
Non-Repeatable ReadT1 reads a row, T2 updates it and commits, T1 re-reads and gets a DIFFERENT value.T1 reads balance=₹50,000. T2 debits ₹20,000 and commits. T1 reads again: balance=₹30,000. Two reads in the same transaction return different values.REPEATABLE READ and above
Phantom ReadT1 reads a SET of rows (query), T2 inserts/deletes rows that match, T1 re-runs the same query and gets different ROWS.T1: SELECT COUNT(*) WHERE dept='CSE' → 50. T2: INSERT student in CSE, COMMIT. T1: same query → 51. A "phantom" row appeared.SERIALIZABLE only
Lost UpdateT1 and T2 both read the same value, both update it — one update is lost.Balance=₹10,000. T1 reads ₹10,000, deducts ₹3,000. T2 reads ₹10,000, deducts ₹5,000. T1 writes ₹7,000. T2 writes ₹5,000. T1's deduction is LOST. Correct: ₹2,000.Proper locking (any level)

3.5 Lock-Based Concurrency Control

Lock Types
┌─────────────────────────────────────────────────────┐
│ Lock Type       │ Symbol │ Allows Concurrent        │
├─────────────────┼────────┼──────────────────────────│
│ Shared (S)      │   S    │ Other S locks: YES ✓     │
│ (Read Lock)     │        │ Other X locks: NO ✗      │
├─────────────────┼────────┼──────────────────────────│
│ Exclusive (X)   │   X    │ Other S locks: NO ✗      │
│ (Write Lock)    │        │ Other X locks: NO ✗      │
└─────────────────┴────────┴──────────────────────────┘

Lock Compatibility Matrix:
         │ Requesting S │ Requesting X │
─────────┼──────────────┼──────────────│
Held: S  │   GRANTED ✓  │   WAIT ✗     │
Held: X  │   WAIT ✗     │   WAIT ✗     │
None     │   GRANTED ✓  │   GRANTED ✓  │

Rule: Multiple readers OK. Any writer = exclusive access.

Two-Phase Locking (2PL)

📌 2PL — The Protocol That Guarantees Serializability

📌 THE RULE

A transaction must acquire all its locks before releasing any. Two phases: Growing phase (acquire locks, never release) → Shrinking phase (release locks, never acquire). Once a lock is released, no new locks can be acquired.

⚙️ EXAMPLE
2PL Example
T1: Lock-S(A), Read(A), Lock-X(B), Write(B) | Unlock(A), Unlock(B)
    ← ─ ─ Growing Phase ─ ─ ─ ─ ─ ─ ─ ─ → ← Shrinking Phase →

VALID 2PL: all locks acquired before any release ✓

T2: Lock-S(A), Read(A), Unlock(A), Lock-X(B), Write(B), Unlock(B)
    ← ─ Growing ─ →     ← ─ ─ ─ NOT 2PL! ─ ─ →
    Released A then acquired B → VIOLATES 2PL ✗
📌 VARIANTS
VariantRuleDeadlock?Cascading Abort?
Basic 2PLGrow then shrinkYes ⚠️Yes ⚠️
Strict 2PLHold ALL exclusive (X) locks until COMMIT/ABORTYes ⚠️No ✓
Rigorous 2PLHold ALL locks (S and X) until COMMIT/ABORTYes ⚠️No ✓

Industry standard: Most databases (Oracle, PostgreSQL) use Strict 2PL — exclusive locks held until COMMIT. This prevents cascading aborts (where one rollback forces other transactions to rollback too).

3.6 Deadlocks — The Deadly Embrace

Deadlock Scenario
T1: Lock-X(A)  ───────────►  Lock-X(B)   ← WAITING for T2 to release B
T2: Lock-X(B)  ───────────►  Lock-X(A)   ← WAITING for T1 to release A

T1 holds A, wants B.
T2 holds B, wants A.
Neither can proceed. DEADLOCK! ☠️

Wait-For Graph:
  T1 ──► T2 ──► T1   (CYCLE detected → deadlock exists)

Resolution: The DBMS detects the cycle and ABORTS one transaction
(the "victim" — usually the one with least work done).
The victim is rolled back, its locks released, and the other proceeds.

Deadlock Handling Strategies

StrategyHow It WorksUsed By
DetectionPeriodically check wait-for graph for cycles. If found, abort one transaction (victim selection). Most common in practice.Oracle, PostgreSQL (every few seconds)
Prevention (Wait-Die)Older transaction waits; younger transaction is aborted ("dies") and restarted. Based on timestamps.Academic / specialized systems
Prevention (Wound-Wait)Older transaction "wounds" (forces rollback of) younger transaction. Younger waits for older.Google Spanner
TimeoutIf a transaction waits longer than N seconds for a lock, assume deadlock and abort it.MySQL InnoDB (innodb_lock_wait_timeout)
Deadlock ≠ Starvation. Deadlock: two or more transactions permanently blocked waiting for each other — resolved by aborting one. Starvation: one transaction keeps getting aborted as the deadlock victim every time — it never completes. Fix starvation with priority aging: increase priority of repeatedly-aborted transactions so they eventually get through.

3.7 SQL Isolation Levels

SQL — Setting Isolation Levels
-- Oracle: default is READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- PostgreSQL: same syntax, default is READ COMMITTED
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ... your queries ...
COMMIT;

-- Session-level (all transactions in this session):
ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;  -- Oracle
SET default_transaction_isolation = 'serializable';  -- PostgreSQL
Isolation LevelDirty ReadNon-Repeatable ReadPhantom ReadPerformance
READ UNCOMMITTED⚠️ Possible⚠️ Possible⚠️ Possible🟢 Fastest
READ COMMITTED✅ Prevented⚠️ Possible⚠️ Possible🟢 Fast (default)
REPEATABLE READ✅ Prevented✅ Prevented⚠️ Possible🟡 Medium
SERIALIZABLE✅ Prevented✅ Prevented✅ Prevented🔴 Slowest
Industry defaults and practice:
Oracle: Default = READ COMMITTED. Oracle uses MVCC (Multi-Version Concurrency Control) — readers never block writers, writers never block readers. Each transaction sees a consistent snapshot. Oracle doesn't implement READ UNCOMMITTED or REPEATABLE READ — only READ COMMITTED and SERIALIZABLE.
PostgreSQL: Default = READ COMMITTED. Supports all four levels. REPEATABLE READ in PG actually provides snapshot isolation (prevents phantoms too in practice).
Rule of thumb: Use READ COMMITTED for 95% of operations. Use SERIALIZABLE only for critical financial transactions (payment reconciliation, ledger posting).
MVCC (Multi-Version Concurrency Control) — Instead of locking rows, the database keeps multiple versions of each row. Readers see a consistent snapshot of the data as of their transaction start time. Writers create new versions without blocking readers. Used by Oracle (Undo Segments), PostgreSQL (tuple versioning), and MySQL InnoDB. MVCC eliminates read-write contention — the biggest performance bottleneck in lock-based systems.

3.8 Recovery Systems — Surviving Crashes

📌 Write-Ahead Logging (WAL) — The Safety Net

📌 THE RULE

Before any data page is written to disk, the corresponding log record MUST be written to the log file first. This ensures that even if the system crashes mid-write, the recovery manager can reconstruct the database state from the log.

⚙️ HOW IT WORKS
WAL Example — Bank Transfer
Log (written FIRST):         Data (written AFTER log):
LSN 1: [T1, BEGIN]
LSN 2: [T1, UPDATE, A, old=1000, new=900]
LSN 3: [T1, UPDATE, B, old=500, new=600]
LSN 4: [T1, COMMIT]          ← Log flushed to disk HERE

Now data pages can be written:
  Page(A): 1000 → 900
  Page(B): 500 → 600

CRASH after LSN 4 but before data pages written?
  Recovery: Read log → REDO T1's changes (A=900, B=600)
  Data is restored correctly! ✓

CRASH after LSN 3 but before COMMIT (LSN 4)?
  Recovery: T1 not committed → UNDO T1's changes
  A=1000 (restored), B=500 (restored)
  Transaction is rolled back. ✓

ARIES Recovery Algorithm

ARIES — Three Phases of Recovery
After a crash, the recovery manager runs three phases:

Phase 1: ANALYSIS
  Scan log from last checkpoint forward.
  Determine: which transactions were active at crash time.
  Build: dirty page table (pages that may not be on disk).

Phase 2: REDO (Repeating History)
  Replay ALL logged operations (committed and uncommitted).
  Bring database to exactly the state at crash time.
  Uses LSN (Log Sequence Number) to avoid re-doing already-applied changes.

Phase 3: UNDO (Rolling Back Losers)
  For transactions that were active (not committed) at crash time:
  Undo their operations in REVERSE order.
  Write compensation log records (CLR) to prevent re-undoing on another crash.

Result: Committed transactions → REDONE (durable).
        Uncommitted transactions → UNDONE (atomic).
        Database is consistent. ✓

Checkpoints

Checkpoint — Reducing Recovery Time
Without checkpoints: must scan ENTIRE log from the beginning. Slow!

Checkpoint: periodically flush all dirty pages to disk and record
            which transactions are active.

Recovery starts from the LAST CHECKPOINT, not the beginning of the log.

Oracle: Automatic checkpoint every 3 seconds (or configurable).
PostgreSQL: checkpoint_timeout (default 5 minutes).

Fuzzy Checkpoint: allows transactions to continue during checkpointing
(non-blocking). Used by all modern databases.
Modern recovery enhancements: (1) Oracle Data Guard — Real-time log shipping to a standby database. If primary crashes, standby takes over in seconds (RPO ≈ 0). (2) PostgreSQL pg_basebackup + WAL archiving — Point-in-time recovery (PITR) to any moment. (3) Cloud-native — AWS RDS automated backups every 5 minutes with 35-day retention. Recovery is one API call. (4) Blockchain-inspired — Some fintechs use append-only immutable logs alongside traditional WAL for regulatory audit (RBI compliance).
Section 4

Industry Problems

🏢 Industry Problem #1 — PhonePe UPI Double-Spend Prevention

Scenario: A user initiates a ₹5,000 UPI payment. Due to network latency, the app retries the request. The backend receives TWO identical requests 200ms apart. How do you prevent the user from being charged twice?

💡 Solution: Idempotency + Serializable Transaction
SQL — Idempotent Transaction
-- Each UPI request has a unique txn_ref_id generated by the app
-- The database uses a UNIQUE constraint to prevent double processing

CREATE TABLE upi_transactions (
    txn_ref_id   VARCHAR2(50)  PRIMARY KEY,  -- App-generated unique ID
    sender_vpa   VARCHAR2(50)  NOT NULL,
    receiver_vpa VARCHAR2(50)  NOT NULL,
    amount       NUMBER(12,2),
    status       VARCHAR2(10),
    created_at   TIMESTAMP     DEFAULT CURRENT_TIMESTAMP
);

-- Transaction procedure (idempotent)
CREATE OR REPLACE PROCEDURE sp_upi_pay (
    p_ref_id     IN  VARCHAR2,
    p_sender     IN  VARCHAR2,
    p_receiver   IN  VARCHAR2,
    p_amount     IN  NUMBER,
    p_result     OUT VARCHAR2
) AS
    v_existing NUMBER;
BEGIN
    -- Check if this exact request was already processed (idempotency)
    SELECT COUNT(*) INTO v_existing
    FROM upi_transactions WHERE txn_ref_id = p_ref_id;

    IF v_existing > 0 THEN
        p_result := 'DUPLICATE — already processed';
        RETURN;  -- Second request safely ignored
    END IF;

    -- Process the payment (SERIALIZABLE to prevent race condition)
    UPDATE accounts SET balance = balance - p_amount
    WHERE vpa = p_sender AND balance >= p_amount;

    IF SQL%ROWCOUNT = 0 THEN
        p_result := 'FAILED: Insufficient balance';
        ROLLBACK; RETURN;
    END IF;

    UPDATE accounts SET balance = balance + p_amount
    WHERE vpa = p_receiver;

    INSERT INTO upi_transactions VALUES
        (p_ref_id, p_sender, p_receiver, p_amount, 'SUCCESS', SYSTIMESTAMP);

    COMMIT;
    p_result := 'SUCCESS';
EXCEPTION
    WHEN DUP_VAL_ON_INDEX THEN  -- Race condition: both requests arrive simultaneously
        ROLLBACK;
        p_result := 'DUPLICATE — concurrent retry caught';
    WHEN OTHERS THEN
        ROLLBACK;
        p_result := 'FAILED: ' || SQLERRM;
END;
/

Key insight: The UNIQUE constraint on txn_ref_id is the final safety net. Even if the SELECT check passes for both concurrent requests, only one INSERT will succeed — the other hits DUP_VAL_ON_INDEX and is rolled back.

🏢 Industry Problem #2 — GATE CS: Precedence Graph & Serializability

Problem: Given schedule S: R1(A), R2(A), W1(A), W2(B), R1(B), W1(B). Is S conflict serializable?

💡 Complete Solution
Solution
Step 1: List all conflicting pairs (different Tx, same item, at least one W)

  R1(A) vs W2(A)? W2(A) doesn't exist. Skip.
  R2(A) vs W1(A): R2 before W1 → T2 → T1
  W1(A) vs R2(A)? W1 is AFTER R2 → already counted above.
  W2(B) vs R1(B): W2 before R1 → T2 → T1
  W2(B) vs W1(B): W2 before W1 → T2 → T1
  R1(B) vs W2(B)? R1 is AFTER W2 → already counted.

Step 2: Build precedence graph
  T2 → T1 (from R2(A) vs W1(A))
  T2 → T1 (from W2(B) vs R1(B))
  T2 → T1 (from W2(B) vs W1(B))

  All edges: T2 → T1. No reverse edge.

Step 3: Check for cycle
  T2 → T1 only. NO CYCLE! ✓

Step 4: Conclusion
  S is CONFLICT SERIALIZABLE.
  Equivalent serial order: T2, T1 (T2 before T1 in the serial schedule).

🏢 Industry Problem #3 — Razorpay Deadlock in Payment Settlement

Scenario: Two settlement processes run concurrently. Process A: Lock merchant_1 account, then lock merchant_2 account. Process B: Lock merchant_2 account, then lock merchant_1 account. Deadlock!

💡 Solution: Consistent Lock Ordering
SQL — Deadlock Prevention
-- WRONG: Different lock ordering → deadlock possible
-- Process A: UPDATE merchant_1 ... UPDATE merchant_2
-- Process B: UPDATE merchant_2 ... UPDATE merchant_1

-- CORRECT: Always lock in the SAME order (by account_id ascending)
CREATE OR REPLACE PROCEDURE sp_settle (p_acc1 NUMBER, p_acc2 NUMBER) AS
    v_first  NUMBER := LEAST(p_acc1, p_acc2);
    v_second NUMBER := GREATEST(p_acc1, p_acc2);
BEGIN
    -- Always lock smaller account_id FIRST
    SELECT balance FROM accounts WHERE account_id = v_first FOR UPDATE;
    SELECT balance FROM accounts WHERE account_id = v_second FOR UPDATE;

    -- Now both are locked in consistent order → no deadlock possible
    UPDATE accounts SET balance = balance - 1000 WHERE account_id = p_acc1;
    UPDATE accounts SET balance = balance + 1000 WHERE account_id = p_acc2;
    COMMIT;
END;
/

-- Both Process A and B call sp_settle → both lock merchant_1 first
-- One waits, the other completes and releases → NO DEADLOCK ✓

DBA Insight: Razorpay's payment engine enforces consistent lock ordering across all settlement procedures. Their DBA team monitors V$LOCK (Oracle) or pg_locks (PostgreSQL) for lock contention, and has alerts for deadlock frequency exceeding 0.01% of transactions.

Section 5

Lab Exercises

Exercise 1: ACID in Action — COMMIT, ROLLBACK, SAVEPOINT

⏱ 30 minutes🟢 Beginner

Schema: Use the e-commerce schema (orders, customers, products, order_items)

Tasks:

  1. Insert a new order with 3 items. COMMIT. Verify data persists.
  2. Insert another order, then ROLLBACK. Verify data is gone.
  3. Insert an order, SAVEPOINT sp1, insert items, ROLLBACK TO sp1. Verify: order exists but items don't.
  4. Simulate Atomicity: Write a PL/SQL block that transfers ₹5,000 between accounts. If debit succeeds but credit fails (account doesn't exist), ROLLBACK the entire transaction.
  5. Test Durability: COMMIT a transaction, then close SQL*Plus. Reconnect and verify data persists.

Exercise 2: Observe Concurrency Problems

⏱ 45 minutes🟡 Intermediate

Setup: Open TWO SQL*Plus sessions (Session A and Session B) connected to the same database.

Tasks:

  1. Dirty Read test: Session A: UPDATE product price (don't COMMIT). Session B: SELECT that product. Does B see the uncommitted change? (Answer: No in Oracle — READ COMMITTED default prevents it.)
  2. Non-Repeatable Read: Session A: SELECT price. Session B: UPDATE price, COMMIT. Session A: SELECT again. Same value? (Answer: Different — non-repeatable read occurred.)
  3. Lost Update: Both sessions: SELECT balance, compute new value, UPDATE. Verify one update is lost.
  4. FOR UPDATE lock: Repeat lost update test using SELECT...FOR UPDATE. Verify it's prevented.
  5. Set Session A to SERIALIZABLE. Repeat non-repeatable read test. Verify it's prevented.

Exercise 3: Precedence Graphs & Serializability

⏱ 40 minutes🟡 Intermediate

Tasks (pen-and-paper + verify):

  1. Schedule: R1(A), W2(A), R2(B), W1(B). Draw precedence graph. Is it conflict serializable?
  2. Schedule: R1(A), R2(B), W1(B), W2(A). Draw precedence graph. Check for cycles.
  3. Schedule: R1(A), R2(A), W2(A), R1(A), W1(A). Build graph. Serializable?
  4. For each serializable schedule, state the equivalent serial order.
  5. Create a schedule with 3 transactions that has a cycle in the precedence graph → NOT serializable.

Exercise 4: Deadlock Detection & Prevention

⏱ 40 minutes🟡 Intermediate

Tasks:

  1. Create a deadlock: Session A: UPDATE accounts SET balance=balance-100 WHERE id=1 (don't commit). Session B: UPDATE accounts SET balance=balance-100 WHERE id=2 (don't commit). Session A: UPDATE ... WHERE id=2 (waits). Session B: UPDATE ... WHERE id=1 (deadlock!).
  2. Observe Oracle's deadlock detection message (ORA-00060). Which session was the victim?
  3. Fix the deadlock by implementing consistent lock ordering (LEAST/GREATEST pattern).
  4. Draw the wait-for graph for the deadlock scenario.
  5. Query V$LOCK (Oracle) or pg_locks (PostgreSQL) to see active locks in real time.

Exercise 5: Complete Payment Transaction System

⏱ 60 minutes🔴 Advanced

Tasks: Design and implement a complete payment processing system:

  1. Create tables: accounts, transactions, audit_log
  2. Procedure sp_payment: transfer with balance check, idempotency (txn_ref_id), audit logging
  3. Trigger: log every balance change to audit_log with old/new values
  4. Test: Run 5 transfers, including one with insufficient balance and one duplicate request
  5. Simulate crash recovery: INSERT without COMMIT, disconnect, reconnect — verify uncommitted data is gone (Atomicity). INSERT with COMMIT, disconnect, reconnect — verify data persists (Durability).
Section 6

MCQ Assessment Bank — 15 Questions

Hover to reveal answer and explanation.

Q1

Which ACID property guarantees that a committed transaction survives a system crash?

  1. Atomicity
  2. Consistency
  3. Isolation
  4. Durability
D. Durability. Once COMMIT is acknowledged, the data is permanently stored — even if the server crashes 1ms later. Achieved via Write-Ahead Logging (WAL): the commit record is flushed to disk before acknowledging the client. On restart, the recovery manager redoes committed transactions from the log. Atomicity = all-or-nothing. Consistency = valid states. Isolation = no interference.
L1 — RememberACID
Q2

In lock-based concurrency, a Shared (S) lock allows:

  1. Other transactions to acquire both S and X locks
  2. Other transactions to acquire S locks but NOT X locks on the same data item
  3. No other locks at all
  4. Only the same transaction to read
B. Shared lock = read lock. Multiple transactions can hold S locks simultaneously (multiple readers OK). But an X (exclusive/write) lock request must WAIT until all S locks are released. Rule: S-S compatible, S-X incompatible, X-X incompatible. This allows concurrent reads while preventing read-write conflicts.
L1 — RememberLocking
Q3

What are the four SQL isolation levels, from weakest to strongest?

  1. READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
  2. SERIALIZABLE, REPEATABLE READ, READ COMMITTED, READ UNCOMMITTED
  3. READ COMMITTED, SERIALIZABLE, REPEATABLE READ, READ UNCOMMITTED
  4. There are only two levels
A. Weakest to strongest: READ UNCOMMITTED (allows dirty reads) → READ COMMITTED (prevents dirty reads) → REPEATABLE READ (prevents non-repeatable reads) → SERIALIZABLE (prevents all anomalies including phantoms). Stronger isolation = more correctness but less concurrency/performance. Most databases default to READ COMMITTED.
L1 — RememberIsolation
Q4

What is a dirty read and why is it dangerous?

  1. Reading data from a corrupted disk
  2. Transaction T2 reads a value written by T1 that hasn't committed yet. If T1 later rolls back, T2 has used data that never officially existed — leading to incorrect results, wrong reports, or cascading errors in dependent transactions
  3. Reading the same row twice
  4. Reading from a locked table
B. Dirty read = reading uncommitted data. Example: T1 updates salary to ₹1,00,000 (uncommitted). T2 reads ₹1,00,000 and sends a tax notice. T1 rolls back (salary reverts to ₹50,000). The tax notice is based on a value that never existed. Prevented by READ COMMITTED isolation (Oracle/PG default). READ UNCOMMITTED is the only level that allows it — rarely used in practice.
L2 — UnderstandConcurrency
Q5

Why does Two-Phase Locking (2PL) guarantee conflict serializability?

  1. It doesn't — 2PL only prevents deadlocks
  2. Because 2PL divides each transaction into a growing phase (only acquire locks) and shrinking phase (only release locks). This ordering constraint ensures that conflicting operations between transactions are ordered consistently, making the resulting schedule equivalent to some serial schedule
  3. 2PL locks the entire database
  4. 2PL uses timestamps instead of locks
B. 2PL creates a total ordering of transactions at their "lock points" (the moment they transition from growing to shrinking). All conflicts between T1 and T2 are resolved in the same direction — no cycles possible in the precedence graph. Note: 2PL guarantees serializability but NOT deadlock freedom — deadlocks can still occur when two transactions wait for each other's locks.
L2 — Understand2PL
Q6

What is MVCC and how does it improve performance over traditional locking?

  1. MVCC and locking are the same
  2. MVCC maintains multiple versions of each row. Readers see a consistent snapshot without acquiring locks. Writers create new versions. Result: readers never block writers and writers never block readers — eliminating the biggest performance bottleneck in lock-based systems
  3. MVCC removes all locks
  4. MVCC only works with NoSQL
B. Traditional locking: reader acquires S-lock, writer must WAIT. MVCC: reader sees an older version of the row (snapshot), writer creates a new version — both proceed concurrently. Oracle uses undo segments for old versions. PostgreSQL stores old versions directly in the table (cleaned up by VACUUM). MVCC is why Oracle and PG handle thousands of concurrent users efficiently.
L2 — UnderstandMVCC
Q7

Given: R1(A), R2(A), W1(A), R1(B), R2(B), W2(B). Build the precedence graph and determine if it's conflict serializable.

  1. Not serializable — cycle exists
  2. Conflicts: R2(A) before W1(A) → T2→T1. R1(B) before W2(B) → T1→T2. Graph: T2→T1 and T1→T2. CYCLE exists → NOT conflict serializable
  3. Serializable — equivalent to T1,T2
  4. Cannot determine without more information
B. Step by step: Conflict 1: R2(A) and W1(A) — R2 before W1 → edge T2→T1. Conflict 2: R1(B) and W2(B) — R1 before W2 → edge T1→T2. Precedence graph: T1→T2→T1. CYCLE detected → NOT conflict serializable. This schedule cannot be equivalent to any serial order because T1 must come before T2 (due to B conflict) AND T2 must come before T1 (due to A conflict) — contradiction.
L3 — ApplySerializability
Q8

Write SQL to set up a SERIALIZABLE transaction that reads account balance, deducts ₹5,000, and commits.

  1. Just use UPDATE directly
  2. SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE accounts SET balance = balance - 5000 WHERE account_id = 1001 AND balance >= 5000; COMMIT; — SERIALIZABLE prevents phantoms and non-repeatable reads, ensuring no other transaction can modify the balance between the check and the update
  3. SERIALIZABLE is not a valid SQL keyword
  4. Use READ UNCOMMITTED for best performance
B. SERIALIZABLE isolation ensures the transaction sees a consistent snapshot. The WHERE clause balance >= 5000 prevents overdraft. At SERIALIZABLE level, if another transaction modifies the same account concurrently, one of them will receive a serialization failure error and must retry. This is the strongest guarantee — used for critical financial operations.
🏢 Razorpay uses SERIALIZABLE for payment ledger postings but READ COMMITTED for dashboard queries.
L3 — ApplyIsolation
Q9

Apply Strict 2PL to: T1 reads A, writes A; T2 reads A, writes A. Show the lock/unlock sequence.

  1. No locking needed
  2. T1: Lock-S(A), R(A), Lock-X(A)↑, W(A), COMMIT→Unlock-X(A). T2: Lock-S(A)→WAIT until T1 commits. After T1 commits: T2 gets Lock-S(A), R(A), Lock-X(A)↑, W(A), COMMIT→Unlock-X(A). Strict 2PL: X-locks held until COMMIT. T2 must wait for T1's commit — no dirty reads, no lost updates
  3. Both can lock simultaneously
  4. Locks are released immediately after each operation
B. Strict 2PL: all exclusive locks held until COMMIT. T1 holds X-lock on A until COMMIT. T2's Lock-S(A) request is incompatible with T1's X-lock → T2 WAITS. After T1 commits and releases X(A), T2 acquires S(A) and proceeds. This prevents dirty reads (T2 can't read T1's uncommitted data) and lost updates (serialized access to A). Lock upgrade: S→X is allowed in the growing phase.
L3 — Apply2PL
Q10

Given three transactions with the following wait-for relationships: T1 waits for T2, T2 waits for T3, T3 waits for T1. Analyze for deadlock.

  1. No deadlock — all transactions will eventually complete
  2. DEADLOCK exists. Wait-for graph: T1→T2→T3→T1 forms a cycle. No transaction can proceed. Resolution: abort one transaction (victim). Choose T3 (e.g., least work done). T3 aborted → T2 proceeds → T2 commits → T1 proceeds → T1 commits. Restart T3.
  3. Only two transactions can deadlock
  4. Wait-for graphs can't have 3 nodes
B. Deadlock = cycle in wait-for graph. T1→T2→T3→T1 is a 3-node cycle. Victim selection criteria: (1) least work done (minimize wasted computation), (2) fewest locks held, (3) farthest from completion, (4) priority (avoid starving the same transaction). After aborting T3, its locks are released, T2 gets the lock it was waiting for, completes, then T1 completes. T3 is restarted.
L4 — AnalyzeDeadlocks
Q11

A schedule shows: T1 reads balance=₹10,000, T2 reads balance=₹10,000, T1 writes balance=₹7,000, T2 writes balance=₹5,000. What concurrency problem occurred? What's the final balance?

  1. No problem — final balance is correct
  2. LOST UPDATE. T1's deduction (₹3,000) is overwritten by T2's deduction (₹5,000). Final balance: ₹5,000 (T2's write). Correct balance should be: ₹10,000 - ₹3,000 - ₹5,000 = ₹2,000. The bank LOST ₹3,000. Fix: use SELECT...FOR UPDATE to lock the row before reading, or use SERIALIZABLE isolation
  3. Dirty read
  4. Phantom read
B. Classic lost update: both transactions read the same initial value, compute independently, and the last writer wins — overwriting the first writer's change. T1 deducted ₹3,000 (wrote ₹7,000), but T2's later write of ₹5,000 overwrites it. Net result: only T2's deduction is reflected. The ₹3,000 from T1 vanished. Fix: SELECT...FOR UPDATE acquires an exclusive lock, serializing the reads.
L4 — AnalyzeConcurrency
Q12

A startup uses READ UNCOMMITTED for all transactions "because it's fastest." Their financial reports occasionally show incorrect totals. Evaluate this decision.

  1. READ UNCOMMITTED is fine for all workloads
  2. Critical mistake. READ UNCOMMITTED allows dirty reads — reports can include uncommitted data that may be rolled back, producing incorrect totals. For financial data, at minimum READ COMMITTED is required (Oracle/PG default). For reconciliation/audit reports, SERIALIZABLE should be used. READ UNCOMMITTED is acceptable ONLY for approximate counts or non-critical monitoring dashboards where slight inaccuracy is tolerable
  3. Use SERIALIZABLE for everything
  4. Isolation levels don't affect correctness
B. READ UNCOMMITTED is the weakest level — allows dirty reads, non-repeatable reads, and phantoms. Financial systems MUST use READ COMMITTED at minimum. RBI guidelines require ACID compliance for banking transactions. The "fastest" argument ignores that incorrect data costs more than slightly slower queries. Most production systems: READ COMMITTED for routine operations, SERIALIZABLE for ledger postings.
L5 — EvaluateIsolation
Q13

A DBA team debates: pessimistic locking (SELECT...FOR UPDATE) vs optimistic concurrency control (version column check). Evaluate both for a high-traffic e-commerce inventory system.

  1. Pessimistic is always better
  2. Pessimistic (FOR UPDATE): acquires lock on read — guarantees no conflict but reduces concurrency (other transactions wait). Best for HIGH-contention scenarios (few products with heavy demand, e.g., flash sale). Optimistic (version check): read without lock, check version on update — retry if version changed. Best for LOW-contention scenarios (many products, few conflicts). For Flipkart's flash sale (1000 users buying the same item): pessimistic. For regular catalog browsing: optimistic
  3. Optimistic is always better
  4. Neither works for databases
B. Trade-off: Pessimistic = "assume conflict will happen, lock preemptively." Safe but slow under high concurrency. Optimistic = "assume conflict is rare, detect and retry." Fast when conflicts are rare, but retry overhead increases under contention. E-commerce: use pessimistic FOR UPDATE for inventory deduction during checkout (high contention on popular items), optimistic for product browsing/cart updates (low contention).
L5 — EvaluateDesign
Q14

Design a crash recovery strategy for a hospital database. Explain what happens during: (1) system crash during an INSERT, (2) disk failure, (3) application error during a multi-step procedure.

  1. Just restart the server
  2. (1) System crash during INSERT: WAL recovery — if COMMIT was logged, REDO the INSERT on restart. If not, UNDO the partial INSERT (atomicity preserved). (2) Disk failure: restore from last backup + apply WAL/archive logs for point-in-time recovery (PITR). Oracle: RMAN backup + archived redo logs. PostgreSQL: pg_basebackup + WAL archive. (3) Application error: PL/SQL EXCEPTION handler catches the error, ROLLBACK undoes all changes, logs the error, returns status to caller. SAVEPOINT allows partial rollback of multi-step procedures
  3. Backups are sufficient
  4. Recovery is automatic and needs no planning
B. Defence-in-depth: (1) WAL handles crash recovery automatically (redo committed, undo uncommitted). (2) RMAN/pg_basebackup + WAL archiving enables PITR — recover to any point in time (e.g., "restore to 2 minutes before the accidental DELETE"). (3) Application-level: EXCEPTION handlers + ROLLBACK + SAVEPOINT provide transaction-level recovery. For hospitals (HIPAA compliance): all three layers are mandatory. Plus: standby database for zero-downtime failover.
L6 — CreateRecovery
Q15

Design a complete transaction processing strategy for a UPI payment system handling 10,000 transactions per second.

  1. Use file-based storage
  2. Architecture: (1) Idempotent transactions with unique txn_ref_id (prevents double-spend on retries). (2) Strict 2PL with SELECT...FOR UPDATE for account locking. (3) READ COMMITTED for routine transactions, SERIALIZABLE for end-of-day reconciliation. (4) Consistent lock ordering (by account_id) to prevent deadlocks. (5) WAL with synchronous replication to standby for durability. (6) Connection pooling + partitioned tables for 10K TPS throughput. (7) ARIES recovery for crash resilience. (8) Circuit breaker pattern for downstream bank failures
  3. NoSQL is required for this scale
  4. Single-threaded processing
B. Complete UPI strategy: Idempotency prevents double-charging on retries. Strict 2PL prevents lost updates on concurrent debits. Consistent lock ordering prevents deadlocks between settlement processes. WAL + synchronous replication ensures zero data loss. Partitioning by account_id hash distributes load. Connection pooling (pgBouncer/Oracle UCP) handles 10K concurrent connections. This is the actual architecture used by PhonePe and Razorpay.
L6 — CreateSystem Design
Section 7

Chapter Summary

TRANSACTION PROCESSING │ ├── ACID PROPERTIES │ ├── Atomicity: all or nothing (COMMIT/ROLLBACK) │ ├── Consistency: valid state → valid state │ ├── Isolation: concurrent txns don't interfere (locks/MVCC) │ └── Durability: committed data survives crashes (WAL) │ ├── TRANSACTION STATES │ Active → Partially Committed → Committed │ Active → Failed → Aborted → (Restart or Terminate) │ ├── SCHEDULES & SERIALIZABILITY │ ├── Serial schedule: no interleaving (correct but slow) │ ├── Conflict serializable: equivalent to some serial schedule │ ├── Precedence graph: nodes=txns, edges=conflicts │ │ No cycle → serializable ✓ | Cycle → NOT serializable ✗ │ └── View serializability (weaker, NP-complete to test) │ ├── CONCURRENCY PROBLEMS │ ├── Dirty read: reading uncommitted data │ ├── Non-repeatable read: same query, different value │ ├── Phantom read: same query, different ROWS │ └── Lost update: last writer wins, earlier write lost │ ├── LOCKING │ ├── S (Shared/Read): multiple S OK, blocks X │ ├── X (Exclusive/Write): blocks both S and X │ ├── 2PL: growing phase → shrinking phase │ ├── Strict 2PL: X-locks held until COMMIT (industry standard) │ └── FOR UPDATE: explicit row locking in SQL │ ├── DEADLOCKS │ ├── Cycle in wait-for graph → deadlock │ ├── Detection: periodic graph scan, abort victim │ ├── Prevention: Wait-Die, Wound-Wait, consistent ordering │ └── Timeout: abort if waiting too long │ ├── ISOLATION LEVELS (weakest → strongest) │ ├── READ UNCOMMITTED: allows dirty reads │ ├── READ COMMITTED: prevents dirty reads (Oracle/PG default) │ ├── REPEATABLE READ: prevents non-repeatable reads │ └── SERIALIZABLE: prevents all anomalies │ ├── MVCC │ ├── Multiple row versions → readers don't block writers │ ├── Oracle: undo segments | PG: tuple versioning + VACUUM │ └── Eliminates read-write contention │ └── RECOVERY ├── WAL: log before data (Write-Ahead Logging) ├── ARIES: Analysis → Redo → Undo ├── Checkpoints: limit recovery scan range └── PITR: point-in-time recovery from backups + WAL

🎯 3 Skills This Chapter Unlocks

  1. Transaction Design — You can design ACID-compliant transactions for banking, payments, and e-commerce. Every enterprise developer must know COMMIT, ROLLBACK, SAVEPOINT, and FOR UPDATE.
  2. Concurrency Reasoning — You can analyze schedules for serializability, detect deadlocks, and choose appropriate isolation levels. This is a core GATE CS topic (5-8 marks) and a senior DBA interview essential.
  3. Recovery Architecture — You understand how databases survive crashes. WAL, checkpoints, and ARIES are the foundation of database reliability that banks, hospitals, and governments depend on daily.

📋 Transaction Processing Quick Reference

ACID: Atomicity (all/nothing), Consistency (valid states),
      Isolation (no interference), Durability (survives crash)

TRANSACTION: BEGIN → DML → COMMIT or ROLLBACK
  SAVEPOINT sp1; ... ROLLBACK TO sp1;

SERIALIZABILITY TEST:
  1. Find conflict pairs (diff Tx, same item, ≥1 write)
  2. Draw precedence graph (edges: earlier conflict → later)
  3. No cycle = serializable ✓ | Cycle = NOT serializable ✗

LOCKS: S (read) — multiple OK | X (write) — exclusive only
  SELECT ... FOR UPDATE;  -- Acquires X-lock on selected rows

2PL: Growing (acquire) → Shrinking (release)
  Strict 2PL: X-locks until COMMIT (industry standard)

DEADLOCK: Cycle in wait-for graph → abort victim
  Prevention: lock resources in consistent order (LEAST/GREATEST)

ISOLATION LEVELS:
  READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE
  Default: READ COMMITTED (Oracle, PostgreSQL)

RECOVERY: WAL (Write-Ahead Log) + ARIES (Analysis → Redo → Undo)
  Checkpoint: limits recovery scan | PITR: backup + WAL replay
Section 8

Interview & Career Preparation

Q1: Explain ACID properties with a real-world example.

Model Answer: Bank transfer of ₹10,000: Atomicity — if debit succeeds but credit fails, both are rolled back (money doesn't vanish). Consistency — total money before = after (₹80,000 stays ₹80,000). Isolation — if someone checks the balance mid-transfer, they see either before or after, never an intermediate state. Durability — after COMMIT, even if the server crashes, the transfer is permanent (WAL ensures this).

Q2: What is conflict serializability? How do you test it?

Model Answer: A schedule is conflict serializable if it can be transformed into a serial schedule by swapping non-conflicting operations. Test: build a precedence graph — node per transaction, edge Ti→Tj if Ti has a conflicting operation before Tj. If the graph has NO CYCLE, the schedule is conflict serializable. The topological sort of the graph gives the equivalent serial order. Two operations conflict if they access the same data item, belong to different transactions, and at least one is a write.

Q3: What is Two-Phase Locking (2PL)?

Model Answer: 2PL requires each transaction to divide its locking into two phases: Growing phase (only acquire locks, never release) and Shrinking phase (only release locks, never acquire). This guarantees conflict serializability. Strict 2PL (industry standard) holds all exclusive locks until COMMIT — prevents cascading aborts and dirty reads. 2PL does NOT prevent deadlocks — two transactions can still wait for each other's locks.

Q4: What is a deadlock? How do databases handle it?

Model Answer: Deadlock: two or more transactions each hold locks that the other needs, creating a cycle where none can proceed. Detection: DBMS periodically checks the wait-for graph for cycles. If found, one transaction (the victim) is aborted and its locks released. Prevention: consistent lock ordering (always lock resources in the same sequence), timeout-based abortion. Oracle detects deadlocks within seconds and raises ORA-00060.

Q5: Explain the four isolation levels.

Model Answer: READ UNCOMMITTED: allows dirty reads (reading uncommitted data). READ COMMITTED: prevents dirty reads; each query sees only committed data (Oracle/PG default). REPEATABLE READ: prevents non-repeatable reads; same query returns same value within a transaction. SERIALIZABLE: prevents all anomalies including phantoms; transactions appear to run serially. Trade-off: stronger isolation = fewer anomalies but more lock contention and lower throughput.

Q6: What is MVCC? How does it differ from locking?

Model Answer: MVCC (Multi-Version Concurrency Control) maintains multiple versions of each row. Instead of blocking readers with locks, each transaction sees a consistent snapshot of the data as of its start time. Readers don't block writers; writers don't block readers. Oracle uses undo segments to store old row versions. PostgreSQL stores old versions in the table (cleaned by VACUUM). MVCC provides better concurrency than pure locking because read-write contention is eliminated.

Q7: What is Write-Ahead Logging (WAL)?

Model Answer: WAL rule: before any data modification is written to the data file, the corresponding log record must be written to the log file first. This ensures recoverability: if the system crashes after writing the log but before writing data, the recovery manager can REDO the operation from the log. If the crash occurs before the COMMIT log record, the recovery manager UNDOs the incomplete transaction. WAL is the foundation of durability in ACID. Both Oracle (redo log) and PostgreSQL (WAL) use this mechanism.

Q8: How would you prevent a lost update in SQL?

Model Answer: Three approaches: (1) Pessimistic: SELECT balance FROM accounts WHERE id=1 FOR UPDATE — locks the row before reading, preventing other transactions from reading/modifying it until COMMIT. (2) Optimistic: add a version column. Read version, UPDATE with WHERE version = read_version. If 0 rows updated, someone else modified it — retry. (3) Atomic UPDATE: UPDATE accounts SET balance = balance - 5000 WHERE id=1 AND balance >= 5000 — reads and writes in one atomic operation. Approach 1 for high-contention; approach 3 is simplest for single-column updates.

Q9: What is the ARIES recovery algorithm?

Model Answer: ARIES (Algorithm for Recovery and Isolation Exploiting Semantics) has three phases: (1) Analysis: scan log from last checkpoint, identify active transactions and dirty pages at crash time. (2) Redo: replay ALL logged operations to bring database to crash-time state — both committed and uncommitted. (3) Undo: roll back uncommitted transactions in reverse order, writing Compensation Log Records (CLRs) to prevent re-undoing on subsequent crashes. ARIES uses LSN (Log Sequence Numbers) to avoid redundant redo and supports fuzzy checkpoints for better performance.

Q10: How do companies like PhonePe handle 10,000 UPI transactions per second?

Model Answer: Architecture: (1) Horizontal partitioning — accounts partitioned by hash(account_id) across multiple database shards. Each shard handles ~1000 TPS. (2) Connection pooling — pgBouncer/Oracle UCP reuses database connections. (3) READ COMMITTED for most operations (fast); SERIALIZABLE only for reconciliation. (4) Idempotent design — unique txn_ref_id prevents double-charging on retries. (5) Consistent lock ordering prevents deadlocks. (6) Synchronous WAL replication to standby for zero RPO. (7) Circuit breakers for downstream bank failures. (8) Monitoring: lock contention, deadlock frequency, WAL lag.