1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 14:07:02 +09:00

Add compare diff

This commit is contained in:
Mikhail Rakhmanov 2025-04-02 16:36:23 +02:00
parent 15a82b8675
commit e03f2e6efd
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
2 changed files with 110 additions and 5 deletions

View file

@ -42,6 +42,11 @@ type Diff interface {
DiffType() spacesyncproto.DiffType
}
type CompareDiff interface {
CompareDiff(ctx context.Context, dl Remote) (newIds, ourChangedIds, theirChangedIds, removedIds []string, err error)
Diff
}
// New creates precalculated Diff container
//
// divideFactor - means how many hashes you want to ask for once
@ -273,17 +278,18 @@ func (d *diff) Ranges(ctx context.Context, ranges []Range, resBuf []RangeResult)
}
type diffCtx struct {
newIds, changedIds, removedIds []string
newIds, changedIds, theirChangedIds, removedIds []string
toSend, prepare []Range
myRes, otherRes []RangeResult
compareFunc func(dctx *diffCtx, my, other []Element)
}
var errMismatched = errors.New("query and results mismatched")
// Diff makes diff with remote container
func (d *diff) Diff(ctx context.Context, dl Remote) (newIds, changedIds, removedIds []string, err error) {
dctx := &diffCtx{}
dctx := &diffCtx{compareFunc: d.compareElementsEqual}
dctx.toSend = append(dctx.toSend, Range{
From: 0,
To: math.MaxUint64,
@ -314,18 +320,51 @@ func (d *diff) Diff(ctx context.Context, dl Remote) (newIds, changedIds, removed
return dctx.newIds, dctx.changedIds, dctx.removedIds, nil
}
func (d *diff) CompareDiff(ctx context.Context, dl Remote) (newIds, ourChangedIds, theirChangedIds, removedIds []string, err error) {
dctx := &diffCtx{compareFunc: d.compareElementsGreater}
dctx.toSend = append(dctx.toSend, Range{
From: 0,
To: math.MaxUint64,
})
for len(dctx.toSend) > 0 {
select {
case <-ctx.Done():
err = ctx.Err()
return
default:
}
if dctx.otherRes, err = dl.Ranges(ctx, dctx.toSend, dctx.otherRes); err != nil {
return
}
if dctx.myRes, err = d.Ranges(ctx, dctx.toSend, dctx.myRes); err != nil {
return
}
if len(dctx.otherRes) != len(dctx.toSend) || len(dctx.myRes) != len(dctx.toSend) {
err = errMismatched
return
}
for i, r := range dctx.toSend {
d.compareResults(dctx, r, dctx.myRes[i], dctx.otherRes[i])
}
dctx.toSend, dctx.prepare = dctx.prepare, dctx.toSend
dctx.prepare = dctx.prepare[:0]
}
return dctx.newIds, dctx.changedIds, dctx.theirChangedIds, dctx.removedIds, nil
}
func (d *diff) compareResults(dctx *diffCtx, r Range, myRes, otherRes RangeResult) {
// both hash equals - do nothing
if bytes.Equal(myRes.Hash, otherRes.Hash) {
return
}
// other has elements
if len(otherRes.Elements) == otherRes.Count {
if len(myRes.Elements) == myRes.Count {
d.compareElements(dctx, myRes.Elements, otherRes.Elements)
dctx.compareFunc(dctx, myRes.Elements, otherRes.Elements)
} else {
r.Elements = true
d.compareElements(dctx, d.getRange(r).Elements, otherRes.Elements)
dctx.compareFunc(dctx, d.getRange(r).Elements, otherRes.Elements)
}
return
}
@ -341,7 +380,7 @@ func (d *diff) compareResults(dctx *diffCtx, r Range, myRes, otherRes RangeResul
return
}
func (d *diff) compareElements(dctx *diffCtx, my, other []Element) {
func (d *diff) compareElementsEqual(dctx *diffCtx, my, other []Element) {
find := func(list []Element, targetEl Element) (has, eq bool) {
for _, el := range list {
if el.Id == targetEl.Id {
@ -369,3 +408,40 @@ func (d *diff) compareElements(dctx *diffCtx, my, other []Element) {
}
}
}
func (d *diff) compareElementsGreater(dctx *diffCtx, my, other []Element) {
find := func(list []Element, targetEl Element) (has, equal, greater bool) {
for _, el := range list {
if el.Id == targetEl.Id {
if el.Head == targetEl.Head {
return true, true, false
}
return true, false, el.Head > targetEl.Head
}
}
return false, false, false
}
for _, el := range my {
has, eq, theirGreater := find(other, el)
if !has {
dctx.removedIds = append(dctx.removedIds, el.Id)
continue
} else {
if eq {
continue
}
if theirGreater {
dctx.theirChangedIds = append(dctx.theirChangedIds, el.Id)
} else {
dctx.changedIds = append(dctx.changedIds, el.Id)
}
}
}
for _, el := range other {
if has, _, _ := find(my, el); !has {
dctx.newIds = append(dctx.newIds, el.Id)
}
}
}

View file

@ -139,6 +139,35 @@ func TestDiff_Diff(t *testing.T) {
assert.Len(t, changedIds, length/2)
assert.Len(t, removedIds, 0)
})
t.Run("compare diff", func(t *testing.T) {
d1 := New(16, 128).(CompareDiff)
d2 := New(16, 128)
length := 10000
for i := 0; i < length; i++ {
id := fmt.Sprint(i)
head := "a" + uuid.NewString()
d1.Set(Element{
Id: id,
Head: head,
})
}
for i := 0; i < length; i++ {
id := fmt.Sprint(i)
head := "b" + uuid.NewString()
d2.Set(Element{
Id: id,
Head: head,
})
}
newIds, changedIds, theirChangedIds, removedIds, err := d1.CompareDiff(ctx, d2)
require.NoError(t, err)
assert.Len(t, newIds, 0)
assert.Len(t, changedIds, 0)
assert.Len(t, theirChangedIds, length)
assert.Len(t, removedIds, 0)
})
t.Run("empty", func(t *testing.T) {
d1 := New(16, 16)
d2 := New(16, 16)