feat(crdt): add PN-Counter, OR-Set, and LWW-Map CRDTs#40
Merged
Conversation
Counter wraps CRDT[counterState] where counterState holds two map[string]int64 maps (Inc, Dec), one slot per node ID. Because each node writes only to its own slot, LWW in CRDT.Merge is always correct: the newer write always carries the larger value, so the max-semantics of a G-Counter fall out of the existing infrastructure for free. Value() = sum(Inc) - sum(Dec). Tests: basic increment/decrement, zero/negative delta no-op, two-node merge, concurrent goroutine increments, idempotency, commutativity, negative values.
Set[T comparable] is an Observed-Remove Set with add-wins conflict resolution. Each Add creates a uniquely HLC-tagged entry; Remove only tombstones entries visible at call time. A concurrent Add from another node produces a different tag, so after Merge the element survives (add wins over concurrent remove). Implemented as a standalone type (map[string]setEntry[T] keyed by HLC.String()) rather than wrapping CRDT[T]: the LWW clock-filter in CRDT.Merge discards remote entries that have no local clock entry, which breaks union semantics for disjoint adds. Tests: add/contains, remove, re-add after remove, Items dedup, duplicate add, two-node convergence, add-wins property, idempotency, remove-non-existent no-op, commutativity.
Map[K comparable, V any] is a distributed key-value map where concurrent writes to the same key are resolved by Last-Write-Wins (higher HLC timestamp wins). Deletes are tombstones, so delete/set ordering is determined purely by timestamp. Standalone implementation (same reasoning as Set): CRDT[T] LWW discards remote entries without local clock history, breaking convergence for disjoint key sets. Exercises two-parameter generics and validates the HLC clock update-before-compare pattern for correct causal ordering. Tests: set/get, delete, overwrite, Keys(), disjoint-key merge, LWW same-key conflict, delete-wins-over-older-set, set-wins-over-older-delete, idempotency, commutativity, string keys, int keys.
Both types previously maintained their own sync.RWMutex, clock, and merge logic. They now delegate entirely to the generic CRDT[T] engine, which provides per-path LWW clocks, tombstones, and full-state merge for free. Map[K,V] wraps CRDT[map[K]V]: each Set/Delete edits the map value, producing OpAdd/OpReplace/OpRemove ops at path "/<key>"; the CRDT clock and tombstone maps handle LWW resolution and delete-wins / re-add-wins correctly. Set[T] wraps CRDT[setInner[T]] where setInner holds a map[string]setEntry[T] keyed by HLC.String(). Each Add allocates a unique tag via Clock().Now(), inserting a new entry at a distinct path "/Entries/<hlc>"; concurrent Adds from other nodes land at different paths and are therefore unaffected by Remove, preserving the Add-Wins (OR-Set) property without any custom merge logic.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces three new state-based Conflict-free Replicated Data Types (CRDTs) to the crdt package. These types provide high-level
abstractions for common distributed data patterns, all leveraging the project's core CRDT[T] generic base and Hybrid Logical Clocks (HLC).
New CRDTs
to a single convergent value (sum(increments) - sum(decrements)).
of a conflict, the addition takes precedence.
both Set and Delete operations with proper tombstone propagation.
Key Changes & Refactoring
CRDT[T] base. This ensures consistent behavior for synchronization, delta application, and state-based merging across all types.
types is now significantly cleaner and less prone to manual locking or timestamp errors.