diff --git a/lib/db/leveldb.go b/lib/db/leveldb.go index a4923875..9893a858 100644 --- a/lib/db/leveldb.go +++ b/lib/db/leveldb.go @@ -167,6 +167,10 @@ func globalKeyFolder(key []byte) []byte { return folder[:izero] } +func isLocalDevice(dev []byte) bool { + return bytes.Equal(dev, protocol.LocalDeviceID[:]) +} + type deletionHandler func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64 func ldbGenericReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, deleteFn deletionHandler) int64 { @@ -297,7 +301,7 @@ func ldbReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) i }) } -func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) int64 { +func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, size *sizeTracker) int64 { runtime.GC() batch := new(leveldb.Batch) @@ -314,12 +318,17 @@ func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) in var maxLocalVer int64 var fk []byte + localDev := isLocalDevice(device) for _, f := range fs { name := []byte(f.Name) fk = deviceKeyInto(fk[:cap(fk)], folder, device, name) l.Debugf("snap.Get %p %x", snap, fk) bs, err := snap.Get(fk, nil) if err == leveldb.ErrNotFound { + if localDev { + size.addFile(f) + } + if lv := ldbInsert(batch, folder, device, f); lv > maxLocalVer { maxLocalVer = lv } @@ -339,6 +348,11 @@ func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) in // Flags might change without the version being bumped when we set the // invalid flag on an existing file. if !ef.Version.Equal(f.Version) || ef.Flags != f.Flags { + if localDev { + size.removeFile(ef) + size.addFile(f) + } + if lv := ldbInsert(batch, folder, device, f); lv > maxLocalVer { maxLocalVer = lv } diff --git a/lib/db/set.go b/lib/db/set.go index 140b1611..db3db7a3 100644 --- a/lib/db/set.go +++ b/lib/db/set.go @@ -13,6 +13,8 @@ package db import ( + stdsync "sync" + "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/sync" @@ -25,6 +27,7 @@ type FileSet struct { folder string db *leveldb.DB blockmap *BlockMap + sizeTracker } // FileIntf is the set of methods implemented by both protocol.FileInfo and @@ -43,6 +46,47 @@ type FileIntf interface { // continue iteration, false to stop. type Iterator func(f FileIntf) bool +type sizeTracker struct { + files int + deleted int + bytes int64 + mut stdsync.Mutex +} + +func (s *sizeTracker) addFile(f FileIntf) { + if f.IsInvalid() { + return + } + s.mut.Lock() + if f.IsDeleted() { + s.deleted++ + } else { + s.files++ + } + s.bytes += f.Size() + s.mut.Unlock() +} + +func (s *sizeTracker) removeFile(f FileIntf) { + if f.IsInvalid() { + return + } + s.mut.Lock() + if f.IsDeleted() { + s.deleted-- + } else { + s.files-- + } + s.bytes -= f.Size() + s.mut.Unlock() +} + +func (s *sizeTracker) Size() (files, deleted int, bytes int64) { + s.mut.Lock() + defer s.mut.Unlock() + return s.files, s.deleted, s.bytes +} + func NewFileSet(folder string, db *leveldb.DB) *FileSet { var s = FileSet{ localVersion: make(map[protocol.DeviceID]int64), @@ -60,6 +104,9 @@ func NewFileSet(folder string, db *leveldb.DB) *FileSet { if f.LocalVersion > s.localVersion[deviceID] { s.localVersion[deviceID] = f.LocalVersion } + if deviceID == protocol.LocalDeviceID { + s.addFile(f) + } return true }) l.Debugf("loaded localVersion for %q: %#v", folder, s.localVersion) @@ -102,7 +149,7 @@ func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) { s.blockmap.Discard(discards) s.blockmap.Update(updates) } - if lv := ldbUpdate(s.db, []byte(s.folder), device[:], fs); lv > s.localVersion[device] { + if lv := ldbUpdate(s.db, []byte(s.folder), device[:], fs, &s.sizeTracker); lv > s.localVersion[device] { s.localVersion[device] = lv } } diff --git a/lib/model/model.go b/lib/model/model.go index b6b12c17..3832f808 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -406,16 +406,7 @@ func (m *Model) LocalSize(folder string) (nfiles, deleted int, bytes int64) { m.fmut.RLock() defer m.fmut.RUnlock() if rf, ok := m.folderFiles[folder]; ok { - rf.WithHaveTruncated(protocol.LocalDeviceID, func(f db.FileIntf) bool { - if f.IsInvalid() { - return true - } - fs, de, by := sizeOfFile(f) - nfiles += fs - deleted += de - bytes += by - return true - }) + nfiles, deleted, bytes = rf.Size() } return }