lib/db: Prevent IndexID creation race (#7211)

This commit is contained in:
Simon Frei 2020-12-21 11:32:59 +01:00 committed by GitHub
parent 78a41828fc
commit 4a787986cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 11 deletions

View File

@ -65,6 +65,7 @@ type Lowlevel struct {
gcKeyCount int gcKeyCount int
indirectGCInterval time.Duration indirectGCInterval time.Duration
recheckInterval time.Duration recheckInterval time.Duration
oneFileSetCreated chan struct{}
} }
func NewLowlevel(backend backend.Backend, opts ...Option) *Lowlevel { func NewLowlevel(backend backend.Backend, opts ...Option) *Lowlevel {
@ -78,6 +79,7 @@ func NewLowlevel(backend backend.Backend, opts ...Option) *Lowlevel {
gcMut: sync.NewRWMutex(), gcMut: sync.NewRWMutex(),
indirectGCInterval: indirectGCDefaultInterval, indirectGCInterval: indirectGCDefaultInterval,
recheckInterval: recheckDefaultInterval, recheckInterval: recheckDefaultInterval,
oneFileSetCreated: make(chan struct{}),
} }
for _, opt := range opts { for _, opt := range opts {
opt(db) opt(db)

View File

@ -39,13 +39,27 @@ type FileSet struct {
type Iterator func(f protocol.FileIntf) bool type Iterator func(f protocol.FileIntf) bool
func NewFileSet(folder string, fs fs.Filesystem, db *Lowlevel) *FileSet { func NewFileSet(folder string, fs fs.Filesystem, db *Lowlevel) *FileSet {
return &FileSet{ select {
case <-db.oneFileSetCreated:
default:
close(db.oneFileSetCreated)
}
s := &FileSet{
folder: folder, folder: folder,
fs: fs, fs: fs,
db: db, db: db,
meta: db.loadMetadataTracker(folder), meta: db.loadMetadataTracker(folder),
updateMutex: sync.NewMutex(), updateMutex: sync.NewMutex(),
} }
if id := s.IndexID(protocol.LocalDeviceID); id == 0 {
// No index ID set yet. We create one now.
id = protocol.NewIndexID()
err := s.db.setIndexID(protocol.LocalDeviceID[:], []byte(s.folder), id)
if err != nil && !backend.IsClosed(err) {
fatalError(err, fmt.Sprintf("%s Creating new IndexID", s.folder), s.db)
}
}
return s
} }
func (s *FileSet) Drop(device protocol.DeviceID) { func (s *FileSet) Drop(device protocol.DeviceID) {
@ -357,16 +371,6 @@ func (s *FileSet) IndexID(device protocol.DeviceID) protocol.IndexID {
} else if err != nil { } else if err != nil {
fatalError(err, opStr, s.db) fatalError(err, opStr, s.db)
} }
if id == 0 && device == protocol.LocalDeviceID {
// No index ID set yet. We create one now.
id = protocol.NewIndexID()
err := s.db.setIndexID(device[:], []byte(s.folder), id)
if backend.IsClosed(err) {
return 0
} else if err != nil {
fatalError(err, opStr, s.db)
}
}
return id return id
} }
@ -433,7 +437,14 @@ func DropFolder(db *Lowlevel, folder string) {
// DropDeltaIndexIDs removes all delta index IDs from the database. // DropDeltaIndexIDs removes all delta index IDs from the database.
// This will cause a full index transmission on the next connection. // This will cause a full index transmission on the next connection.
// Must be called before using FileSets, i.e. before NewFileSet is called for
// the first time.
func DropDeltaIndexIDs(db *Lowlevel) { func DropDeltaIndexIDs(db *Lowlevel) {
select {
case <-db.oneFileSetCreated:
panic("DropDeltaIndexIDs must not be called after NewFileSet for the same Lowlevel")
default:
}
opStr := "DropDeltaIndexIDs" opStr := "DropDeltaIndexIDs"
l.Debugf(opStr) l.Debugf(opStr)
dbi, err := db.NewPrefixIterator([]byte{KeyTypeIndexID}) dbi, err := db.NewPrefixIterator([]byte{KeyTypeIndexID})

View File

@ -1757,6 +1757,32 @@ func TestNoIndexIDResetOnDrop(t *testing.T) {
} }
} }
func TestConcurrentIndexID(t *testing.T) {
done := make(chan struct{})
var ids [2]protocol.IndexID
setID := func(s *db.FileSet, i int) {
ids[i] = s.IndexID(protocol.LocalDeviceID)
done <- struct{}{}
}
max := 100
if testing.Short() {
max = 10
}
for i := 0; i < max; i++ {
ldb := db.NewLowlevel(backend.OpenMemory())
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeFake, ""), ldb)
go setID(s, 0)
go setID(s, 1)
<-done
<-done
ldb.Close()
if ids[0] != ids[1] {
t.Fatalf("IDs differ after %v rounds", i)
}
}
}
func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) { func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) {
fs.Drop(device) fs.Drop(device)
fs.Update(device, files) fs.Update(device, files)