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 ... endNode 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.timestampRoot node ID constant - always (0, 0)
val create : int -> tCreate 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 -> tCreate a model with a specific starting clock time
Node Index Operations
val get_node : t -> NodeIndex.key -> Node.t optionGet a node by its timestamp ID
val add_node : t -> Node.t -> unitAdd a node to the model's index
val has_node : t -> NodeIndex.key -> boolCheck if a node exists in the index
val nodes : t -> Node.t listGet all nodes in the model
val node_count : t -> intGet the number of nodes in the model
val copy_index : 'a NodeIndex.t -> 'a NodeIndex.tClock Operations
val clock : t -> Clock.clock_vectorGet the current clock vector
val session_id : t -> intGet the local clock's session ID
val current_time : t -> intGet the current logical time
val tick : t -> Clock.timestampTick the clock and return a new timestamp
val tick_n : t -> int -> Clock.timestampTick the clock N times and return the first timestamp
val observe : t -> Clock.timestamp -> unitObserve a timestamp from another replica
View Functions
val resolve_ref : t -> Clock.timestamp -> Value.tResolve 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.tGet the view of a specific node, resolving all references
val view : t -> Value.tGet the current view of the document. This resolves the root val node to get the actual content.
Replica Operations
val fork : t -> int -> tFork 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 -> tClone the model (same session ID, independent state). Useful for creating a snapshot.
Node Creation Helpers
val new_con : t -> value:Value.t -> Node.tCreate and register a new constant node
val new_val : t -> Node.tCreate and register a new value node
val new_obj : t -> Node.tCreate and register a new object node
val new_vec : t -> Node.tCreate and register a new vector node
val new_arr : t -> Node.tCreate and register a new array node
val new_str : t -> Node.tCreate and register a new string node
val new_bin : t -> Node.tCreate and register a new binary node
Convenience Operations
val set_root : t -> Node.t -> unitSet the document root value to a node
val root_node : t -> Node.t optionGet the root's target node (if set)
Pretty Printing
val pp : Format.formatter -> t -> unitPretty print model info
val to_string : t -> stringConvert to string for debugging
Patch Application
val rga_after : Clock.timestamp -> Clock.timestamp -> Clock.timestamp optionval apply_op : t -> Clock.timestamp -> Op.op_data -> unitApply 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 -> unitApply a patch to the model. Each operation in the patch is applied in order with its computed ID.
val apply_batch : t -> Patch.batch -> unitApply 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
otherbut 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 inother's document order — an approximation that preservesother'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.tval deep_clone : t -> tLike clone but nodes are copied too, so mutating one model never affects the other.
val observe_remote_clock : t -> sid:int -> time:int -> unitval rga_coverage : 'a Rga.t -> (int, (int * int) list) Hashtbl.tval missing_intervals :
('a, ('b * 'b) list) Hashtbl.t ->
sid:'a ->
start:'b ->
stop:'b ->
('b * 'b) listval 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) ->
unitval str_slice : Node.str_chunk -> off:int -> len:int -> stringval list_slice : 'a list -> off:int -> len:int -> 'a listval merge_node : Node.t -> Node.t -> unitval absorb : t -> t -> unitMerge other into model in place. other is not modified and remains independently usable.
val merge : t -> t -> tmerge a b is a new model containing the merged state of both replicas; neither input is modified.