diff --git a/crdt/gcounter.go b/crdt/gcounter.go index 5196b3b..f91fa62 100644 --- a/crdt/gcounter.go +++ b/crdt/gcounter.go @@ -43,11 +43,11 @@ func (g GCounter[K]) Add(slot K, delta int) error { return nil } -func (g *GCounter[K]) Merge(dest *GCounter[K]) { +// Merge into some destination val +func (g *GCounter[K]) MergeInto(dest *GCounter[K]) { for slot, count := range g.slots { v := max(count, dest.slots[slot]) dest.slots[slot] = v - g.slots[slot] = v } } diff --git a/crdt/gcounter_test.go b/crdt/gcounter_test.go index 001e9e2..f2e9f70 100644 --- a/crdt/gcounter_test.go +++ b/crdt/gcounter_test.go @@ -62,4 +62,37 @@ func TestGCounter(t *testing.T) { t.Fatalf("new gcounter has count of %d, should be 7", n) } }) + + t.Run("merge", func(t *testing.T) { + type hostname string + + host1 := NewGCounter[hostname]() + host2 := NewGCounter[hostname]() + + host1.Add("host1", 3) + host2.Add("host2", 6) + + host2.MergeInto(&host1) + if n := host1.Total(); n != 9 { + t.Errorf("merging once produced %d instead of 9", n) + } + + host2.MergeInto(&host1) + if n := host1.Total(); n != 9 { + t.Errorf("merging a second time changed the target") + } + + if n := host2.Total(); n != 6 { + t.Errorf("merging changed the source") + } + host1.MergeInto(&host2) + if host1.Total() != host2.Total() { + t.Errorf("merging both ways didn't converge") + } + + host2.Incr("host2") + if host1.Total() == host2.Total() { + t.Errorf("improper post-merge mutation") + } + }) }