lib/db: Fix accounting bug when dropping indexes (#7774)

This commit is contained in:
Simon Frei 2021-06-17 10:15:11 +02:00 committed by GitHub
parent aa6c55dec1
commit 23a0e18292
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 28 deletions

View File

@ -20,7 +20,7 @@ import (
// do not put restrictions on downgrades (e.g. for repairs after a bugfix). // do not put restrictions on downgrades (e.g. for repairs after a bugfix).
const ( const (
dbVersion = 14 dbVersion = 14
dbMigrationVersion = 18 dbMigrationVersion = 19
dbMinSyncthingVersion = "v1.9.0" dbMinSyncthingVersion = "v1.9.0"
) )
@ -102,7 +102,7 @@ func (db *schemaUpdater) updateSchema() error {
{14, 14, "v1.9.0", db.updateSchemaTo14}, {14, 14, "v1.9.0", db.updateSchemaTo14},
{14, 16, "v1.9.0", db.checkRepairMigration}, {14, 16, "v1.9.0", db.checkRepairMigration},
{14, 17, "v1.9.0", db.migration17}, {14, 17, "v1.9.0", db.migration17},
{14, 18, "v1.9.0", db.dropIndexIDsMigration}, {14, 19, "v1.9.0", db.dropIndexIDsMigration},
} }
for _, m := range migrations { for _, m := range migrations {

View File

@ -1784,6 +1784,41 @@ func TestConcurrentIndexID(t *testing.T) {
} }
} }
func TestNeedRemoveLastValid(t *testing.T) {
db := newLowlevelMemory(t)
defer db.Close()
folder := "test"
testFs := fs.NewFilesystem(fs.FilesystemTypeFake, "")
fs := newFileSet(t, folder, testFs, db)
files := []protocol.FileInfo{
{Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1},
}
fs.Update(remoteDevice0, files)
files[0].Version = files[0].Version.Update(myID)
fs.Update(remoteDevice1, files)
files[0].LocalFlags = protocol.FlagLocalIgnored
fs.Update(protocol.LocalDeviceID, files)
snap := snapshot(t, fs)
c := snap.NeedSize(remoteDevice0)
if c.Files != 1 {
t.Errorf("Expected 1 needed files initially, got %v", c.Files)
}
snap.Release()
fs.Drop(remoteDevice1)
snap = snapshot(t, fs)
c = snap.NeedSize(remoteDevice0)
if c.Files != 0 {
t.Errorf("Expected no needed files, got %v", c.Files)
}
snap.Release()
}
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)

View File

@ -331,15 +331,12 @@ func (vl *VersionList) pop(device, name []byte) (FileVersion, bool, bool, error)
oldFV := vl.RawVersions[i].copy() oldFV := vl.RawVersions[i].copy()
if invDevice { if invDevice {
vl.RawVersions[i].InvalidDevices = popDeviceAt(vl.RawVersions[i].InvalidDevices, j) vl.RawVersions[i].InvalidDevices = popDeviceAt(vl.RawVersions[i].InvalidDevices, j)
} else { return oldFV, true, false, nil
vl.RawVersions[i].Devices = popDeviceAt(vl.RawVersions[i].Devices, j)
} }
vl.RawVersions[i].Devices = popDeviceAt(vl.RawVersions[i].Devices, j)
// If the last valid device of the previous global was removed above, // If the last valid device of the previous global was removed above,
// the next entry is now the global entry (unless all entries are invalid). // the global changed.
if len(vl.RawVersions[i].Devices) == 0 && globalPos == i { return oldFV, true, len(vl.RawVersions[i].Devices) == 0 && globalPos == i, nil
return oldFV, true, globalPos == vl.findGlobal(), nil
}
return oldFV, true, false, nil
} }
// Get returns a FileVersion that contains the given device and whether it has // Get returns a FileVersion that contains the given device and whether it has

View File

@ -659,10 +659,7 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// Must happen before updating global meta: If this is the first // Must happen before updating global meta: If this is the first
// item from this device, it will be initialized with the global state. // item from this device, it will be initialized with the global state.
needBefore := false needBefore := haveOldGlobal && Need(oldGlobalFV, haveRemoved, removedFV.Version)
if haveOldGlobal {
needBefore = Need(oldGlobalFV, haveRemoved, removedFV.Version)
}
needNow := Need(globalFV, true, file.Version) needNow := Need(globalFV, true, file.Version)
if needBefore { if needBefore {
if keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, oldGlobalFV); err != nil { if keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, oldGlobalFV); err != nil {
@ -677,7 +674,8 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
} }
} }
if needNow { if needNow {
if keyBuf, global, err = t.updateGlobalGetGlobal(keyBuf, folder, name, file, globalFV); err != nil { keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV)
if err != nil {
return nil, false, err return nil, false, err
} }
gotGlobal = true gotGlobal = true
@ -711,8 +709,11 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// Add the new global to the global size counter // Add the new global to the global size counter
if !gotGlobal { if !gotGlobal {
if keyBuf, global, err = t.updateGlobalGetGlobal(keyBuf, folder, name, file, globalFV); err != nil { if globalFV.Version.Equal(file.Version) {
return nil, false, err // The inserted file is the global file
global = file
} else {
keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV)
} }
gotGlobal = true gotGlobal = true
} }
@ -721,10 +722,7 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// check for local (if not already done before) // check for local (if not already done before)
if !bytes.Equal(device, protocol.LocalDeviceID[:]) { if !bytes.Equal(device, protocol.LocalDeviceID[:]) {
localFV, haveLocal := fl.Get(protocol.LocalDeviceID[:]) localFV, haveLocal := fl.Get(protocol.LocalDeviceID[:])
needBefore := false needBefore := haveOldGlobal && Need(oldGlobalFV, haveLocal, localFV.Version)
if haveOldGlobal {
needBefore = Need(oldGlobalFV, haveLocal, localFV.Version)
}
needNow := Need(globalFV, haveLocal, localFV.Version) needNow := Need(globalFV, haveLocal, localFV.Version)
if needBefore { if needBefore {
meta.removeNeeded(protocol.LocalDeviceID, oldGlobal) meta.removeNeeded(protocol.LocalDeviceID, oldGlobal)
@ -761,14 +759,6 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
return keyBuf, true, nil return keyBuf, true, nil
} }
func (t readWriteTransaction) updateGlobalGetGlobal(keyBuf, folder, name []byte, file protocol.FileInfo, fv FileVersion) ([]byte, protocol.FileIntf, error) {
if fv.Version.Equal(file.Version) {
// Inserted a new newest version
return keyBuf, file, nil
}
return t.getGlobalFromFileVersion(keyBuf, folder, name, true, fv)
}
func (t readWriteTransaction) updateLocalNeed(keyBuf, folder, name []byte, add bool) ([]byte, error) { func (t readWriteTransaction) updateLocalNeed(keyBuf, folder, name []byte, add bool) ([]byte, error) {
var err error var err error
keyBuf, err = t.keyer.GenerateNeedFileKey(keyBuf, folder, name) keyBuf, err = t.keyer.GenerateNeedFileKey(keyBuf, folder, name)
@ -824,6 +814,7 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
} }
oldGlobalFV, haveOldGlobal := fl.GetGlobal() oldGlobalFV, haveOldGlobal := fl.GetGlobal()
oldGlobalFV = oldGlobalFV.copy()
if !haveOldGlobal { if !haveOldGlobal {
// Shouldn't ever happen, but doesn't hurt to handle. // Shouldn't ever happen, but doesn't hurt to handle.