fix(db): version vector serialisation :( (#10050)

ffs
This commit is contained in:
Jakob Borg
2025-04-09 08:46:49 -07:00
committed by GitHub
parent 7cfa871d58
commit ddea2e449c
4 changed files with 91 additions and 1 deletions

View File

@@ -1109,6 +1109,54 @@ func TestErrorWrap(t *testing.T) {
}
}
func TestStrangeDeletedGlobalBug(t *testing.T) {
// This exercises an edge case with serialisation and ordering of
// version vectors. It does not need to make sense, it just needs to
// pass.
t.Parallel()
sdb, err := OpenTemp()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := sdb.Close(); err != nil {
t.Fatal(err)
}
})
// One remote device announces the original version of the file
file := genFile("test", 1, 1)
file.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 35494436325452, Value: 1742900373}}}
t.Log("orig", file.Version)
sdb.Update(folderID, protocol.DeviceID{42}, []protocol.FileInfo{file})
// Another one announces a newer one that is deleted
del := file
del.SetDeleted(43)
del.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 55445057455644, Value: 1742918457}, {ID: 35494436325452, Value: 1742900373}}}
t.Log("del", del.Version)
sdb.Update(folderID, protocol.DeviceID{43}, []protocol.FileInfo{del})
// We have an instance of the original file
sdb.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file})
// Which one is the global? It should be the deleted one, clearly.
g, _, err := sdb.GetGlobalFile(folderID, "test")
if err != nil {
t.Fatal(err)
}
if !g.Deleted {
t.Log(g)
t.Fatal("should be deleted")
}
}
func mustCollect[T any](t *testing.T) func(it iter.Seq[T], errFn func() error) []T {
t.Helper()
return func(it iter.Seq[T], errFn func() error) []T {

View File

@@ -7,9 +7,11 @@
package sqlite
import (
"cmp"
"database/sql/driver"
"errors"
"iter"
"slices"
"github.com/jmoiron/sqlx"
"github.com/syncthing/syncthing/internal/gen/bep"
@@ -71,6 +73,11 @@ func (v *dbVector) Scan(value any) error {
if err != nil {
return wrap(err)
}
// This is only necessary because I messed up counter serialisation and
// thereby ordering in 2.0.0 betas, and can be removed in the future.
slices.SortFunc(vec.Counters, func(a, b protocol.Counter) int { return cmp.Compare(a.ID, b.ID) })
v.Vector = vec
return nil

View File

@@ -0,0 +1,33 @@
// Copyright (C) 2025 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package sqlite
import (
"testing"
"github.com/syncthing/syncthing/lib/protocol"
)
func TestDbvector(t *testing.T) {
vec := protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 7}, {ID: 123456789, Value: 42424242}}}
dbVec := dbVector{vec}
val, err := dbVec.Value()
if err != nil {
t.Fatal(val)
}
var dbVec2 dbVector
if err := dbVec2.Scan(val); err != nil {
t.Fatal(err)
}
if !dbVec2.Vector.Equal(vec) {
t.Log(vec)
t.Log(dbVec2.Vector)
t.Fatal("should match")
}
}

View File

@@ -31,7 +31,9 @@ func (v *Vector) String() string {
if i > 0 {
buf.WriteRune(',')
}
fmt.Fprintf(&buf, "%x:%d", c.ID, c.Value)
var idbs [8]byte
binary.BigEndian.PutUint64(idbs[:], uint64(c.ID))
fmt.Fprintf(&buf, "%x:%d", idbs, c.Value)
}
return buf.String()
}