code wiki / (root) / nx_crdt_sync.nx

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}