Remove folder without restart (fixes #2262)

This commit is contained in:
Jakob Borg
2015-11-13 13:30:52 +01:00
parent 56f1c295b6
commit 73285cadb6

View File

@@ -75,15 +75,16 @@ type Model struct {
clientName string clientName string
clientVersion string clientVersion string
folderCfgs map[string]config.FolderConfiguration // folder -> cfg folderCfgs map[string]config.FolderConfiguration // folder -> cfg
folderFiles map[string]*db.FileSet // folder -> files folderFiles map[string]*db.FileSet // folder -> files
folderDevices map[string][]protocol.DeviceID // folder -> deviceIDs folderDevices map[string][]protocol.DeviceID // folder -> deviceIDs
deviceFolders map[protocol.DeviceID][]string // deviceID -> folders deviceFolders map[protocol.DeviceID][]string // deviceID -> folders
deviceStatRefs map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef deviceStatRefs map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef
folderIgnores map[string]*ignore.Matcher // folder -> matcher object folderIgnores map[string]*ignore.Matcher // folder -> matcher object
folderRunners map[string]service // folder -> puller or scanner folderRunners map[string]service // folder -> puller or scanner
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef folderRunnerTokens map[string][]suture.ServiceToken // folder -> tokens for puller or scanner
fmut sync.RWMutex // protects the above folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
fmut sync.RWMutex // protects the above
conn map[protocol.DeviceID]Connection conn map[protocol.DeviceID]Connection
deviceVer map[protocol.DeviceID]string deviceVer map[protocol.DeviceID]string
@@ -105,28 +106,29 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
l.Debugln(line) l.Debugln(line)
}, },
}), }),
cfg: cfg, cfg: cfg,
db: ldb, db: ldb,
finder: db.NewBlockFinder(ldb), finder: db.NewBlockFinder(ldb),
progressEmitter: NewProgressEmitter(cfg), progressEmitter: NewProgressEmitter(cfg),
id: id, id: id,
shortID: id.Short(), shortID: id.Short(),
cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles, cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles,
protectedFiles: protectedFiles, protectedFiles: protectedFiles,
deviceName: deviceName, deviceName: deviceName,
clientName: clientName, clientName: clientName,
clientVersion: clientVersion, clientVersion: clientVersion,
folderCfgs: make(map[string]config.FolderConfiguration), folderCfgs: make(map[string]config.FolderConfiguration),
folderFiles: make(map[string]*db.FileSet), folderFiles: make(map[string]*db.FileSet),
folderDevices: make(map[string][]protocol.DeviceID), folderDevices: make(map[string][]protocol.DeviceID),
deviceFolders: make(map[protocol.DeviceID][]string), deviceFolders: make(map[protocol.DeviceID][]string),
deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference), deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
folderIgnores: make(map[string]*ignore.Matcher), folderIgnores: make(map[string]*ignore.Matcher),
folderRunners: make(map[string]service), folderRunners: make(map[string]service),
folderStatRefs: make(map[string]*stats.FolderStatisticsReference), folderRunnerTokens: make(map[string][]suture.ServiceToken),
conn: make(map[protocol.DeviceID]Connection), folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
deviceVer: make(map[protocol.DeviceID]string), conn: make(map[protocol.DeviceID]Connection),
devicePaused: make(map[protocol.DeviceID]bool), deviceVer: make(map[protocol.DeviceID]string),
devicePaused: make(map[protocol.DeviceID]bool),
fmut: sync.NewRWMutex(), fmut: sync.NewRWMutex(),
pmut: sync.NewRWMutex(), pmut: sync.NewRWMutex(),
@@ -163,7 +165,6 @@ func (m *Model) StartFolderRW(folder string) {
} }
p := newRWFolder(m, m.shortID, cfg) p := newRWFolder(m, m.shortID, cfg)
m.folderRunners[folder] = p m.folderRunners[folder] = p
m.fmut.Unlock()
if len(cfg.Versioning.Type) > 0 { if len(cfg.Versioning.Type) > 0 {
factory, ok := versioner.Factories[cfg.Versioning.Type] factory, ok := versioner.Factories[cfg.Versioning.Type]
@@ -176,14 +177,17 @@ func (m *Model) StartFolderRW(folder string) {
// The versioner implements the suture.Service interface, so // The versioner implements the suture.Service interface, so
// expects to be run in the background in addition to being called // expects to be run in the background in addition to being called
// when files are going to be archived. // when files are going to be archived.
m.Add(service) token := m.Add(service)
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
} }
p.versioner = versioner p.versioner = versioner
} }
m.warnAboutOverwritingProtectedFiles(folder) m.warnAboutOverwritingProtectedFiles(folder)
m.Add(p) token := m.Add(p)
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
m.fmut.Unlock()
l.Okln("Ready to synchronize", folder, "(read-write)") l.Okln("Ready to synchronize", folder, "(read-write)")
} }
@@ -232,13 +236,49 @@ func (m *Model) StartFolderRO(folder string) {
} }
s := newROFolder(m, folder, time.Duration(cfg.RescanIntervalS)*time.Second) s := newROFolder(m, folder, time.Duration(cfg.RescanIntervalS)*time.Second)
m.folderRunners[folder] = s m.folderRunners[folder] = s
token := m.Add(s)
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
m.fmut.Unlock() m.fmut.Unlock()
m.Add(s)
l.Okln("Ready to synchronize", folder, "(read only; no external updates accepted)") l.Okln("Ready to synchronize", folder, "(read only; no external updates accepted)")
} }
func (m *Model) RemoveFolder(folder string) {
m.fmut.Lock()
m.pmut.Lock()
// Stop the services running for this folder
for _, id := range m.folderRunnerTokens[folder] {
m.Remove(id)
}
// Close connections to affected devices
for _, dev := range m.folderDevices[folder] {
if conn, ok := m.conn[dev]; ok {
closeRawConn(conn)
}
}
// Clean up our config maps
delete(m.folderCfgs, folder)
delete(m.folderFiles, folder)
delete(m.folderDevices, folder)
delete(m.folderIgnores, folder)
delete(m.folderRunners, folder)
delete(m.folderRunnerTokens, folder)
delete(m.folderStatRefs, folder)
for dev, folders := range m.deviceFolders {
m.deviceFolders[dev] = stringSliceWithout(folders, folder)
}
// Remove it from the database
db.DropFolder(m.db, folder)
m.pmut.Unlock()
m.fmut.Unlock()
}
type ConnectionInfo struct { type ConnectionInfo struct {
protocol.Statistics protocol.Statistics
Connected bool Connected bool
@@ -1797,9 +1837,9 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
for folderID, fromCfg := range fromFolders { for folderID, fromCfg := range fromFolders {
toCfg, ok := toFolders[folderID] toCfg, ok := toFolders[folderID]
if !ok { if !ok {
// A folder was removed. Requires restart. // The folder was removed.
l.Debugln(m, "requires restart, removing folder", folderID) m.RemoveFolder(folderID)
return false continue
} }
// This folder exists on both sides. Compare the device lists, as we // This folder exists on both sides. Compare the device lists, as we
@@ -1961,3 +2001,14 @@ func closeRawConn(conn io.Closer) error {
} }
return conn.Close() return conn.Close()
} }
func stringSliceWithout(ss []string, s string) []string {
for i := range ss {
if ss[i] == s {
copy(ss[i:], ss[i+1:])
ss = ss[:len(ss)-1]
return ss
}
}
return ss
}