Skip to content
Strata is pre-1.0. APIs, protocol, and storage format may change.

strata_server/store

Persistent document store backed by shelf (ETS + DETS).

One actor owns the in-memory documents and deletion parent links. A single store.dets checkpoint contains both. Shelf atomically replaces that file on flush, so recovery sees a complete checkpoint, never one half of a create/delete. Acceptance is in memory until a successful flush.

create and delete manage a parent-child tree. Each child document records its deletion parent id in the checkpoint. On delete, the child and all descendants are removed and their ids and former deletion parents added to the checkpoint's tombstones. apply and create reject deleted ids. IDs cannot be reused; create a new ID for a replacement. Tombstones have no expiry or garbage collection. A successful checkpoint preserves deletion identity across restart.

delete is idempotent for a tombstoned child: it merges parent_state and reports the child as removed again, without touching the tombstone. A concurrent add-wins edit on the parent can put a deleted child's reference back, and the repeat delete is what removes it. A live child under a different parent is still rejected with NotAChild.

Errors returned when a synchronous actor call does not reply in time.

pub type ActorCallError {
ActorCallTimedOut
}

Rejections and ambiguous outcomes from normal delta application.

pub type ApplyError {
ApplyDocumentLimit(SizeLimitExceeded)
ApplyDocumentDeleted
ApplyStateInvalid(json.DecodeError)
ApplyMergeFailed(crdt.MergeError)
ApplyStorageFailed(shelf.ShelfError)
ApplyTimedOut
}

The queued operation may still complete. This is not a rejection.

Errors returned by create.

pub type CreateError {
CreateDocumentLimit(SizeLimitExceeded)
ChildExists
CreateChildDeleted(child: String)
CreateSelfParent(document_id: String)
CreateParentDeleted(parent: String)
CreateChildStateInvalid(child: String, reason: json.DecodeError)
CreateParentStateInvalid(parent: String, reason: json.DecodeError)
CreateMergeFailed(crdt.MergeError)
CreateTimedOut
}

A document with the given child id already exists.

Deleted identities cannot be reused; allocate a new child id.

create rejected a self-referential parent-child link.

create rejected a parent document that was already deleted.

CreateChildStateInvalid(child: String, reason: json.DecodeError)

Section titled “CreateChildStateInvalid(child: String, reason: json.DecodeError)”

The supplied child ORMap state could not be decoded.

CreateParentStateInvalid(parent: String, reason: json.DecodeError)

Section titled “CreateParentStateInvalid(parent: String, reason: json.DecodeError)”

The supplied parent ORMap state could not be decoded.

The queued create can still complete.

Errors returned by delete.

pub type DeleteError {
DeleteDocumentLimit(SizeLimitExceeded)
NotAChild(child: String, parent: String)
DeleteParentStateInvalid(parent: String, reason: json.DecodeError)
DeleteMergeFailed(crdt.MergeError)
DeleteTimedOut
}

The live child has no recorded parent link, or its recorded parent is a different document, including after deletion.

DeleteParentStateInvalid(parent: String, reason: json.DecodeError)

Section titled “DeleteParentStateInvalid(parent: String, reason: json.DecodeError)”

The supplied parent ORMap state could not be decoded.

The queued delete can still complete.

Read-only metadata for a known document.

pub type DocumentSummary {
DocumentSummary(id: String, byte_size: Int, last_write_ms: Int, parent: option.Option(String))
}

A merged document would exceed its allowed encoded size.

pub type SizeLimitExceeded {
SizeLimitExceeded(document_id: String, bytes: Int, limit: Int)
}

Errors start can return.

pub type StartError {
ActorStartError(actor.StartError)
}

The OTP actor failed to start (typically a downstream shelf.open failure surfacing as the initialiser returning Error).

Opaque handle to the store actor.

pub type Store

Default flush interval (5 seconds) — see start.

pub const default_flush_interval_ms: Int

Default accepted encoded document size, including causal metadata (16 MiB).

pub const default_max_document_bytes: Int

Merge a remote ORMap (encoded as a JSON string) into the store. Returns the accepted merged state, or an explicit error. Acceptance is in memory, not a crash-durable receipt. A timeout is ambiguous.

pub fn apply(
Store,
String,
String
) -> Result(json.Json, ApplyError)

Build a supervised store with a stable name across owner replacement. Normal close shuts down a supervisor using AnySignificant; failures restart.

pub fn child_spec(String) -> #(Store, supervision.ChildSpecification(Nil))

Build the supervised store with an explicit encoded-document growth budget.

pub fn child_spec_with_limit(
String,
Int
) -> #(Store, supervision.ChildSpecification(Nil))

Flush pending writes and close the store. Call before shutdown to avoid losing unflushed data.

pub fn close(Store) -> Result(Nil, shelf.ShelfError)

Close and return the final operational snapshot, including close latency. Measurements are not persisted and otherwise end with the store owner.

pub fn close_with_observations(Store) -> Result(observations.Observations, shelf.ShelfError)

Create a child document under parent in one owner turn.

Records the parent link, stores child_state, and merges parent_state into the parent document — all in one actor message. Fails without side effects when the child already exists or the parent is tombstoned.

A timeout returns CreateTimedOut: the queued operation can still complete.

pub fn create(
Store,
parent: String,
child: String,
child_state: String,
parent_state: String
) -> Result(Nil, CreateError)

Delete a child document and every descendant in one owner turn.

Verifies the parent link, merges parent_state, then removes the child and every descendant by walking parent links. Returns all removed document ids. Fails without side effects when the parent link does not match a live child, or when parent_state does not decode.

Repeating the call for an already deleted child succeeds: the merge still runs, so a reference re-added by a concurrent edit is removed, and the returned list names the child again. The child stays deleted and stays tombstoned.

A timeout returns DeleteTimedOut: the queued operation can still complete.

pub fn delete(
Store,
parent: String,
child: String,
parent_state: String
) -> Result(List(String), DeleteError)

Get metadata for all known documents, sorted by id.

Returns Error(ActorCallTimedOut) if the store actor does not reply before the configured timeout.

pub fn document_summaries(Store) -> Result(List(DocumentSummary), ActorCallError)

Persist one complete document/parent checkpoint. On failure, memory is unchanged and a later flush may retry. A timeout leaves the outcome unknown.

pub fn flush(Store) -> Result(Nil, shelf.ShelfError)

Monitor this store owner for lifecycle coordination.

pub fn monitor(Store) -> process.Monitor

Read bounded measurements. This shares the store mailbox and can time out during long work; reading metrics does not recursively instrument itself.

pub fn operational_data(Store) -> Result(observations.Observations, ActorCallError)

Get the current ORMap state for a document as a json.Json value (ready to embed in a server frame).

Returns Ok(None) if no state has been recorded yet, or Error(ActorCallTimedOut) if the store actor does not reply before the configured timeout.

pub fn snapshot(
Store,
String
) -> Result(option.Option(json.Json), ActorCallError)

Start with the default flush interval (5 seconds).

pub fn start(String) -> Result(Store, StartError)

Start a store actor with a custom flush interval.

base_directory is a directory inside which the DETS file lives. It is created if missing. Pass an interval of 0 to disable periodic flushing (the actor still flushes on close).

pub fn start_with_interval(
base_directory: String,
flush_interval_ms: Int
) -> Result(Store, StartError)

Start with a document growth budget. Zero disables it; negatives are invalid. Recovered documents above the budget remain readable and may shrink.

pub fn start_with_limit(
String,
Int,
Int
) -> Result(Store, StartError)