Connecting It All Together

Concept. A complete transaction system needs a correctness standard, conflict-serializability, and engines that target it. 2PL hits it exactly, locking rows through the lock manager. MVCC trades it for non-blocking reads: its default, snapshot isolation, is a weaker bar that permits write skew, and it reaches full serializability only at the SERIALIZABLE level. Together they let thousands of transactions run concurrently, each at a chosen correctness level.

Intuition. When Mickey's playlist update and Minnie's profile edit run at the same time, 2PL serializes them only on the rows they share, the lock manager blocks them on those rows alone (not the whole table), and the resulting schedule is provably equivalent to running them one-after-the-other. All the theory shows up in a single concurrent execution.

Implementation Motivation

The earlier pages built the theory piece by piece. Here it assembles into a working transaction system.

The Goal: Implement 2PL locking (or S2PL) to produce conflict-serializable schedules and beat serial execution.


Implementation: S2PL in Practice

Sample Code for S2PL

# Strict 2PL: acquire locks as you read/write, release them all at commit.
class Transaction:
    def __init__(self, txn_id):
        self.id = txn_id
        self.locks_held = set()

    def read(self, item):
        lock_manager.acquire_lock(self.id, item, "SHARED")      # grow
        self.locks_held.add(item)
        return database.read(item)

    def write(self, item, value):
        lock_manager.acquire_lock(self.id, item, "EXCLUSIVE")   # grow
        self.locks_held.add(item)
        database.write(item, value)

    def commit(self):
        for item in self.locks_held:                            # shrink: all at once
            lock_manager.release_lock(self.id, item)
        self.locks_held.clear()

Try it yourself: See the full implementation in the Transactions Colab


The Big Picture

The full transaction-system architecture, top to bottom: the application issues transactions, the transaction manager coordinates them, the lock manager or MVCC grants locks or keeps versions and its grant order is the conflict-serializable schedule, and the recovery manager makes commits survive crashes, together delivering ACID.

Figure 1. The whole concurrency stack in one view: schedules decide the interleaving, the lock manager or MVCC enforces it, and conflict-serializability is the bar 2PL passes (MVCC meets a weaker one, snapshot isolation, by default). Each piece had its own page; here they connect into one transaction system.

Two routes, two correctness levels: 2PL makes a reader wait for a writer and produces conflict-serializable executions; MVCC keeps old versions so readers never block, producing snapshot isolation by default and conflict-serializable executions only at the SERIALIZABLE level.

Component Integration

One transaction, top to bottom:

1. Application: issues transactions (BEGIN ... COMMIT)
2. Transaction Manager: coordinates execution
3. Lock Manager (or MVCC): grants locks (2PL, conflict-serializable) or keeps versions (MVCC, snapshot isolation by default)
4. Recovery Manager: makes commits survive crashes (the next module)

Together they deliver the ACID guarantees.

The performance numbers (49 → 31 time units, ~37% speedup on the worked example; 10× to 100× on IO-bound workloads at scale) come from Microschedules & Deadlock.


The Power of Transaction Managers: Beyond Simple Locks

The same three ideas scale far past two transactions on one machine. The exact lock that serializes Mickey and Minnie here also runs stock exchanges, airline seat maps, and bank ledgers, where the same conflict-serializability guarantee holds across millions of transactions a second. Pushing 2PL across many machines is its own problem, and that is where Module 5 picks up.

The same two-phase locking at three operating points: it distributes (one transaction locks rows in New York, San Francisco, and Tokyo with 2PL held across all three), it recovers (the recovery manager replays the write-ahead log after a crash so committed work survives), and it scales (stock exchanges and bank ledgers push millions of transactions a second).

Figure 2. The same protocol stretches three ways. It distributes, holding two-phase locking across machines in different cities; it recovers, replaying the write-ahead log after a crash so committed work survives; and it scales, running millions of transactions a second on stock exchanges and bank ledgers. Same core, different operating point.


Next

Change Tracking: Log the Diff → Concurrency control protects transactions from each other. Recovery protects them from crashes. Together they make ACID real, and recovery starts with one idea: log the diff, don't copy.