Browse Source

lib/db: Configurable block GC time (#6295)

Also retain the interval over restarts by storing last GC time in the
database. This to make sure that GC eventually happens even if the
interval is configured to a long time (say, a month).
Jakob Borg 5 years ago
parent
commit
bf4c8439e8
3 changed files with 40 additions and 2 deletions
  1. 4 0
      cmd/syncthing/main.go
  2. 34 2
      lib/db/lowlevel.go
  3. 2 0
      lib/db/schemaupdater.go

+ 4 - 0
cmd/syncthing/main.go

@@ -119,6 +119,10 @@ are mostly useful for developers. Use with care.
                    "h", "m" and "s" abbreviations for hours minutes and seconds.
                    "h", "m" and "s" abbreviations for hours minutes and seconds.
                    Valid values are like "720h", "30s", etc.
                    Valid values are like "720h", "30s", etc.
 
 
+ STGCBLOCKSEVERY   Set to a time interval to override the default database
+                   block GC interval of 13 hours. Same format as the
+                   STRECHECKDBEVERY variable.
+
  GOMAXPROCS        Set the maximum number of CPU cores to use. Defaults to all
  GOMAXPROCS        Set the maximum number of CPU cores to use. Defaults to all
                    available CPU cores.
                    available CPU cores.
 
 

+ 34 - 2
lib/db/lowlevel.go

@@ -9,6 +9,7 @@ package db
 import (
 import (
 	"bytes"
 	"bytes"
 	"encoding/binary"
 	"encoding/binary"
+	"os"
 	"time"
 	"time"
 
 
 	"github.com/syncthing/syncthing/lib/db/backend"
 	"github.com/syncthing/syncthing/lib/db/backend"
@@ -25,9 +26,18 @@ const (
 	// false positive rate instead.
 	// false positive rate instead.
 	blockGCBloomCapacity          = 100000
 	blockGCBloomCapacity          = 100000
 	blockGCBloomFalsePositiveRate = 0.01 // 1%
 	blockGCBloomFalsePositiveRate = 0.01 // 1%
-	blockGCInterval               = 13 * time.Hour
+	blockGCDefaultInterval        = 13 * time.Hour
+	blockGCTimeKey                = "lastGCTime"
 )
 )
 
 
+var blockGCInterval = blockGCDefaultInterval
+
+func init() {
+	if dur, err := time.ParseDuration(os.Getenv("STGCBLOCKSEVERY")); err == nil {
+		blockGCInterval = dur
+	}
+}
+
 // Lowlevel is the lowest level database interface. It has a very simple
 // Lowlevel is the lowest level database interface. It has a very simple
 // purpose: hold the actual backend database, and the in-memory state
 // purpose: hold the actual backend database, and the in-memory state
 // that belong to that database. In the same way that a single on disk
 // that belong to that database. In the same way that a single on disk
@@ -463,7 +473,7 @@ func (db *Lowlevel) dropPrefix(prefix []byte) error {
 }
 }
 
 
 func (db *Lowlevel) gcRunner() {
 func (db *Lowlevel) gcRunner() {
-	t := time.NewTicker(blockGCInterval)
+	t := time.NewTimer(db.timeUntil(blockGCTimeKey, blockGCInterval))
 	defer t.Stop()
 	defer t.Stop()
 	for {
 	for {
 		select {
 		select {
@@ -473,10 +483,32 @@ func (db *Lowlevel) gcRunner() {
 			if err := db.gcBlocks(); err != nil {
 			if err := db.gcBlocks(); err != nil {
 				l.Warnln("Database block GC failed:", err)
 				l.Warnln("Database block GC failed:", err)
 			}
 			}
+			db.recordTime(blockGCTimeKey)
+			t.Reset(db.timeUntil(blockGCTimeKey, blockGCInterval))
 		}
 		}
 	}
 	}
 }
 }
 
 
+// recordTime records the current time under the given key, affecting the
+// next call to timeUntil with the same key.
+func (db *Lowlevel) recordTime(key string) {
+	miscDB := NewMiscDataNamespace(db)
+	_ = miscDB.PutInt64(key, time.Now().Unix()) // error wilfully ignored
+}
+
+// timeUntil returns how long we should wait until the next interval, or
+// zero if it should happen directly.
+func (db *Lowlevel) timeUntil(key string, every time.Duration) time.Duration {
+	miscDB := NewMiscDataNamespace(db)
+	lastTime, _, _ := miscDB.Int64(key) // error wilfully ignored
+	nextTime := time.Unix(lastTime, 0).Add(every)
+	sleepTime := time.Until(nextTime)
+	if sleepTime < 0 {
+		sleepTime = 0
+	}
+	return sleepTime
+}
+
 func (db *Lowlevel) gcBlocks() error {
 func (db *Lowlevel) gcBlocks() error {
 	// The block GC uses a bloom filter to track used block lists. This means
 	// The block GC uses a bloom filter to track used block lists. This means
 	// iterating over all items, adding their block lists to the filter, then
 	// iterating over all items, adding their block lists to the filter, then

+ 2 - 0
lib/db/schemaupdater.go

@@ -451,5 +451,7 @@ func (db *schemaUpdater) updateSchema7to8(_ int) error {
 		return err
 		return err
 	}
 	}
 
 
+	db.recordTime(blockGCTimeKey)
+
 	return t.commit()
 	return t.commit()
 }
 }