docs/components/write_flow/01_write_apis.md
Files: db/db_impl/db_impl_write.cc, include/rocksdb/write_batch.h, db/write_batch.cc, include/rocksdb/options.h
RocksDB provides multiple write operations, all ultimately funneling through DBImpl::WriteImpl():
| API | ValueType | Description |
|---|---|---|
Put(key, value) | kTypeValue | Insert or overwrite key |
Delete(key) | kTypeDeletion | Tombstone covering all older versions |
SingleDelete(key) | kTypeSingleDeletion | Tombstone pairing with exactly one Put |
DeleteRange(start, end) | kTypeRangeDeletion | Tombstone covering [start, end) |
Merge(key, operand) | kTypeMerge | Apply merge operator to key |
PutEntity(key, columns) | kTypeWideColumnEntity | Wide-column insert |
TimedPut(key, value, write_unix_time) | kTypeValuePreferredSeqno | Put with explicit write time for compaction |
Write(WriteBatch) | Mixed | Atomic batch of operations |
All single-operation APIs construct a WriteBatch internally and call DB::Write(), which dispatches to DBImpl::WriteImpl(). The Merge operation additionally validates that a merge operator is configured via ColumnFamilyOptions::merge_operator (see include/rocksdb/advanced_options.h).
A WriteBatch serializes multiple operations into a single binary buffer (the rep_ field), ensuring atomicity: all operations succeed or all fail together.
Header (12 bytes):
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 8 | sequence | Placeholder; filled by write leader before WAL append |
| 8 | 4 | count | Number of operations in the batch |
Per-operation encoding (variable length, repeated):
| Field | Encoding | Description |
|---|---|---|
| tag | uint8 | ValueType identifier |
| cf_id | varint32 | Column family ID (only for CF-prefixed op tags) |
| key | varint32 length + bytes | User key |
| value | varint32 length + bytes | Value (for Put/Merge/PutEntity ops) |
Important: The sequence number field in the header is initialized to 0. The write group leader stamps the assigned sequence into the merged batch header via WriteBatchInternal::SetSequence() before appending the record to the WAL. Individual writer sequence assignments for memtable insertion happen after the WAL write completes.
The WriteBatch class (see include/rocksdb/write_batch.h) tracks several metadata fields:
| Field | Type | Purpose |
|---|---|---|
rep_ | std::string | Binary buffer containing header + operations |
content_flags_ | atomic<uint32_t> | Bitmask of operation types present (enables fast checks like HasDeleteRange()) |
prot_info_ | unique_ptr<ProtectionInfo> | Optional per-entry checksums (8 bytes per key when enabled) |
save_points_ | unique_ptr<SavePoints> | Rollback snapshots for transaction support |
The content_flags_ field enables O(1) queries like HasMerge() and HasDeleteRange() without scanning the batch contents. These flags are set during operation insertion and checked during write path validation.
Before entering the write path, DBImpl::WriteImpl() validates the request. The following per-write option combinations are rejected:
| Condition | Reason |
|---|---|
sync && disableWAL | Cannot sync without a WAL |
HasDeleteRange() && row_cache | DeleteRange invalidation not supported with row cache |
disableWAL && recycle_log_file_num > 0 | Recycled WAL corruption detection requires sequential sequences |
protection_bytes_per_key not 0 or 8 | Only two protection levels supported |
rate_limiter_priority not IO_TOTAL or IO_USER | Implementation constraint |
The disableWAL && recycle_log_file_num check has an exception: WritePreparedTxnDB uses disableWAL internally for split writes (WAL-only prepare + memtable-only commit), which is allowed when two_write_queues && disable_memtable.
These immutable DBOptions conflicts are also checked at runtime in WriteImpl():
| Condition | Reason |
|---|---|
two_write_queues && enable_pipelined_write | Incompatible write modes |
unordered_write && enable_pipelined_write | Incompatible write modes |
seq_per_batch && enable_pipelined_write | Pipelined write does not support seq_per_batch |
When WriteOptions::low_pri is set and compaction pressure exists (WriteController::NeedSpeedupCompaction() returns true), the write is rate-limited via WriteController::low_pri_rate_limiter() before entering the main write path. This rate limiter is separate from the main write delay mechanism and allows low-priority writes to make slow progress even under compaction pressure.
For two-phase commit (2PC), commit and rollback batches are exempt from low-priority throttling to avoid blocking transaction completion.
After validation, WriteImpl() dispatches to one of four write modes based on configuration:
two_write_queues_ && disable_memtable): Routes to WriteImplWALOnly() via the non-memtable write threadunordered_write): WAL write via WriteImplWALOnly(), then independent memtable insert via UnorderedWriteMemtable()enable_pipelined_write): Routes to PipelinedWriteImpl() with overlapping WAL and memtable phasesSee Write Modes for detailed descriptions of each mode.