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).
This commit is contained in:
Jakob Borg 2020-01-26 15:13:28 +01:00 committed by GitHub
parent 8fc2dfad0c
commit bf4c8439e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 2 deletions

View File

@ -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.

View File

@ -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

View File

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