Skip to content

Commit e8d7ecb

Browse files
committed
db: tolerate unknown options
Instead of throwing an error when we encounter unfamiliar options, we will instead tolerate it and log a message. This will allow us to be more lenient about options handling (backporting, renaming, etc.). It also addresses the case in which a user could revert before a major upgrade finalization and have some new option written that the lesser version doesn't recognize and error out at.
1 parent 75afc70 commit e8d7ecb

File tree

3 files changed

+73
-80
lines changed

3 files changed

+73
-80
lines changed

metamorphic/options.go

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -66,47 +66,37 @@ func parseOptions(
6666
}
6767
return pebble.KeySchema{}, errors.Newf("unknown key schema %q", name)
6868
},
69-
SkipUnknown: func(name, value string) bool {
69+
OnUnknown: func(name, value string) {
7070
if strings.EqualFold(value, "false") {
7171
// TODO(radu): audit all settings and use ParseBool wherever necessary.
7272
panic(fmt.Sprintf("%s: boolean options can only be set to true", name))
7373
}
7474
switch name {
7575
case "TestOptions":
76-
return true
7776
case "TestOptions.strictfs":
7877
opts.strictFS = true
7978
opts.Opts.FS = vfs.NewCrashableMem()
80-
return true
8179
case "TestOptions.key_format":
8280
opts.KeyFormat = keyFormatsByName[value]
83-
return true
8481
case "TestOptions.ingest_using_apply":
8582
opts.ingestUsingApply = true
86-
return true
8783
case "TestOptions.delete_sized":
8884
opts.deleteSized = true
89-
return true
9085
case "TestOptions.replace_single_delete":
9186
opts.replaceSingleDelete = true
92-
return true
9387
case "TestOptions.use_disk":
9488
opts.useDisk = true
9589
opts.Opts.FS = vfs.Default
96-
return true
9790
case "TestOptions.initial_state_desc":
9891
opts.initialStateDesc = value
99-
return true
10092
case "TestOptions.initial_state_path":
10193
opts.initialStatePath = value
102-
return true
10394
case "TestOptions.threads":
10495
v, err := strconv.Atoi(value)
10596
if err != nil {
10697
panic(err)
10798
}
10899
opts.Threads = v
109-
return true
110100
case "TestOptions.disable_block_property_collector":
111101
v, err := strconv.ParseBool(value)
112102
if err != nil {
@@ -116,99 +106,81 @@ func parseOptions(
116106
if v {
117107
opts.Opts.BlockPropertyCollectors = nil
118108
}
119-
return true
120109
case "TestOptions.enable_value_blocks":
121110
opts.enableValueBlocks = true
122111
opts.Opts.Experimental.EnableValueBlocks = func() bool { return true }
123-
return true
124112
case "TestOptions.disable_value_blocks_for_ingest_sstables":
125113
opts.disableValueBlocksForIngestSSTables = true
126-
return true
127114
case "TestOptions.async_apply_to_db":
128115
opts.asyncApplyToDB = true
129-
return true
130116
case "TestOptions.shared_storage_enabled":
131117
opts.sharedStorageEnabled = true
132118
if opts.Opts.Experimental.CreateOnShared == remote.CreateOnSharedNone {
133119
opts.Opts.Experimental.CreateOnShared = remote.CreateOnSharedAll
134120
}
135-
return true
136121
case "TestOptions.external_storage_enabled":
137122
opts.externalStorageEnabled = true
138-
return true
139123
case "TestOptions.secondary_cache_enabled":
140124
opts.secondaryCacheEnabled = true
141125
opts.Opts.Experimental.SecondaryCacheSizeBytes = 1024 * 1024 * 32 // 32 MBs
142-
return true
143126
case "TestOptions.seed_efos":
144127
v, err := strconv.ParseUint(value, 10, 64)
145128
if err != nil {
146129
panic(err)
147130
}
148131
opts.seedEFOS = v
149-
return true
150132
case "TestOptions.io_latency_mean":
151133
v, err := time.ParseDuration(value)
152134
if err != nil {
153135
panic(err)
154136
}
155137
opts.ioLatencyMean = v
156-
return true
157138
case "TestOptions.io_latency_probability":
158139
v, err := strconv.ParseFloat(value, 64)
159140
if err != nil {
160141
panic(err)
161142
}
162143
opts.ioLatencyProbability = v
163-
return true
164144
case "TestOptions.io_latency_seed":
165145
v, err := strconv.ParseInt(value, 10, 64)
166146
if err != nil {
167147
panic(err)
168148
}
169149
opts.ioLatencySeed = v
170-
return true
171150
case "TestOptions.ingest_split":
172151
// TODO(radu): this should be on by default.
173152
opts.ingestSplit = true
174153
opts.Opts.Experimental.IngestSplit = func() bool {
175154
return true
176155
}
177-
return true
178156
case "TestOptions.use_shared_replicate":
179157
opts.useSharedReplicate = true
180-
return true
181158
case "TestOptions.use_external_replicate":
182159
opts.useExternalReplicate = true
183-
return true
184160
case "TestOptions.use_excise":
185161
// TODO(radu): this should be on by default.
186162
opts.useExcise = true
187-
return true
188163
case "TestOptions.use_delete_only_compaction_excises":
189164
opts.useDeleteOnlyCompactionExcises = true
190165
opts.Opts.Experimental.EnableDeleteOnlyCompactionExcises = func() bool {
191166
return opts.useDeleteOnlyCompactionExcises
192167
}
193-
return true
194168
case "TestOptions.disable_downloads":
195169
opts.disableDownloads = true
196-
return true
197170
case "TestOptions.use_jemalloc_size_classes":
198171
opts.Opts.AllocatorSizeClasses = pebble.JemallocSizeClasses
199-
return true
200172
default:
201173
if customOptionParsers == nil {
202-
return false
174+
return
203175
}
204176
name = strings.TrimPrefix(name, "TestOptions.")
205177
if p, ok := customOptionParsers[name]; ok {
206178
if customOpt, ok := p(value); ok {
207179
opts.CustomOpts = append(opts.CustomOpts, customOpt)
208-
return true
180+
return
209181
}
210182
}
211-
return false
183+
return
212184
}
213185
},
214186
}

options.go

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,7 +1960,7 @@ type ParseHooks struct {
19601960
NewFilterPolicy func(name string) (FilterPolicy, error)
19611961
NewKeySchema func(name string) (KeySchema, error)
19621962
NewMerger func(name string) (*Merger, error)
1963-
SkipUnknown func(name, value string) bool
1963+
OnUnknown func(name, value string)
19641964
}
19651965

19661966
// Parse parses the options from the specified string. Note that certain
@@ -1976,11 +1976,6 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
19761976
}
19771977

19781978
visitKeyValue := func(i, j int, section, key, value string) error {
1979-
// WARNING: DO NOT remove entries from the switches below because doing so
1980-
// causes a key previously written to the OPTIONS file to be considered unknown,
1981-
// a backwards incompatible change. Instead, leave in support for parsing the
1982-
// key but simply don't parse the value.
1983-
19841979
parseComparer := func(name string) (*Comparer, error) {
19851980
switch name {
19861981
case DefaultComparer.Name:
@@ -2000,11 +1995,15 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
20001995
switch key {
20011996
case "pebble_version":
20021997
default:
2003-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
1998+
if hooks != nil && hooks.OnUnknown != nil {
1999+
hooks.OnUnknown(section+"."+key, value)
20042000
return nil
20052001
}
2006-
return errors.Errorf("pebble: unknown option: %s.%s",
2007-
errors.Safe(section), errors.Safe(key))
2002+
// Tolerate unknown options, but log them.
2003+
if o.Logger != nil {
2004+
o.Logger.Infof("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2005+
}
2006+
return nil
20082007
}
20092008
return nil
20102009

@@ -2059,8 +2058,6 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
20592058
o.private.disableLazyCombinedIteration, err = strconv.ParseBool(value)
20602059
case "disable_wal":
20612060
o.DisableWAL, err = strconv.ParseBool(value)
2062-
case "enable_columnar_blocks":
2063-
// Do nothing; option existed in older versions of pebble.
20642061
case "flush_delay_delete_range":
20652062
o.FlushDelayDeleteRange, err = time.ParseDuration(value)
20662063
case "flush_delay_range_key":
@@ -2074,7 +2071,18 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
20742071
// version is valid right here.
20752072
var v uint64
20762073
v, err = strconv.ParseUint(value, 10, 64)
2077-
if vers := FormatMajorVersion(v); vers > internalFormatNewest || vers == FormatDefault {
2074+
vers := FormatMajorVersion(v)
2075+
if vers > internalFormatNewest {
2076+
// Tolerate new unknown format versions in OPTIONS file (they
2077+
// may be stale if a format upgrade was reverted before
2078+
// finalization). The actual format version is determined by
2079+
// the format version marker file.
2080+
if o.Logger != nil {
2081+
o.Logger.Infof(
2082+
"pebble: format major version %d in OPTIONS file is newer than supported (%d), ignoring",
2083+
vers, internalFormatNewest)
2084+
}
2085+
} else if vers == FormatDefault {
20782086
err = errors.Newf("unsupported format major version %d", o.FormatMajorVersion)
20792087
}
20802088
if err == nil {
@@ -2118,8 +2126,6 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
21182126
o.L0CompactionThreshold, err = strconv.Atoi(value)
21192127
case "l0_stop_writes_threshold":
21202128
o.L0StopWritesThreshold, err = strconv.Atoi(value)
2121-
case "l0_sublevel_compactions":
2122-
// Do nothing; option existed in older versions of pebble.
21232129
case "lbase_max_bytes":
21242130
o.LBaseMaxBytes, err = strconv.ParseInt(value, 10, 64)
21252131
case "level_multiplier":
@@ -2146,9 +2152,6 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
21462152
o.MemTableSize, err = strconv.ParseUint(value, 10, 64)
21472153
case "mem_table_stop_writes_threshold":
21482154
o.MemTableStopWritesThreshold, err = strconv.Atoi(value)
2149-
case "min_compaction_rate":
2150-
// Do nothing; option existed in older versions of pebble, and
2151-
// may be meaningful again eventually.
21522155
case "min_deletion_rate":
21532156
var rate uint64
21542157
rate, err = strconv.ParseUint(value, 10, 64)
@@ -2159,13 +2162,8 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
21592162
o.DeletionPacing.FreeSpaceThresholdBytes, err = strconv.ParseUint(value, 10, 64)
21602163
case "free_space_timeframe":
21612164
o.DeletionPacing.FreeSpaceTimeframe, err = time.ParseDuration(value)
2162-
case "obsolete_bytes_max_ratio":
2163-
// No longer used.
21642165
case "obsolete_bytes_timeframe":
21652166
o.DeletionPacing.BacklogTimeframe, err = time.ParseDuration(value)
2166-
case "min_flush_rate":
2167-
// Do nothing; option existed in older versions of pebble, and
2168-
// may be meaningful again eventually.
21692167
case "multilevel_compaction_heuristic":
21702168
switch {
21712169
case value == "none":
@@ -2196,10 +2194,11 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
21962194
err = errors.Wrapf(err, "unexpected wamp heuristic arguments: %s", value)
21972195
}
21982196
default:
2199-
err = errors.Newf("unrecognized multilevel compaction heuristic: %s", value)
2197+
// Tolerate unknown options, but log them.
2198+
if o.Logger != nil {
2199+
o.Logger.Infof("pebble: unrecognized multilevel compaction heuristic: %s", value)
2200+
}
22002201
}
2201-
case "point_tombstone_weight":
2202-
// Do nothing; deprecated.
22032202
case "strict_wal_tail":
22042203
var strictWALTail bool
22052204
strictWALTail, err = strconv.ParseBool(value)
@@ -2240,20 +2239,18 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
22402239
case "leveldb":
22412240
case "rocksdbv2":
22422241
default:
2243-
return errors.Errorf("pebble: unknown table format: %q", errors.Safe(value))
2242+
// Tolerate unknown options, but log them.
2243+
if o.Logger != nil {
2244+
o.Logger.Infof("pebble: unknown table format: %q", errors.Safe(value))
2245+
}
2246+
return nil
22442247
}
2245-
case "table_property_collectors":
2246-
// No longer implemented; ignore.
22472248
case "validate_on_ingest":
22482249
o.Experimental.ValidateOnIngest, err = strconv.ParseBool(value)
22492250
case "wal_dir":
22502251
o.WALDir = value
22512252
case "wal_bytes_per_sync":
22522253
o.WALBytesPerSync, err = strconv.Atoi(value)
2253-
case "max_writer_concurrency":
2254-
// No longer implemented; ignore.
2255-
case "force_writer_parallelism":
2256-
// No longer implemented; ignore.
22572254
case "secondary_cache_size_bytes":
22582255
o.Experimental.SecondaryCacheSizeBytes, err = strconv.ParseInt(value, 10, 64)
22592256
case "create_on_shared":
@@ -2265,11 +2262,15 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
22652262
case "iterator_tracking_max_age":
22662263
o.Experimental.IteratorTracking.MaxAge, err = time.ParseDuration(value)
22672264
default:
2268-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
2265+
if hooks != nil && hooks.OnUnknown != nil {
2266+
hooks.OnUnknown(section+"."+key, value)
22692267
return nil
22702268
}
2271-
return errors.Errorf("pebble: unknown option: %s.%s",
2272-
errors.Safe(section), errors.Safe(key))
2269+
// Tolerate unknown options, but log them.
2270+
if o.Logger != nil {
2271+
o.Logger.Infof("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2272+
}
2273+
return nil
22732274
}
22742275
return err
22752276

@@ -2292,10 +2293,15 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
22922293
case "garbage_ratio_high_priority":
22932294
valSepPolicy.GarbageRatioHighPriority, err = strconv.ParseFloat(value, 64)
22942295
default:
2295-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
2296+
if hooks != nil && hooks.OnUnknown != nil {
2297+
hooks.OnUnknown(section+"."+key, value)
22962298
return nil
22972299
}
2298-
return errors.Errorf("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2300+
// Tolerate unknown options, but log them.
2301+
if o.Logger != nil {
2302+
o.Logger.Infof("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2303+
}
2304+
return nil
22992305
}
23002306
return err
23012307

@@ -2324,11 +2330,15 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
23242330
case "elevated_write_stall_threshold_lag":
23252331
o.WALFailover.ElevatedWriteStallThresholdLag, err = time.ParseDuration(value)
23262332
default:
2327-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
2333+
if hooks != nil && hooks.OnUnknown != nil {
2334+
hooks.OnUnknown(section+"."+key, value)
23282335
return nil
23292336
}
2330-
return errors.Errorf("pebble: unknown option: %s.%s",
2331-
errors.Safe(section), errors.Safe(key))
2337+
// Tolerate unknown options, but log them.
2338+
if o.Logger != nil {
2339+
o.Logger.Infof("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2340+
}
2341+
return nil
23322342
}
23332343
return err
23342344

@@ -2368,24 +2378,38 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
23682378
case "table":
23692379
l.FilterType = TableFilter
23702380
default:
2371-
return errors.Errorf("pebble: unknown filter type: %q", errors.Safe(value))
2381+
// Tolerate unknown options, but log them.
2382+
if o.Logger != nil {
2383+
o.Logger.Infof("pebble: unknown filter type: %q", errors.Safe(value))
2384+
}
2385+
return nil
23722386
}
23732387
case "index_block_size":
23742388
l.IndexBlockSize, err = strconv.Atoi(value)
23752389
case "target_file_size":
23762390
o.TargetFileSizes[index], err = strconv.ParseInt(value, 10, 64)
23772391
default:
2378-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
2392+
if hooks != nil && hooks.OnUnknown != nil {
2393+
hooks.OnUnknown(section+"."+key, value)
23792394
return nil
23802395
}
2381-
return errors.Errorf("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2396+
// Tolerate unknown options, but log them.
2397+
if o.Logger != nil {
2398+
o.Logger.Infof("pebble: unknown option: %s.%s", errors.Safe(section), errors.Safe(key))
2399+
}
2400+
return nil
23822401
}
23832402
return err
23842403
}
2385-
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
2404+
if hooks != nil && hooks.OnUnknown != nil {
2405+
hooks.OnUnknown(section+"."+key, value)
23862406
return nil
23872407
}
2388-
return errors.Errorf("pebble: unknown section %q or key %q", errors.Safe(section), errors.Safe(key))
2408+
// Tolerate unknown sections and keys, but log them.
2409+
if o.Logger != nil {
2410+
o.Logger.Infof("pebble: unknown section %q or key %q", errors.Safe(section), errors.Safe(key))
2411+
}
2412+
return nil
23892413
}
23902414
err := parseOptions(s, parseOptionsFuncs{
23912415
visitKeyValue: visitKeyValue,

0 commit comments

Comments
 (0)