Skip to content

Commit fa6fb35

Browse files
committed
randomize leveldb compaction config
1 parent 29b4e6b commit fa6fb35

File tree

3 files changed

+61
-12
lines changed

3 files changed

+61
-12
lines changed

database/factory/factory.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import (
2121
//
2222
// It also wraps the database with a corruptable DB.
2323
//
24-
// dbName is the name of the database, either leveldb, memdb, or pebbledb.
25-
// dbPath is the path to the database folder.
24+
// name is the name of the database, either leveldb, memdb, or pebbledb.
25+
// path is the path to the database folder.
2626
// readOnly indicates if the database should be read-only.
27-
// dbConfig is the database configuration in JSON format.
27+
// config is the database configuration in JSON format.
2828
func New(
2929
name string,
3030
path string,
@@ -40,6 +40,9 @@ func New(
4040
switch name {
4141
case leveldb.Name:
4242
db, err = leveldb.New(path, config, logger, reg)
43+
if err != nil {
44+
return nil, fmt.Errorf("couldn't create %s at %s: %w", leveldb.Name, path, err)
45+
}
4346
case memdb.Name:
4447
db = memdb.New()
4548
case pebbledb.Name:

database/leveldb/db.go

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/json"
1010
"fmt"
1111
"math"
12+
"math/rand"
1213
"slices"
1314
"sync"
1415
"time"
@@ -60,6 +61,18 @@ const (
6061
// levelDBByteOverhead is the number of bytes of constant overhead that
6162
// should be added to a batch size per operation.
6263
levelDBByteOverhead = 8
64+
65+
// minCompactionL0Trigger is the minimum value for CompactionL0Trigger.
66+
minCompactionL0Trigger = 3
67+
68+
// maxCompactionL0Trigger is the maximum value for CompactionL0Trigger.
69+
maxCompactionL0Trigger = 7
70+
71+
// minCompactionTableSize is the minimum value for CompactionTableSize.
72+
minCompactionTableSize = 2 * opt.MiB
73+
74+
// maxCompactionTableSize is the maximum value for CompactionTableSize.
75+
maxCompactionTableSize = 4 * opt.MiB
6376
)
6477

6578
var (
@@ -98,40 +111,40 @@ type config struct {
98111
// BlockSize is the minimum uncompressed size in bytes of each 'sorted table'
99112
// block.
100113
//
101-
// The default value is 4KiB.
114+
// The default value is [opt.DefaultBlockSize].
102115
BlockSize int `json:"blockSize"`
103116
// CompactionExpandLimitFactor limits compaction size after expanded.
104117
// This will be multiplied by table size limit at compaction target level.
105118
//
106-
// The default value is 25.
119+
// The default value is [opt.DefaultCompactionExpandLimitFactor].
107120
CompactionExpandLimitFactor int `json:"compactionExpandLimitFactor"`
108121
// CompactionGPOverlapsFactor limits overlaps in grandparent (Level + 2)
109122
// that a single 'sorted table' generates. This will be multiplied by
110123
// table size limit at grandparent level.
111124
//
112-
// The default value is 10.
125+
// The default value is [opt.DefaultCompactionGPOverlapsFactor].
113126
CompactionGPOverlapsFactor int `json:"compactionGPOverlapsFactor"`
114127
// CompactionL0Trigger defines number of 'sorted table' at level-0 that will
115128
// trigger compaction.
116129
//
117-
// The default value is 4.
130+
// The default value is a random value between [minCompactionL0Trigger] and [maxCompactionL0Trigger].
118131
CompactionL0Trigger int `json:"compactionL0Trigger"`
119132
// CompactionSourceLimitFactor limits compaction source size. This doesn't apply to
120133
// level-0.
121134
// This will be multiplied by table size limit at compaction target level.
122135
//
123-
// The default value is 1.
136+
// The default value is [opt.DefaultCompactionSourceLimitFactor].
124137
CompactionSourceLimitFactor int `json:"compactionSourceLimitFactor"`
125138
// CompactionTableSize limits size of 'sorted table' that compaction generates.
126139
// The limits for each level will be calculated as:
127140
// CompactionTableSize * (CompactionTableSizeMultiplier ^ Level)
128141
// The multiplier for each level can also fine-tuned using CompactionTableSizeMultiplierPerLevel.
129142
//
130-
// The default value is 2MiB.
143+
// The default value is a random value between [minCompactionTableSize] and [maxCompactionTableSize].
131144
CompactionTableSize int `json:"compactionTableSize"`
132145
// CompactionTableSizeMultiplier defines multiplier for CompactionTableSize.
133146
//
134-
// The default value is 1.
147+
// The default value is [opt.DefaultCompactionTableSizeMultiplier].
135148
CompactionTableSizeMultiplier float64 `json:"compactionTableSizeMultiplier"`
136149
// CompactionTableSizeMultiplierPerLevel defines per-level multiplier for
137150
// CompactionTableSize.
@@ -145,11 +158,11 @@ type config struct {
145158
// The multiplier for each level can also fine-tuned using
146159
// CompactionTotalSizeMultiplierPerLevel.
147160
//
148-
// The default value is 10MiB.
161+
// The default value is [opt.DefaultCompactionTotalSize].
149162
CompactionTotalSize int `json:"compactionTotalSize"`
150163
// CompactionTotalSizeMultiplier defines multiplier for CompactionTotalSize.
151164
//
152-
// The default value is 10.
165+
// The default value is [opt.DefaultCompactionTotalSizeMultiplier].
153166
CompactionTotalSizeMultiplier float64 `json:"compactionTotalSizeMultiplier"`
154167
// DisableSeeksCompaction allows disabling 'seeks triggered compaction'.
155168
// The purpose of 'seeks triggered compaction' is to optimize database so
@@ -185,6 +198,21 @@ type config struct {
185198
MetricUpdateFrequency time.Duration `json:"metricUpdateFrequency"`
186199
}
187200

201+
// randomizeCompactionParams sets compaction parameters in cfg to random values
202+
// to spread out compaction behavior across different nodes.
203+
func randomizeCompactionParams(cfg *config) {
204+
r := rand.New(rand.NewSource(time.Now().UnixNano())) // #nosec G404 -- non-crypto randomness is fine here
205+
206+
// generate random defaults for CompactionL0Trigger
207+
compactionL0Trigger := r.Intn(maxCompactionL0Trigger-minCompactionL0Trigger+1) + minCompactionL0Trigger
208+
209+
// generate random defaults for CompactionTableSize
210+
compactionTableSize := (r.Intn(maxCompactionTableSize-minCompactionTableSize+1) + minCompactionTableSize)
211+
212+
cfg.CompactionL0Trigger = compactionL0Trigger
213+
cfg.CompactionTableSize = compactionTableSize
214+
}
215+
188216
// New returns a wrapped LevelDB object.
189217
func New(file string, configBytes []byte, log logging.Logger, reg prometheus.Registerer) (database.Database, error) {
190218
parsedConfig := config{
@@ -196,6 +224,9 @@ func New(file string, configBytes []byte, log logging.Logger, reg prometheus.Reg
196224
MaxManifestFileSize: DefaultMaxManifestFileSize,
197225
MetricUpdateFrequency: DefaultMetricUpdateFrequency,
198226
}
227+
228+
randomizeCompactionParams(&parsedConfig)
229+
199230
if len(configBytes) > 0 {
200231
if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
201232
return nil, fmt.Errorf("%w: %w", ErrInvalidConfig, err)

database/leveldb/db_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,18 @@ func BenchmarkInterface(b *testing.B) {
7373
}
7474
}
7575
}
76+
77+
func TestSetCompactionParamsFromNodeId(t *testing.T) {
78+
t.Run("randomized compaction params within expected ranges", func(t *testing.T) {
79+
for range 100 {
80+
var cfg config
81+
randomizeCompactionParams(&cfg)
82+
83+
require.GreaterOrEqual(t, cfg.CompactionL0Trigger, minCompactionL0Trigger)
84+
require.LessOrEqual(t, cfg.CompactionL0Trigger, maxCompactionL0Trigger)
85+
86+
require.GreaterOrEqual(t, cfg.CompactionTableSize, minCompactionTableSize)
87+
require.LessOrEqual(t, cfg.CompactionTableSize, maxCompactionTableSize)
88+
}
89+
})
90+
}

0 commit comments

Comments
 (0)