Timestamp-Based vs Lock-Based Concurrency Control
Disadvantages of Timestamp-Based Protocols
Read aloud is unavailable in this browser.
The Price of Timestamp Ordering
No concurrency control mechanism is without cost. While timestamp-based protocols offer compelling advantages—deadlock freedom, non-blocking operations, and distributed system suitability—they come with significant disadvantages that can make them unsuitable for certain workloads and system requirements.
Understanding these disadvantages is not about disparaging timestamp protocols; it's about engineering wisdom. The best architects know not only when to use a technique but when not to use it. The scenarios where timestamp protocols suffer are well-understood, and recognizing them prevents costly architectural mistakes.
This page examines the fundamental limitations of timestamp ordering: cascading aborts, wasted work, high-contention performance collapse, timestamp management complexity, and long transaction challenges. By the end, you'll have a balanced view that enables sound protocol selection.
By the end of this page, you will understand the specific disadvantages of timestamp-based protocols: cascading aborts, restart overhead and wasted work, performance degradation under high contention, timestamp generation complexity, long transaction problems, and starvation risks. You'll know when to avoid timestamp protocols in favor of alternatives.
Cascading Aborts
One of the most significant problems with basic timestamp ordering is the risk of cascading aborts—a chain reaction where one transaction's abort forces the abort of multiple other transactions that read its uncommitted data.
The Cascade Mechanism
In timestamp ordering, transactions may read uncommitted data written by other transactions. Consider this scenario:
- Transaction T₁ (TS=100) writes value X = 50
- Transaction T₂ (TS=150) reads X (sees T₁'s uncommitted value 50)
- Transaction T₂ uses X in its subsequent operations
- Transaction T₁ aborts (due to some other conflict)
Now T₂ has a problem: it read and used a value that never officially existed. T₂'s computation is based on dirty data. T₂ must also abort.
But the cascade doesn't stop there:
- Transaction T₃ (TS=200) read Y, which T₂ wrote based on X
- T₂ aborts → T₃ also has dirty data → T₃ must abort
- Any transaction that read T₃'s uncommitted writes must also abort
This cascade can propagate through many transactions, amplifying the impact of a single abort.
Quantifying the Cost
The cascading abort problem is severe because:
Work Multiplication: If each transaction does W work, and a cascade affects N transactions, total wasted work is N × W. Unlike lock-based systems where work is preserved until completion or victim selection, cascades destroy work that was "almost done."
Unpredictable Latency: A transaction may complete 99% of its work, then be aborted because of a cascade triggered by a transaction that started long ago. Latency becomes highly variable.
Resource Waste: CPU, I/O, memory allocations, network calls—all the resources consumed by cascaded transactions are wasted.
Propagation Delay: Cascades may not trigger immediately. T₃ may not discover it needs to abort until T₁'s abort is processed and propagated. In distributed systems, this can take significant time.
Prevention Strategies
Several approaches mitigate cascading aborts:
Strict Timestamp Ordering: Only allow reading committed data. Transactions wait for uncommitted writes to commit before reading. This re-introduces blocking but eliminates cascades.
Thomas Write Rule Variant: Carefully control which writes are visible to prevent dirty reads.
MVCC: Multi-version approaches provide committed snapshots, avoiding dirty reads while maintaining non-blocking properties.
However, each mitigation either re-introduces some blocking or adds complexity, eroding the purity of timestamp ordering's non-blocking advantage.
More concurrent transactions mean more opportunity for dirty reads. Hot data items that many transactions access become cascade amplifiers. A single abort of a core transaction can cascade through dozens of dependent transactions, magnifying the wasted work dramatically.
Restart Overhead and Wasted Work
The fundamental mechanism of timestamp ordering—abort and restart on conflict—carries inherent overhead that lock-based blocking avoids. This wasted work becomes the dominant performance factor under certain conditions.
Anatomy of a Restart
When a transaction aborts, the following must occur:
- Rollback: All changes made by the transaction must be undone
- Resource Release: Memory, buffer pins, file handles must be freed
- Log Management: Undo records must be written for recovery purposes
- Timestamp Assignment: A new, higher timestamp must be assigned
- State Reset: Transaction begins fresh with no completed operations
- Re-execution: All work must be performed again from the beginning
Each restart is a full repetition of the transaction's work, plus overhead for cleanup and setup.
Cost Analysis
Let's model the restart cost more precisely:
Parameters:
- W = Work units per transaction (CPU, I/O, etc.)
- P = Probability of conflict requiring restart
- R = Overhead per restart (rollback, cleanup, etc.)
Expected Work per Successful Transaction: With geometric distribution of retries:
Expected Work = W × (1 + P + P² + P³ + ...) + R × (P + P² + P³ + ...) = W / (1 - P) + R × P / (1 - P) = (W + R × P) / (1 - P)
As P approaches 1 (high contention), expected work approaches infinity. The system enters livelock where transactions repeatedly abort each other.
| Conflict Probability (P) | Expected Attempts | Expected Work (W=100, R=10) |
|---|---|---|
| 0.01 (1%) | 1.01 | 101.1 (1% overhead) |
| 0.10 (10%) | 1.11 | 112.2 (12% overhead) |
| 0.25 (25%) | 1.33 | 136.7 (37% overhead) |
| 0.50 (50%) | 2.00 | 210.0 (110% overhead) |
| 0.75 (75%) | 4.00 | 430.0 (330% overhead) |
| 0.90 (90%) | 10.00 | 1090.0 (990% overhead) |
| 0.99 (99%) | 100.00 | 10990.0 (10890% overhead) |
The Contrast with Lock-Based Blocking
In lock-based systems, a transaction that cannot proceed waits rather than restarts:
Lock-Based Expected Work: = W + Average Wait Time
Even under high contention, wait time is bounded by the time for conflicting transactions to complete. Work is never wasted—only delayed.
Key Insight: Lock-based systems trade CPU utilization (idle during waits) for work preservation. Timestamp systems trade work (discarded on restart) for CPU utilization (never idle waiting).
When work is expensive (long transactions, external service calls, complex computations), lock-based blocking is often preferable. When work is cheap and contention is low, timestamp restart overhead is minimal.
- •Full rollback of completed operations
- •Re-execution from beginning
- •All resources consumed again
- •Potential repeated restarts
- •Unpredictable completion time
- •Scales poorly with transaction length
- •No work discarded
- •Resume from wait point
- •Resources held during wait
- •Bounded wait (no repeat)
- •More predictable latency
- •Independent of transaction length
Rule of thumb: If your transaction involves expensive operations (network calls to external services, complex computations, writes to slow storage), favor lock-based approaches. The cost of redoing that work on restart exceeds the cost of waiting. If transactions are lightweight (in-memory operations, simple lookups), timestamp restarts are acceptable.
High-Contention Performance Collapse
Under high contention—when many transactions access the same data items—timestamp protocols can experience performance collapse: throughput drops dramatically as most work is wasted on restarts. This is the most severe practical disadvantage.
The Contention Death Spiral
High contention creates a feedback loop:
- Hot Spot: Multiple transactions target the same data item
- Conflicts: Timestamp comparisons frequently fail
- Restarts: Transactions abort and restart with new timestamps
- More Conflicts: Restarted transactions conflict with currently active ones
- More Restarts: The restart rate increases
- Throughput Collapse: Work completion rate drops while CPU spins on restarts
This differs fundamentally from lock-based degradation:
Lock-Based Under Contention:
- Transactions queue for the hot resource
- Each completes before the next begins
- Throughput serializes but remains stable
- Work is never wasted
Timestamp Under Contention:
- All transactions proceed concurrently
- Most abort and restart
- Effective throughput may drop below serial execution
- Massive work is wasted
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
// HIGH CONTENTION SCENARIO: Bank Counter Example// ================================================ // Scenario: 100 transactions trying to increment a single counter // TIMESTAMP PROTOCOL BEHAVIOR:timestamp_simulation(): counter = 0 w_timestamp = 0 active_transactions = 100 completed = 0 total_attempts = 0 while completed < 100: for each active transaction T with TS(T): total_attempts++ // T tries to write counter++ if TS(T) < w_timestamp: // Conflict: another transaction wrote after T started // T must abort and restart restart(T) with new_timestamp > current_max_timestamp else: // T succeeds counter++ w_timestamp = TS(T) completed++ remove T from active // RESULT: With 100 concurrent transactions on 1 item: // - Expected attempts: O(n²) in worst case // - Total_attempts might be 5000+ for 100 completions // - Efficiency: 100/5000 = 2% useful work // LOCK-BASED BEHAVIOR:lock_simulation(): counter = 0 queue = [T1, T2, ..., T100] // All 100 transactions for each T in queue: // T waits for lock, then executes acquire_lock(counter) counter++ // Exactly 1 operation release_lock(counter) completed++ // RESULT: // - Exactly 100 operations for 100 completions // - Efficiency: 100% (no wasted work) // - But: sequential execution, wait time overheadWhen Collapse Occurs
The contention threshold for collapse depends on:
Hot Spot Intensity: How many transactions access the same item simultaneously? A single global sequence number is extremely hot. Per-user data with millions of users is cool.
Transaction Duration: Longer transactions means longer conflict windows. Two 10ms transactions are 100x more likely to overlap than two 0.1ms transactions.
Workload Pattern: Zipfian access (80% of accesses to 20% of data) creates hot spots. Uniform random access distributes contention.
Time Locality: If transactions accessing the same item arrive in bursts, contention is higher than uniform arrival.
Mitigation Strategies
Several techniques can prevent or recover from collapse:
Back-off Strategies: After restart, delay before retrying. Randomized exponential backoff reduces collision probability.
Batching: Combine multiple conflicting operations into a single transaction that executes once.
Partitioning: Redesign data model to reduce hot spots. E.g., per-region counters instead of global counter.
Hybrid Protocols: Use locks for known hot spots, timestamps for the rest.
However, these mitigations add complexity and may not fully solve pathological cases.
A global counter, sequence generator, or any single frequently-updated value is the worst-case scenario for timestamp protocols. Every transaction conflicts with every other. Never use pure timestamp ordering for such workloads—use locks, batching, or sharded counters.
Timestamp Generation Complexity
The correctness of timestamp protocols depends critically on globally unique, totally ordered timestamps. Generating such timestamps—especially in distributed systems—introduces non-trivial complexity.
Requirements for Correct Timestamps
Uniqueness: No two transactions may share a timestamp. If TS(T₁) = TS(T₂), the protocol cannot determine their relative order.
Total Ordering: For any two timestamps, it must be decidable which is greater. Partial orders don't suffice.
Monotonicity: Timestamps assigned later must be greater than those assigned earlier so that causally later transactions have higher timestamps.
Consistency: If T₁ happens-before T₂ (causally), TS(T₁) < TS(T₂) must hold. Otherwise, the timestamp order would violate causality.
Single-Node Timestamp Generation
On a single node, timestamp generation is straightforward:
System Clock: Use current time. Risk: clock can jump backward (NTP adjustments, leap seconds).
Logical Counter: Increment on each transaction. Simple and safe but provides no correlation to real time.
Hybrid: Combine physical time with logical counter. Handles clock quirks while providing physical time benefits.
Distributed Timestamp Challenges
In distributed systems, timestamp generation becomes difficult:
| Approach | Description | Challenges |
|---|---|---|
| Centralized | Single coordinator assigns all timestamps | Coordinator is bottleneck and single point of failure |
| Physical Clocks | Each node uses its local clock | Clock skew causes inconsistencies; NTP not precise enough |
| Lamport Clocks | Logical counters updated on messages | No physical time; can't query "as of time T" |
| Vector Clocks | Vector of per-node counters | O(n) space per timestamp; no total order natively |
| Hybrid Logical Clocks | Physical time + logical counter | Complex; must handle clock skew and jumps |
| TrueTime (Spanner) | GPS + atomic clocks with uncertainty bounds | Expensive hardware; Google-scale infrastructure |
Clock Skew Problems
In distributed systems, physical clocks are never perfectly synchronized. Consider:
- Node A's clock says 10:00:00.000, assigns TS = 1000000
- Node B's clock says 10:00:00.050 (50ms ahead), assigns TS = 1000050
- Causally, T_B depends on T_A (T_B started after seeing T_A's effect)
- But TS(T_B) > TS(T_A), suggesting T_B should serialize after T_A ✓
- Consider the reverse: T_A starts after T_B's effect but gets lower TS
- Timestamp order now violates causality!
This can cause:
- Incorrect serialization (reads see "future" writes)
- Phantom conflicts (aborts that shouldn't occur)
- Lost updates (writes that seem "older" than they are)
Solutions and Their Costs
Wait-Out Uncertainty (TrueTime): If timestamp uncertainty is [earliest, latest], wait until physical time exceeds latest before commit. Adds latency proportional to uncertainty.
Logical Timestamps (Hybrid Clocks): Accept that timestamp order may not match physical time exactly. Sacrifice "as of" query precision.
Synchronized Clocks (PTP/GPS): Invest in precise time synchronization infrastructure. Expensive and complex to operate.
Each solution adds either latency, complexity, or infrastructure cost. Lock-based protocols avoid these issues entirely—locks are local state, not time-dependent.
Timestamp protocols assume reliable, consistent timestamp generation—often taken for granted in single-node systems but extremely difficult to achieve globally in distributed settings. Many distributed timestamp protocol failures stem from clock-related assumptions that don't hold in practice.
Long Transaction Problems
Long-running transactions pose particular challenges for timestamp protocols. Their extended duration creates pathological interactions that can degrade system performance for all transactions.
The Old Timestamp Problem
A long-running transaction receives its timestamp when it begins. If the transaction runs for an extended period, its timestamp becomes "old" relative to new transactions. This creates problems:
Scenario: Transaction T_long starts at TS = 1000, runs for 10 seconds
- During those 10 seconds, 1000 other short transactions start with TS = 1001-2000
- Many of these transactions write to data items T_long hasn't yet accessed
- When T_long finally accesses those items, W-timestamp > TS(T_long)
- T_long aborts after 10 seconds of work
- T_long restarts with TS = 2001, but now it's racing against even more transactions
This pattern can cause starvation of long transactions, where the long transaction repeatedly aborts despite performing substantial work each time.
Impact Analysis
Work Waste: Each restart discards seconds or minutes of work. The ratio of wasted work to useful work can be enormous.
Resource Holding: During execution, long transactions hold resources (memory, connections) longer, increasing overall resource pressure.
Blocking Others (Indirect): While timestamp protocols don't block directly, a long transaction's old timestamp can cause many short transactions to abort when they conflict with it—effectively blocking progress.
Deadline Failures: If the long transaction has a deadline (e.g., batch job must complete by midnight), repeated restarts may cause deadline misses.
Lock-Based Contrast
Long transactions in lock-based systems have different problems:
Lock Holding Duration: They hold locks for extended periods, blocking others Deadlock Contribution: More locks held means more deadlock opportunities But: Their work is never wasted. When a long transaction completes, it's done.
The trade-off is clear:
- Timestamp: Long transaction's work may be wasted multiple times
- Lock: Long transaction delays others but always completes its work
- •Transaction Decomposition: Break long transaction into shorter ones. Complex for ACID requirements but reduces conflict window.
- •Priority Scheduling: Give long transactions priority access. But may starve short transactions.
- •Reserved Timestamps: Allocate timestamp ranges or reservations for long transactions. Adds complexity.
- •Pessimistic Locking for Long Txns: Use locks when transaction is expected to be long. Hybrid approach.
- •Batch Processing Windows: Run long transactions during low-activity periods. Operational constraint.
Pure timestamp ordering is poorly suited for workloads with a mix of long and short transactions accessing the same data. Long transactions suffer repeated restarts while short transactions may abort when conflicting with persistent old timestamps. Consider lock-based or hybrid approaches for such workloads.
Starvation Risks
While timestamp protocols eliminate deadlock, they introduce starvation risks—situations where certain transactions never complete, repeatedly aborting despite using resources. Starvation is subtler than deadlock but equally problematic.
Starvation Scenarios
Scenario 1: The Slow Reader
A transaction T_slow (TS = 1000) performs many read operations slowly:
- T_slow reads item A (succeeds, takes 100ms)
- Meanwhile, T_fast (TS = 1500) writes to item B
- T_slow (still TS = 1000) tries to read B
- 1000 < 1500 (W-timestamp of B) → T_slow aborts
- T_slow restarts with TS = 1600, but the pattern repeats
If new fast transactions continuously write to items T_slow needs, T_slow may never complete.
Scenario 2: Conflicting Restart Pattern
Transactions T₁ and T₂ repeatedly conflict:
- T₁ (TS=100) and T₂ (TS=200) both access X and Y
- T₁ writes X, T₂ writes Y
- T₂ tries to read X → aborts (100 < 200 for X's W-TS)
- T₂ restarts as TS=300
- T₁ tries to write Y → aborts (100 < 200 for Y's R-TS from T₂'s failed attempt)
- Pattern may continue indefinitely depending on timing
Starvation vs Deadlock
| Aspect | Deadlock | Starvation |
|---|---|---|
| Definition | Circular wait, no progress possible | Repeated restarts, no completion |
| Detection | Wait-for graph cycle | Difficult—no clear pattern |
| Scope | Finite set of transactions | Potentially affects single transaction |
| Recovery | Abort victim | No clear victim (everyone active) |
| Lock-based | Can occur | Can be prevented with fair queuing |
| Timestamp | Cannot occur | Can occur (no queuing) |
Why Starvation is Harder to Handle
Starvation is insidious because:
No Clear Detection: Unlike deadlock (cycle in wait-for graph), starvation has no definitive signal. A transaction that restarted 10 times might succeed on attempt 11—or attempt 1000.
No Clear Victim: In timestamp protocols, the starving transaction is "aborted" but not for anyone's benefit. It's just unlucky timing.
Progressive Degradation: As load increases, starvation probability increases for certain transactions. The system partially works (some transactions complete) but certain patterns consistently fail.
Difficult Reproduction: Starvation depends on timing and interleaving. It may not reproduce in testing but manifest in production under specific load patterns.
Mitigation Strategies
Restart Limits with Escalation:
- After N restarts, escalate transaction priority
- May involve acquiring "reservation" on needed resources
- Adds complexity but provides progress guarantee
Wound-Wait / Wait-Die:
- Timestamp-based priority: older transactions have priority
- Wound-wait: Older wounds (aborts) younger on conflict
- Wait-die: Younger dies (aborts) on conflict with older
- Ensures older transactions eventually complete
Random Backoff:
- After restart, delay random time before retry
- Reduces collision probability
- No guarantee but statistically effective
12345678910111213141516171819202122232425262728293031323334
// WOUND-WAIT PROTOCOL// Ensures older transactions have priority to prevent starvation function handleConflict(Tᵢ, Tⱼ, resource): // Tᵢ is requesting access, Tⱼ currently holds/conflicts if TS(Tᵢ) < TS(Tⱼ): // Tᵢ is OLDER - it has priority // "Wound" Tⱼ: abort Tⱼ so Tᵢ can proceed abort(Tⱼ) grant_access(Tᵢ, resource) log("Older transaction wounds younger") else: // Tᵢ is YOUNGER - it yields // In pure form: Tᵢ aborts and restarts // In wait variant: Tᵢ waits for Tⱼ (reintroduces blocking) abort_and_restart(Tᵢ) log("Younger transaction yields") // Properties:// - Older transactions eventually complete (younger yield)// - No deadlock (all waits are in one direction: young → old)// - But: younger transactions may repeatedly restart// - Trade-off: Younger starvation possible instead of older // WAIT-DIE PROTOCOL (Alternative)function handleConflict_WaitDie(Tᵢ, Tⱼ, resource): if TS(Tᵢ) < TS(Tⱼ): // Tᵢ is OLDER - it waits for younger Tⱼ // (Reintroduces blocking but no deadlock: waits are old→young) wait(Tᵢ, until Tⱼ completes) else: // Tᵢ is YOUNGER - it "dies" (aborts) abort_and_restart(Tᵢ)All starvation prevention mechanisms add complexity to the basic timestamp protocol. Wound-wait and wait-die are well-established but introduce blocking (wait-die) or forced aborts (wound-wait). The elegance of pure timestamp ordering's simplicity is eroded when starvation must be addressed.
Storage and Bookkeeping Overhead
While timestamp protocols avoid the lock table overhead of lock-based protocols, they introduce their own storage and bookkeeping costs that can be significant at scale.
Per-Item Timestamp Storage
Every data item requires timestamp metadata:
Basic Protocol:
- W-timestamp: 8 bytes (64-bit timestamp)
- R-timestamp: 8 bytes
- Total: 16 bytes per data item
For a database with 1 billion rows, each averaging 100 bytes:
- Data storage: 100 GB
- Timestamp overhead: 16 GB (16% overhead)
With MVCC (multiple versions):
- Each version needs creation timestamp
- Potentially deletion/obsolescence timestamp
- Transaction ID for visibility determination
- Overhead compounds with version count
Comparison with Lock Table
Lock-based systems maintain a lock table, but this is typically smaller:
Lock Table Entry Size: ~32-64 bytes per locked item (lock mode, holder list, wait queue)
But: Lock entries only exist for currently locked items. Most data is not locked at any given time.
For the same 1 billion rows:
- If 0.1% of data is locked at any time: 1M entries × 64 bytes = 64 MB
- Timestamps: 16 GB (always present)
Timestamp overhead is fixed and proportional to data size. Lock table overhead is variable and proportional to concurrency level.
| Aspect | Timestamp Protocol | Lock-Based Protocol |
|---|---|---|
| Per-item metadata | 16+ bytes always | 0 bytes when unlocked |
| Active transaction overhead | Minimal | Lock table entries |
| 100M rows, 1% locked | 1.6 GB timestamps | ~64 MB lock table |
| 100M rows, 50% locked | 1.6 GB timestamps | ~3.2 GB lock table |
| MVCC versions | Metadata per version | N/A (single version) |
| Scales with | Data volume | Concurrency level |
Bookkeeping Operations
Timestamp protocols require maintenance operations:
Timestamp Updates: Every read updates R-timestamp (compare-and-swap operation). This is more write activity than lock-based reads (which don't modify any persistent state on read).
Version Garbage Collection (MVCC): Old versions must be identified and removed. This requires tracking active transactions and their snapshot timestamps, then background GC processes.
Transaction Activity Tracking: To determine version visibility, the system must know which transactions are active and their start times. This requires structure maintenance.
Impact on Index Structures
If timestamps are stored with data, they affect index organization:
Primary Storage: Timestamp fields increase row size, affecting I/O and cache efficiency.
Secondary Indexes: Index entries may need timestamp awareness for correct point-in-time queries.
Index Maintenance: Updates to R-timestamp on reads may require index updates if R-timestamp is indexed (unusual but possible for audit purposes).
Cache Efficiency: Larger rows mean fewer rows per cache line, reducing cache hit rates.
R-timestamp updates mean that even read-only transactions modify data pages. This has implications: read-only replicas may need to apply R-timestamp updates (or maintain separate timestamp storage), and page cache dirty rates are higher than in lock-based read operations. This "read amplification" is often overlooked in timestamp protocol analysis.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.