LWW Register

Creator
Creator
Seonglae Cho
Created
Created
2023 Oct 5 13:42
Editor
Edited
Edited
2025 May 20 15:4
Refs
Refs
CRDT

Last Write Wins Register

class LWWRegister<T> { readonly id: string; state: [peer: string, timestamp: number, value: T]; get value() { return this.state[2]; } constructor(id: string, state: [string, number, T]) { this.id = id; this.state = state; } set(value: T) { // set the peer ID to the local ID, increment the local timestamp by 1 and set the value this.state = [this.id, this.state[1] + 1, value]; } merge(state: [peer: string, timestamp: number, value: T]) { const [remotePeer, remoteTimestamp] = state; const [localPeer, localTimestamp] = this.state; // if the local timestamp is greater than the remote timestamp, discard the incoming value if (localTimestamp > remoteTimestamp) return; // if the timestamps are the same but the local peer ID is greater than the remote peer ID, discard the incoming value if (localTimestamp === remoteTimestamp && localPeer > remotePeer) return; // otherwise, overwrite the local state with the remote state this.state = state; } }
 
class LWWMap<T> { readonly id: string; #data = new Map<string, LWWRegister<T | null>>(); constructor(id: string, state: State<T>) { this.id = id; // create a new register for each key in the initial state for (const [key, register] of Object.entries(state)) { this.#data.set(key, new LWWRegister(this.id, register)); } } get value() { const value: Value<T> = {}; // build up an object where each value is set to the value of the register at the corresponding key for (const [key, register] of this.#data.entries()) { if (register.value !== null) value[key] = register.value; } return value; } get state() { const state: State<T> = {}; // build up an object where each value is set to the full state of the register at the corresponding key for (const [key, register] of this.#data.entries()) { if (register) state[key] = register.state; } return state; } has(key: string) { return this.#data.get(key)?.value !== null; } get(key: string) { return this.#data.get(key)?.value; } set(key: string, value: T) { // get the register at the given key const register = this.#data.get(key); // if the register already exists, set the value if (register) register.set(value); // otherwise, instantiate a new `LWWRegister` with the value else this.#data.set(key, new LWWRegister(this.id, [this.id, 1, value])); } delete(key: string) { // set the register to null, if it exists this.#data.get(key)?.set(null); } merge(state: State<T>) { // recursively merge each key's register with the incoming state for that key for (const [key, remote] of Object.entries(state)) { const local = this.#data.get(key); // if the register already exists, merge it with the incoming state if (local) local.merge(remote); // otherwise, instantiate a new `LWWRegister` with the incoming state else this.#data.set(key, new LWWRegister(this.id, remote)); } } }
 
 
 
 
 

Recommendations