retrace_server

Durable admission, session indexing and archival. The caller owns the pool, object store, migrations and worker lifetime; no process starts on import.

Types

What erasure needs from the host: object removal, and the host’s own decision on each attempt that might still publish. quiesced runs inside the session lock on every pass, so it answers from local state.

pub type Eraser {
  Eraser(
    remove: fn(String) -> Result(Nil, StorageError),
    quiesced: fn(WriteAttempt) -> Quiescence,
  )
}

Constructors

Progress of erasing a fenced session. Only Erased is definitive.

pub type Erasure {
  Erasing
  AwaitingWriters(unresolved: Int)
  Erased
}

Constructors

  • Erasing

    No upload can publish later; further passes delete the remaining keys.

  • AwaitingWriters(unresolved: Int)

    This many admitted uploads might still publish. Deletion of their keys waits, and the session stays incompletely erased.

  • Erased

    Every upload is resolved, every inventoried key is deleted and the recording rows are cleared.

pub type Error {
  InvalidBatch(wire.Error)
  Database(pog.QueryError)
  UnsupportedSchema(String)
  Conflict
  OutOfOrder(expected: Int, received: Int)
  NotFound
  Storage(StorageError)
  CorruptArchive
  Deleted
}

Constructors

  • InvalidBatch(wire.Error)
  • Database(pog.QueryError)
  • UnsupportedSchema(String)

    The database holds a schema from before write attempts. migrate refuses it unchanged: start from an empty database.

  • Conflict
  • OutOfOrder(expected: Int, received: Int)
  • NotFound
  • Storage(StorageError)
  • CorruptArchive
  • Deleted
pub type ObjectStore {
  ObjectStore(
    put: fn(String, BitArray) -> Result(Nil, StorageError),
    get: fn(String) -> Result(BitArray, StorageError),
  )
}

Constructors

  • ObjectStore(
      put: fn(String, BitArray) -> Result(Nil, StorageError),
      get: fn(String) -> Result(BitArray, StorageError),
    )

The host’s answer to whether an upload can still publish. Retrace never infers it from errors, time or process liveness.

pub type Quiescence {
  MayPublish
  Quiescent(basis: String)
}

Constructors

  • MayPublish
  • Quiescent(basis: String)
pub type StorageError {
  Unavailable
  Missing
  InvalidObject
}

Constructors

  • Unavailable
  • Missing
  • InvalidObject
pub type Summary {
  Summary(
    session: wire.Session,
    sequence: Int,
    elapsed_ms: Int,
    pending: Int,
    archived: Int,
  )
}

Constructors

  • Summary(
      session: wire.Session,
      sequence: Int,
      elapsed_ms: Int,
      pending: Int,
      archived: Int,
    )

One admitted upload dispatch, retained with its session and immutable key.

pub type WriteAttempt {
  WriteAttempt(
    id: Int,
    session_id: String,
    object_key: String,
    state: WriteState,
    basis: option.Option(String),
    since_admission_seconds: Int,
    since_failure_seconds: option.Option(Int),
  )
}

Constructors

  • WriteAttempt(
      id: Int,
      session_id: String,
      object_key: String,
      state: WriteState,
      basis: option.Option(String),
      since_admission_seconds: Int,
      since_failure_seconds: option.Option(Int),
    )

    Arguments

    basis

    The host’s stated reason once the attempt is Quiesced.

    since_admission_seconds

    Whole seconds since admission, rounded down, from the database clock. Admission precedes the PUT by an unbounded pause, so this is not a bound on any remote write.

    since_failure_seconds

    Whole seconds, rounded down, since put returned its error. The request was sent before that return, so a provider-qualified maximum request lifetime elapsed since then covers it. None until a failure is recorded.

Where a recorded upload stands. Only InFlight and Failed can still publish; neither is proof that it will not.

pub type WriteState {
  InFlight
  Failed
  Completed
  Quiesced
}

Constructors

  • InFlight

    Admitted, and no outcome recorded. The PUT may not have been sent yet, may be on the wire, or may have been lost with its caller. No clock bounds it: a paused worker can still send arbitrarily long after admission. Proving it quiescent needs two facts: no local dispatch can still happen, and no request already issued can publish. Stopping the worker or revoking its credentials establishes at most the first.

  • Failed

    put returned an error. The provider may still finish the request.

  • Completed

    put returned success, so the provider finished the write.

  • Quiesced

    The host’s quiesced function proved that it cannot publish later.

Values

pub fn accept(
  db: pog.Connection,
  batch: wire.Batch,
) -> Result(Int, Error)

Atomically admit one immutable batch. Exact retries return its end cursor, including after archival; overlapping or differently chunked input conflicts.

pub fn accept_in_transaction(
  tx: pog.Connection,
  batch: wire.Batch,
) -> Result(Int, Error)

Compose host authorization/accounting in the same caller-owned transaction. The connection MUST already be inside a transaction; this function does not start or commit one. Never nest pog.transaction (Pog 4 commits nested BEGIN).

pub fn archive_next(
  db: pog.Connection,
  store: ObjectStore,
) -> Result(Bool, Error)

Archive at most one pending batch. The upload is admitted durably before dispatch and outside the lock, so a failed or lost put leaves an attempt that keeps erasure incomplete until the host proves it quiescent. Concurrent workers may upload the same immutable bytes to the same key; the conditional database update is safe.

pub fn archive_segment_next(
  db: pog.Connection,
  store: ObjectStore,
  idle_seconds: Int,
) -> Result(Bool, Error)

Compact one idle session range into a verified immutable generation containing at most 1000 events in whole original batches. A late arrival replaces its partially filled segment. True also reports a durable oversized deferral; sources stay readable and unchanged instead of splitting a small session. Plans are resumed before new work. The host owns scheduling (usually 60s idle).

pub fn erase(
  db: pog.Connection,
  eraser: Eraser,
  id: String,
) -> Result(Erasure, Error)

Idempotently erase a fenced session and report its definitive state. Each pass first asks the host, through eraser.quiesced, about every attempt that might still publish. Keys owned by an attempt not yet proven quiescent are never deleted or counted as done, and the result is AwaitingWriters. Each pass deletes at most 8 keys; recording rows are cleared and the tombstone is marked reconciled only once every attempt is completed or quiescent and every key deleted. Repeating a completed session deletes its retained keys again, which also covers a backup restore that brought objects back.

pub fn erase_next(
  db: pog.Connection,
  eraser: Eraser,
  interval_seconds: Int,
) -> Result(option.Option(#(String, Erasure)), Error)

Advance the least recently checked unfinished tombstone, or one completed longer ago than interval_seconds, and report its erasure state. None means no tombstone is due.

pub fn erasure_status(
  db: pog.Connection,
  id: String,
) -> Result(option.Option(Erasure), Error)

Report whether a session is erased, erasing or waiting on uploads that might still publish. None means the session has no fence.

pub fn fence(
  db: pog.Connection,
  id: String,
) -> Result(Nil, Error)

Commit a permanent admission/read/archive fence and snapshot every known object key. The caller owns authorization and retention deadlines.

pub fn fence_in_transaction(
  tx: pog.Connection,
  id: String,
) -> Result(Nil, Error)

As fence, within a caller-owned transaction. Does not begin/commit it.

pub fn migrate(db: pog.Connection) -> Result(Nil, Error)

Create the schema. A database that already holds tombstones from before write attempts is refused with UnsupportedSchema before any statement runs.

pub fn read(
  db: pog.Connection,
  store: ObjectStore,
  id: String,
  after: Int,
) -> Result(List(wire.Batch), Error)

Read at most 128 batches after an absolute cursor. Use the last returned entry’s sequence for the next page. Archive payload integrity is checked.

pub fn read_concurrent(
  db: pog.Connection,
  store: ObjectStore,
  id: String,
  after: Int,
  concurrency: Int,
) -> Result(List(wire.Batch), Error)

Read the same ordered, verified page as read, with independent adapter calls bounded to concurrency (clamped to 1..16). The adapter must be safe to invoke from different processes and bound its external I/O. All read-owned workers terminate before return or when the caller dies; borrowed resources are never stopped. An adapter exception becomes Storage(Unavailable). A deletion fence committed while the page was loading refuses the result.

pub fn reclaim_next(
  db: pog.Connection,
  remove: fn(String) -> Result(Nil, StorageError),
  interval_seconds: Int,
) -> Result(Bool, Error)

Reconcile at most eight retired keys. First cleanup is immediate; the interval only controls repeat DELETE after successful cleanup, covering ambiguous late PUT outcomes. The registry remains available to future fencing and erasure.

pub fn sessions(
  db: pog.Connection,
  user_ref: String,
) -> Result(List(Summary), Error)

Exact user-reference filtering; empty filter lists the 100 latest sessions. Authorization and tenant scoping belong to the hosting service.

pub fn write_attempts(
  db: pog.Connection,
  id: String,
  limit: Int,
) -> Result(List(WriteAttempt), Error)

List up to limit write attempts of a session, oldest first, with their states and the basis the host gave for each quiescent one.

✨ Search Document