Write batches
A WriteBatch groups any number of Put / Merge / Delete / DeleteRange operations into a single atomic write. Either every operation in the batch lands, or none do. Batches are also significantly faster than individual writes because they amortise the WAL fsync and the MemTable insertion overhead.
Upstream reference: Basic Operations — WriteBatch.
The basics
using var batch = new WriteBatch();
batch.Put("a", "1");
batch.Put("b", "2");
batch.Delete("c");
db.Write(batch);
Anything you can do on RocksDb (put, merge, delete, delete-range) you can do on a WriteBatch. The batch buffers the operations in-memory until you hand it to db.Write.
API surface
Every overload accepts an optional ColumnFamilyHandle. The methods are fluent — they return the batch so you can chain.
| Method | What it does |
|---|---|
Put(key, value, cf?) |
Insert / overwrite. |
Merge(key, value, cf?) |
Append an operand for the merge operator. |
Delete(key, cf?) |
Write a tombstone. |
DeleteRange(startKey, sklen, endKey, eklen, cf?) |
Tombstone a contiguous half-open range. |
PutLogData(blob, len) |
Add metadata that travels in the WAL but isn't applied to the MemTable. |
Clear() |
Reset the batch (reuse it). |
Count() |
Number of operations queued. |
SetSavePoint() / RollbackToSavePoint() |
Speculative writes. |
ToBytes() |
Serialize for replication / persistence. |
Iterate(state, putCb, deleteCb) |
Walk every operation. |
Spans are supported on net6+:
batch.Put("k"u8, "v"u8);
batch.Delete("k"u8);
Atomicity
A WriteBatch is the only way to make a multi-key write atomic in RocksDB. Either everything in the batch is visible after db.Write, or nothing is.
using var batch = new WriteBatch();
batch.Delete("draft:42");
batch.Put("order:42", "pending");
batch.Put("invoice:42","draft");
db.Write(batch); // atomic
Even if the process dies mid-write, recovery from the WAL replays the whole batch or none of it.
DeleteRange
DeleteRange(start, end) tombstones every key in [start, end) — a half-open range — in a single record. Far cheaper than millions of individual Delete calls when you need to purge a bucket.
using var batch = new WriteBatch();
batch.DeleteRange("user:1000:"u8, 11, "user:1001:"u8, 11);
db.Write(batch);
See the DeleteRange wiki page for performance caveats — range tombstones add cost to subsequent reads in the affected range until compaction clears them.
Save points
Save points let you roll back the tail of a batch:
using var batch = new WriteBatch();
batch.Put("a", "1");
batch.SetSavePoint();
batch.Put("b", "2");
batch.Put("c", "3");
if (!ShouldKeepTail())
{
batch.RollbackToSavePoint(); // drops b and c
}
db.Write(batch);
Persisting and replaying batches
ToBytes() returns the serialized batch — exactly the format RocksDB writes to the WAL. You can ship those bytes across the wire and replay them on another instance, which is what the Replication guide does for WAL streaming.
byte[] wire = batch.ToBytes();
// Later, on another node:
using var replay = new WriteBatch(wire);
otherDb.Write(replay);
WriteBatch.Iterate exposes every operation in the batch via callbacks — useful for inspection or selective replay:
batch.Iterate(IntPtr.Zero,
put: (state, k, klen, v, vlen) => { /* … */ },
deleted: (state, k, klen) => { /* … */ });
WriteBatchWithIndex
A regular WriteBatch is write-only — you can't read your own pending writes. WriteBatchWithIndex indexes the batch in-memory so you can:
Geta key and see staged writes on top of the database.- Iterate over a "virtual" view that merges staged writes with the underlying DB.
using var wbi = new WriteBatchWithIndex();
wbi.Put("k", "staged");
string v = wbi.Get(db, "k"); // "staged" — your own pending write
wbi.Delete("other");
using var baseIt = db.NewIterator();
using var it = wbi.NewIterator(baseIt);
for (it.SeekToFirst(); it.Valid(); it.Next())
{
// sees staged writes merged with the live DB
}
db.Write(wbi);
Upstream reference: Write Batch With Index.
Tuning the write
Pass WriteOptions to db.Write for per-call durability / WAL behaviour:
var sync = new WriteOptions().SetSync(true);
db.Write(batch, sync);
var noWal = new WriteOptions().DisableWal(1);
db.Write(batch, noWal); // dangerous — see WAL docs