lib: Apply config changes sequentially (ref #5298) (#7188)

This commit is contained in:
Simon Frei
2021-01-15 15:43:34 +01:00
committed by GitHub
parent b2d82da20d
commit f63cdbfcfa
20 changed files with 1032 additions and 790 deletions
+59 -52
View File
@@ -136,6 +136,7 @@ type model struct {
// such as scans and pulls.
folderIOLimiter *byteSemaphore
fatalChan chan error
started chan struct{}
// fields protected by fmut
fmut sync.RWMutex
@@ -162,7 +163,6 @@ type model struct {
// for testing only
foldersRunning int32
started chan struct{}
}
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *byteSemaphore) service
@@ -221,6 +221,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
globalRequestLimiter: newByteSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
folderIOLimiter: newByteSemaphore(cfg.Options().MaxFolderConcurrency()),
fatalChan: make(chan error),
started: make(chan struct{}),
// fields protected by fmut
fmut: sync.NewRWMutex(),
@@ -243,9 +244,6 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
remotePausedFolders: make(map[protocol.DeviceID]map[string]struct{}),
indexSenders: make(map[protocol.DeviceID]*indexSenderRegistry),
// for testing only
started: make(chan struct{}),
}
for devID := range cfg.Devices() {
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID)
@@ -259,10 +257,10 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
func (m *model) serve(ctx context.Context) error {
defer m.closeAllConnectionsAndWait()
m.cfg.Subscribe(m)
cfg := m.cfg.Subscribe(m)
defer m.cfg.Unsubscribe(m)
if err := m.initFolders(); err != nil {
if err := m.initFolders(cfg); err != nil {
close(m.started)
return svcutil.AsFatalErr(err, svcutil.ExitError)
}
@@ -277,17 +275,14 @@ func (m *model) serve(ctx context.Context) error {
}
}
func (m *model) initFolders() error {
cacheIgnoredFiles := m.cfg.Options().CacheIgnoredFiles
existingDevices := m.cfg.Devices()
existingFolders := m.cfg.Folders()
clusterConfigDevices := make(deviceIDSet, len(existingDevices))
for _, folderCfg := range existingFolders {
func (m *model) initFolders(cfg config.Configuration) error {
clusterConfigDevices := make(deviceIDSet, len(cfg.Devices))
for _, folderCfg := range cfg.Folders {
if folderCfg.Paused {
folderCfg.CreateRoot()
continue
}
err := m.newFolder(folderCfg, cacheIgnoredFiles)
err := m.newFolder(folderCfg, cfg.Options.CacheIgnoredFiles)
if err != nil {
return err
}
@@ -295,7 +290,7 @@ func (m *model) initFolders() error {
}
ignoredDevices := observedDeviceSet(m.cfg.IgnoredDevices())
m.cleanPending(existingDevices, existingFolders, ignoredDevices, nil)
m.cleanPending(cfg.DeviceMap(), cfg.FolderMap(), ignoredDevices, nil)
m.resendClusterConfig(clusterConfigDevices.AsSlice())
return nil
@@ -1184,7 +1179,6 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
panic("bug: ClusterConfig called on closed or nonexistent connection")
}
changed := false
deviceCfg, ok := m.cfg.Device(deviceID)
if !ok {
l.Debugln("Device disappeared from config while processing cluster-config")
@@ -1219,21 +1213,33 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
// Needs to happen outside of the fmut, as can cause CommitConfiguration
if deviceCfg.AutoAcceptFolders {
changedFolders := make([]config.FolderConfiguration, 0, len(cm.Folders))
for _, folder := range cm.Folders {
if fcfg, fchanged := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID]); fchanged {
changedFolders = append(changedFolders, fcfg)
w, _ := m.cfg.Modify(func(cfg *config.Configuration) {
changedFcfg := make(map[string]config.FolderConfiguration)
haveFcfg := cfg.FolderMap()
for _, folder := range cm.Folders {
from, ok := haveFcfg[folder.ID]
if to, changed := m.handleAutoAccepts(deviceID, folder, ccDeviceInfos[folder.ID], from, ok, cfg.Options.DefaultFolderPath); changed {
changedFcfg[folder.ID] = to
}
}
}
if len(changedFolders) > 0 {
// Need to wait for the waiter, as this calls CommitConfiguration,
// which sets up the folder and as we return from this call,
// ClusterConfig starts poking at m.folderFiles and other things
// that might not exist until the config is committed.
w, _ := m.cfg.SetFolders(changedFolders)
w.Wait()
changed = true
}
if len(changedFcfg) == 0 {
return
}
for i := range cfg.Folders {
if fcfg, ok := changedFcfg[cfg.Folders[i].ID]; ok {
cfg.Folders[i] = fcfg
delete(changedFcfg, cfg.Folders[i].ID)
}
}
for _, fcfg := range changedFcfg {
cfg.Folders = append(cfg.Folders, fcfg)
}
})
// Need to wait for the waiter, as this calls CommitConfiguration,
// which sets up the folder and as we return from this call,
// ClusterConfig starts poking at m.folderFiles and other things
// that might not exist until the config is committed.
w.Wait()
}
tempIndexFolders, paused, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexSenderRegistry)
@@ -1257,11 +1263,12 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
}
if deviceCfg.Introducer {
folders, devices, foldersDevices, introduced := m.handleIntroductions(deviceCfg, cm)
folders, devices, deintroduced := m.handleDeintroductions(deviceCfg, foldersDevices, folders, devices)
if introduced || deintroduced {
changed = true
cfg := m.cfg.RawCopy()
m.cfg.Modify(func(cfg *config.Configuration) {
folders, devices, foldersDevices, introduced := m.handleIntroductions(deviceCfg, cm, cfg.FolderMap(), cfg.DeviceMap())
folders, devices, deintroduced := m.handleDeintroductions(deviceCfg, foldersDevices, folders, devices)
if !introduced && !deintroduced {
return
}
cfg.Folders = make([]config.FolderConfiguration, 0, len(folders))
for _, fcfg := range folders {
cfg.Folders = append(cfg.Folders, fcfg)
@@ -1270,14 +1277,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
for _, dcfg := range devices {
cfg.Devices = append(cfg.Devices, dcfg)
}
m.cfg.Replace(cfg)
}
}
if changed {
if err := m.cfg.Save(); err != nil {
l.Warnln("Failed to save config", err)
}
})
}
return nil
@@ -1492,10 +1492,8 @@ func (m *model) resendClusterConfig(ids []protocol.DeviceID) {
}
// handleIntroductions handles adding devices/folders that are shared by an introducer device
func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (map[string]config.FolderConfiguration, map[protocol.DeviceID]config.DeviceConfiguration, folderDeviceSet, bool) {
func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, folders map[string]config.FolderConfiguration, devices map[protocol.DeviceID]config.DeviceConfiguration) (map[string]config.FolderConfiguration, map[protocol.DeviceID]config.DeviceConfiguration, folderDeviceSet, bool) {
changed := false
folders := m.cfg.Folders()
devices := m.cfg.Devices()
foldersDevices := make(folderDeviceSet)
@@ -1521,7 +1519,7 @@ func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm
foldersDevices.set(device.ID, folder.ID)
if _, ok := m.cfg.Device(device.ID); !ok {
if _, ok := devices[device.ID]; !ok {
// The device is currently unknown. Add it to the config.
devices[device.ID] = m.introduceDevice(device, introducerCfg)
} else if fcfg.SharedWith(device.ID) {
@@ -1602,9 +1600,8 @@ func (m *model) handleDeintroductions(introducerCfg config.DeviceConfiguration,
// handleAutoAccepts handles adding and sharing folders for devices that have
// AutoAcceptFolders set to true.
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *indexSenderStartInfo) (config.FolderConfiguration, bool) {
if cfg, ok := m.cfg.Folder(folder.ID); !ok {
defaultPath := m.cfg.Options().DefaultFolderPath
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *indexSenderStartInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
if !haveCfg {
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
pathAlternatives := []string{
fs.SanitizePath(folder.Label),
@@ -2179,9 +2176,16 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
conn.ClusterConfig(cm)
if (device.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" {
device.Name = hello.DeviceName
m.cfg.SetDevice(device)
m.cfg.Save()
m.cfg.Modify(func(cfg *config.Configuration) {
for i := range cfg.Devices {
if cfg.Devices[i].DeviceID == deviceID {
if cfg.Devices[i].Name == "" || cfg.Options.OverwriteRemoteDevNames {
cfg.Devices[i].Name = hello.DeviceName
}
return
}
}
})
}
m.deviceWasSeen(deviceID)
@@ -2651,6 +2655,9 @@ func (m *model) VerifyConfiguration(from, to config.Configuration) error {
func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// TODO: This should not use reflect, and should take more care to try to handle stuff without restart.
// Delay processing config changes until after the initial setup
<-m.started
// Go through the folder configs and figure out if we need to restart or not.
// Tracks devices affected by any configuration change to resend ClusterConfig.