From 36ef17df8f95d0891f2e6e1cb87c93b7e7ba8fa3 Mon Sep 17 00:00:00 2001 From: Hireworks <129852174+hireworksltd@users.noreply.github.com> Date: Tue, 12 Nov 2024 07:51:52 +0000 Subject: [PATCH] fix(model): check if remote folder state before pulling files (fixes #9686) (#9732) ### Purpose As discussed in #9686 Syncthing currently does not check folderstate on remote device before pulling. If no devices have a valid folderstate (i.e all devices have the folder paused) it will still attempt to pull. On large folders this will cause a hanging "Syncing" status. This checks whether at least one connected device has the file available and has a valid folderstate. ### Testing Tested locally on multiple devices. We're new to Go (all our stuff is Python) so please bear with! Interested if there may be a better place to slot this in. Thanks, Jon --------- Co-authored-by: Simon Frei --- lib/model/folder_sendrecv.go | 13 +++++-------- lib/model/model.go | 28 +++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index 6ff984f23..7834ee7c7 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -505,13 +505,10 @@ nextFile: continue nextFile } - devices := snap.Availability(fileName) - for _, dev := range devices { - if f.model.ConnectedTo(dev) { - // Handle the file normally, by copying and pulling, etc. - f.handleFile(fi, snap, copyChan) - continue nextFile - } + devices := f.model.fileAvailability(f.FolderConfiguration, snap, fi) + if len(devices) > 0 { + f.handleFile(fi, snap, copyChan) + continue } f.newPullError(fileName, errNotAvailable) f.queue.Done(fileName) @@ -1547,7 +1544,7 @@ func (f *sendReceiveFolder) pullBlock(state pullBlockState, snap *db.Snapshot, o } var lastError error - candidates := f.model.availabilityInSnapshot(f.FolderConfiguration, snap, state.file, state.block) + candidates := f.model.blockAvailability(f.FolderConfiguration, snap, state.file, state.block) loop: for { select { diff --git a/lib/model/model.go b/lib/model/model.go index b4ee3925e..3615eca16 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -2882,16 +2882,31 @@ func (m *model) Availability(folder string, file protocol.FileInfo, block protoc } defer snap.Release() - return m.availabilityInSnapshotRLocked(cfg, snap, file, block), nil + return m.blockAvailabilityRLocked(cfg, snap, file, block), nil } -func (m *model) availabilityInSnapshot(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { +func (m *model) blockAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { m.mut.RLock() defer m.mut.RUnlock() - return m.availabilityInSnapshotRLocked(cfg, snap, file, block) + return m.blockAvailabilityRLocked(cfg, snap, file, block) } -func (m *model) availabilityInSnapshotRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { +func (m *model) blockAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability { + var candidates []Availability + + candidates = append(candidates, m.fileAvailabilityRLocked(cfg, snap, file)...) + candidates = append(candidates, m.blockAvailabilityFromTemporaryRLocked(cfg, file, block)...) + + return candidates +} + +func (m *model) fileAvailability(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability { + m.mut.RLock() + defer m.mut.RUnlock() + return m.fileAvailabilityRLocked(cfg, snap, file) +} + +func (m *model) fileAvailabilityRLocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo) []Availability { var availabilities []Availability for _, device := range snap.Availability(file.Name) { if _, ok := m.remoteFolderStates[device]; !ok { @@ -2905,13 +2920,16 @@ func (m *model) availabilityInSnapshotRLocked(cfg config.FolderConfiguration, sn availabilities = append(availabilities, Availability{ID: device, FromTemporary: false}) } } + return availabilities +} +func (m *model) blockAvailabilityFromTemporaryRLocked(cfg config.FolderConfiguration, file protocol.FileInfo, block protocol.BlockInfo) []Availability { + var availabilities []Availability for _, device := range cfg.Devices { if m.deviceDownloads[device.DeviceID].Has(cfg.ID, file.Name, file.Version, int(block.Offset/int64(file.BlockSize()))) { availabilities = append(availabilities, Availability{ID: device.DeviceID, FromTemporary: true}) } } - return availabilities }