From fe34b08ece37707f80febf7b0267b199a5abe16d Mon Sep 17 00:00:00 2001 From: Lode Hoste Date: Fri, 1 May 2015 14:30:17 +0200 Subject: [PATCH 1/3] Reschedule the next scan interval (fixes #1591) --- cmd/syncthing/gui.go | 9 +++- internal/model/.model.go.swp | Bin 0 -> 16384 bytes internal/model/model.go | 11 +++++ internal/model/rofolder.go | 23 +++++++-- internal/model/rwfolder.go | 49 +++++++++++++------ test/delay_scan_test.go | 91 +++++++++++++++++++++++++++++++++++ test/syncthingprocess.go | 14 ++++++ 7 files changed, 178 insertions(+), 19 deletions(-) create mode 100644 internal/model/.model.go.swp create mode 100644 test/delay_scan_test.go diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 8ce194fda..7e91f9c74 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -155,7 +155,7 @@ func (s *apiSvc) Serve() { postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio) // folder file [perpage] [page] postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores) // folder postRestMux.HandleFunc("/rest/db/override", s.postDBOverride) // folder - postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] + postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] [delay] postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig) // postRestMux.HandleFunc("/rest/system/discovery", s.postSystemDiscovery) // device addr postRestMux.HandleFunc("/rest/system/error", s.postSystemError) // @@ -779,14 +779,21 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { err := s.model.ScanFolderSubs(folder, subs) if err != nil { http.Error(w, err.Error(), 500) + return } } else { errors := s.model.ScanFolders() if len(errors) > 0 { http.Error(w, "Error scanning folders", 500) json.NewEncoder(w).Encode(errors) + return } } + nextStr := qs.Get("next") + next, err := strconv.Atoi(nextStr) + if err == nil { + s.model.DelayScan(folder, time.Duration(next)*time.Second) + } } func (s *apiSvc) postDBPrio(w http.ResponseWriter, r *http.Request) { diff --git a/internal/model/.model.go.swp b/internal/model/.model.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..2ab0c78acd41357e243e6b6bf779d50a48d36227 GIT binary patch literal 16384 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 E00bSlY5)KL literal 0 HcmV?d00001 diff --git a/internal/model/model.go b/internal/model/model.go index fc77f5502..a5bb03e84 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -49,6 +49,7 @@ type service interface { Stop() Jobs() ([]string, []string) // In progress, Queued BringToFront(string) + DelayScan(d time.Duration) setState(state folderState) setError(err error) @@ -1322,6 +1323,16 @@ nextSub: return nil } +func (m *Model) DelayScan(folder string, next time.Duration) { + m.fmut.Lock() + runner, ok := m.folderRunners[folder] + m.fmut.Unlock() + if !ok { + return + } + runner.DelayScan(next) +} + // numHashers returns the number of hasher routines to use for a given folder, // taking into account configuration and available CPU cores. func (m *Model) numHashers(folder string) int { diff --git a/internal/model/rofolder.go b/internal/model/rofolder.go index ac867273a..76c928dd8 100644 --- a/internal/model/rofolder.go +++ b/internal/model/rofolder.go @@ -19,6 +19,8 @@ type roFolder struct { folder string intv time.Duration + timer *time.Timer + tmut sync.Mutex // protects timer model *Model stop chan struct{} } @@ -31,6 +33,8 @@ func newROFolder(model *Model, folder string, interval time.Duration) *roFolder }, folder: folder, intv: interval, + timer: time.NewTimer(time.Millisecond), + tmut: sync.NewMutex(), model: model, stop: make(chan struct{}), } @@ -42,13 +46,18 @@ func (s *roFolder) Serve() { defer l.Debugln(s, "exiting") } - timer := time.NewTimer(time.Millisecond) - defer timer.Stop() + defer func() { + s.tmut.Lock() + s.timer.Stop() + s.tmut.Unlock() + }() reschedule := func() { // Sleep a random time between 3/4 and 5/4 of the configured interval. sleepNanos := (s.intv.Nanoseconds()*3 + rand.Int63n(2*s.intv.Nanoseconds())) / 4 - timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) + s.tmut.Lock() + s.timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) + s.tmut.Unlock() } initialScanCompleted := false @@ -57,7 +66,7 @@ func (s *roFolder) Serve() { case <-s.stop: return - case <-timer.C: + case <-s.timer.C: if err := s.model.CheckFolderHealth(s.folder); err != nil { l.Infoln("Skipping folder", s.folder, "scan due to folder error:", err) reschedule() @@ -105,3 +114,9 @@ func (s *roFolder) BringToFront(string) {} func (s *roFolder) Jobs() ([]string, []string) { return nil, nil } + +func (s *roFolder) DelayScan(next time.Duration) { + s.tmut.Lock() + s.timer.Reset(next) + s.tmut.Unlock() +} diff --git a/internal/model/rwfolder.go b/internal/model/rwfolder.go index fdb7b5ccd..07849483d 100644 --- a/internal/model/rwfolder.go +++ b/internal/model/rwfolder.go @@ -74,6 +74,9 @@ type rwFolder struct { stop chan struct{} queue *jobQueue dbUpdates chan protocol.FileInfo + scanTimer *time.Timer + pullTimer *time.Timer + tmut sync.Mutex // protects scanTimer and pullTimer } func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFolder { @@ -96,8 +99,11 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo shortID: shortID, order: cfg.Order, - stop: make(chan struct{}), - queue: newJobQueue(), + stop: make(chan struct{}), + queue: newJobQueue(), + pullTimer: time.NewTimer(checkPullIntv), + scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately. + tmut: sync.NewMutex(), } } @@ -109,12 +115,11 @@ func (p *rwFolder) Serve() { defer l.Debugln(p, "exiting") } - pullTimer := time.NewTimer(checkPullIntv) - scanTimer := time.NewTimer(time.Millisecond) // The first scan should be done immediately. - defer func() { - pullTimer.Stop() - scanTimer.Stop() + p.tmut.Lock() + p.pullTimer.Stop() + p.scanTimer.Stop() + p.tmut.Unlock() // TODO: Should there be an actual FolderStopped state? p.setState(FolderIdle) }() @@ -135,7 +140,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next rescan in", intv) } - scanTimer.Reset(intv) + p.tmut.Lock() + p.scanTimer.Reset(intv) + p.tmut.Unlock() } // We don't start pulling files until a scan has been completed. @@ -151,12 +158,14 @@ func (p *rwFolder) Serve() { // information is available. Before that though, I'd like to build a // repeatable benchmark of how long it takes to sync a change from // device A to device B, so we have something to work against. - case <-pullTimer.C: + case <-p.pullTimer.C: if !initialScanCompleted { if debug { l.Debugln(p, "skip (initial)") } - pullTimer.Reset(nextPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(nextPullIntv) + p.tmut.Unlock() continue } @@ -180,7 +189,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (curVer == prevVer)", prevVer) } - pullTimer.Reset(checkPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(checkPullIntv) + p.tmut.Unlock() continue } @@ -218,7 +229,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", nextPullIntv) } - pullTimer.Reset(nextPullIntv) + p.tmut.Lock() + p.pullTimer.Reset(nextPullIntv) + p.tmut.Unlock() break } @@ -231,7 +244,9 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", pauseIntv) } - pullTimer.Reset(pauseIntv) + p.tmut.Lock() + p.pullTimer.Reset(pauseIntv) + p.tmut.Unlock() break } } @@ -240,7 +255,7 @@ func (p *rwFolder) Serve() { // The reason for running the scanner from within the puller is that // this is the easiest way to make sure we are not doing both at the // same time. - case <-scanTimer.C: + case <-p.scanTimer.C: if err := p.model.CheckFolderHealth(p.folder); err != nil { l.Infoln("Skipping folder", p.folder, "scan due to folder error:", err) rescheduleScan() @@ -1165,6 +1180,12 @@ func (p *rwFolder) Jobs() ([]string, []string) { return p.queue.Jobs() } +func (p *rwFolder) DelayScan(next time.Duration) { + p.tmut.Lock() + p.scanTimer.Reset(next) + p.tmut.Unlock() +} + // dbUpdaterRoutine aggregates db updates and commits them in batches no // larger than 1000 items, and no more delayed than 2 seconds. func (p *rwFolder) dbUpdaterRoutine() { diff --git a/test/delay_scan_test.go b/test/delay_scan_test.go new file mode 100644 index 000000000..081100aa6 --- /dev/null +++ b/test/delay_scan_test.go @@ -0,0 +1,91 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// +build integration + +package integration + +import ( + "io/ioutil" + "log" + "sync" + "testing" + "time" +) + +func TestDelayScan(t *testing.T) { + log.Println("Cleaning...") + err := removeAll("s1", "h1/index*") + if err != nil { + t.Fatal(err) + } + + log.Println("Generating files...") + err = generateFiles("s1", 50, 18, "../LICENSE") + if err != nil { + t.Fatal(err) + } + + log.Println("Generating .stignore...") + err = ioutil.WriteFile("s1/.stignore", []byte("some ignore data\n"), 0644) + if err != nil { + t.Fatal(err) + } + + log.Println("Starting up...") + st := syncthingProcess{ // id1 + instance: "1", + argv: []string{"-home", "h1"}, + port: 8081, + apiKey: apiKey, + } + err = st.start() + if err != nil { + t.Fatal(err) + } + + // Wait for one scan to succeed, or up to 20 seconds... + // This is to let startup, UPnP etc complete. + for i := 0; i < 20; i++ { + err := st.rescan("default") + if err != nil { + time.Sleep(time.Second) + continue + } + break + } + + // Wait for UPnP and stuff + time.Sleep(10 * time.Second) + + var wg sync.WaitGroup + log.Println("Starting scans...") + for j := 0; j < 20; j++ { + j := j + wg.Add(1) + go func() { + defer wg.Done() + err := st.rescanNext("default", time.Duration(1)*time.Second) + log.Println(j) + if err != nil { + log.Println(err) + t.Fatal(err) + } + }() + } + + wg.Wait() + log.Println("Scans done") + time.Sleep(2 * time.Second) + + // This is where the real test is currently, since stop() checks for data + // race output in the log. + log.Println("Stopping...") + _, err = st.stop() + if err != nil { + t.Fatal(err) + } +} diff --git a/test/syncthingprocess.go b/test/syncthingprocess.go index 94e77eaa3..18ccb0c19 100644 --- a/test/syncthingprocess.go +++ b/test/syncthingprocess.go @@ -20,6 +20,7 @@ import ( "net/http" "os" "os/exec" + "strconv" "time" "github.com/syncthing/protocol" @@ -322,6 +323,19 @@ func (p *syncthingProcess) rescan(folder string) error { return nil } +func (p *syncthingProcess) rescanNext(folder string, next time.Duration) error { + resp, err := p.post("/rest/db/scan?folder="+folder+"&next="+strconv.Itoa(int(next.Seconds())), nil) + if err != nil { + return err + } + data, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if resp.StatusCode != 200 { + return fmt.Errorf("Rescan %q: status code %d: %s", folder, resp.StatusCode, data) + } + return nil +} + func (p *syncthingProcess) reset(folder string) error { resp, err := p.post("/rest/system/reset?folder="+folder, nil) if err != nil { From 1bd85d8bafbd11ddc3d4eacfad8bc7576eac0df8 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 3 May 2015 14:18:32 +0200 Subject: [PATCH 2/3] Use a channel instead of locks --- internal/model/rofolder.go | 35 ++++++++++++++++------------------- internal/model/rwfolder.go | 23 ++++++----------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/internal/model/rofolder.go b/internal/model/rofolder.go index 76c928dd8..ae97d3a5a 100644 --- a/internal/model/rofolder.go +++ b/internal/model/rofolder.go @@ -17,12 +17,12 @@ import ( type roFolder struct { stateTracker - folder string - intv time.Duration - timer *time.Timer - tmut sync.Mutex // protects timer - model *Model - stop chan struct{} + folder string + intv time.Duration + timer *time.Timer + model *Model + stop chan struct{} + delayScan chan time.Duration } func newROFolder(model *Model, folder string, interval time.Duration) *roFolder { @@ -31,12 +31,12 @@ func newROFolder(model *Model, folder string, interval time.Duration) *roFolder folder: folder, mut: sync.NewMutex(), }, - folder: folder, - intv: interval, - timer: time.NewTimer(time.Millisecond), - tmut: sync.NewMutex(), - model: model, - stop: make(chan struct{}), + folder: folder, + intv: interval, + timer: time.NewTimer(time.Millisecond), + model: model, + stop: make(chan struct{}), + delayScan: make(chan time.Duration), } } @@ -47,17 +47,13 @@ func (s *roFolder) Serve() { } defer func() { - s.tmut.Lock() s.timer.Stop() - s.tmut.Unlock() }() reschedule := func() { // Sleep a random time between 3/4 and 5/4 of the configured interval. sleepNanos := (s.intv.Nanoseconds()*3 + rand.Int63n(2*s.intv.Nanoseconds())) / 4 - s.tmut.Lock() s.timer.Reset(time.Duration(sleepNanos) * time.Nanosecond) - s.tmut.Unlock() } initialScanCompleted := false @@ -97,6 +93,9 @@ func (s *roFolder) Serve() { } reschedule() + + case next := <-s.delayScan: + s.timer.Reset(next) } } } @@ -116,7 +115,5 @@ func (s *roFolder) Jobs() ([]string, []string) { } func (s *roFolder) DelayScan(next time.Duration) { - s.tmut.Lock() - s.timer.Reset(next) - s.tmut.Unlock() + s.delayScan <- next } diff --git a/internal/model/rwfolder.go b/internal/model/rwfolder.go index 07849483d..6fc103399 100644 --- a/internal/model/rwfolder.go +++ b/internal/model/rwfolder.go @@ -76,7 +76,7 @@ type rwFolder struct { dbUpdates chan protocol.FileInfo scanTimer *time.Timer pullTimer *time.Timer - tmut sync.Mutex // protects scanTimer and pullTimer + delayScan chan time.Duration } func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFolder { @@ -103,7 +103,7 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo queue: newJobQueue(), pullTimer: time.NewTimer(checkPullIntv), scanTimer: time.NewTimer(time.Millisecond), // The first scan should be done immediately. - tmut: sync.NewMutex(), + delayScan: make(chan time.Duration), } } @@ -116,10 +116,8 @@ func (p *rwFolder) Serve() { } defer func() { - p.tmut.Lock() p.pullTimer.Stop() p.scanTimer.Stop() - p.tmut.Unlock() // TODO: Should there be an actual FolderStopped state? p.setState(FolderIdle) }() @@ -140,9 +138,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next rescan in", intv) } - p.tmut.Lock() p.scanTimer.Reset(intv) - p.tmut.Unlock() } // We don't start pulling files until a scan has been completed. @@ -163,9 +159,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (initial)") } - p.tmut.Lock() p.pullTimer.Reset(nextPullIntv) - p.tmut.Unlock() continue } @@ -189,9 +183,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "skip (curVer == prevVer)", prevVer) } - p.tmut.Lock() p.pullTimer.Reset(checkPullIntv) - p.tmut.Unlock() continue } @@ -229,9 +221,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", nextPullIntv) } - p.tmut.Lock() p.pullTimer.Reset(nextPullIntv) - p.tmut.Unlock() break } @@ -244,9 +234,7 @@ func (p *rwFolder) Serve() { if debug { l.Debugln(p, "next pull in", pauseIntv) } - p.tmut.Lock() p.pullTimer.Reset(pauseIntv) - p.tmut.Unlock() break } } @@ -283,6 +271,9 @@ func (p *rwFolder) Serve() { l.Infoln("Completed initial scan (rw) of folder", p.folder) initialScanCompleted = true } + + case next := <-p.delayScan: + p.scanTimer.Reset(next) } } } @@ -1181,9 +1172,7 @@ func (p *rwFolder) Jobs() ([]string, []string) { } func (p *rwFolder) DelayScan(next time.Duration) { - p.tmut.Lock() - p.scanTimer.Reset(next) - p.tmut.Unlock() + p.delayScan <- next } // dbUpdaterRoutine aggregates db updates and commits them in batches no From bd5a64bac08a707f26fe984a9da742fa4c94fdad Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 3 May 2015 14:18:50 +0200 Subject: [PATCH 3/3] Reschedule before scan --- cmd/syncthing/gui.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 7e91f9c74..a5e046e43 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -775,8 +775,14 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { qs := r.URL.Query() folder := qs.Get("folder") if folder != "" { + nextStr := qs.Get("next") + next, err := strconv.Atoi(nextStr) + if err == nil { + s.model.DelayScan(folder, time.Duration(next)*time.Second) + } + subs := qs["sub"] - err := s.model.ScanFolderSubs(folder, subs) + err = s.model.ScanFolderSubs(folder, subs) if err != nil { http.Error(w, err.Error(), 500) return @@ -789,11 +795,6 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) { return } } - nextStr := qs.Get("next") - next, err := strconv.Atoi(nextStr) - if err == nil { - s.model.DelayScan(folder, time.Duration(next)*time.Second) - } } func (s *apiSvc) postDBPrio(w http.ResponseWriter, r *http.Request) {