Skip to content

Commit

Permalink
compaction: elision-only compactions for tables with only range keys
Browse files Browse the repository at this point in the history
Currently, a table is marked as eligible for elision-only compaction by
the `elisionOnlyAnnotator` under the following circumstances:

- the table's range deletion estimate is greater than or equal to 10% of
  the total table size, OR
- the number of deletions is greater than or equal to 10% of the table's
  total point key entries.

If a table contains only range keys, the second predicate is true (given
that `0 >= 0`). If the table contains a range key delete or a range key
unset, an elision-only compaction is possible (i.e. if there are no
spans underneath, or snapshots preventing the elision, etc.).

However, if the table contains just range key sets, the table is always
marked for elision, but an elision-only compaction has no effect. The
same elision-only compaction will continue to be scheduled, which can
tie up a compaction slot.

Update the elision-only compaction heuristics to only schedule such
compactions when there is at least one range deletion, range key
deletion, or range key unset. This requires storing an additional uint
on the table stats, which allows determining whether a table contains
only range key sets.
  • Loading branch information
nicktrav committed Jun 11, 2022
1 parent ae99f4f commit 2d7219a
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 0 deletions.
6 changes: 6 additions & 0 deletions compaction_picker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,12 @@ func (a elisionOnlyAnnotator) Accumulate(f *fileMetadata, dst interface{}) (inte
if !f.Stats.Valid {
return dst, false
}
// Tables without range dels, range key dels or range key unsets are not
// eligible for consideration for elision. Note the number of range key DELs +
// UNSETs can be computed using the number of range key SETs.
if f.Stats.NumDeletions+(f.Stats.NumRangeKeys-f.Stats.NumRangeKeySets) == 0 {
return dst, true
}
// Bottommost files are large and not worthwhile to compact just
// to remove a few tombstones. Consider a file ineligible if its
// own range deletions delete less than 10% of its data and its
Expand Down
1 change: 1 addition & 0 deletions compaction_picker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,7 @@ func TestCompactionOutputFileSize(t *testing.T) {
}
m.Stats.Valid = true
m.Stats.RangeDeletionsBytesEstimate = uint64(v)
m.Stats.NumDeletions = 1 // At least one range del responsible for the deletion bytes.
}
}
m.SmallestSeqNum = m.Smallest.SeqNum()
Expand Down
1 change: 1 addition & 0 deletions compaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,7 @@ func TestCompactionTombstones(t *testing.T) {
compactInfo = &info
},
},
FormatMajorVersion: FormatNewest,
}
var err error
d, err = runDBDefineCmd(td, opts)
Expand Down
1 change: 1 addition & 0 deletions data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,7 @@ func runTableStatsCmd(td *datadriven.TestData, d *DB) string {
fmt.Fprintf(&b, "num-entries: %d\n", f.Stats.NumEntries)
fmt.Fprintf(&b, "num-deletions: %d\n", f.Stats.NumDeletions)
fmt.Fprintf(&b, "num-range-keys: %d\n", f.Stats.NumRangeKeys)
fmt.Fprintf(&b, "num-range-key-sets: %d\n", f.Stats.NumRangeKeySets)
fmt.Fprintf(&b, "point-deletions-bytes-estimate: %d\n", f.Stats.PointDeletionsBytesEstimate)
fmt.Fprintf(&b, "range-deletions-bytes-estimate: %d\n", f.Stats.RangeDeletionsBytesEstimate)
return b.String()
Expand Down
5 changes: 5 additions & 0 deletions internal/manifest/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ type TableStats struct {
NumDeletions uint64
// NumRangeKeys is the total number of range keys in the table.
NumRangeKeys uint64
// NumRangeKeySets is the total number of range key sets in the table.
// NOTE: this field is currently used only for calculating the sum of range
// key DELs + UNSETs in the table, without the need for separate fields (i.e.
// it can be computed via subtraction).
NumRangeKeySets uint64
// Estimate of the total disk space that may be dropped by this table's
// point deletions by compacting them.
PointDeletionsBytesEstimate uint64
Expand Down
2 changes: 2 additions & 0 deletions table_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ func (d *DB) loadTableStats(
// additional stats that may provide improved heuristics for compaction
// picking.
stats.NumRangeKeys = r.Properties.NumRangeKeys()
stats.NumRangeKeySets = r.Properties.NumRangeKeySets
return
})
if err != nil {
Expand Down Expand Up @@ -535,6 +536,7 @@ func maybeSetStatsFromProperties(meta *fileMetadata, props *sstable.Properties)
NumEntries: props.NumEntries,
NumDeletions: props.NumDeletions,
NumRangeKeys: props.NumRangeKeys(),
NumRangeKeySets: props.NumRangeKeySets,
PointDeletionsBytesEstimate: pointEstimate,
RangeDeletionsBytesEstimate: 0,
}
Expand Down
1 change: 1 addition & 0 deletions testdata/compaction_delete_only_hints
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 26

Expand Down
135 changes: 135 additions & 0 deletions testdata/compaction_tombstones
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 2
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

Expand All @@ -36,6 +37,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 2
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

Expand All @@ -57,6 +59,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 26

Expand All @@ -80,6 +83,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

Expand Down Expand Up @@ -119,6 +123,7 @@ wait-pending-table-stats
num-entries: 6
num-deletions: 2
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 76

Expand Down Expand Up @@ -152,6 +157,7 @@ wait-pending-table-stats
num-entries: 11
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 149
range-deletions-bytes-estimate: 0

Expand Down Expand Up @@ -196,6 +202,7 @@ wait-pending-table-stats
num-entries: 5
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 16488

Expand Down Expand Up @@ -233,6 +240,7 @@ wait-pending-table-stats
num-entries: 3
num-deletions: 3
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 13167
range-deletions-bytes-estimate: 0

Expand All @@ -244,3 +252,130 @@ range-deletions-bytes-estimate: 0
maybe-compact
----
[JOB 100] compacted(default) L5 [000004] (794 B) + L6 [000006] (13 K) -> L6 [] (0 B), in 1.0s (2.0s total), output rate 0 B/s

# A table with range keys is only eligible for elision if it contains a
# RANGEKEYDEL or RANGEKEYUNSET.

define
L6
rangekey:a-b:{(#1,RANGEKEYDEL)}
----
6:
000004:[a#1,RANGEKEYDEL-b#72057594037927935,RANGEKEYDEL]

wait-pending-table-stats
000004
----
num-entries: 0
num-deletions: 0
num-range-keys: 1
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

maybe-compact
----
[JOB 100] compacted(elision-only) L6 [000004] (895 B) + L6 [] (0 B) -> L6 [] (0 B), in 1.0s (2.0s total), output rate 0 B/s

version
----

define
L6
rangekey:a-b:{(#1,RANGEKEYUNSET,@1)}
----
6:
000004:[a#1,RANGEKEYUNSET-b#72057594037927935,RANGEKEYUNSET]

wait-pending-table-stats
000004
----
num-entries: 0
num-deletions: 0
num-range-keys: 1
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

maybe-compact
----
[JOB 100] compacted(elision-only) L6 [000004] (899 B) + L6 [] (0 B) -> L6 [] (0 B), in 1.0s (2.0s total), output rate 0 B/s

version
----

define
L6
rangekey:a-b:{(#1,RANGEKEYSET,@1)}
----
6:
000004:[a#1,RANGEKEYSET-b#72057594037927935,RANGEKEYSET]

wait-pending-table-stats
000004
----
num-entries: 0
num-deletions: 0
num-range-keys: 1
num-range-key-sets: 1
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

maybe-compact
----
(none)

version
----
6:
000004:[a#1,RANGEKEYSET-b#72057594037927935,RANGEKEYSET]

# A slightly more complicated case than the one above. A table with three
# separate range key spans, each corresponding to one of the range key types.
# Snapshots initially prevent any elision.

define snapshots=(25,75)
L6
rangekey:a-b:{(#100,RANGEKEYDEL)}
rangekey:b-c:{(#50,RANGEKEYUNSET,@1)}
rangekey:c-d:{(#10,RANGEKEYSET,@1,foo)}
----
6:
000004:[a#100,RANGEKEYDEL-d#72057594037927935,RANGEKEYSET]

wait-pending-table-stats
000004
----
num-entries: 0
num-deletions: 0
num-range-keys: 3
num-range-key-sets: 1
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

# Snapshots prevent any elision-only compaction.
maybe-compact
----
(none)

# Closing the snapshot at 25, despite it being the earliest snapshot, does not
# schedule an elision-only compaction, as there is still a snapshot open below
# the highest seq num of the file (100).
close-snapshot
25
----
(none)

# Closing the last remaining snapshot triggers an elision-only compaction, as
# there is is nothing left keeping the RANGEKEYUNSET and RANGEKEYDEL around.
close-snapshot
75
----
[JOB 100] compacted(elision-only) L6 [000004] (941 B) + L6 [] (0 B) -> L6 [000005] (903 B), in 1.0s (2.0s total), output rate 903 B/s

# The only span remaining in the table is the RANGEKEYSET. The RANGEKEYDEL and
# RANGEKEYUNSET were elided.
version
----
6:
000005:[c#10,RANGEKEYSET-d#72057594037927935,RANGEKEYSET]
2 changes: 2 additions & 0 deletions testdata/ingest
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 0
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 0

Expand Down Expand Up @@ -348,6 +349,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 2
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 1666

Expand Down
2 changes: 2 additions & 0 deletions testdata/manual_compaction
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ wait-pending-table-stats
num-entries: 1
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 1552

Expand All @@ -105,6 +106,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 776

Expand Down
2 changes: 2 additions & 0 deletions testdata/manual_compaction_set_with_del
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ wait-pending-table-stats
num-entries: 1
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 1552

Expand All @@ -105,6 +106,7 @@ wait-pending-table-stats
num-entries: 2
num-deletions: 1
num-range-keys: 0
num-range-key-sets: 0
point-deletions-bytes-estimate: 0
range-deletions-bytes-estimate: 776

Expand Down
Loading

0 comments on commit 2d7219a

Please sign in to comment.