Skip to content

Commit 28f63bc

Browse files
committed
db: edit default heuristic determining levels eligible for compaction
When deciding whether a level is even eligible for compaction picking, we consider the 'compensated fill factor': a statistic about the size of a level plus its garbage relative to the level's ideal size given the level multiplier and database size. Previously, we considered a level eligible to be the input level of a compaction if the compensated fill factor is >= 1.0 AND the compensated fill factor divided by the next level's fill factor is also > 1.0. We've preserved this behavior for historical consistency but have no clear justification for it. Experimentally, we've observed instances where the LSM is known to contain significant garbage (>14%) but no compactions are pursued because most of the data is in L6 and L6's fill factor is positive (see cockroachdb/cockroach#151633). This commit adjusts the default behavior to only require a compensated score >= 1.0 for L0. Other levels only require a 'compensated fill factor' >= 1.0 to be eligible for compaction. A new experimental UseDeprecatedCompensatedScore Option is introduced to allow opting out of the new behavior at runtime. This option is intended as an escape hatch if we observe unintended side effects in production. Otherwise, it'll ultimately be removed.
1 parent 4d91c2c commit 28f63bc

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

compaction_picker.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,15 +1052,29 @@ func (p *compactionPickerByScore) calculateLevelScores(
10521052
score /= denominator
10531053
compensatedScore /= denominator
10541054
}
1055-
// The level requires compaction iff both compensatedFillFactor and
1055+
1056+
// We calculated a compensated score above by dividing the
1057+
// compensatedFillFactor by the next level's fill factor. Previous
1058+
// versions of Pebble had a default behavior of only considering levels
1059+
// with a compensatedScore >= 1.0 eligible for compaction. This wasn't a
1060+
// principled decision and has been experimentally observed to limit
1061+
// productive compactions that would reclaim disk space, but were
1062+
// prohibited because the output level's fill factor was > 1.0.
1063+
//
1064+
// We allow the use of old behavior through the
1065+
// UseDeprecatedCompensatedScore option, which if true, only considers
1066+
// the level eligible for compaction iff both compensatedFillFactor and
10561067
// compensatedScore are >= 1.0.
10571068
//
1058-
// TODO(radu): this seems ad-hoc. In principle, the state of other levels
1059-
// should not come into play when we're determining this level's eligibility
1060-
// for compaction. The score should take care of correctly prioritizing the
1061-
// levels.
1069+
// Otherwise, only L0 requires the compensatedScore to be >= 1.0; all
1070+
// other levels only require the compensatedFillFactor to be >= 1.0. L0
1071+
// is treated exceptionally out of concern that a large, LBase that's
1072+
// being compacted into the next level may prevent L0->Lbase compactions
1073+
// and attempting to pick L0 compactions may result in intra-L0
1074+
// compactions.
10621075
const compensatedScoreThreshold = 1.0
1063-
if compensatedScore < compensatedScoreThreshold {
1076+
if (level == 0 || p.opts.Experimental.UseDeprecatedCompensatedScore()) &&
1077+
compensatedScore < compensatedScoreThreshold {
10641078
// No need to compact this level; score stays 0.
10651079
continue
10661080
}

options.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,15 @@ type Options struct {
609609
// due to garbage.
610610
CompactionGarbageFractionForMaxConcurrency func() float64
611611

612+
// UseDeprecatedCompensatedScore is a temporary option to revert the
613+
// compaction picker to the previous behavior of only considering
614+
// compaction out of a level if its compensated fill factor divided by
615+
// the next level's fill factor is >= 1.0. The details of this setting
616+
// are documented within its use within the compaction picker.
617+
//
618+
// The default value is false.
619+
UseDeprecatedCompensatedScore func() bool
620+
612621
// IngestSplit, if it returns true, allows for ingest-time splitting of
613622
// existing sstables into two virtual sstables to allow ingestion sstables to
614623
// slot into a lower level than they otherwise would have.
@@ -1565,6 +1574,9 @@ func (o *Options) EnsureDefaults() {
15651574
if o.WALFailover != nil {
15661575
o.WALFailover.FailoverOptions.EnsureDefaults()
15671576
}
1577+
if o.Experimental.UseDeprecatedCompensatedScore == nil {
1578+
o.Experimental.UseDeprecatedCompensatedScore = func() bool { return false }
1579+
}
15681580
if o.Experimental.LevelMultiplier <= 0 {
15691581
o.Experimental.LevelMultiplier = defaultLevelMultiplier
15701582
}

0 commit comments

Comments
 (0)