Browse Source

lib/db: Prevent GC concurrently with migration (fixes #6389) (#6390)

Jakob Borg 5 years ago
parent
commit
52e72e0122
2 changed files with 20 additions and 3 deletions
  1. 15 3
      lib/db/lowlevel.go
  2. 5 0
      lib/db/schemaupdater.go

+ 15 - 3
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())

+ 5 - 0
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 {