Conflict Serializability

Imagine a bank database processing two transactions at once. One transfers money between accounts. The other computes the total balance across all accounts. If the database interleaves their operations carelessly, problems arise. The balance report might read one account before the transfer credits it. It might also read another account after the transfer debits it. This produces a total that never existed at any real point in time. Concurrent execution is essential for throughput, but uncontrolled interleaving corrupts correctness. The database needs a precise, checkable definition of "safe interleaving." It also needs a fast way to test it. Conflict serializability is that definition. It identifies exactly which pairs of operations from different transactions can be safely reordered without changing the outcome. It builds a rigorous notion of equivalence between an interleaved schedule and some serial (one-transaction-at-a-time) execution. Crucially, conflict serializability is not just a definition. It comes with an efficient, purely mechanical test: draw a precedence graph and check for cycles. This turns a subtle semantic question ("did this schedule behave as if transactions ran one after another?") into a graph algorithm. Any scheduler can run this algorithm in linear time. This matters because real database engines cannot afford to run transactions strictly one at a time. That would throw away all the parallelism modern hardware offers. Instead, engines allow interleaving but enforce rules. These rules include locking protocols, timestamp ordering, and optimistic validation. They are designed specifically so that every schedule they produce is conflict serializable. The precedence-graph test is the theoretical yardstick against which those protocols are justified. A protocol is correct precisely because it can be proven to only ever generate acyclic precedence graphs. This article defines conflicting operations and conflict equivalence precisely. It walks through the precedence-graph algorithm step by step, with pseudocode and a worked trace. It proves why cycle-freedom is exactly equivalent to serializability. It also discusses the pitfalls of applying the test naively. It closes by connecting conflict serializability to the concurrency-control protocols. These are the Two-Phase Locking and Timestamp-Ordering Protocol that engineers actually deploy to enforce it.

Key takeaways
  • Two operations conflict if they belong to different transactions, act on the same data item, and at least one is a write.
  • A schedule is conflict serializable if you can transform it into a serial schedule by swapping only non-conflicting adjacent operations.
  • Build the precedence graph: one node per transaction, an edge Ti→TjT_i \to T_j whenever an operation of TiT_i precedes and conflicts with an operation of TjT_j.
  • Theorem: a schedule is conflict serializable if and only if its precedence graph is acyclic.
  • Testing acyclicity takes O(V+E)O(V+E) time via DFS-based cycle detection — cheap enough to reason about, though real systems enforce serializability by construction rather than by testing every schedule.
  • Conflict serializability is a sufficient, not necessary, condition for correctness — it is stricter than (a subset of) general Serializability and Schedules, but it is the condition that concrete protocols like locking actually guarantee.
The core insight

A schedule is conflict serializable exactly when its precedence graph — built from conflicting operation pairs — contains no cycle.

The idea

Two operations from different transactions conflict if they access the same data item and at least one of them is a write. Formally, for operations OiO_i (from TiT_i) and OjO_j (from TjT_j, i≠ji \neq j) on the same item QQ: read/read never conflicts, read/write and write/read conflict, and write/write conflicts. Read-read pairs are the only non-conflicting case because swapping two reads never changes what either transaction observes. Conflicting operations are the ones whose relative order matters. If two operations do not conflict, swapping their order in a schedule cannot change the final database state or what any transaction reads, because either they touch disjoint data or they are both reads of the same data. This gives a notion of equivalence: two schedules SS and S′S' over the same set of transactions are conflict equivalent if SS can be transformed into S′S' by a sequence of swaps of consecutive, non-conflicting operations belonging to different transactions. Conflict equivalence is coarser than requiring identical operation order — it says the schedules agree on the order of every conflicting pair. A schedule is conflict serializable if it is conflict equivalent to some serial schedule (one in which transactions run entirely one after another, with no interleaving). This is a precise, checkable strengthening of general serializability (which only requires equivalence in final effect, allowing view equivalence and phenomena like blind writes). Every conflict serializable schedule is (view) serializable, but the converse can fail — there exist view serializable schedules containing blind writes that are not conflict serializable. In practice, conflict serializability is the workhorse notion because, unlike general serializability, it is checkable in polynomial time and it is exactly the guarantee that mainstream concurrency-control protocols provide.

How it works

The mechanical test for conflict serializability builds a precedence graph (also called a serialization graph) G=(V,E)G = (V, E):

  • VV contains one node per transaction in the schedule.
  • For every pair of conflicting operations Oi∈TiO_i \in T_i and Oj∈TjO_j \in T_j where OiO_i appears before OjO_j in the schedule, add a directed edge Ti→TjT_i \to T_j.

Multiple conflicts between the same pair of transactions collapse to a single edge (we only care about existence of at least one precedence, and — critically — the direction must be consistent; if TiT_i's operations always precede TjT_j's conflicting operations, or vice versa, in a serializable schedule the direction between any given ordered pair is fixed by the earliest conflict, but it is worth checking that all conflicts between the same pair agree in direction, since a schedule with conflicting operations pointing both ways between the same two transactions is trivially non-serializable — that shows up as a 2-cycle, which the general cycle test already catches). Once the graph is built, the test is: the schedule is conflict serializable if and only if the precedence graph is acyclic. If acyclic, any topological sort of the graph gives a serial schedule that is conflict equivalent to the original — this is not just a heuristic, it is the constructive half of the proof below. ``` ALGORITHM BuildPrecedenceGraph(schedule S, transactions T_1..T_n) // S is a list of operations: each op has (transaction_id, type in {R,W}, data_item) create graph G with nodes T_1 .. T_n, no edges for each unordered pair (T_i, T_j), i != j: conflict_found_direction = NONE // tracks first-seen direction, for sanity check for position p = 1 .. length(S): O_p = S[p] if O_p.transaction == T_i: for position q = p+1 .. length(S): O_q = S[q] if O_q.transaction == T_j and O_q.item == O_p.item and (O_p.type == W or O_q.type == W): add edge T_i -> T_j to G // T_i's op precedes T_j's conflicting op // (symmetric scan also covers T_j before T_i, yielding T_j -> T_i if it occurs) return G ALGORITHM IsConflictSerializable(G): return NOT HasCycle(G) ALGORITHM HasCycle(G): // standard DFS-based cycle detection mark all nodes WHITE // unvisited for each node v in G: if color[v] == WHITE: if DFS_Visit(v) == CYCLE_FOUND: return true return false ALGORITHM DFS_Visit(u): color[u] = GRAY // on current recursion stack for each edge (u -> v) in G: if color[v] == GRAY: return CYCLE_FOUND // back edge -> cycle if color[v] == WHITE: if DFS_Visit(v) == CYCLE_FOUND: return CYCLE_FOUND color[u] = BLACK // fully explored, safe return NO_CYCLE ALGORITHM SerialOrderFromGraph(G): // constructive: acyclic => produces a valid serial order return TopologicalSort(G) // Kahn's algorithm or DFS-finish-order reversal ``` A practical implementation builds the graph in a single left-to-right scan of the schedule, maintaining for each data item the set of transactions that have touched it so far and whether the touch was a read or write, adding an edge each time a new operation conflicts with a previously recorded one. That single-pass construction is what makes real systems (and dependency-graph-based scheduler audits) run this in linear time rather than the naive quadratic pairwise scan shown above for clarity.

Figure
Precedence graph construction from an interleaved schedule
Schedule S (time flows left to right) T1: R(A) W(A) W(B) T2: R(A) R(B) T3: W(B) R(A) Conflicts detected: 1) T1.W(A) before T2.R(A) → edge T1 → T2 (write/read on A) 2) T2.R(B) before T3.W(B) → edge T2 → T3 (read/write on B) 3) T3.W(B) before T1.W(B) → edge T3 → T1 (write/write on B) 4) T1.R(A) before T3.R(A) → no edge (read/read never conflicts) Resulting precedence graph T1 T2 T3 Cycle T1 → T2 → T3 → T1 ⇒ NOT conflict serializable

Complexity & correctness

Building the graph. With nn transactions and a schedule of mm operations, the naive pairwise scan shown for clarity is O(m2)O(m^2) in the worst case (comparing every operation against every later operation). The single-pass streaming version — for each data item, remember the last reader(s) and last writer, and emit an edge on every new access that conflicts with a remembered one — processes each operation in O(1)O(1) amortized work against a hash map keyed by data item, giving O(m)O(m) time and O(m+n)O(m + n) space overall (the graph has at most O(n2)O(n^2) edges, but redundant edges between the same ordered pair are typically collapsed, capping the graph itself at O(n2)O(n^2) edges and O(n)O(n) nodes).

Testing acyclicity. Standard DFS-based cycle detection visits every node and edge once: O(V+E)=O(n+n2)=O(n2)O(V + E) = O(n + n^2) = O(n^2) in the worst case, but in practice with sparse conflict structure this is close to O(n)O(n). This is exponentially cheaper than the alternative of enumerating all n!n! possible serial orders and checking each for equivalence.

Correctness — the theorem and why it holds. > Theorem. A schedule SS is conflict serializable if and only if its precedence graph G(S)G(S) is acyclic. (⇐\Leftarrow) Acyclic implies conflict serializable — constructive direction. If G(S)G(S) has no cycle, it admits at least one topological order Tπ(1),Tπ(2),…,Tπ(n)T_{\pi(1)}, T_{\pi(2)}, \dots, T_{\pi(n)}. Claim: the serial schedule S′S' that runs transactions in exactly this order is conflict equivalent to SS. Every edge Ti→TjT_i \to T_j in G(S)G(S) means some operation of TiT_i conflicts with, and precedes, some operation of TjT_j in SS; because the order is topological, TiT_i comes before TjT_j in S′S' too, so every conflicting pair keeps the same relative order in both schedules. Two schedules over the same operations that agree on the order of every conflicting pair are, by definition, conflict equivalent (one can be reached from the other by swapping only non-conflicting adjacent pairs, since disagreement about order only ever arises from conflicting pairs by definition of the graph). Hence SS is conflict equivalent to a serial schedule, i.e., conflict serializable. (⇒\Rightarrow) Conflict serializable implies acyclic — contrapositive argument. Suppose G(S)G(S) has a cycle T1→T2→⋯→Tk→T1T_1 \to T_2 \to \cdots \to T_k \to T_1. Each edge Ti→Ti+1T_i \to T_{i+1} certifies a conflicting operation pair ordered with TiT_i first. If SS were conflict equivalent to some serial schedule S′S', then S′S' must place transactions in an order consistent with every edge, since conflict-equivalent schedules preserve the relative order of every conflicting pair. But the cycle demands T1T_1 before T2T_2 before … before TkT_k before T1T_1 again — a strict total order cannot satisfy a cyclic set of precedence constraints. Contradiction; so no such serial S′S' exists, and SS is not conflict serializable. ■\blacksquare

Honesty about scope and tradeoffs. Conflict serializability is a sufficient condition for the isolation guarantee databases actually want (equivalence to some serial execution), and it is strictly stronger than necessary: there exist correct, view-serializable schedules (typically ones with "blind writes" — a write with no matching read in between two other writes to the same item) that are not conflict serializable, yet still produce a state indistinguishable from a serial execution. Conflict serializability trades a small amount of completeness for a test that is efficiently checkable and, more importantly, efficiently enforceable on the fly: locking protocols and timestamp protocols can guarantee acyclicity of the precedence graph as they go, without ever materializing or inspecting the graph after the fact. That is the real payoff — the theorem is the justification for why those protocols are correct, not a runtime check bolted onto production schedulers.

Worked example

Consider three transactions accessing accounts AA and BB:

StepOperationTransaction
1R(A)T1
2R(A)T2
3W(A)T1
4R(B)T2
5W(B)T3
6R(A)T3
7W(B)T1

Scanning left to right and recording, per data item, the transactions that have accessed it so far:

Item A — accesses in order: T1(R), T2(R), T1(W), T3(R).

  • T1(R) vs T2(R): both reads, no conflict.
  • T1(W) [step 3] vs earlier T2(R) [step 2]: conflict (write follows read on same item) — since T2's read precedes T1's write, this records T2→T1T2 \to T1.
  • T1(W) [step 3] vs T3(R) [step 6]: T1's write precedes T3's read — conflict, edge T1→T3T1 \to T3.

Item B — accesses in order: T2(R) [step 4], T3(W) [step 5], T1(W) [step 7].

  • T2(R) before T3(W): conflict, edge T2→T3T2 \to T3.
  • T2(R) before T1(W) [step 7 is later]: conflict, edge T2→T1T2 \to T1 (already have this edge from item A, no new edge needed).
  • T3(W) before T1(W): conflict, edge T3→T1T3 \to T1.

Collected edges: T2→T1T2 \to T1, T1→T3T1 \to T3, T2→T3T2 \to T3, T3→T1T3 \to T1. Run `HasCycle`: start DFS at T2T2 (color GRAY). Follow T2→T1T2 \to T1: T1T1 GRAY. Follow T1→T3T1 \to T3: T3T3 GRAY. Follow T3→T1T3 \to T1: T1T1 is GRAY (on the current stack) — back edge found, cycle detected: T1→T3→T1T1 \to T3 \to T1.

Verdict: not conflict serializable. Indeed, the cycle says T1T1's write to AA must precede T3T3's read of AA (so T1T1 before T3T3), but T3T3's write to BB must precede T1T1's write to BB (so T3T3 before T1T1) — no serial order can satisfy both simultaneously, exactly matching the proof's contrapositive argument. Now modify step 7 to `R(B)` by T1 instead of `W(B)`, removing the write/write conflict on BB between T3 and T1. Recomputing: the edge T3→T1T3 \to T1 disappears (read/write with T3's write first and T1's read after is still a conflict — read after write on the same item still conflicts! — so actually this edge would remain). To genuinely break the cycle, suppose instead T1 never touches BB at all. Then the edges are just T2→T1T2 \to T1, T1→T3T1 \to T3, T2→T3T2 \to T3 — a DAG. Topological order: T2,T1,T3T2, T1, T3. The equivalent serial schedule runs all of T2T2's operations, then all of T1T1's, then all of T3T3's, and this serial execution is guaranteed to produce results consistent with the original interleaved schedule on every conflicting access.

Common pitfalls & traps

PitfallWhy it happensFix
Treating read-read as a conflictConfusing "same data item" with "conflict"; forgetting the "at least one write" clauseRecheck the definition: R/R pairs never generate an edge
Forgetting self-conflicts don't countApplying the rule to two operations from the same transactionConflicts are only defined between different transactions; intra-transaction order is fixed by program order already
Adding an edge for every conflicting operation pair instead of collapsingLeads to redundant multi-edges; doesn't break correctness but wastes time/spaceTrack only the direction between each transaction pair (or dedupe edges); a single edge per direction suffices
Confusing conflict serializability with view serializabilityAssuming "not conflict serializable" means "definitely wrong"Remember conflict serializability is sufficient but not necessary; some schedules with blind writes are correct (view serializable) yet fail the conflict test
Building the graph after the fact and expecting it to prevent bad schedulesThe precedence graph is a diagnostic on a completed (or hypothetical) schedule, not a live enforcement mechanismReal systems don't build precedence graphs online for every schedule; they use protocols (2PL, timestamp ordering) proven to never create cycles
Assuming acyclicity implies the original schedule is literally identical to a serial oneAcyclicity only proves conflict-equivalence to some serial order, not that SS itself is serialThe topological sort gives a valid serial-equivalent order — SS can still be heavily interleaved

Pioneers & history

Origins · 1 minPhilip A. Bernstein and how this was discovered
PA
Philip A. Bernstein
1948–present · Database systems researcher who, with Nathan Goodman, formalized concurrency control theory including conflict serializability and the precedence-graph test in the late 1970s and early 1980s

Bernstein's work with Nathan Goodman gave the database community a rigorous mathematical toolkit — conflict equivalence, serialization graphs, and formal correctness proofs for locking and timestamp protocols — that turned concurrency control from folklore into a provable discipline still taught unchanged today.

History
1976
— Kung and Papadimitriou (with Eswaran, Gray, Lorie, Traiger at IBM) formalize serializability as the correctness criterion for concurrent transaction execution.
1979
— Bernstein and Goodman publish foundational papers formalizing conflict equivalence and the serialization (precedence) graph test.
1981
— Bernstein, Goodman, and Hadzilacos's survey systematizes concurrency control theory, cementing conflict serializability as the standard sufficient condition used to justify locking protocols.
1980
s — Two-phase locking and timestamp-ordering protocols are proven correct precisely by showing they can never generate a cyclic precedence graph.
Present — Conflict serializability remains the textbook criterion underlying isolation guarantees in relational database engines, though many systems relax it deliberately via snapshot isolation for performance.

Where this matters

Every mainstream relational database — PostgreSQL, MySQL/InnoDB, Oracle, SQL Server — implements a concurrency-control subsystem whose correctness proof rests on conflict serializability. Lock managers enforcing Two-Phase Locking are designed, and proven correct, by showing that the locking discipline can never produce a cycle in the precedence graph: once a transaction releases a lock, it acquires no new ones, which structurally forecloses the possibility of a cyclic wait-and-conflict pattern. Systems using the Timestamp-Ordering Protocol instead assign transactions a total order up front and abort any operation that would violate it, which is equivalent to guaranteeing the precedence graph's edges always point in the timestamp order — an acyclic graph by construction. Beyond textbook engines, distributed databases and NewSQL systems (Spanner, CockroachDB, YugabyteDB) still reason about conflict graphs when justifying their serializable isolation levels, even though they layer in techniques like multi-version concurrency control (MVCC) and global timestamp oracles. Query optimizers and replication systems that reorder operations for performance must respect conflict relationships to avoid silently breaking application correctness. Database testing tools (Jepsen-style linearizability/serializability checkers) construct precedence-like graphs from observed traces to certify or falsify that a production system actually delivered the isolation level it advertised.

Ask the tutor: Can you show why a schedule with a blind write can be view serializable but fail the conflict-serializability cycle test?

Why does read-read never count as a conflict?

Two reads of the same item by different transactions never affect each other's outcome or the database state — reordering them changes nothing observable. Conflict serializability is built entirely around operations whose order matters, and the only way order can matter is if at least one operation modifies data that the other observes or overwrites. If both operations only observe (read) the data, swapping their execution order produces identical results for both transactions and leaves the database unchanged. This is why the formal definition explicitly excludes read/read pairs: including them would add spurious edges to the precedence graph, potentially creating false cycles and rejecting perfectly valid, serializable schedules.

Is conflict serializability the same thing as serializability?

No — conflict serializability is a specific, stricter sufficient condition for the broader property of Serializability and Schedules. General serializability only requires that a schedule produce the same final database state and the same values read by each transaction as some serial schedule (this weaker notion is called view serializability). Conflict serializability additionally requires that every pair of conflicting operations appear in the same relative order as in that serial schedule. Every conflict serializable schedule is view serializable, but schedules with "blind writes" can be view serializable without being conflict serializable. Engines use conflict serializability anyway because, unlike general view serializability (which is NP-hard to test), it is checkable and enforceable in polynomial time.

Why is testing general view serializability harder than testing conflict serializability?

Testing view serializability directly is known to be NP-complete in the general case, because it requires searching over all possible serial orders and checking a more permissive equivalence (matching which transaction wrote the value each transaction reads, including "final writer" semantics for blind writes) rather than a simple pairwise order-preservation check. Conflict serializability sidesteps this entirely: it reduces to building a graph in roughly linear time and checking acyclicity in O(V+E) time, a textbook-cheap algorithm. This tractability, not superior correctness coverage, is precisely why conflict serializability — despite being strictly stronger and thus rejecting some correct schedules — is the criterion actually used to design and verify real concurrency-control protocols.

Do real databases build a precedence graph for every transaction schedule at runtime?

Almost never, directly. Building and checking a precedence graph after transactions have already executed is a useful analysis and teaching tool, but it doesn't prevent bad interleavings from happening in the first place — by the time you'd detect a cycle, the damage (if any) is done. Instead, real systems use protocols — Two-Phase Locking, the Timestamp-Ordering Protocol, or multiversion concurrency control — that are mathematically proven, once and for all, to never produce a cyclic precedence graph no matter how transactions interleave. The precedence graph is the theoretical certificate of correctness for these protocols, not a live runtime data structure most engines maintain explicitly.

What is a "blind write" and why does it break conflict serializability but not view serializability?

A blind write is a write to a data item by a transaction that never reads that item first. Consider T1 writes A, then T2 writes A (with no read in between by either), then T3 reads A. Conflict serializability cares about the order of every write/write pair, so it would demand a fixed relative order between T1 and T2's writes even though, if T3 only ever sees T2's value, the actual order between T1 and T2 might not matter for the final observable outcome. View serializability's equivalence notion only requires each transaction see the correct predecessor's value and the final writer to match — it can tolerate reordering the "irrelevant" blind write, producing a valid view-serializable schedule that nonetheless has a cycle in its conflict precedence graph.

How does conflict serializability connect to locking protocols like Two-Phase Locking?

Two-Phase Locking (2PL) requires every transaction to acquire all its locks before releasing any — a "growing phase" followed by a "shrinking phase." The correctness proof for 2PL is a direct application of the precedence-graph theorem: one shows that if two transactions' lock phases were structured to create a cycle in the precedence graph, some transaction would have had to acquire a lock after another released a conflicting one in a way that violates the two-phase rule, a contradiction. Because 2PL structurally forbids the interleavings that would create cycles, every schedule 2PL produces is guaranteed conflict serializable — the graph theorem is the reason 2PL is trusted, not just a coincidental resemblance.

Can a schedule have multiple valid serial orders it is conflict equivalent to?

Yes. If the precedence graph is acyclic but not a total order (i.e., it has incomparable pairs of transactions with no path between them in either direction), then multiple distinct topological sorts exist, and the schedule is conflict equivalent to each of the corresponding serial schedules. This reflects that some transactions' relative order genuinely doesn't matter for correctness — they never touched the same data in a conflicting way — so any of several serial executions would have produced the same observable behavior for every conflicting access. The schedule is still unambiguously conflict serializable; it just isn't equivalent to a unique serial order.

Before this

Where this leads