Serialize scans and pulls (fixes #1391)

This commit is contained in:
Jakob Borg
2015-06-20 19:26:25 +02:00
parent 1d2235abe7
commit f73d5a9ab2
4 changed files with 216 additions and 4 deletions

View File

@@ -55,6 +55,7 @@ type service interface {
BringToFront(string)
DelayScan(d time.Duration)
IndexUpdated() // Remote index was updated notification
Scan(subs []string) error
setState(state folderState)
setError(err error)
@@ -1226,6 +1227,21 @@ func (m *Model) ScanFolder(folder string) error {
}
func (m *Model) ScanFolderSubs(folder string, subs []string) error {
m.fmut.Lock()
runner, ok := m.folderRunners[folder]
m.fmut.Unlock()
// Folders are added to folderRunners only when they are started. We can't
// scan them before they have started, so that's what we need to check for
// here.
if !ok {
return errors.New("no such folder")
}
return runner.Scan(subs)
}
func (m *Model) internalScanFolderSubs(folder string, subs []string) error {
for i, sub := range subs {
sub = osutil.NativeFilename(sub)
if p := filepath.Clean(filepath.Join(folder, sub)); !strings.HasPrefix(p, folder) {

View File

@@ -22,9 +22,15 @@ type roFolder struct {
timer *time.Timer
model *Model
stop chan struct{}
scanNow chan rescanRequest
delayScan chan time.Duration
}
type rescanRequest struct {
subs []string
err chan error
}
func newROFolder(model *Model, folder string, interval time.Duration) *roFolder {
return &roFolder{
stateTracker: stateTracker{
@@ -36,6 +42,7 @@ func newROFolder(model *Model, folder string, interval time.Duration) *roFolder
timer: time.NewTimer(time.Millisecond),
model: model,
stop: make(chan struct{}),
scanNow: make(chan rescanRequest),
delayScan: make(chan time.Duration),
}
}
@@ -76,7 +83,7 @@ func (s *roFolder) Serve() {
l.Debugln(s, "rescan")
}
if err := s.model.ScanFolder(s.folder); err != nil {
if err := s.model.internalScanFolderSubs(s.folder, nil); err != nil {
// Potentially sets the error twice, once in the scanner just
// by doing a check, and once here, if the error returned is
// the same one as returned by CheckFolderHealth, though
@@ -92,11 +99,34 @@ func (s *roFolder) Serve() {
}
if s.intv == 0 {
return
continue
}
reschedule()
case req := <-s.scanNow:
if err := s.model.CheckFolderHealth(s.folder); err != nil {
l.Infoln("Skipping folder", s.folder, "scan due to folder error:", err)
req.err <- err
continue
}
if debug {
l.Debugln(s, "forced rescan")
}
if err := s.model.internalScanFolderSubs(s.folder, req.subs); err != nil {
// Potentially sets the error twice, once in the scanner just
// by doing a check, and once here, if the error returned is
// the same one as returned by CheckFolderHealth, though
// duplicate set is handled by setError.
s.setError(err)
req.err <- err
continue
}
req.err <- nil
case next := <-s.delayScan:
s.timer.Reset(next)
}
@@ -110,6 +140,15 @@ func (s *roFolder) Stop() {
func (s *roFolder) IndexUpdated() {
}
func (s *roFolder) Scan(subs []string) error {
req := rescanRequest{
subs: subs,
err: make(chan error),
}
s.scanNow <- req
return <-req.err
}
func (s *roFolder) String() string {
return fmt.Sprintf("roFolder/%s@%p", s.folder, s)
}

View File

@@ -32,7 +32,7 @@ import (
const (
pauseIntv = 60 * time.Second
nextPullIntv = 10 * time.Second
shortPullIntv = 5 * time.Second
shortPullIntv = time.Second
)
// A pullBlockState is passed to the puller routine for each block that needs
@@ -90,6 +90,7 @@ type rwFolder struct {
scanTimer *time.Timer
pullTimer *time.Timer
delayScan chan time.Duration
scanNow chan rescanRequest
remoteIndex chan struct{} // An index update was received, we should re-evaluate needs
}
@@ -118,6 +119,7 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo
pullTimer: time.NewTimer(shortPullIntv),
scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately.
delayScan: make(chan time.Duration),
scanNow: make(chan rescanRequest),
remoteIndex: make(chan struct{}, 1), // This needs to be 1-buffered so that we queue a notification if we're busy doing a pull when it comes.
}
}
@@ -278,7 +280,7 @@ func (p *rwFolder) Serve() {
l.Debugln(p, "rescan")
}
if err := p.model.ScanFolder(p.folder); err != nil {
if err := p.model.internalScanFolderSubs(p.folder, nil); err != nil {
// Potentially sets the error twice, once in the scanner just
// by doing a check, and once here, if the error returned is
// the same one as returned by CheckFolderHealth, though
@@ -296,6 +298,29 @@ func (p *rwFolder) Serve() {
initialScanCompleted = true
}
case req := <-p.scanNow:
if err := p.model.CheckFolderHealth(p.folder); err != nil {
l.Infoln("Skipping folder", p.folder, "scan due to folder error:", err)
req.err <- err
continue
}
if debug {
l.Debugln(p, "forced rescan")
}
if err := p.model.internalScanFolderSubs(p.folder, req.subs); err != nil {
// Potentially sets the error twice, once in the scanner just
// by doing a check, and once here, if the error returned is
// the same one as returned by CheckFolderHealth, though
// duplicate set is handled by setError.
p.setError(err)
req.err <- err
continue
}
req.err <- nil
case next := <-p.delayScan:
p.scanTimer.Reset(next)
}
@@ -317,6 +342,15 @@ func (p *rwFolder) IndexUpdated() {
}
}
func (p *rwFolder) Scan(subs []string) error {
req := rescanRequest{
subs: subs,
err: make(chan error),
}
p.scanNow <- req
return <-req.err
}
func (p *rwFolder) String() string {
return fmt.Sprintf("rwFolder/%s@%p", p.folder, p)
}