An OrArray is an Observed-Remove Array, a list-like CRDT that supports concurrent insertions, updates, removals, and moves. It is one of the three core primitives in DSON, alongside OrMap and MvReg.
Stable Positioning
Instead of integer indices (which would require updating all subsequent elements upon insertion/deletion), OrArray uses stable identifiers in the form of real numbers (f64).
- To insert an element between positions $p_1$ and $p_2$, use
Position::between to calculate the new position as $(p_1 + p_2)/2$. - The array is represented as a sequence of values sorted by their position in ascending order.
- Because of concurrency, a single element might have a set of possible positions. Replicas resolve this by deterministically choosing the maximum position from the set.
Unique Identifiers
Each element is identified by a Uid (derived from the Dot used during its creation). This allows the system to track the same element even if it moves to different positions due to concurrent operations.
Usage Pattern
OrArray is typically wrapped in a CausalDotStore. You perform modifications by creating a "delta" CRDT and then merging it back into the original store using .join().
// Create a new CausalDotStore containing an OrArray.
let mut doc: CausalDotStore<OrArray> = CausalDotStore::new();
let id = Identifier::new(0, 0);
// Create a delta to insert a value at the beginning of the array.
let delta = dson::api::array::insert(
|cc, id| MvReg::default().write(MvRegValue::U64(42), cc, id).map_store(Value::Register),
doc.store.len(),
)(&doc.store, &doc.context, id);
// Merge the delta into the document.
doc = doc.join(delta, &mut DummySentinel).unwrap();
// The value can now be read from the array.
let array = doc.store.value().unwrap();
let val = doc.store.get(0).unwrap();
assert_eq!(val.reg.value().unwrap(), &MvRegValue::U64(42));