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
-
Eraser( remove: fn(String) -> Result(Nil, StorageError), quiesced: fn(WriteAttempt) -> Quiescence, )
Progress of erasing a fenced session. Only Erased is definitive.
pub type Erasure {
Erasing
AwaitingWriters(unresolved: Int)
Erased
}
Constructors
-
ErasingNo 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.
-
ErasedEvery 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.
migraterefuses 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
putreturned its error. The request was sent before that return, so a provider-qualified maximum request lifetime elapsed since then covers it.Noneuntil 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
-
InFlightAdmitted, 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.
-
Failedputreturned an error. The provider may still finish the request. -
Completedputreturned success, so the provider finished the write. -
QuiescedThe host’s
quiescedfunction 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.