mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-08 05:57:03 +09:00
Manage seen heads
This commit is contained in:
parent
a63285c8ef
commit
9d8321e5a2
3 changed files with 85 additions and 12 deletions
|
@ -2,6 +2,7 @@ package objecttree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/anyproto/any-sync/util/slice"
|
"github.com/anyproto/any-sync/util/slice"
|
||||||
)
|
)
|
||||||
|
@ -41,11 +42,15 @@ func NewChangeDiffer(tree ReadableObjectTree, hasChanges hasChangesFunc) (*Chang
|
||||||
return diff, nil
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ChangeDiffer) RemoveBefore(ids []string) (removed []string, notFound []string) {
|
func (d *ChangeDiffer) RemoveBefore(ids []string, seenHeads []string) (removed []string, notFound []string, heads []string) {
|
||||||
var attached []*Change
|
var (
|
||||||
|
attached []*Change
|
||||||
|
attachedIds []string
|
||||||
|
)
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if ch, ok := d.attached[id]; ok {
|
if ch, ok := d.attached[id]; ok {
|
||||||
attached = append(attached, ch)
|
attached = append(attached, ch)
|
||||||
|
attachedIds = append(attachedIds, id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if we have it at the bottom
|
// check if we have it at the bottom
|
||||||
|
@ -53,7 +58,11 @@ func (d *ChangeDiffer) RemoveBefore(ids []string) (removed []string, notFound []
|
||||||
notFound = append(notFound, id)
|
notFound = append(notFound, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
heads = make([]string, 0, len(seenHeads))
|
||||||
|
heads = append(heads, seenHeads...)
|
||||||
d.dfsPrev(attached, func(ch *Change) (isContinue bool) {
|
d.dfsPrev(attached, func(ch *Change) (isContinue bool) {
|
||||||
|
heads = append(heads, ch.Id)
|
||||||
|
heads = append(heads, ch.PreviousIds...)
|
||||||
removed = append(removed, ch.Id)
|
removed = append(removed, ch.Id)
|
||||||
return true
|
return true
|
||||||
}, func(changes []*Change) {
|
}, func(changes []*Change) {
|
||||||
|
@ -66,6 +75,12 @@ func (d *ChangeDiffer) RemoveBefore(ids []string) (removed []string, notFound []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
slices.Sort(heads)
|
||||||
|
heads = slice.RemoveRepeatedSorted(heads)
|
||||||
|
heads = append(heads, attachedIds...)
|
||||||
|
heads = append(heads, seenHeads...)
|
||||||
|
heads = slice.RemoveUniqueElementsSorted(heads)
|
||||||
|
heads = slice.DiscardDuplicatesSorted(heads)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,15 +177,21 @@ func NewDiffManager(initHeads, curHeads []string, treeBuilder treeBuilderFunc, o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DiffManager) Init() {
|
func (d *DiffManager) Init() {
|
||||||
removed, _ := d.differ.RemoveBefore(d.seenHeads)
|
removed, _, seenHeads := d.differ.RemoveBefore(d.seenHeads, []string{})
|
||||||
|
d.seenHeads = seenHeads
|
||||||
d.onRemove(removed)
|
d.onRemove(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DiffManager) SeenHeads() []string {
|
||||||
|
return d.seenHeads
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DiffManager) Remove(ids []string) {
|
func (d *DiffManager) Remove(ids []string) {
|
||||||
removed, notFound := d.differ.RemoveBefore(ids)
|
removed, notFound, seenHeads := d.differ.RemoveBefore(ids, d.seenHeads)
|
||||||
for _, id := range notFound {
|
for _, id := range notFound {
|
||||||
d.notFound[id] = struct{}{}
|
d.notFound[id] = struct{}{}
|
||||||
}
|
}
|
||||||
|
d.seenHeads = seenHeads
|
||||||
d.onRemove(removed)
|
d.onRemove(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +224,7 @@ func (d *DiffManager) Update(objTree ObjectTree) {
|
||||||
d.differ.Add(toAdd...)
|
d.differ.Add(toAdd...)
|
||||||
d.heads = make([]string, 0, len(d.heads))
|
d.heads = make([]string, 0, len(d.heads))
|
||||||
d.heads = append(d.heads, objTree.Heads()...)
|
d.heads = append(d.heads, objTree.Heads()...)
|
||||||
removed, _ := d.differ.RemoveBefore(toRemove)
|
removed, _, seenHeads := d.differ.RemoveBefore(toRemove, d.seenHeads)
|
||||||
|
d.seenHeads = seenHeads
|
||||||
d.onRemove(removed)
|
d.onRemove(removed)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,10 @@ func TestChangeDiffer_Add(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
differ.Add(changes...)
|
differ.Add(changes...)
|
||||||
res, notFound := differ.RemoveBefore([]string{"7"})
|
res, notFound, seenHeads := differ.RemoveBefore([]string{"7"}, []string{"0"})
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
require.Equal(t, len(changes), len(res))
|
require.Equal(t, len(changes), len(res))
|
||||||
|
require.Equal(t, []string{"7"}, seenHeads)
|
||||||
})
|
})
|
||||||
t.Run("remove in two parts", func(t *testing.T) {
|
t.Run("remove in two parts", func(t *testing.T) {
|
||||||
changes := []*Change{
|
changes := []*Change{
|
||||||
|
@ -42,12 +43,14 @@ func TestChangeDiffer_Add(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
differ.Add(changes...)
|
differ.Add(changes...)
|
||||||
res, notFound := differ.RemoveBefore([]string{"4"})
|
res, notFound, seenHeads := differ.RemoveBefore([]string{"4"}, []string{"0"})
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
require.Equal(t, 2, len(res))
|
require.Equal(t, 2, len(res))
|
||||||
res, notFound = differ.RemoveBefore([]string{"7"})
|
require.Equal(t, []string{"4"}, seenHeads)
|
||||||
|
res, notFound, seenHeads = differ.RemoveBefore([]string{"7"}, seenHeads)
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
require.Equal(t, 6, len(res))
|
require.Equal(t, 6, len(res))
|
||||||
|
require.Equal(t, []string{"7"}, seenHeads)
|
||||||
})
|
})
|
||||||
t.Run("add and remove", func(t *testing.T) {
|
t.Run("add and remove", func(t *testing.T) {
|
||||||
changes := []*Change{
|
changes := []*Change{
|
||||||
|
@ -60,9 +63,10 @@ func TestChangeDiffer_Add(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
differ.Add(changes...)
|
differ.Add(changes...)
|
||||||
res, notFound := differ.RemoveBefore([]string{"3"})
|
res, notFound, seenHeads := differ.RemoveBefore([]string{"3"}, []string{"0"})
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
require.Equal(t, len(changes), len(res))
|
require.Equal(t, len(changes), len(res))
|
||||||
|
require.Equal(t, []string{"3"}, seenHeads)
|
||||||
changes = []*Change{
|
changes = []*Change{
|
||||||
newChange("4", "0", "0"),
|
newChange("4", "0", "0"),
|
||||||
newChange("5", "0", "4"),
|
newChange("5", "0", "4"),
|
||||||
|
@ -70,15 +74,17 @@ func TestChangeDiffer_Add(t *testing.T) {
|
||||||
newChange("7", "0", "3", "6"),
|
newChange("7", "0", "3", "6"),
|
||||||
}
|
}
|
||||||
differ.Add(changes...)
|
differ.Add(changes...)
|
||||||
res, notFound = differ.RemoveBefore([]string{"7"})
|
res, notFound, seenHeads = differ.RemoveBefore([]string{"7"}, seenHeads)
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
require.Equal(t, len(changes), len(res))
|
require.Equal(t, len(changes), len(res))
|
||||||
|
require.Equal(t, []string{"7"}, seenHeads)
|
||||||
})
|
})
|
||||||
t.Run("remove not found", func(t *testing.T) {
|
t.Run("remove not found", func(t *testing.T) {
|
||||||
differ, _ := NewChangeDiffer(nil, func(ids ...string) bool {
|
differ, _ := NewChangeDiffer(nil, func(ids ...string) bool {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
_, notFound := differ.RemoveBefore([]string{"3", "4", "5"})
|
_, notFound, seenHeads := differ.RemoveBefore([]string{"3", "4", "5"}, []string{"0"})
|
||||||
|
require.Equal(t, seenHeads, []string{"0"})
|
||||||
require.Len(t, notFound, 3)
|
require.Len(t, notFound, 3)
|
||||||
})
|
})
|
||||||
t.Run("exists in storage", func(t *testing.T) {
|
t.Run("exists in storage", func(t *testing.T) {
|
||||||
|
@ -91,8 +97,9 @@ func TestChangeDiffer_Add(t *testing.T) {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
res, notFound := differ.RemoveBefore([]string{"3", "4", "5"})
|
res, notFound, seenHeads := differ.RemoveBefore([]string{"3", "4", "5"}, []string{"7"})
|
||||||
require.Len(t, res, 0)
|
require.Len(t, res, 0)
|
||||||
require.Len(t, notFound, 0)
|
require.Len(t, notFound, 0)
|
||||||
|
require.Equal(t, seenHeads, []string{"7"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,6 +159,50 @@ func DiscardDuplicatesSorted[T comparable](sorted []T) []T {
|
||||||
return sorted[:cnt]
|
return sorted[:cnt]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveRepeatedSorted[T constraints.Ordered](nums []T) []T {
|
||||||
|
if len(nums) == 0 {
|
||||||
|
return nums
|
||||||
|
}
|
||||||
|
writeIndex := 0
|
||||||
|
i := 0
|
||||||
|
for i < len(nums) {
|
||||||
|
count := 1
|
||||||
|
for i+1 < len(nums) && nums[i] == nums[i+1] {
|
||||||
|
count++
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if count == 1 {
|
||||||
|
nums[writeIndex] = nums[i]
|
||||||
|
writeIndex++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nums[:writeIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveUniqueElementsSorted[T constraints.Ordered](nums []T) []T {
|
||||||
|
if len(nums) == 0 {
|
||||||
|
return nums
|
||||||
|
}
|
||||||
|
writeIndex := 0
|
||||||
|
i := 0
|
||||||
|
for i < len(nums) {
|
||||||
|
count := 1
|
||||||
|
for i+1 < len(nums) && nums[i] == nums[i+1] {
|
||||||
|
count++
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if count > 1 {
|
||||||
|
for j := 0; j < count; j++ {
|
||||||
|
nums[writeIndex] = nums[i]
|
||||||
|
writeIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nums[:writeIndex]
|
||||||
|
}
|
||||||
|
|
||||||
func DiscardDuplicatesSortedFunc[T any](sorted []T, equal func(T, T) bool) []T {
|
func DiscardDuplicatesSortedFunc[T any](sorted []T, equal func(T, T) bool) []T {
|
||||||
if len(sorted) <= 1 {
|
if len(sorted) <= 1 {
|
||||||
return sorted
|
return sorted
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue