From 52e72e01222df8be766a3b76516a7cfa23b4ed97 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sat, 29 Feb 2020 19:51:32 +0100 Subject: [PATCH] lib/db: Prevent GC concurrently with migration (fixes #6389) (#6390) --- lib/db/lowlevel.go | 18 +++++++++++++++--- lib/db/schemaupdater.go | 5 +++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/db/lowlevel.go b/lib/db/lowlevel.go index a3ebd3cb6..2b315d56f 100644 --- a/lib/db/lowlevel.go +++ b/lib/db/lowlevel.go @@ -488,8 +488,18 @@ func (db *Lowlevel) dropPrefix(prefix []byte) error { } func (db *Lowlevel) gcRunner() { - t := time.NewTimer(db.timeUntil(indirectGCTimeKey, indirectGCInterval)) + // Calculate the time for the next GC run. Even if we should run GC + // directly, give the system a while to get up and running and do other + // stuff first. (We might have migrations and stuff which would be + // better off running before GC.) + next := db.timeUntil(indirectGCTimeKey, indirectGCInterval) + if next < time.Minute { + next = time.Minute + } + + t := time.NewTimer(next) defer t.Stop() + for { select { case <-db.gcStop: @@ -558,10 +568,11 @@ func (db *Lowlevel) gcIndirect() error { // Iterate the FileInfos, unmarshal the block and version hashes and // add them to the filter. - it, err := db.NewPrefixIterator([]byte{KeyTypeDevice}) + it, err := t.NewPrefixIterator([]byte{KeyTypeDevice}) if err != nil { return err } + defer it.Release() for it.Next() { var bl BlocksHashOnly if err := bl.Unmarshal(it.Value()); err != nil { @@ -579,10 +590,11 @@ func (db *Lowlevel) gcIndirect() error { // Iterate over block lists, removing keys with hashes that don't match // the filter. - it, err = db.NewPrefixIterator([]byte{KeyTypeBlockList}) + it, err = t.NewPrefixIterator([]byte{KeyTypeBlockList}) if err != nil { return err } + defer it.Release() matchedBlocks := 0 for it.Next() { key := blockListKey(it.Key()) diff --git a/lib/db/schemaupdater.go b/lib/db/schemaupdater.go index 0486adfdc..7dd454d29 100644 --- a/lib/db/schemaupdater.go +++ b/lib/db/schemaupdater.go @@ -57,6 +57,11 @@ type schemaUpdater struct { } func (db *schemaUpdater) updateSchema() error { + // Updating the schema can touch any and all parts of the database. Make + // sure we do not run GC concurrently with schema migrations. + db.gcMut.Lock() + defer db.gcMut.Unlock() + miscDB := NewMiscDataNamespace(db.Lowlevel) prevVersion, _, err := miscDB.Int64("dbVersion") if err != nil {