From a992559abcfff1d5f14eece3dc52dd400ae6bb01 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Fri, 26 Jul 2019 22:18:42 +0200 Subject: [PATCH] lib/db: Add hacky way to adjust database parameters (#5889) This adds a set of magical environment variables that can be used to tweak the database parameters. It's totally undocumented and not intended to be a long term or supported thing. It's ugly, but there is a backstory. I have a couple of large installations where the database options are inefficient or otherwise suboptimal (24/7 compaction going on and stuff like that). I don't *know* the correct database parameters, nor yet the formula or method to derive them by, so this requires experimentation. Experimentation needs to happen partly in production, and rolling out new builds for every tweak isn't practical. This provides override points for all reasonable values, while not changing anything by default. Ideally, at the end of such experimentation, we'll know which values are relevant to change and in what manner, and can provide a more user friendly knob to do so - or do it automatically based on the database size. --- lib/db/lowlevel.go | 48 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/db/lowlevel.go b/lib/db/lowlevel.go index eda38af66..d079ad109 100644 --- a/lib/db/lowlevel.go +++ b/lib/db/lowlevel.go @@ -8,6 +8,7 @@ package db import ( "os" + "strconv" "strings" "sync" "sync/atomic" @@ -23,7 +24,10 @@ import ( const ( dbMaxOpenFiles = 100 dbWriteBuffer = 16 << 20 - dbFlushBatch = dbWriteBuffer / 4 // Some leeway for any leveldb in-memory optimizations +) + +var ( + dbFlushBatch = debugEnvValue("WriteBuffer", dbWriteBuffer) / 4 // Some leeway for any leveldb in-memory optimizations ) // Lowlevel is the lowest level database interface. It has a very simple @@ -47,8 +51,32 @@ type Lowlevel struct { // the database is erased and created from scratch. func Open(location string) (*Lowlevel, error) { opts := &opt.Options{ - OpenFilesCacheCapacity: dbMaxOpenFiles, - WriteBuffer: dbWriteBuffer, + BlockCacheCapacity: debugEnvValue("BlockCacheCapacity", 0), + BlockCacheEvictRemoved: debugEnvValue("BlockCacheEvictRemoved", 0) != 0, + BlockRestartInterval: debugEnvValue("BlockRestartInterval", 0), + BlockSize: debugEnvValue("BlockSize", 0), + CompactionExpandLimitFactor: debugEnvValue("CompactionExpandLimitFactor", 0), + CompactionGPOverlapsFactor: debugEnvValue("CompactionGPOverlapsFactor", 0), + CompactionL0Trigger: debugEnvValue("CompactionL0Trigger", 0), + CompactionSourceLimitFactor: debugEnvValue("CompactionSourceLimitFactor", 0), + CompactionTableSize: debugEnvValue("CompactionTableSize", 0), + CompactionTableSizeMultiplier: float64(debugEnvValue("CompactionTableSizeMultiplier", 0)) / 10.0, + CompactionTotalSize: debugEnvValue("CompactionTotalSize", 0), + CompactionTotalSizeMultiplier: float64(debugEnvValue("CompactionTotalSizeMultiplier", 0)) / 10.0, + DisableBufferPool: debugEnvValue("DisableBufferPool", 0) != 0, + DisableBlockCache: debugEnvValue("DisableBlockCache", 0) != 0, + DisableCompactionBackoff: debugEnvValue("DisableCompactionBackoff", 0) != 0, + DisableLargeBatchTransaction: debugEnvValue("DisableLargeBatchTransaction", 0) != 0, + NoSync: debugEnvValue("NoSync", 0) != 0, + NoWriteMerge: debugEnvValue("NoWriteMerge", 0) != 0, + OpenFilesCacheCapacity: debugEnvValue("OpenFilesCacheCapacity", dbMaxOpenFiles), + WriteBuffer: debugEnvValue("WriteBuffer", dbWriteBuffer), + // The write slowdown and pause can be overridden, but even if they + // are not and the compaction trigger is overridden we need to + // adjust so that we don't pause writes for L0 compaction before we + // even *start* L0 compaction... + WriteL0SlowdownTrigger: debugEnvValue("WriteL0SlowdownTrigger", 2*debugEnvValue("CompactionL0Trigger", opt.DefaultCompactionL0Trigger)), + WriteL0PauseTrigger: debugEnvValue("WriteL0SlowdownTrigger", 3*debugEnvValue("CompactionL0Trigger", opt.DefaultCompactionL0Trigger)), } return open(location, opts) } @@ -80,6 +108,12 @@ func open(location string, opts *opt.Options) (*Lowlevel, error) { if err != nil { return nil, errorSuggestion{err, "is another instance of Syncthing running?"} } + + if debugEnvValue("CompactEverything", 0) != 0 { + if err := db.CompactRange(util.Range{}); err != nil { + l.Warnln("Compacting database:", err) + } + } return NewLowlevel(db, location), nil } @@ -304,3 +338,11 @@ func (it *iter) execIfNotClosed(fn func() bool) bool { } return fn() } + +func debugEnvValue(key string, def int) int { + v, err := strconv.ParseInt(os.Getenv("STDEBUG_"+key), 10, 63) + if err != nil { + return def + } + return int(v) +}