Table of Contents

Bulk load

In one line: an opt-in, exclusive second write path for seeding/importing — it skips per-row WAL entirely and substitutes one session-wide durability barrier. A third axis, orthogonal to DurabilityMode.

The normal transaction path is tuned for OLTP: every commit appends a WAL record, and every open transaction pins the pages it touched. Seeding a multi-million-entity database inverts that trade — the per-row WAL traffic and epoch-pinned pages exhaust the page cache and the load dies on a backpressure timeout, whatever DurabilityMode you picked. Bulk load exists for that one workload, and is isolated from every other transaction's durability contract.

BeginBulkLoad opens a session over a single unit of work and transaction, configured to write no per-row WAL records. Internally it recycles its transaction every few thousand operations so the page cache can keep evicting; the UoW itself stays open — and MVCC-invisible to everyone else — for the whole session. CompleteBulkLoad() is the single barrier that makes the load durable and visible: commit the last transaction, force a checkpoint, and wait for a closing manifest record to reach disk.

⚠️ All-or-nothing at session granularity. Nothing you write is durable or visible until CompleteBulkLoad() returns — Dispose without it, or a crash mid-session, discards the entire load. It is also exclusive (one per engine) and thread-affine; Update/Destroy only reach entities spawned in the same session; and CompleteBulkLoad blocks on a real checkpoint cycle with no latency budget. Concurrent readers are unaffected throughout — they keep seeing the pre-bulk snapshot.

How it relates

In the API

Learn & use