The problem here is that we would update the sequence index before updating the FileInfos, which would result in a high sequence number pointing to a low-sequence FileInfo. The index sender would pick up the high sequence number, send the old file, and think everything was good. On the receiving side the old file is a no-op and ignored. The file remains out of sync until another update for it happens. This fixes that by correcting the order of operations in the database update: first we remove old sequence index entries, then we update the FileInfos (which now don't have anything pointing to them) and then we add the sequence indexes (which the index sender can see). The other option is to add "proper" transactions where required at the database layer. I actually have a branch for that, but it's literally thousands of lines of diff and I'm putting that off for another day as this solves the problem...
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/d4l3k/messagediff"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
@@ -914,6 +915,56 @@ func TestWithHaveSequence(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStressWithHaveSequence(t *testing.T) {
|
||||
// This races two loops against each other: one that contiously does
|
||||
// updates, and one that continously does sequence walks. The test fails
|
||||
// if the sequence walker sees a discontinuity.
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("Takes a long time")
|
||||
}
|
||||
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
folder := "test"
|
||||
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
var localHave []protocol.FileInfo
|
||||
for i := 0; i < 100; i++ {
|
||||
localHave = append(localHave, protocol.FileInfo{Name: fmt.Sprintf("file%d", i), Blocks: genBlocks(i * 10)})
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
t0 := time.Now()
|
||||
go func() {
|
||||
for time.Since(t0) < 10*time.Second {
|
||||
for j, f := range localHave {
|
||||
localHave[j].Version = f.Version.Update(42)
|
||||
}
|
||||
|
||||
s.Update(protocol.LocalDeviceID, localHave)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
var prevSeq int64 = 0
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
break loop
|
||||
default:
|
||||
}
|
||||
s.WithHaveSequence(prevSeq+1, func(fi db.FileIntf) bool {
|
||||
if fi.SequenceNo() < prevSeq+1 {
|
||||
t.Fatal("Skipped ", prevSeq+1, fi.SequenceNo())
|
||||
}
|
||||
prevSeq = fi.SequenceNo()
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue4925(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user