Slap.Yjs.Store (slap_yjs v0.4.0)

Copy Markdown View Source

Yjs documents as Slap.SnapshotLogs, in the reference y-durable-streams server's layout, so its HTTP clients can later read the same documents:

<prefix>/yjs/<service>/docs/<doc>/.updates                     application/octet-stream
<prefix>/yjs/<service>/docs/<doc>/.index                       application/json
<prefix>/yjs/<service>/docs/<doc>/.snapshots/<offset>_snapshot application/octet-stream

Each entry in .updates is a batch of lib0-framed updates (Slap.Yjs.Frame), and a snapshot is an update holding the document's state. <prefix> defaults to /v1/stream and can be set per call. Pass prefix: and cluster: to each operation to select another store. For load/2, :timeout bounds each Slap.SnapshotLog.next/3 call, including its waits and retries. For other operations it bounds each underlying Streams call. Define a cluster module with use Slap.Streams.Cluster, otp_app: :my_app and run one Streams cluster per VM.

Store operations return {:error, {:bad_request, reason}} for invalid document ids, frames and offsets. Invalid :prefix, :cluster or :timeout settings and unknown options raise ArgumentError. The base/2 and path/3 path builders raise for invalid document ids.

Summary

Types

A document: {service, name}, each a non-empty path segment.

Functions

Appends a batch of frames (Slap.Yjs.Frame); returns the offset after it.

The document's base path: its Slap.SnapshotLog.

Deletes the document for good: later calls on it return {:error, :deleted}, and its id cannot be used again (Slap.SnapshotLog.delete/1).

Opens the document (creating it if needed) and reads it: the current snapshot, and every update after it, up to the durable tail (:offset, where the next append goes).

The path of the document's .updates, .index or a snapshot stream.

The snapshot at offset.

Publishes the document's state as a snapshot at offset (Slap.SnapshotLog.snapshot/4). The state may hold more than the updates before offset (pending and local updates): applying an update twice changes nothing. :timeout bounds each underlying Streams call.

The document's stored snapshots, oldest first (Slap.SnapshotLog.snapshots/1).

The durable tail of the document's updates.

Types

doc()

@type doc() :: {String.t(), String.t()}

A document: {service, name}, each a non-empty path segment.

error()

@type error() :: Slap.SnapshotLog.error() | {:bad_frames, term()}

loaded()

@type loaded() :: %{
  snapshot: binary() | nil,
  snapshot_offset: non_neg_integer() | nil,
  updates: [binary()],
  offset: non_neg_integer()
}

Functions

append(doc, frames, opts \\ [])

@spec append(doc(), binary(), keyword()) ::
  {:ok, non_neg_integer()} | {:error, error()}

Appends a batch of frames (Slap.Yjs.Frame); returns the offset after it.

base(doc, opts \\ [])

@spec base(doc(), keyword()) :: String.t()

The document's base path: its Slap.SnapshotLog.

delete_doc(doc, opts \\ [])

@spec delete_doc(doc(), keyword()) :: :ok | {:error, error()}

Deletes the document for good: later calls on it return {:error, :deleted}, and its id cannot be used again (Slap.SnapshotLog.delete/1).

load(doc, opts \\ [])

@spec load(doc(), keyword()) :: {:ok, loaded()} | {:error, error()}

Opens the document (creating it if needed) and reads it: the current snapshot, and every update after it, up to the durable tail (:offset, where the next append goes).

path(doc, stream, opts \\ [])

@spec path(doc(), :updates | :index | {:snapshot, non_neg_integer()}, keyword()) ::
  String.t()

The path of the document's .updates, .index or a snapshot stream.

read_snapshot(doc, offset, opts \\ [])

@spec read_snapshot(doc(), non_neg_integer(), keyword()) ::
  {:ok, binary()} | {:error, error()}

The snapshot at offset.

snapshot(doc, offset, state, opts \\ [])

@spec snapshot(doc(), non_neg_integer(), binary(), keyword()) ::
  :ok | {:error, error()}

Publishes the document's state as a snapshot at offset (Slap.SnapshotLog.snapshot/4). The state may hold more than the updates before offset (pending and local updates): applying an update twice changes nothing. :timeout bounds each underlying Streams call.

snapshots(doc, opts \\ [])

@spec snapshots(doc(), keyword()) ::
  {:ok, [Slap.SnapshotLog.snapshot_info()]} | {:error, error()}

The document's stored snapshots, oldest first (Slap.SnapshotLog.snapshots/1).

tail(doc, opts \\ [])

@spec tail(doc(), keyword()) :: {:ok, non_neg_integer()} | {:error, error()}

The durable tail of the document's updates.