Class BulkLoadSession
An opt-in, throughput-first write path that skips per-row WAL and brackets a bulk with a manifest pair. Obtained via DatabaseEngine.BeginBulkLoad;
trades per-row durability for throughput on a strictly opt-in API.
public sealed class BulkLoadSession : IDisposable
- Inheritance
-
BulkLoadSession
- Implements
- Inherited Members
Remarks
Contract: the bulk is committed iff both BulkBegin (emitted at session open) and BulkEnd (emitted by CompleteBulkLoad())
reach the WAL FUA. On crash without BulkEnd durable, the bulk's UoW remains Pending in the registry and is voided on next reopen per
UR-03 — none of the bulk's revisions become visible. Page allocations on a discarded bulk leak in v1; recovery's Phase 3b (P3) will reclaim them
when allocation tracking is added.
Concurrency: a bulk session is exclusive (only one per engine in v1) and thread-affine (only the thread that called
BeginBulkLoad may call methods on it). Regular UnitOfWorks continue to run during the session and see the pre-bulk MVCC snapshot.
Implementation note (v1): the bulk session wraps a regular UnitOfWork + a single Transaction, constructed with the
internal SuppressWalSerialization flag (BL-01). All MVCC plumbing (revision chains, EntityMap, indexes, UowRegistry) reuses the standard
infrastructure; the only divergence is that PersistAndFinalize skips the per-tx commit batch (no Slot records emitted). Pages still get
dirty-marked, so CompleteBulkLoad()'s forced checkpoint flushes them.
Properties
BulkBeginLsn
LSN of the BulkBegin chunk emitted at session open. Set during construction; immutable for the session's lifetime. The matching BulkEnd
chunk (emitted by CompleteBulkLoad()) cross-references this value via Typhon.Engine.Internals.BulkManifestHeader.BulkBeginLsn.
public long BulkBeginLsn { get; }
Property Value
BulkSessionId
64-bit id assigned at session open. Stable across BulkBegin and BulkEnd; used by recovery to identify the bulk.
public long BulkSessionId { get; }
Property Value
EntitiesDestroyed
Telemetry: entities destroyed so far via Destroy(EntityId).
public long EntitiesDestroyed { get; }
Property Value
EntitiesSpawned
Telemetry: entities spawned so far via Spawn<TArch>(params ReadOnlySpan<ComponentValue>).
public long EntitiesSpawned { get; }
Property Value
EntitiesUpdated
Telemetry: entities updated so far via Update<T>(EntityId, in T).
public long EntitiesUpdated { get; }
Property Value
IsClosed
true after CompleteBulkLoad() returned successfully, or after Dispose() ran (discard path). Subsequent Spawn / Update / Destroy / CompleteBulkLoad() calls throw BulkSessionClosedException.
public bool IsClosed { get; }
Property Value
Options
Options the session was opened with. Never null — defaults to new BulkLoadOptions() if the caller passed null
to BeginBulkLoad.
public BulkLoadOptions Options { get; }
Property Value
Methods
CompleteBulkLoad()
Synchronous durability barrier: commits the underlying transaction, flushes the UoW, runs a forced checkpoint (waited synchronously), emits the
BulkEnd manifest, waits for BulkEnd durable, then returns. On return the bulk is fully committed and visible to subsequent transactions.
public void CompleteBulkLoad()
Remarks
Implements the 6-step barrier from claude/design/Durability/BulkLoad/02-write-path.md + invariant BL-04 in claude/rules/durability.md.
Exceptions
- BulkSessionClosedException
Session has already been completed or disposed.
- BulkLoadCheckpointTimeoutException
Checkpoint did not complete within CheckpointTimeout; the session remains alive.
Destroy(EntityId)
Destroy an entity that was spawned earlier in this session.
public void Destroy(EntityId entity)
Parameters
entityEntityIdBulk-spawned entity id.
Exceptions
- BulkSessionClosedException
Session has been completed or disposed.
Dispose()
Discard the bulk session. If CompleteBulkLoad() was not called, the underlying transaction is rolled back — none of the bulk's revisions
become visible to other UoWs. The bulk's UoW remains Pending in the registry and is voided on next reopen per UR-03; no BulkEnd
chunk is emitted, so recovery treats the bulk as if it never completed.
public void Dispose()
Remarks
v1 limitation: pages allocated during a discarded bulk leak (occupied in the bitmap, no visible data) until P3's recovery Phase 3b adds allocation tracking + reclamation.
Calling Dispose() after a successful CompleteBulkLoad() is a no-op.
OpenMut(EntityId)
Open an entity for mutation. Returned EntityRef behaves like the one from OpenMut(EntityId); use Write<T> on
it to assign component values.
public EntityRef OpenMut(EntityId entity)
Parameters
entityEntityId
Returns
Remarks
Per the bulk contract, the entity should have been spawned earlier in the same session. Bulk sessions are not designed to update entities that exist on disk pre-bulk (use the standard UnitOfWork path for that — the bulk path's durability boundary covers the whole session, so a pre-bulk entity update would lose atomicity vs. concurrent readers).
Exceptions
- BulkSessionClosedException
Session has been completed or disposed.
Spawn<TArch>(params ReadOnlySpan<ComponentValue>)
Spawn a new entity of archetype TArch. Same signature shape as Spawn<TArch>(params ReadOnlySpan<ComponentValue>).
Returns the new entity's engine-wide id.
public EntityId Spawn<TArch>(params ReadOnlySpan<ComponentValue> values) where TArch : Archetype<TArch>
Parameters
valuesReadOnlySpan<ComponentValue>Initial component values; unspecified components take archetype defaults.
Returns
- EntityId
The newly-spawned entity's id.
Type Parameters
TArchConcrete archetype type.
Exceptions
- BulkSessionClosedException
Session has been completed or disposed.
Update<T>(EntityId, in T)
Bulk-update a component on an entity. Convenience for OpenMut(entity).Write<T>().
public void Update<T>(EntityId entity, in T value) where T : unmanaged
Parameters
entityEntityIdBulk-spawned entity id.
valueTNew component value.
Type Parameters
TComponent type (must be
unmanaged— blittable; matches engine convention).
Exceptions
- BulkSessionClosedException
Session has been completed or disposed.