Writer

The commit protocol behind Log.append. Use it directly for fine control over batching and revalidation (see Lower-level API).

class cairndb.Committer(storage: BlobStorage, config: CommitterConfig | None = None, revalidate: Callable[[list[Event], list[Commit]], Awaitable[list[Event | None]]] | None = None)[source]

Durable, concurrent-safe writer for the commit log.

Usage:

committer = Committer(storage)
seq = await committer.append(event)   # durable once this returns
...
await committer.close()

Or as an async context manager:

async with Committer(storage) as committer:
    await committer.append(event)

Any number of Committer instances may write to the same log from any number of processes; the storage layer’s put-if-absent serializes them.

Initialize the committer.

Parameters:
  • storage – Blob storage backend

  • config – Committer configuration (defaults are sensible)

  • revalidate – Optional hook to re-check events against commits that interleaved after a lost race. Omit for pure-fact events.

async append(event: Event) → SequenceNumber[source]

Append one event to the log.

Returns once the event’s commit object is durably stored.

Returns:

The event’s global sequence number.

Raises:
  • CommitError – If the commit could not be won within max_commit_attempts, or the committer is closed.

  • EventRejectedError – If the revalidate hook rejected the event.

  • StorageError – If storage failed with a non-race error.

async append_many(events: list[Event]) → list[SequenceNumber][source]

Append several events, preserving their relative order.

Events are packed into as few commit objects as possible. Returns once every event is durable.

async close() → None[source]

Drain pending events (committing them), then stop.

property tail: int | None

Highest commit number this committer knows to exist (None = unknown).

class cairndb.CommitterConfig(max_events_per_commit: int = 1000, max_batch_wait_seconds: float = 0.0, max_commit_attempts: int = 20, lost_race_backoff_seconds: float = 0.05, tail_hint: int = 0)[source]

Committer configuration.

max_events_per_commit

Maximum number of events batched into one commit object (1 to 100_000).

Type:

int

max_batch_wait_seconds

Extra time to wait for more events before committing a batch (0 to 10). 0 commits immediately; group commit still batches whatever arrives while a put is in flight.

Type:

float

max_commit_attempts

Attempts per batch before failing appends with CommitError (at least 1).

Type:

int

lost_race_backoff_seconds

Backoff before retrying when a put was rejected but the winning commit is not visible yet — a concurrent-conditional-write conflict (0 to 5).

Type:

float

tail_hint

Cold-start optimization: commits at or below this number are known to exist (e.g. from a snapshot), so tail discovery only lists commits after it.

Type:

int

cairndb.RevalidateHook

Callable[[list[Event], list[Commit]], Awaitable[list[Event | None]]]

Called after a lost commit race with the pending events and the commits that interleaved. It returns one entry per pending event: the event to commit in its place (possibly modified), or None to reject it. A rejected event fails its append() with EventRejectedError.