Skip to content

Commit ff44e8a

Browse files
committed
pebble: clarify some multilevel compaction areas
This commit adds some comments to some of the existing ML compaction code. It also removes the `extraLevels` fields from the pickedCompaction struct, since we can read `inputs` directly. Closes: #4511
1 parent 268e3fa commit ff44e8a

File tree

3 files changed

+61
-58
lines changed

3 files changed

+61
-58
lines changed

compaction.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,15 @@ func newCompaction(
420420
if pc.startLevel.l0SublevelInfo != nil {
421421
c.startLevel.l0SublevelInfo = pc.startLevel.l0SublevelInfo
422422
}
423-
c.outputLevel = &c.inputs[1]
424423

425-
if len(pc.extraLevels) > 0 {
426-
c.extraLevels = pc.extraLevels
427-
c.outputLevel = &c.inputs[len(c.inputs)-1]
424+
c.outputLevel = &c.inputs[len(c.inputs)-1]
425+
426+
if len(pc.inputs) > 2 {
427+
// TODO(xinhaoz): Look into removing extraLevels on the compaction struct.
428+
c.extraLevels = make([]*compactionLevel, 0, len(pc.inputs)-2)
429+
for i := 1; i < len(pc.inputs)-1; i++ {
430+
c.extraLevels = append(c.extraLevels, &c.inputs[i])
431+
}
428432
}
429433
// Compute the set of outputLevel+1 files that overlap this compaction (these
430434
// are the grandparent sstables).

compaction_picker.go

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,8 @@ type pickedCompaction struct {
181181
// - in multilevel compaction, the output level is the lowest level involved in
182182
// the compaction
183183
outputLevel *compactionLevel
184-
// extraLevels contain additional levels in between the input and output
185-
// levels that get compacted in multi level compactions
186-
extraLevels []*compactionLevel
187-
inputs []compactionLevel
184+
// inputs contain levels involved in the compaction in ascending order
185+
inputs []compactionLevel
188186
// LBase at the time of compaction picking. Might be uninitialized for
189187
// intra-L0 compactions.
190188
baseLevel int
@@ -316,7 +314,6 @@ func (pc *pickedCompaction) String() string {
316314
builder.WriteString(fmt.Sprintf(`inputs=%s, `, pc.inputs))
317315
builder.WriteString(fmt.Sprintf(`startlevel=%s, `, pc.startLevel))
318316
builder.WriteString(fmt.Sprintf(`outputLevel=%s, `, pc.outputLevel))
319-
builder.WriteString(fmt.Sprintf(`extraLevels=%s, `, pc.extraLevels))
320317
builder.WriteString(fmt.Sprintf(`l0SublevelInfo=%s, `, pc.startLevel.l0SublevelInfo))
321318
builder.WriteString(fmt.Sprintf(`lcf=%s`, pc.lcf))
322319
return builder.String()
@@ -348,15 +345,12 @@ func (pc *pickedCompaction) clone() *pickedCompaction {
348345
}
349346

350347
newPC.inputs = make([]compactionLevel, len(pc.inputs))
351-
newPC.extraLevels = make([]*compactionLevel, 0, len(pc.extraLevels))
352348
for i := range pc.inputs {
353349
newPC.inputs[i] = pc.inputs[i].Clone()
354350
if i == 0 {
355351
newPC.startLevel = &newPC.inputs[i]
356352
} else if i == len(pc.inputs)-1 {
357353
newPC.outputLevel = &newPC.inputs[i]
358-
} else {
359-
newPC.extraLevels = append(newPC.extraLevels, &newPC.inputs[i])
360354
}
361355
}
362356

@@ -394,51 +388,56 @@ func (pc *pickedCompaction) maybeExpandBounds(smallest InternalKey, largest Inte
394388
}
395389
}
396390

397-
// setupInputs returns true if a compaction has been set up. It returns false if
398-
// a concurrent compaction is occurring on the start or output level files.
391+
// setupInputs returns true if a compaction has been set up using the provided inputLevel and
392+
// pc.outputLevel. It returns false if a concurrent compaction is occurring on the start or
393+
// output level files. Note that inputLevel is not necessarily pc.startLevel. In multiLevel
394+
// compactions, inputs are set by calling setupInputs once for each adjacent pair of levels.
395+
// This will preserve level invariants when expanding the compaction. pc.smallest and pc.largest
396+
// will be updated to reflect the key range of the inputs.
399397
func (pc *pickedCompaction) setupInputs(
400398
opts *Options,
401399
diskAvailBytes uint64,
402-
startLevel *compactionLevel,
400+
inputLevel *compactionLevel,
403401
problemSpans *problemspans.ByLevel,
404402
) bool {
405-
// maxExpandedBytes is the maximum size of an expanded compaction. If
406-
// growing a compaction results in a larger size, the original compaction
407-
// is used instead.
408-
maxExpandedBytes := expandedCompactionByteSizeLimit(
409-
opts, adjustedOutputLevel(pc.outputLevel.level, pc.baseLevel), diskAvailBytes,
410-
)
411-
412-
if !canCompactTables(startLevel.files, startLevel.level, problemSpans) {
403+
if !canCompactTables(inputLevel.files, inputLevel.level, problemSpans) {
413404
return false
414405
}
415406

416-
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp, startLevel.files.All()))
407+
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp, inputLevel.files.All()))
417408

418-
// Determine the sstables in the output level which overlap with the input
419-
// sstables. No need to do this for intra-L0 compactions; outputLevel.files is
420-
// left empty for those.
421-
if startLevel.level != pc.outputLevel.level {
409+
// Setup output files and attempt to grow the inputLevel files with
410+
// the expanded key range. No need to do this for intra-L0 compactions;
411+
// outputLevel.files is left empty for those.
412+
if inputLevel.level != pc.outputLevel.level {
413+
// Determine the sstables in the output level which overlap with the compaction
414+
// key range.
422415
pc.outputLevel.files = pc.version.Overlaps(pc.outputLevel.level, pc.userKeyBounds())
423416
if !canCompactTables(pc.outputLevel.files, pc.outputLevel.level, problemSpans) {
424417
return false
425418
}
426419

427-
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp,
428-
startLevel.files.All(), pc.outputLevel.files.All()))
429-
}
420+
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp, pc.outputLevel.files.All()))
430421

431-
// Grow the sstables in startLevel.level as long as it doesn't affect the number
432-
// of sstables included from pc.outputLevel.level.
433-
if pc.lcf != nil && startLevel.level == 0 && pc.outputLevel.level != 0 {
434-
pc.growL0ForBase(maxExpandedBytes)
435-
} else if pc.grow(pc.smallest, pc.largest, maxExpandedBytes, startLevel, problemSpans) {
436-
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp,
437-
startLevel.files.All(), pc.outputLevel.files.All()))
422+
// maxExpandedBytes is the maximum size of an expanded compaction. If
423+
// growing a compaction results in a larger size, the original compaction
424+
// is used instead.
425+
maxExpandedBytes := expandedCompactionByteSizeLimit(
426+
opts, adjustedOutputLevel(pc.outputLevel.level, pc.baseLevel), diskAvailBytes,
427+
)
428+
429+
// Grow the sstables in inputLevel.level as long as it doesn't affect the number
430+
// of sstables included from pc.outputLevel.level.
431+
if pc.lcf != nil && inputLevel.level == 0 {
432+
pc.growL0ForBase(maxExpandedBytes)
433+
} else if pc.grow(pc.smallest, pc.largest, maxExpandedBytes, inputLevel, problemSpans) {
434+
// inputLevel was expanded, adjust key range if necessary.
435+
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp, inputLevel.files.All()))
436+
}
438437
}
439438

440-
if pc.startLevel.level == 0 {
441-
// We don't change the input files for the compaction beyond this point.
439+
if inputLevel.level == 0 {
440+
// If L0 is involved, it should always be the startLevel of the compaction.
442441
pc.startLevel.l0SublevelInfo = generateSublevelInfo(pc.cmp, pc.startLevel.files)
443442
}
444443

@@ -451,34 +450,35 @@ func (pc *pickedCompaction) setupInputs(
451450
func (pc *pickedCompaction) grow(
452451
sm, la InternalKey,
453452
maxExpandedBytes uint64,
454-
startLevel *compactionLevel,
453+
inputLevel *compactionLevel,
455454
problemSpans *problemspans.ByLevel,
456455
) bool {
457456
if pc.outputLevel.files.Empty() {
458457
return false
459458
}
460-
grow0 := pc.version.Overlaps(startLevel.level, base.UserKeyBoundsFromInternal(sm, la))
461-
if !canCompactTables(grow0, startLevel.level, problemSpans) {
459+
expandedInputLevel := pc.version.Overlaps(inputLevel.level, base.UserKeyBoundsFromInternal(sm, la))
460+
if !canCompactTables(expandedInputLevel, inputLevel.level, problemSpans) {
462461
return false
463462
}
464-
if grow0.Len() <= startLevel.files.Len() {
463+
if expandedInputLevel.Len() <= inputLevel.files.Len() {
465464
return false
466465
}
467-
if grow0.AggregateSizeSum()+pc.outputLevel.files.AggregateSizeSum() >= maxExpandedBytes {
466+
if expandedInputLevel.AggregateSizeSum()+pc.outputLevel.files.AggregateSizeSum() >= maxExpandedBytes {
468467
return false
469468
}
469+
// Check that expanding the input level does not change the number of overlapping files in output level.
470470
// We need to include the outputLevel iter because without it, in a multiLevel scenario,
471-
// sm1 and la1 could shift the output level keyspace when pc.outputLevel.files is set to grow1.
472-
sm1, la1 := manifest.KeyRange(pc.cmp, grow0.All(), pc.outputLevel.files.All())
473-
grow1 := pc.version.Overlaps(pc.outputLevel.level, base.UserKeyBoundsFromInternal(sm1, la1))
474-
if grow1.Len() != pc.outputLevel.files.Len() {
471+
// expandedInputLevel's key range not fully cover all files currently in pc.outputLevel,
472+
// since pc.outputLevel was created using the entire key range which includes higher levels.
473+
expandedOutputLevel := pc.version.Overlaps(pc.outputLevel.level,
474+
base.UserKeyBoundsFromInternal(manifest.KeyRange(pc.cmp, expandedInputLevel.All(), pc.outputLevel.files.All())))
475+
if expandedOutputLevel.Len() != pc.outputLevel.files.Len() {
475476
return false
476477
}
477-
if !canCompactTables(grow1, pc.outputLevel.level, problemSpans) {
478+
if !canCompactTables(expandedOutputLevel, pc.outputLevel.level, problemSpans) {
478479
return false
479480
}
480-
startLevel.files = grow0
481-
pc.outputLevel.files = grow1
481+
inputLevel.files = expandedInputLevel
482482
return true
483483
}
484484

@@ -565,7 +565,7 @@ func (pc *pickedCompaction) estimatedInputSize() uint64 {
565565
return bytesToCompact
566566
}
567567

568-
// setupMultiLevelCandidated returns true if it successfully added another level
568+
// setupMultiLevelCandidate returns true if it successfully added another level
569569
// to the compaction.
570570
func (pc *pickedCompaction) setupMultiLevelCandidate(opts *Options, diskAvailBytes uint64) bool {
571571
pc.inputs = append(pc.inputs, compactionLevel{level: pc.outputLevel.level + 1})
@@ -574,9 +574,8 @@ func (pc *pickedCompaction) setupMultiLevelCandidate(opts *Options, diskAvailByt
574574
// - startLevel and outputLevel pointers may be obsolete after appending to pc.inputs.
575575
// - push outputLevel to extraLevels and move the new level to outputLevel
576576
pc.startLevel = &pc.inputs[0]
577-
pc.extraLevels = []*compactionLevel{&pc.inputs[1]}
578577
pc.outputLevel = &pc.inputs[2]
579-
return pc.setupInputs(opts, diskAvailBytes, pc.extraLevels[len(pc.extraLevels)-1], nil /* TODO(radu) */)
578+
return pc.setupInputs(opts, diskAvailBytes, &pc.inputs[1], nil /* TODO(radu) */)
580579
}
581580

582581
// canCompactTables returns true if the tables in the level slice are not
@@ -1947,8 +1946,8 @@ func newPickedManualCompaction(
19471946
return nil, false
19481947
}
19491948
if pc.outputLevel.level != outputLevel {
1950-
if len(pc.extraLevels) > 0 {
1951-
// multilevel compactions relax this invariant
1949+
if len(pc.inputs) > 2 {
1950+
// Multilevel compactions relax this invariant.
19521951
} else {
19531952
panic("pebble: compaction picked unexpected output level")
19541953
}

compaction_picker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ func TestPickedCompactionSetupInputs(t *testing.T) {
11521152
}
11531153

11541154
if initMultiLevel {
1155-
extraLevel := pc.extraLevels[0].level
1155+
extraLevel := pc.inputs[1].level
11561156
fmt.Fprintf(&buf, "init-multi-level(%d,%d,%d)\n", pc.startLevel.level, extraLevel,
11571157
pc.outputLevel.level)
11581158
fmt.Fprintf(&buf, "Original WriteAmp %.2f; ML WriteAmp %.2f\n", origPC.predictedWriteAmp(), pc.predictedWriteAmp())

0 commit comments

Comments
 (0)