1.11. Transactional Guarantees#
QuasarDB provides ACID guarantees through a combination of Multi-Version Concurrency Control (MVCC) and Copy-on-Write (CoW) mechanisms. Understanding these concepts helps you make informed decisions about insertion modes and performance tradeoffs.
1.11.1. Multi-Version Concurrency Control (MVCC)#
1.11.1.1. What is MVCC?#
MVCC is a concurrency control method that allows multiple transactions to access the same data simultaneously without blocking each other. Instead of locking data, MVCC creates multiple versions of each data item.
Key characteristics:
Each transaction sees a consistent snapshot of the database at a point in time
Readers never block writers, and writers never block readers
Multiple versions of the same data can coexist temporarily
Old versions are cleaned up through trimming
Benefits:
High concurrency: queries and writes proceed in parallel without contention
Consistent reads: queries always see a stable snapshot of data
No read locks: queries never wait for writes to complete
Tradeoffs:
Storage overhead: multiple versions consume disk space until trimmed
Cleanup cost: periodic trimming is required to reclaim space
1.11.2. Copy-on-Write (CoW)#
1.11.2.1. What is Copy-on-Write?#
Copy-on-Write is a transactional write strategy that ensures strong consistency by creating a complete copy of modified data before making changes visible.
When you write data using Copy-on-Write:
Buffer writes: New data is buffered separately from existing data
Create new version: A new version of the affected shard is written to disk
Atomic switch: Once complete, the new version atomically replaces the old version
Cleanup: The old version becomes eligible for trimming
Strong consistency guarantee:
Data is never visible to queries until the entire transaction completes successfully
Either all changes are visible, or none are (atomic commit)
Queries always see fully committed, consistent data
1.11.2.2. Why QuasarDB Uses Copy-on-Write#
Copy-on-Write provides several benefits for transactional integrity:
Transactional atomicity:
All changes within a batch become visible simultaneously. If 1 million rows are inserted, queries see either 0 rows or all 1 million rows—never partial results.
Crash safety:
Incomplete writes are automatically discarded on recovery. If a transaction fails mid-write, no partial data pollutes the database.
Consistent snapshots:
Queries always see a stable view of committed data, making it ideal for analytical workloads requiring consistent results.
1.11.2.3. The Cost of Copy-on-Write#
Copy-on-Write provides strong guarantees but has performance implications:
Write amplification:
Every incremental insert into an existing shard requires:
Reading the existing shard from disk
Merging new data with existing data
Re-sorting the combined dataset
Writing the entire shard back to disk
For example, inserting 1,000 rows into a shard containing 500,000 rows requires rewriting all 501,000 rows. This amplification increases with shard size.
Lock contention:
While the new version is being written, the shard remains locked for writes. Multiple concurrent writes to the same shard must wait sequentially.
Disk I/O pressure:
Large incremental inserts into many existing shards generate significant disk write traffic, potentially saturating I/O bandwidth.
1.11.3. Insertion Modes and Copy-on-Write#
QuasarDB’s batch inserter provides multiple insertion modes with different Copy-on-Write behaviors:
1.11.3.1. Default Mode (with Copy-on-Write)#
Behavior:
Uses Copy-on-Write for all operations
Guarantees strong consistency
Data visibility: only after complete commit
Use when:
You need strict transactional guarantees
Analytical queries require consistent snapshots
Write amplification is acceptable for your workload
Example scenarios:
End-of-day batch processing where consistency matters more than speed
Loading reference data that other queries depend on
Scenarios where partial data visibility would corrupt downstream processing
1.11.3.2. Fast Mode (without Copy-on-Write)#
Behavior:
Bypasses Copy-on-Write mechanism
Uses direct, in-place writes
Data visibility: may be visible before commit completes
Use when:
You need maximum write throughput
Disk I/O is the bottleneck
Eventual consistency is acceptable
Example scenarios:
High-volume ingestion where every second counts
Bulk historical data loads where partial visibility doesn’t matter
Write-heavy workloads constrained by disk bandwidth
Important caveat:
Queries may observe partial data from uncommitted transactions. For example, if you insert 1 million rows, queries might see 427,000 rows before the transaction completes. This is acceptable for many use cases but may not be suitable for workflows requiring strong consistency.
1.11.3.3. Asynchronous Mode (special case)#
Behavior:
Buffers data in-memory in async pipelines before writing to disk
Multiple sources write to shared buffers
Periodic background flush combines writes from many sources
After flush: uses Copy-on-Write for transactional commit
Use when:
Many processes write small batches to the same tables simultaneously
You want to reduce write amplification by combining writes from multiple sources
You can tolerate buffering delay (typically seconds)
Example scenarios:
Streaming data from hundreds of IoT sensors into the same table
Real-time financial tick data from multiple market feeds
Log aggregation from distributed services
1.11.3.4. Truncate Mode (with Copy-on-Write)#
Behavior:
Uses Copy-on-Write to atomically replace existing data
Old data is deleted, new data is inserted in single transaction
Ensures atomic replacement: queries never see a partially truncated state
Use when:
Replaying or replacing historical data
Updating entire time ranges atomically
Example scenarios:
Reprocessing historical data with improved algorithms
Correcting data quality issues by replacing affected ranges
1.11.4. Choosing the Right Insertion Mode#
Insertion mode |
Copy-on-Write? |
Consistency |
Write amplification |
Best for |
|---|---|---|---|---|
Default |
Yes |
Strong |
High |
General-purpose, consistency-critical workloads |
Fast |
No |
Eventual |
Low |
Bulk loads, I/O-constrained scenarios |
Asynchronous |
Yes (on flush) |
Strong (post-flush) |
Reduced |
Streaming, many concurrent small writes |
Truncate |
Yes |
Strong |
High |
Historical data replacement |
1.11.4.1. Decision guide#
Choose Default mode if:
You need queries to always see fully committed data
Partial data visibility would corrupt your application logic
Write performance is acceptable
Choose Fast mode if:
Disk I/O is your bottleneck (performance monitoring shows high write latency)
You can tolerate queries seeing partial transaction results
You need maximum write throughput
Choose Asynchronous mode if:
Many processes write small batches to the same tables simultaneously
You can tolerate a few seconds of buffering delay
You want to reduce write amplification automatically
Choose Truncate mode if:
You need to atomically replace existing data ranges
You’re reprocessing or correcting historical data
1.11.5. See Also#
Batch inserter - Detailed batch inserter API documentation
Compaction and Trimming - LSM-tree compaction and MVCC trimming
Shards - Understanding shards and write amplification
Async Pipelines - How asynchronous buffering works
Reaching your performance goals - Performance tuning guide