Crdt.Model

crdt · API reference

Document model container

Document model container.

A Model represents a complete CRDT document with clock, root node, and node index. The root is always a val node at timestamp (0, 0) which points to the actual document content.

Key operations:

  • create: Initialize a new empty model with a session ID
  • get_node/add_node: Manage the node index
  • view: Materialize the current document as a Value.t
  • fork: Create an independent replica with a new session ID

Types

module NodeIndex : sig ... end

Node index - maps timestamps to nodes. The model is already a mutable aggregate, so keep node indexing mutable too: json-joy uses a mutable index, and this avoids allocating a persistent map path for every inserted node.

type t = {
  clock : Clock.clock_vector;
  root : Node.t; (* Root val node at (0, 0) *)
  index : Node.t NodeIndex.t; (* Timestamp -> Node map *)
}

A CRDT document model

Constructors

val root_id : Clock.timestamp

Root node ID constant - always (0, 0)

val create : int -> t

Create a new empty model with the given session ID. The model starts with just a root val node at (0, 0).

val create_at : int -> int -> t

Create a model with a specific starting clock time

Node Index Operations

val get_node : t -> NodeIndex.key -> Node.t option

Get a node by its timestamp ID

val add_node : t -> Node.t -> unit

Add a node to the model's index

val has_node : t -> NodeIndex.key -> bool

Check if a node exists in the index

val nodes : t -> Node.t list

Get all nodes in the model

val node_count : t -> int

Get the number of nodes in the model

val copy_index : 'a NodeIndex.t -> 'a NodeIndex.t

Clock Operations

val clock : t -> Clock.clock_vector

Get the current clock vector

val session_id : t -> int

Get the local clock's session ID

val current_time : t -> int

Get the current logical time

val tick : t -> Clock.timestamp

Tick the clock and return a new timestamp

val tick_n : t -> int -> Clock.timestamp

Tick the clock N times and return the first timestamp

val observe : t -> Clock.timestamp -> unit

Observe a timestamp from another replica

View Functions

val resolve_ref : t -> Clock.timestamp -> Value.t

Resolve a timestamp reference to its actual value. This follows references through val nodes to get the final value.

val view_node : t -> Node.t -> Value.t

Get the view of a specific node, resolving all references

val view : t -> Value.t

Get the current view of the document. This resolves the root val node to get the actual content.

Replica Operations

val fork : t -> int -> t

Fork the model to create an independent replica with a new session ID. The new replica starts with a copy of the current state.

val clone : t -> t

Clone the model (same session ID, independent state). Useful for creating a snapshot.

Node Creation Helpers

val new_con : t -> value:Value.t -> Node.t

Create and register a new constant node

val new_val : t -> Node.t

Create and register a new value node

val new_obj : t -> Node.t

Create and register a new object node

val new_vec : t -> Node.t

Create and register a new vector node

val new_arr : t -> Node.t

Create and register a new array node

val new_str : t -> Node.t

Create and register a new string node

val new_bin : t -> Node.t

Create and register a new binary node

Convenience Operations

val set_root : t -> Node.t -> unit

Set the document root value to a node

val root_node : t -> Node.t option

Get the root's target node (if set)

Pretty Printing

val pp : Format.formatter -> t -> unit

Pretty print model info

val to_string : t -> string

Convert to string for debugging

Patch Application

val rga_after : Clock.timestamp -> Clock.timestamp -> Clock.timestamp option
val apply_op : t -> Clock.timestamp -> Op.op_data -> unit

Apply a single operation to the model.

parameter model The model to modify parameter op_id The ID assigned to this operation parameter op The operation to apply

val apply : t -> Patch.t -> unit

Apply a patch to the model. Each operation in the patch is applied in order with its computed ID.

val apply_batch : t -> Patch.batch -> unit

Apply a batch of patches to the model

Merge

State-based merge of two replicas of the same document, the primitive peer-to-peer snapshot sync needs (patch exchange covers everything else). Semantics per node type:

  • con: immutable; identical by construction (same id = same op).
  • val: LWW; the reference with the higher timestamp wins.
  • obj / vec: LWW per key / slot by write timestamp.
  • str / bin / arr (RGA): element spans present in other but not here are re-inserted using their recorded insert anchor (chunk.parent); spans split off their original chunk anchor on their preceding element, which is exact. A chunk that lost its anchor to a split is anchored on its predecessor in other's document order — an approximation that preserves other's ordering. Tombstones are unioned (a delete on either side wins). Both inputs converge: view (merge a b) = view (merge b a) whenever the two replicas share op history (forks of one document). Merging unrelated documents is not meaningful.
val copy_node : Node.t -> Node.t
val deep_clone : t -> t

Like clone but nodes are copied too, so mutating one model never affects the other.

val observe_remote_clock : t -> sid:int -> time:int -> unit
val rga_coverage : 'a Rga.t -> (int, (int * int) list) Hashtbl.t
val missing_intervals : 
  ('a, ('b * 'b) list) Hashtbl.t ->
  sid:'a ->
  start:'b ->
  stop:'b ->
  ('b * 'b) list
val merge_rga : 
  a_rga:'d Rga.t ->
  b_rga:'d Rga.t ->
  insert:
    (after:Clock.timestamp option ->
      chunk_id:Clock.timestamp ->
      chunk:'d Rga.chunk ->
      off:int ->
      len:int ->
      unit) ->
  delete:(Clock.timespan list -> unit) ->
  unit
val str_slice : Node.str_chunk -> off:int -> len:int -> string
val list_slice : 'a list -> off:int -> len:int -> 'a list
val merge_node : Node.t -> Node.t -> unit
val absorb : t -> t -> unit

Merge other into model in place. other is not modified and remains independently usable.

val merge : t -> t -> t

merge a b is a new model containing the merged state of both replicas; neither input is modified.