nx_crdt_sync.nx source
↩ module page · 60 lines · 2692 B
1// nx_crdt_sync.nx -- CRDT SYNC op-log: the rung that makes two replicas MERGE instead of collide. Each edit is
2// recorded as an op record (kind, id, value, ref) in an append-only log; a replica APPLIES any op-log (its own or a
3// peer's) into a sequence-CRDT doc. Because apply is idempotent + order-independent (the CRDT property), two actors
4// that each make edits, then exchange logs and apply the union, CONVERGE to the identical document -- with BOTH sets
5// of edits present. This is the operational answer to "two Claude sessions edited the same file": no lock, no
6// rejection, no lost edit. Composes nx_crdt_seq + nx_lamport_clock. 100% integer. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_crdt_seq.nx"
9import "nx_lamport_clock.nx"
10
11const OPLOG_MAX: i64 = 1024
12
13// op record (6 i64): [0]=kind(0=insert 1=delete) [1]=id_clock [2]=id_actor [3]=value [4]=ref_clock [5]=ref_actor
14func op_rec(log: *i64, i: i64) -> *i64 { return (log as i64 + (1 + i*6)*8) as *i64 }
15
16func oplog_new() -> *i64 {
17 let l: *i64 = sys_mmap((1 + OPLOG_MAX*6)*8) as *i64
18 l[0] = 0
19 return l
20}
21func oplog_count(log: *i64) -> i64 { return log[0] }
22
23func oplog_append(log: *i64, kind: i64, idc: i64, ida: i64, value: i64, refc: i64, refa: i64) -> i64 {
24 let n: i64 = log[0]
25 if n >= OPLOG_MAX { return 0 - 1 }
26 let r: *i64 = op_rec(log, n)
27 r[0] = kind; r[1] = idc; r[2] = ida; r[3] = value; r[4] = refc; r[5] = refa
28 log[0] = n + 1
29 return n
30}
31
32// EDIT: an actor inserts `value` after reference (refc,refa) -- mints a causal id via its clock, applies to its own
33// doc, AND records the op in its log for sync. Returns the new element's id_clock.
34func sync_insert(doc: *i64, log: *i64, clk: *i64, value: i64, refc: i64, refa: i64) -> i64 {
35 let idc: i64 = lc_tick(clk)
36 let ida: i64 = lc_actor(clk)
37 seq_add(doc, idc, ida, value, refc, refa)
38 oplog_append(log, 0, idc, ida, value, refc, refa)
39 return idc
40}
41func sync_delete(doc: *i64, log: *i64, clk: *i64, idc: i64, ida: i64) -> i64 {
42 seq_delete(doc, idc, ida)
43 oplog_append(log, 1, idc, ida, 0, 0, 0)
44 return 0
45}
46
47// APPLY a (peer's) op-log into `doc`, advancing `clk` so future local ids stay causally after what we observed.
48// Idempotent by construction (seq_add/seq_delete no-op on a known id) -> safe to apply overlapping logs repeatedly.
49func sync_apply_log(doc: *i64, clk: *i64, log: *i64) -> i64 {
50 let n: i64 = log[0]
51 var i: i64 = 0
52 while i < n {
53 let r: *i64 = op_rec(log, i)
54 lc_observe(clk, r[1])
55 if r[0] == 0 { seq_add(doc, r[1], r[2], r[3], r[4], r[5]) }
56 if r[0] == 1 { seq_delete(doc, r[1], r[2]) }
57 i = i + 1
58 }
59 return n
60}