Skip to content

Commit f213f58

Browse files
committed
options: make TargetByteDeletionRate dynamically configurable
Change this setting to a function. This change will be backported to older releases, as it provides an important "escape hatch" if delete pacing goes wrong. Informs #5424
1 parent a5edd16 commit f213f58

File tree

9 files changed

+61
-16
lines changed

9 files changed

+61
-16
lines changed

compaction_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3108,7 +3108,7 @@ func TestSharedObjectDeletePacing(t *testing.T) {
31083108
"": remote.NewInMem(),
31093109
})
31103110
opts.Experimental.CreateOnShared = remote.CreateOnSharedAll
3111-
opts.TargetByteDeletionRate = 1
3111+
opts.TargetByteDeletionRate = func() int { return 1 }
31123112
opts.Logger = testutils.Logger{T: t}
31133113

31143114
d, err := Open("", &opts)

metamorphic/options.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,8 @@ func RandomOptions(rng *rand.Rand, kf KeyFormat, cfg RandomOptionsCfg) *TestOpti
689689
opts.FormatMajorVersion += pebble.FormatMajorVersion(rng.IntN(n + 1))
690690
opts.Experimental.L0CompactionConcurrency = 1 + rng.IntN(4) // 1-4
691691
opts.Experimental.LevelMultiplier = 5 << rng.IntN(7) // 5 - 320
692-
opts.TargetByteDeletionRate = 1 << uint(20+rng.IntN(10)) // 1MB - 1GB
692+
targetByteDeletionRate := 1 << uint(20+rng.IntN(10)) // 1MB - 1GB
693+
opts.TargetByteDeletionRate = func() int { return targetByteDeletionRate }
693694
opts.Experimental.ValidateOnIngest = rng.IntN(2) != 0
694695
opts.L0CompactionThreshold = 1 + rng.IntN(100) // 1 - 100
695696
opts.L0CompactionFileThreshold = 1 << rng.IntN(11) // 1 - 1024

metamorphic/options_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func TestOptionsRoundtrip(t *testing.T) {
7575
"EventListener:",
7676
"CompactionConcurrencyRange:",
7777
"MaxConcurrentDownloads:",
78+
"TargetByteDeletionRate:",
7879
"Experimental.CompactionGarbageFractionForMaxConcurrency:",
7980
"Experimental.DisableIngestAsFlushable:",
8081
"Experimental.EnableColumnarBlocks:",
@@ -116,6 +117,7 @@ func TestOptionsRoundtrip(t *testing.T) {
116117
expectEqualFn(t, o.Opts.Experimental.IngestSplit, parsed.Opts.Experimental.IngestSplit)
117118
expectEqualFn(t, o.Opts.Experimental.CompactionGarbageFractionForMaxConcurrency, parsed.Opts.Experimental.CompactionGarbageFractionForMaxConcurrency)
118119
expectEqualFn(t, o.Opts.Experimental.ValueSeparationPolicy, parsed.Opts.Experimental.ValueSeparationPolicy)
120+
expectEqualFn(t, o.Opts.TargetByteDeletionRate, parsed.Opts.TargetByteDeletionRate)
119121

120122
expBaseline, expUpper := o.Opts.CompactionConcurrencyRange()
121123
parsedBaseline, parsedUpper := parsed.Opts.CompactionConcurrencyRange()

obsolete_files.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func openCleanupManager(
9898
deletePacer: newDeletionPacer(
9999
crtime.NowMono(),
100100
opts.FreeSpaceThresholdBytes,
101-
int64(opts.TargetByteDeletionRate),
101+
opts.TargetByteDeletionRate,
102102
opts.FreeSpaceTimeframe,
103103
opts.ObsoleteBytesMaxRatio,
104104
opts.ObsoleteBytesTimeframe,

obsolete_files_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func TestCleanupManagerCloseWithPacing(t *testing.T) {
147147
opts := &Options{
148148
FS: mem,
149149
FreeSpaceThresholdBytes: 1,
150-
TargetByteDeletionRate: 1024, // 1 KB/s - slow pacing
150+
TargetByteDeletionRate: func() int { return 1024 }, // 1 KB/s - slow pacing
151151
}
152152
opts.EnsureDefaults()
153153

options.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,8 +1152,8 @@ type Options struct {
11521152
// This value is only a best-effort target; the effective rate can be
11531153
// higher if deletions are falling behind or disk space is running low.
11541154
//
1155-
// Setting this to 0 disables deletion pacing, which is also the default.
1156-
TargetByteDeletionRate int
1155+
// A returned value of 0 disables deletion pacing (this is also the default).
1156+
TargetByteDeletionRate func() int
11571157

11581158
// FreeSpaceThresholdBytes specifies the minimum amount of free disk space that Pebble
11591159
// attempts to maintain. If free disk space drops below this threshold, deletions
@@ -1511,6 +1511,10 @@ func (o *Options) EnsureDefaults() {
15111511
o.Cleaner = DeleteCleaner{}
15121512
}
15131513

1514+
if o.TargetByteDeletionRate == nil {
1515+
o.TargetByteDeletionRate = func() int { return 0 }
1516+
}
1517+
15141518
if o.FreeSpaceThresholdBytes == 0 {
15151519
o.FreeSpaceThresholdBytes = 16 << 30 // 16 GB
15161520
}
@@ -1797,7 +1801,7 @@ func (o *Options) String() string {
17971801
fmt.Fprintf(&buf, " max_open_files=%d\n", o.MaxOpenFiles)
17981802
fmt.Fprintf(&buf, " mem_table_size=%d\n", o.MemTableSize)
17991803
fmt.Fprintf(&buf, " mem_table_stop_writes_threshold=%d\n", o.MemTableStopWritesThreshold)
1800-
fmt.Fprintf(&buf, " min_deletion_rate=%d\n", o.TargetByteDeletionRate)
1804+
fmt.Fprintf(&buf, " min_deletion_rate=%d\n", o.TargetByteDeletionRate())
18011805
fmt.Fprintf(&buf, " free_space_threshold_bytes=%d\n", o.FreeSpaceThresholdBytes)
18021806
fmt.Fprintf(&buf, " free_space_timeframe=%s\n", o.FreeSpaceTimeframe.String())
18031807
fmt.Fprintf(&buf, " obsolete_bytes_max_ratio=%f\n", o.ObsoleteBytesMaxRatio)
@@ -2168,7 +2172,11 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
21682172
// Do nothing; option existed in older versions of pebble, and
21692173
// may be meaningful again eventually.
21702174
case "min_deletion_rate":
2171-
o.TargetByteDeletionRate, err = strconv.Atoi(value)
2175+
var rate int
2176+
rate, err = strconv.Atoi(value)
2177+
if err == nil {
2178+
o.TargetByteDeletionRate = func() int { return rate }
2179+
}
21722180
case "free_space_threshold_bytes":
21732181
o.FreeSpaceThresholdBytes, err = strconv.ParseUint(value, 10, 64)
21742182
case "free_space_timeframe":

options_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ func TestOptionsParse(t *testing.T) {
398398
opts.FlushDelayDeleteRange = 10 * time.Second
399399
opts.FlushDelayRangeKey = 11 * time.Second
400400
opts.Experimental.LevelMultiplier = 5
401-
opts.TargetByteDeletionRate = 200
401+
opts.TargetByteDeletionRate = func() int { return 200 }
402402
opts.WALFailover = &WALFailoverOptions{
403403
Secondary: wal.Dir{Dirname: "wal_secondary", FS: vfs.Default},
404404
}

pacer.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type deletionPacer struct {
4646
history history
4747
}
4848

49-
targetByteDeletionRate int64
49+
targetByteDeletionRate func() int
5050

5151
getInfo func() deletionPacerInfo
5252
}
@@ -62,7 +62,7 @@ const deletePacerHistory = 5 * time.Minute
6262
func newDeletionPacer(
6363
now crtime.Mono,
6464
freeSpaceThreshold uint64,
65-
targetByteDeletionRate int64,
65+
targetByteDeletionRate func() int,
6666
freeSpaceTimeframe time.Duration,
6767
obsoleteBytesMaxRatio float64,
6868
obsoleteBytesTimeframe time.Duration,
@@ -99,12 +99,13 @@ func (p *deletionPacer) ReportDeletion(now crtime.Mono, bytesToDelete uint64) {
9999
//
100100
// PacingDelay is thread-safe.
101101
func (p *deletionPacer) PacingDelay(now crtime.Mono, bytesToDelete uint64) (waitSeconds float64) {
102-
if p.targetByteDeletionRate == 0 {
102+
targetByteDeletionRate := p.targetByteDeletionRate()
103+
if targetByteDeletionRate == 0 {
103104
// Pacing disabled.
104105
return 0.0
105106
}
106107

107-
baseRate := float64(p.targetByteDeletionRate)
108+
baseRate := float64(targetByteDeletionRate)
108109
// If recent deletion rate is more than our target, use that so that we don't
109110
// fall behind.
110111
historicRate := func() float64 {

pacer_test.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ import (
1010
"math"
1111
"math/rand/v2"
1212
"slices"
13+
"sync/atomic"
1314
"testing"
1415
"time"
1516

1617
"github.com/cockroachdb/crlib/crtime"
1718
"github.com/stretchr/testify/require"
1819
)
1920

21+
const MB = 1 << 20
22+
const GB = 1 << 30
23+
2024
func TestDeletionPacer(t *testing.T) {
21-
const MB = 1 << 20
22-
const GB = 1 << 30
2325
testCases := []struct {
2426
freeBytes uint64
2527
obsoleteBytes uint64
@@ -126,7 +128,7 @@ func TestDeletionPacer(t *testing.T) {
126128
pacer := newDeletionPacer(
127129
start,
128130
opts.FreeSpaceThresholdBytes,
129-
100*MB,
131+
func() int { return 100 * MB },
130132
opts.FreeSpaceTimeframe,
131133
opts.ObsoleteBytesMaxRatio,
132134
opts.ObsoleteBytesTimeframe,
@@ -142,6 +144,37 @@ func TestDeletionPacer(t *testing.T) {
142144
}
143145
}
144146

147+
func TestDeletionPacerCfgChange(t *testing.T) {
148+
getInfo := func() deletionPacerInfo {
149+
return deletionPacerInfo{
150+
freeBytes: 100 * GB,
151+
liveBytes: 10 * GB,
152+
obsoleteBytes: 10 * MB,
153+
}
154+
}
155+
156+
var targetRate atomic.Int32
157+
targetRate.Store(100 * MB)
158+
159+
start := crtime.NowMono()
160+
var opts Options
161+
opts.EnsureDefaults()
162+
pacer := newDeletionPacer(
163+
start,
164+
opts.FreeSpaceThresholdBytes,
165+
func() int { return int(targetRate.Load()) },
166+
opts.FreeSpaceTimeframe,
167+
opts.ObsoleteBytesMaxRatio,
168+
opts.ObsoleteBytesTimeframe,
169+
getInfo,
170+
)
171+
require.InDelta(t, 1.0/100, pacer.PacingDelay(start, 1*MB), 1e-4)
172+
targetRate.Store(200 * MB)
173+
require.InDelta(t, 1.0/200, pacer.PacingDelay(start, 1*MB), 1e-4)
174+
targetRate.Store(0)
175+
require.Equal(t, 0.0, pacer.PacingDelay(start, 1*MB), 1e-4)
176+
}
177+
145178
// TestDeletionPacerHistory tests the history helper by crosschecking Sum()
146179
// against a naive implementation.
147180
func TestDeletionPacerHistory(t *testing.T) {

0 commit comments

Comments
 (0)