From bdbca75dfa77107f28ba3bf710b245e5f524977b Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Mon, 16 Mar 2015 21:14:19 +0100 Subject: [PATCH 1/2] Refactor state tracking (...) Move state tracking into the puller/scanner objects. This is a first step towards resolving #1391. Rename Puller and Scanner to roFolder and rwFolder as they have more duties than just pulling and scanning, and don't need to be exported. --- internal/model/folderstate.go | 89 ++++++++++++++++++++++ internal/model/model.go | 135 ++++++++++------------------------ internal/model/model_test.go | 2 + internal/model/puller.go | 108 ++++++++++++++++----------- internal/model/puller_test.go | 12 +-- internal/model/scanner.go | 30 +++++--- test/ignore_test.go | 18 ++++- test/parallell_scan_test.go | 22 +++++- test/transfer-bench_test.go | 18 ++++- 9 files changed, 276 insertions(+), 158 deletions(-) create mode 100644 internal/model/folderstate.go diff --git a/internal/model/folderstate.go b/internal/model/folderstate.go new file mode 100644 index 000000000..cb964afb1 --- /dev/null +++ b/internal/model/folderstate.go @@ -0,0 +1,89 @@ +// Copyright (C) 2015 The Syncthing Authors. +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +package model + +import ( + "sync" + "time" + + "github.com/syncthing/syncthing/internal/events" +) + +type folderState int + +const ( + FolderIdle folderState = iota + FolderScanning + FolderSyncing + FolderCleaning +) + +func (s folderState) String() string { + switch s { + case FolderIdle: + return "idle" + case FolderScanning: + return "scanning" + case FolderCleaning: + return "cleaning" + case FolderSyncing: + return "syncing" + default: + return "unknown" + } +} + +type stateTracker struct { + folder string + + mut sync.Mutex + current folderState + changed time.Time +} + +func (s *stateTracker) setState(newState folderState) { + s.mut.Lock() + if newState != s.current { + /* This should hold later... + if s.current != FolderIdle && (newState == FolderScanning || newState == FolderSyncing) { + panic("illegal state transition " + s.current.String() + " -> " + newState.String()) + } + */ + + eventData := map[string]interface{}{ + "folder": s.folder, + "to": newState.String(), + "from": s.current.String(), + } + + if !s.changed.IsZero() { + eventData["duration"] = time.Since(s.changed).Seconds() + } + + s.current = newState + s.changed = time.Now() + + events.Default.Log(events.StateChanged, eventData) + } + s.mut.Unlock() +} + +func (s *stateTracker) getState() (current folderState, changed time.Time) { + s.mut.Lock() + current, changed = s.current, s.changed + s.mut.Unlock() + return +} diff --git a/internal/model/model.go b/internal/model/model.go index b4d7f3b1c..0cdf7fd17 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -36,30 +36,6 @@ import ( "github.com/syndtr/goleveldb/leveldb" ) -type folderState int - -const ( - FolderIdle folderState = iota - FolderScanning - FolderSyncing - FolderCleaning -) - -func (s folderState) String() string { - switch s { - case FolderIdle: - return "idle" - case FolderScanning: - return "scanning" - case FolderCleaning: - return "cleaning" - case FolderSyncing: - return "syncing" - default: - return "unknown" - } -} - // How many files to send in each Index/IndexUpdate message. const ( indexTargetSize = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed) @@ -73,6 +49,9 @@ type service interface { Stop() Jobs() ([]string, []string) // In progress, Queued BringToFront(string) + + setState(folderState) + getState() (folderState, time.Time) } type Model struct { @@ -95,10 +74,6 @@ type Model struct { folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef fmut sync.RWMutex // protects the above - folderState map[string]folderState // folder -> state - folderStateChanged map[string]time.Time // folder -> time when state changed - smut sync.RWMutex - protoConn map[protocol.DeviceID]protocol.Connection rawConn map[protocol.DeviceID]io.Closer deviceVer map[protocol.DeviceID]string @@ -120,26 +95,24 @@ var ( // for file data without altering the local folder in any way. func NewModel(cfg *config.Wrapper, deviceName, clientName, clientVersion string, ldb *leveldb.DB) *Model { m := &Model{ - cfg: cfg, - db: ldb, - deviceName: deviceName, - clientName: clientName, - clientVersion: clientVersion, - folderCfgs: make(map[string]config.FolderConfiguration), - folderFiles: make(map[string]*db.FileSet), - folderDevices: make(map[string][]protocol.DeviceID), - deviceFolders: make(map[protocol.DeviceID][]string), - deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference), - folderIgnores: make(map[string]*ignore.Matcher), - folderRunners: make(map[string]service), - folderStatRefs: make(map[string]*stats.FolderStatisticsReference), - folderState: make(map[string]folderState), - folderStateChanged: make(map[string]time.Time), - protoConn: make(map[protocol.DeviceID]protocol.Connection), - rawConn: make(map[protocol.DeviceID]io.Closer), - deviceVer: make(map[protocol.DeviceID]string), - finder: db.NewBlockFinder(ldb, cfg), - progressEmitter: NewProgressEmitter(cfg), + cfg: cfg, + db: ldb, + deviceName: deviceName, + clientName: clientName, + clientVersion: clientVersion, + folderCfgs: make(map[string]config.FolderConfiguration), + folderFiles: make(map[string]*db.FileSet), + folderDevices: make(map[string][]protocol.DeviceID), + deviceFolders: make(map[protocol.DeviceID][]string), + deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference), + folderIgnores: make(map[string]*ignore.Matcher), + folderRunners: make(map[string]service), + folderStatRefs: make(map[string]*stats.FolderStatisticsReference), + protoConn: make(map[protocol.DeviceID]protocol.Connection), + rawConn: make(map[protocol.DeviceID]io.Closer), + deviceVer: make(map[protocol.DeviceID]string), + finder: db.NewBlockFinder(ldb, cfg), + progressEmitter: NewProgressEmitter(cfg), } if cfg.Options().ProgressUpdateIntervalS > -1 { go m.progressEmitter.Serve() @@ -153,7 +126,6 @@ func NewModel(cfg *config.Wrapper, deviceName, clientName, clientVersion string, } } deadlockDetect(&m.fmut, time.Duration(timeout)*time.Second) - deadlockDetect(&m.smut, time.Duration(timeout)*time.Second) deadlockDetect(&m.pmut, time.Duration(timeout)*time.Second) return m } @@ -172,18 +144,7 @@ func (m *Model) StartFolderRW(folder string) { if ok { panic("cannot start already running folder " + folder) } - p := &Puller{ - folder: folder, - dir: cfg.Path, - scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second, - model: m, - ignorePerms: cfg.IgnorePerms, - lenientMtimes: cfg.LenientMtimes, - progressEmitter: m.progressEmitter, - copiers: cfg.Copiers, - pullers: cfg.Pullers, - queue: newJobQueue(), - } + p := newRWFolder(m, cfg) m.folderRunners[folder] = p m.fmut.Unlock() @@ -216,11 +177,7 @@ func (m *Model) StartFolderRO(folder string) { if ok { panic("cannot start already running folder " + folder) } - s := &Scanner{ - folder: folder, - intv: time.Duration(cfg.RescanIntervalS) * time.Second, - model: m, - } + s := newROFolder(m, folder, time.Duration(cfg.RescanIntervalS)*time.Second) m.folderRunners[folder] = s m.fmut.Unlock() @@ -1154,11 +1111,15 @@ func (m *Model) ScanFolderSub(folder, sub string) error { } m.fmut.Lock() - fs, ok := m.folderFiles[folder] + fs := m.folderFiles[folder] folderCfg := m.folderCfgs[folder] ignores := m.folderIgnores[folder] + 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") } @@ -1189,7 +1150,7 @@ func (m *Model) ScanFolderSub(folder, sub string) error { Hashers: folderCfg.Hashers, } - m.setState(folder, FolderScanning) + runner.setState(FolderScanning) fchan, err := w.Walk() if err != nil { @@ -1289,7 +1250,7 @@ func (m *Model) ScanFolderSub(folder, sub string) error { fs.Update(protocol.LocalDeviceID, batch) } - m.setState(folder, FolderIdle) + runner.setState(FolderIdle) return nil } @@ -1332,40 +1293,24 @@ func (m *Model) clusterConfig(device protocol.DeviceID) protocol.ClusterConfigMe return cm } -func (m *Model) setState(folder string, state folderState) { - m.smut.Lock() - oldState := m.folderState[folder] - changed, ok := m.folderStateChanged[folder] - if state != oldState { - m.folderState[folder] = state - m.folderStateChanged[folder] = time.Now() - eventData := map[string]interface{}{ - "folder": folder, - "to": state.String(), - } - if ok { - eventData["duration"] = time.Since(changed).Seconds() - eventData["from"] = oldState.String() - } - events.Default.Log(events.StateChanged, eventData) - } - m.smut.Unlock() -} - func (m *Model) State(folder string) (string, time.Time) { - m.smut.RLock() - state := m.folderState[folder] - changed := m.folderStateChanged[folder] - m.smut.RUnlock() + m.fmut.RLock() + runner, ok := m.folderRunners[folder] + m.fmut.RUnlock() + if !ok { + return "", time.Time{} + } + state, changed := runner.getState() return state.String(), changed } func (m *Model) Override(folder string) { m.fmut.RLock() fs := m.folderFiles[folder] + runner := m.folderRunners[folder] m.fmut.RUnlock() - m.setState(folder, FolderScanning) + runner.setState(FolderScanning) batch := make([]protocol.FileInfo, 0, indexBatchSize) fs.WithNeed(protocol.LocalDeviceID, func(fi db.FileIntf) bool { need := fi.(protocol.FileInfo) @@ -1391,7 +1336,7 @@ func (m *Model) Override(folder string) { if len(batch) > 0 { fs.Update(protocol.LocalDeviceID, batch) } - m.setState(folder, FolderIdle) + runner.setState(FolderIdle) } // CurrentLocalVersion returns the change version for the given folder. diff --git a/internal/model/model_test.go b/internal/model/model_test.go index bb72f7b28..f2c28cada 100644 --- a/internal/model/model_test.go +++ b/internal/model/model_test.go @@ -90,6 +90,7 @@ func TestRequest(t *testing.T) { // device1 shares default, but device2 doesn't m.AddFolder(defaultFolderConfig) + m.StartFolderRO("default") m.ScanFolder("default") // Existing, shared file @@ -470,6 +471,7 @@ func TestIgnores(t *testing.T) { db, _ := leveldb.Open(storage.NewMemStorage(), nil) m := NewModel(defaultConfig, "device", "syncthing", "dev", db) m.AddFolder(defaultFolderConfig) + m.StartFolderRO("default") expected := []string{ ".*", diff --git a/internal/model/puller.go b/internal/model/puller.go index d9e64f0e5..1a66c30a9 100644 --- a/internal/model/puller.go +++ b/internal/model/puller.go @@ -54,31 +54,53 @@ var ( errNoDevice = errors.New("no available source device") ) -type Puller struct { - folder string - dir string - scanIntv time.Duration +type rwFolder struct { + stateTracker + model *Model - stop chan struct{} - versioner versioner.Versioner - ignorePerms bool - lenientMtimes bool progressEmitter *ProgressEmitter - copiers int - pullers int - queue *jobQueue + + folder string + dir string + scanIntv time.Duration + versioner versioner.Versioner + ignorePerms bool + lenientMtimes bool + copiers int + pullers int + + stop chan struct{} + queue *jobQueue +} + +func newRWFolder(m *Model, cfg config.FolderConfiguration) *rwFolder { + return &rwFolder{ + stateTracker: stateTracker{folder: cfg.ID}, + + model: m, + progressEmitter: m.progressEmitter, + + folder: cfg.ID, + dir: cfg.Path, + scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second, + ignorePerms: cfg.IgnorePerms, + lenientMtimes: cfg.LenientMtimes, + copiers: cfg.Copiers, + pullers: cfg.Pullers, + + stop: make(chan struct{}), + queue: newJobQueue(), + } } // Serve will run scans and pulls. It will return when Stop()ed or on a // critical error. -func (p *Puller) Serve() { +func (p *rwFolder) Serve() { if debug { l.Debugln(p, "starting") defer l.Debugln(p, "exiting") } - p.stop = make(chan struct{}) - pullTimer := time.NewTimer(checkPullIntv) scanTimer := time.NewTimer(time.Millisecond) // The first scan should be done immediately. @@ -86,7 +108,7 @@ func (p *Puller) Serve() { pullTimer.Stop() scanTimer.Stop() // TODO: Should there be an actual FolderStopped state? - p.model.setState(p.folder, FolderIdle) + p.setState(FolderIdle) }() var prevVer int64 @@ -143,7 +165,7 @@ loop: if debug { l.Debugln(p, "pulling", prevVer, curVer) } - p.model.setState(p.folder, FolderSyncing) + p.setState(FolderSyncing) tries := 0 for { tries++ @@ -191,7 +213,7 @@ loop: break } } - p.model.setState(p.folder, FolderIdle) + p.setState(FolderIdle) // 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 @@ -200,12 +222,12 @@ loop: if debug { l.Debugln(p, "rescan") } - p.model.setState(p.folder, FolderScanning) + p.setState(FolderScanning) if err := p.model.ScanFolder(p.folder); err != nil { p.model.cfg.InvalidateFolder(p.folder, err.Error()) break loop } - p.model.setState(p.folder, FolderIdle) + p.setState(FolderIdle) if p.scanIntv > 0 { // Sleep a random time between 3/4 and 5/4 of the configured interval. sleepNanos := (p.scanIntv.Nanoseconds()*3 + rand.Int63n(2*p.scanIntv.Nanoseconds())) / 4 @@ -224,19 +246,19 @@ loop: } } -func (p *Puller) Stop() { +func (p *rwFolder) Stop() { close(p.stop) } -func (p *Puller) String() string { - return fmt.Sprintf("puller/%s@%p", p.folder, p) +func (p *rwFolder) String() string { + return fmt.Sprintf("rwFolder/%s@%p", p.folder, p) } // pullerIteration runs a single puller iteration for the given folder and // returns the number items that should have been synced (even those that // might have failed). One puller iteration handles all files currently // flagged as needed in the folder. -func (p *Puller) pullerIteration(ignores *ignore.Matcher) int { +func (p *rwFolder) pullerIteration(ignores *ignore.Matcher) int { pullChan := make(chan pullBlockState) copyChan := make(chan copyBlocksState) finisherChan := make(chan *sharedPullerState) @@ -422,7 +444,7 @@ nextFile: } // handleDir creates or updates the given directory -func (p *Puller) handleDir(file protocol.FileInfo) { +func (p *rwFolder) handleDir(file protocol.FileInfo) { var err error events.Default.Log(events.ItemStarted, map[string]interface{}{ "folder": p.folder, @@ -497,7 +519,7 @@ func (p *Puller) handleDir(file protocol.FileInfo) { } // deleteDir attempts to delete the given directory -func (p *Puller) deleteDir(file protocol.FileInfo) { +func (p *rwFolder) deleteDir(file protocol.FileInfo) { var err error events.Default.Log(events.ItemStarted, map[string]interface{}{ "folder": p.folder, @@ -532,7 +554,7 @@ func (p *Puller) deleteDir(file protocol.FileInfo) { } // deleteFile attempts to delete the given file -func (p *Puller) deleteFile(file protocol.FileInfo) { +func (p *rwFolder) deleteFile(file protocol.FileInfo) { var err error events.Default.Log(events.ItemStarted, map[string]interface{}{ "folder": p.folder, @@ -564,7 +586,7 @@ func (p *Puller) deleteFile(file protocol.FileInfo) { // renameFile attempts to rename an existing file to a destination // and set the right attributes on it. -func (p *Puller) renameFile(source, target protocol.FileInfo) { +func (p *rwFolder) renameFile(source, target protocol.FileInfo) { var err error events.Default.Log(events.ItemStarted, map[string]interface{}{ "folder": p.folder, @@ -634,7 +656,7 @@ func (p *Puller) renameFile(source, target protocol.FileInfo) { // handleFile queues the copies and pulls as necessary for a single new or // changed file. -func (p *Puller) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksState, finisherChan chan<- *sharedPullerState) { +func (p *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksState, finisherChan chan<- *sharedPullerState) { events.Default.Log(events.ItemStarted, map[string]interface{}{ "folder": p.folder, "item": file.Name, @@ -732,7 +754,7 @@ func (p *Puller) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksSt // shortcutFile sets file mode and modification time, when that's the only // thing that has changed. -func (p *Puller) shortcutFile(file protocol.FileInfo) (err error) { +func (p *rwFolder) shortcutFile(file protocol.FileInfo) (err error) { realName := filepath.Join(p.dir, file.Name) if !p.ignorePerms { err = os.Chmod(realName, os.FileMode(file.Flags&0777)) @@ -763,7 +785,7 @@ func (p *Puller) shortcutFile(file protocol.FileInfo) (err error) { } // shortcutSymlink changes the symlinks type if necessery. -func (p *Puller) shortcutSymlink(file protocol.FileInfo) (err error) { +func (p *rwFolder) shortcutSymlink(file protocol.FileInfo) (err error) { err = symlinks.ChangeType(filepath.Join(p.dir, file.Name), file.Flags) if err == nil { p.model.updateLocal(p.folder, file) @@ -775,7 +797,7 @@ func (p *Puller) shortcutSymlink(file protocol.FileInfo) (err error) { // copierRoutine reads copierStates until the in channel closes and performs // the relevant copies when possible, or passes it to the puller routine. -func (p *Puller) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pullBlockState, out chan<- *sharedPullerState) { +func (p *rwFolder) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pullBlockState, out chan<- *sharedPullerState) { buf := make([]byte, protocol.BlockSize) for state := range in { @@ -857,7 +879,7 @@ func (p *Puller) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pullBl } } -func (p *Puller) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPullerState) { +func (p *rwFolder) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPullerState) { for state := range in { if state.failed() != nil { continue @@ -918,7 +940,7 @@ func (p *Puller) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPulle } } -func (p *Puller) performFinish(state *sharedPullerState) { +func (p *rwFolder) performFinish(state *sharedPullerState) { var err error defer func() { events.Default.Log(events.ItemFinished, map[string]interface{}{ @@ -931,7 +953,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { if !p.ignorePerms { err = os.Chmod(state.tempName, os.FileMode(state.file.Flags&0777)) if err != nil { - l.Warnln("puller: final:", err) + l.Warnln("Puller: final:", err) return } } @@ -947,7 +969,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { // sync. l.Infof("Puller (folder %q, file %q): final: %v (continuing anyway as requested)", p.folder, state.file.Name, err) } else { - l.Warnln("puller: final:", err) + l.Warnln("Puller: final:", err) return } } @@ -958,7 +980,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { if p.versioner != nil { err = p.versioner.Archive(state.realName) if err != nil { - l.Warnln("puller: final:", err) + l.Warnln("Puller: final:", err) return } } @@ -972,7 +994,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { // Replace the original content with the new one err = osutil.Rename(state.tempName, state.realName) if err != nil { - l.Warnln("puller: final:", err) + l.Warnln("Puller: final:", err) return } @@ -980,7 +1002,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { if state.file.IsSymlink() { content, err := ioutil.ReadFile(state.realName) if err != nil { - l.Warnln("puller: final: reading symlink:", err) + l.Warnln("Puller: final: reading symlink:", err) return } @@ -990,7 +1012,7 @@ func (p *Puller) performFinish(state *sharedPullerState) { return symlinks.Create(path, string(content), state.file.Flags) }, state.realName) if err != nil { - l.Warnln("puller: final: creating symlink:", err) + l.Warnln("Puller: final: creating symlink:", err) return } } @@ -999,14 +1021,14 @@ func (p *Puller) performFinish(state *sharedPullerState) { p.model.updateLocal(p.folder, state.file) } -func (p *Puller) finisherRoutine(in <-chan *sharedPullerState) { +func (p *rwFolder) finisherRoutine(in <-chan *sharedPullerState) { for state := range in { if closed, err := state.finalClose(); closed { if debug { l.Debugln(p, "closing", state.file.Name) } if err != nil { - l.Warnln("puller: final:", err) + l.Warnln("Puller: final:", err) continue } @@ -1029,11 +1051,11 @@ func (p *Puller) finisherRoutine(in <-chan *sharedPullerState) { } // Moves the given filename to the front of the job queue -func (p *Puller) BringToFront(filename string) { +func (p *rwFolder) BringToFront(filename string) { p.queue.BringToFront(filename) } -func (p *Puller) Jobs() ([]string, []string) { +func (p *rwFolder) Jobs() ([]string, []string) { return p.queue.Jobs() } diff --git a/internal/model/puller_test.go b/internal/model/puller_test.go index 1f47d24dc..179a2e56f 100644 --- a/internal/model/puller_test.go +++ b/internal/model/puller_test.go @@ -72,7 +72,7 @@ func TestHandleFile(t *testing.T) { // Update index m.updateLocal("default", existingFile) - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, @@ -126,7 +126,7 @@ func TestHandleFileWithTemp(t *testing.T) { // Update index m.updateLocal("default", existingFile) - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, @@ -197,7 +197,7 @@ func TestCopierFinder(t *testing.T) { } } - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, @@ -331,7 +331,7 @@ func TestLastResortPulling(t *testing.T) { t.Error("Expected block not found") } - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, @@ -384,7 +384,7 @@ func TestDeregisterOnFailInCopy(t *testing.T) { emitter := NewProgressEmitter(defaultConfig) go emitter.Serve() - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, @@ -471,7 +471,7 @@ func TestDeregisterOnFailInPull(t *testing.T) { emitter := NewProgressEmitter(defaultConfig) go emitter.Serve() - p := Puller{ + p := rwFolder{ folder: "default", dir: "testdata", model: m, diff --git a/internal/model/scanner.go b/internal/model/scanner.go index bac1a4b06..a164f9289 100644 --- a/internal/model/scanner.go +++ b/internal/model/scanner.go @@ -12,14 +12,26 @@ import ( "time" ) -type Scanner struct { +type roFolder struct { + stateTracker + folder string intv time.Duration model *Model stop chan struct{} } -func (s *Scanner) Serve() { +func newROFolder(model *Model, folder string, interval time.Duration) *roFolder { + return &roFolder{ + stateTracker: stateTracker{folder: folder}, + folder: folder, + intv: interval, + model: model, + stop: make(chan struct{}), + } +} + +func (s *roFolder) Serve() { if debug { l.Debugln(s, "starting") defer l.Debugln(s, "exiting") @@ -39,12 +51,12 @@ func (s *Scanner) Serve() { l.Debugln(s, "rescan") } - s.model.setState(s.folder, FolderScanning) + s.setState(FolderScanning) if err := s.model.ScanFolder(s.folder); err != nil { s.model.cfg.InvalidateFolder(s.folder, err.Error()) return } - s.model.setState(s.folder, FolderIdle) + s.setState(FolderIdle) if !initialScanCompleted { l.Infoln("Completed initial scan (ro) of folder", s.folder) @@ -62,16 +74,16 @@ func (s *Scanner) Serve() { } } -func (s *Scanner) Stop() { +func (s *roFolder) Stop() { close(s.stop) } -func (s *Scanner) String() string { - return fmt.Sprintf("scanner/%s@%p", s.folder, s) +func (s *roFolder) String() string { + return fmt.Sprintf("roFolder/%s@%p", s.folder, s) } -func (s *Scanner) BringToFront(string) {} +func (s *roFolder) BringToFront(string) {} -func (s *Scanner) Jobs() ([]string, []string) { +func (s *roFolder) Jobs() ([]string, []string) { return nil, nil } diff --git a/test/ignore_test.go b/test/ignore_test.go index bc99b2f89..d932c7438 100644 --- a/test/ignore_test.go +++ b/test/ignore_test.go @@ -13,6 +13,7 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/syncthing/syncthing/internal/symlinks" ) @@ -71,7 +72,22 @@ func TestIgnores(t *testing.T) { // Rescan and verify that we see them all - p.post("/rest/scan?folder=default", nil) + // 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++ { + resp, err := p.post("/rest/scan?folder=default", nil) + if err != nil { + time.Sleep(time.Second) + continue + } + if resp.StatusCode != 200 { + resp.Body.Close() + time.Sleep(time.Second) + continue + } + break + } + m, err := p.model("default") if err != nil { t.Fatal(err) diff --git a/test/parallell_scan_test.go b/test/parallell_scan_test.go index 3e202b507..f20140b63 100644 --- a/test/parallell_scan_test.go +++ b/test/parallell_scan_test.go @@ -29,7 +29,7 @@ func TestParallellScan(t *testing.T) { t.Fatal(err) } - log.Println("Generaing .stignore...") + log.Println("Generating .stignore...") err = ioutil.WriteFile("s1/.stignore", []byte("some ignore data\n"), 0644) if err != nil { t.Fatal(err) @@ -46,7 +46,25 @@ func TestParallellScan(t *testing.T) { if err != nil { t.Fatal(err) } - time.Sleep(5 * time.Second) + + // 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++ { + resp, err := st.post("/rest/scan?folder=default", nil) + if err != nil { + time.Sleep(time.Second) + continue + } + if resp.StatusCode != 200 { + resp.Body.Close() + time.Sleep(time.Second) + continue + } + break + } + + // Wait for UPnP and stuff + time.Sleep(10 * time.Second) var wg sync.WaitGroup log.Println("Starting scans...") diff --git a/test/transfer-bench_test.go b/test/transfer-bench_test.go index 0ce41c6f8..5170457a5 100644 --- a/test/transfer-bench_test.go +++ b/test/transfer-bench_test.go @@ -43,8 +43,22 @@ func TestBenchmarkTransfer(t *testing.T) { t.Fatal(err) } - // Make sure the sender has the full index before they connect - sender.post("/rest/scan?folder=default", nil) + // Wait for one scan to succeed, or up to 20 seconds... This is to let + // startup, UPnP etc complete and make sure the sender has the full index + // before they connect. + for i := 0; i < 20; i++ { + resp, err := sender.post("/rest/scan?folder=default", nil) + if err != nil { + time.Sleep(time.Second) + continue + } + if resp.StatusCode != 200 { + resp.Body.Close() + time.Sleep(time.Second) + continue + } + break + } log.Println("Starting receiver...") receiver := syncthingProcess{ // id2 From 1c3158099cd25452bfdb5a7f388188f26d8e9372 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Mon, 16 Mar 2015 21:16:29 +0100 Subject: [PATCH 2/2] Rename files to match type names --- internal/model/{scanner.go => rofolder.go} | 0 internal/model/{puller.go => rwfolder.go} | 0 internal/model/{puller_test.go => rwfolder_test.go} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename internal/model/{scanner.go => rofolder.go} (100%) rename internal/model/{puller.go => rwfolder.go} (100%) rename internal/model/{puller_test.go => rwfolder_test.go} (100%) diff --git a/internal/model/scanner.go b/internal/model/rofolder.go similarity index 100% rename from internal/model/scanner.go rename to internal/model/rofolder.go diff --git a/internal/model/puller.go b/internal/model/rwfolder.go similarity index 100% rename from internal/model/puller.go rename to internal/model/rwfolder.go diff --git a/internal/model/puller_test.go b/internal/model/rwfolder_test.go similarity index 100% rename from internal/model/puller_test.go rename to internal/model/rwfolder_test.go