all: Handle errors opening db/creating file-set (ref #5907) (#7150)

This commit is contained in:
Simon Frei
2020-12-21 12:59:22 +01:00
committed by GitHub
parent b5de49917c
commit 78bd0341a8
25 changed files with 542 additions and 396 deletions
+59 -14
View File
@@ -134,6 +134,7 @@ type model struct {
// folderIOLimiter limits the number of concurrent I/O heavy operations,
// such as scans and pulls.
folderIOLimiter *byteSemaphore
fatalChan chan error
// fields protected by fmut
fmut sync.RWMutex
@@ -217,6 +218,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
shortID: id.Short(),
globalRequestLimiter: newByteSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
folderIOLimiter: newByteSemaphore(cfg.Options().MaxFolderConcurrency()),
fatalChan: make(chan error),
// fields protected by fmut
fmut: sync.NewRWMutex(),
@@ -253,7 +255,27 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
}
func (m *model) serve(ctx context.Context) error {
// Add and start folders
defer m.closeAllConnectionsAndWait()
m.cfg.Subscribe(m)
defer m.cfg.Unsubscribe(m)
if err := m.initFolders(); err != nil {
close(m.started)
return util.AsFatalErr(err, util.ExitError)
}
close(m.started)
select {
case <-ctx.Done():
return ctx.Err()
case err := <-m.fatalChan:
return util.AsFatalErr(err, util.ExitError)
}
}
func (m *model) initFolders() error {
cacheIgnoredFiles := m.cfg.Options().CacheIgnoredFiles
existingDevices := m.cfg.Devices()
existingFolders := m.cfg.Folders()
@@ -263,7 +285,10 @@ func (m *model) serve(ctx context.Context) error {
folderCfg.CreateRoot()
continue
}
m.newFolder(folderCfg, cacheIgnoredFiles)
err := m.newFolder(folderCfg, cacheIgnoredFiles)
if err != nil {
return err
}
clusterConfigDevices.add(folderCfg.DeviceIDs())
}
@@ -271,12 +296,10 @@ func (m *model) serve(ctx context.Context) error {
m.cleanPending(existingDevices, existingFolders, ignoredDevices, nil)
m.resendClusterConfig(clusterConfigDevices.AsSlice())
m.cfg.Subscribe(m)
return nil
}
close(m.started)
<-ctx.Done()
m.cfg.Unsubscribe(m)
func (m *model) closeAllConnectionsAndWait() {
m.pmut.RLock()
closed := make([]chan struct{}, 0, len(m.conn))
for id, conn := range m.conn {
@@ -287,7 +310,13 @@ func (m *model) serve(ctx context.Context) error {
for _, c := range closed {
<-c
}
return nil
}
func (m *model) fatal(err error) {
select {
case m.fatalChan <- err:
default:
}
}
// StartDeadlockDetector starts a deadlock detector on the models locks which
@@ -472,7 +501,7 @@ func (m *model) cleanupFolderLocked(cfg config.FolderConfiguration) {
delete(m.folderVersioners, cfg.ID)
}
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) {
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) error {
if len(to.ID) == 0 {
panic("bug: cannot restart empty folder ID")
}
@@ -512,7 +541,11 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
// Create a new fset. Might take a while and we do it under
// locking, but it's unsafe to create fset:s concurrently so
// that's the price we pay.
fset = db.NewFileSet(folder, to.Filesystem(), m.db)
var err error
fset, err = db.NewFileSet(folder, to.Filesystem(), m.db)
if err != nil {
return fmt.Errorf("restarting %v: %w", to.Description(), err)
}
}
m.addAndStartFolderLocked(to, fset, cacheIgnoredFiles)
}
@@ -547,12 +580,17 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
infoMsg = "Restarted"
}
l.Infof("%v folder %v (%v)", infoMsg, to.Description(), to.Type)
return nil
}
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) {
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) error {
// Creating the fileset can take a long time (metadata calculation) so
// we do it outside of the lock.
fset := db.NewFileSet(cfg.ID, cfg.Filesystem(), m.db)
fset, err := db.NewFileSet(cfg.ID, cfg.Filesystem(), m.db)
if err != nil {
return fmt.Errorf("adding %v: %w", cfg.Description(), err)
}
m.fmut.Lock()
defer m.fmut.Unlock()
@@ -569,6 +607,7 @@ func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool
m.pmut.RUnlock()
m.addAndStartFolderLocked(cfg, fset, cacheIgnoredFiles)
return nil
}
func (m *model) UsageReportingStats(report *contract.Report, version int, preview bool) {
@@ -2579,7 +2618,10 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
l.Infoln("Paused folder", cfg.Description())
} else {
l.Infoln("Adding folder", cfg.Description())
m.newFolder(cfg, to.Options.CacheIgnoredFiles)
if err := m.newFolder(cfg, to.Options.CacheIgnoredFiles); err != nil {
m.fatal(err)
return true
}
}
clusterConfigDevices.add(cfg.DeviceIDs())
}
@@ -2603,7 +2645,10 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// This folder exists on both sides. Settings might have changed.
// Check if anything differs that requires a restart.
if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) || from.Options.CacheIgnoredFiles != to.Options.CacheIgnoredFiles {
m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles)
if err := m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles); err != nil {
m.fatal(err)
return true
}
clusterConfigDevices.add(fromCfg.DeviceIDs())
clusterConfigDevices.add(toCfg.DeviceIDs())
}