Skip to content

Commit d37d2f4

Browse files
committed
pebble: materialize virtual tables only if backing contains >= 30% garbage
Virtual rewrite compactions should run more conservatively. This commit introduces a new option to configure the garbage ratio required by a backing table to trigger a virtual rewrite compaction. We also adjust the estimation of the data still referenced such that blob references are assumed to remain within the backing's virtual tables.
1 parent 347d5dc commit d37d2f4

File tree

5 files changed

+93
-21
lines changed

5 files changed

+93
-21
lines changed

compaction_picker.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,12 @@ func (p *compactionPickerByScore) pickVirtualRewriteCompaction(
17181718
// or selecting files that aren't contiguous in a level. Successfully materializing
17191719
// one of the backing's virtual table will also make the backing more likely to be
17201720
// picked again, since the space amp will increase.
1721-
_, vtablesByLevel := p.latestVersionState.virtualBackings.ReplacementCandidate()
1721+
referencedDataPct, _, vtablesByLevel := p.latestVersionState.virtualBackings.ReplacementCandidate()
1722+
1723+
if 1-referencedDataPct < p.opts.Experimental.VirtualTableRewriteUnreferencedFraction() {
1724+
return nil
1725+
}
1726+
17221727
for level, tables := range vtablesByLevel {
17231728
for _, vt := range tables {
17241729
if vt.IsCompacting() {

internal/manifest/testdata/virtual_backings/rewrite_candidates

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Test rewrite candidates heap with blob value sizes.
2+
# Blob value sizes should not be included in the unreferenced data.
23

34
# Add backings with values in blob files.
45
add n=1 size=100 blobValueSize=50
@@ -50,7 +51,7 @@ add-table n=1 size=50 table=1
5051
000003: size=150 refBlobValueSize=75 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
5152
000004: size=300 refBlobValueSize=150 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
5253
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
53-
rewrite candidates heap: 000001(33.3%)
54+
rewrite candidates heap: 000001(50.0%)
5455
unused virtual backings: 000002 000003 000004 000005
5556

5657
add-table n=2 size=10 table=2
@@ -61,7 +62,7 @@ add-table n=2 size=10 table=2
6162
000003: size=150 refBlobValueSize=75 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
6263
000004: size=300 refBlobValueSize=150 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
6364
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
64-
rewrite candidates heap: 000002(3.3%) 000001(33.3%)
65+
rewrite candidates heap: 000002(5.0%) 000001(50.0%)
6566
unused virtual backings: 000003 000004 000005
6667

6768
add-table n=3 size=90 table=3
@@ -72,7 +73,7 @@ add-table n=3 size=90 table=3
7273
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
7374
000004: size=300 refBlobValueSize=150 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
7475
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
75-
rewrite candidates heap: 000002(3.3%) 000001(33.3%) 000003(40.0%)
76+
rewrite candidates heap: 000002(5.0%) 000001(50.0%) 000003(60.0%)
7677
unused virtual backings: 000004 000005
7778

7879
add-table n=4 size=45 table=4
@@ -83,7 +84,7 @@ add-table n=4 size=45 table=4
8384
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
8485
000004: size=300 refBlobValueSize=150 useCount=1 protectionCount=0 virtualizedSize=45 tables: [000004]
8586
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
86-
rewrite candidates heap: 000002(3.3%) 000004(10.0%) 000003(40.0%) 000001(33.3%)
87+
rewrite candidates heap: 000002(5.0%) 000004(15.0%) 000003(60.0%) 000001(50.0%)
8788
unused virtual backings: 000005
8889

8990
add-table n=5 size=100 table=5
@@ -94,7 +95,7 @@ add-table n=5 size=100 table=5
9495
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
9596
000004: size=300 refBlobValueSize=150 useCount=1 protectionCount=0 virtualizedSize=45 tables: [000004]
9697
000005: size=250 refBlobValueSize=125 useCount=1 protectionCount=0 virtualizedSize=100 tables: [000005]
97-
rewrite candidates heap: 000002(3.3%) 000004(10.0%) 000003(40.0%) 000001(33.3%) 000005(26.7%)
98+
rewrite candidates heap: 000002(5.0%) 000004(15.0%) 000003(60.0%) 000001(50.0%) 000005(40.0%)
9899

99100
add-table n=2 size=80 table=6
100101
----
@@ -104,7 +105,7 @@ add-table n=2 size=80 table=6
104105
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
105106
000004: size=300 refBlobValueSize=150 useCount=1 protectionCount=0 virtualizedSize=45 tables: [000004]
106107
000005: size=250 refBlobValueSize=125 useCount=1 protectionCount=0 virtualizedSize=100 tables: [000005]
107-
rewrite candidates heap: 000004(10.0%) 000005(26.7%) 000003(40.0%) 000001(33.3%) 000002(30.0%)
108+
rewrite candidates heap: 000004(15.0%) 000005(40.0%) 000003(60.0%) 000001(50.0%) 000002(45.0%)
108109

109110
# Remove some tables to demonstrate heap updates.
110111
remove-table n=5 table=5
@@ -115,7 +116,7 @@ remove-table n=5 table=5
115116
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
116117
000004: size=300 refBlobValueSize=150 useCount=1 protectionCount=0 virtualizedSize=45 tables: [000004]
117118
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
118-
rewrite candidates heap: 000004(10.0%) 000002(30.0%) 000003(40.0%) 000001(33.3%)
119+
rewrite candidates heap: 000004(15.0%) 000002(45.0%) 000003(60.0%) 000001(50.0%)
119120
unused virtual backings: 000005
120121

121122
# Remove backing 4's table. Should remove from heap since virtualizedSize=0.
@@ -127,5 +128,5 @@ remove-table n=4 table=4
127128
000003: size=150 refBlobValueSize=75 useCount=1 protectionCount=0 virtualizedSize=90 tables: [000003]
128129
000004: size=300 refBlobValueSize=150 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
129130
000005: size=250 refBlobValueSize=125 useCount=0 protectionCount=0 virtualizedSize=0 tables: []
130-
rewrite candidates heap: 000002(30.0%) 000001(33.3%) 000003(40.0%)
131+
rewrite candidates heap: 000002(45.0%) 000001(50.0%) 000003(60.0%)
131132
unused virtual backings: 000004 000005

internal/manifest/virtual_backings.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ import (
7171
type VirtualBackings struct {
7272
m map[base.DiskFileNum]*backingWithMetadata
7373
// rewriteCandidates is a min heap of virtual backings ordered by
74-
// referencedDataPct. Used to pick a candidate for rewriting.
74+
// referencedDataFraction. Used to pick a candidate for rewriting.
7575
rewriteCandidates virtualBackingRewriteCandidatesHeap
7676

7777
// unused are all the backings in m that are not inUse(). Used for
@@ -108,9 +108,14 @@ type backingWithMetadata struct {
108108
heapIndex int
109109
}
110110

111-
func (bm *backingWithMetadata) referencedDataPct() float64 {
112-
return float64(bm.virtualizedSize) /
113-
float64(bm.backing.Size+bm.backing.ReferencedBlobValueSizeTotal)
111+
// referencedDataFraction returns the percentage of data in the backing that is
112+
// referenced by virtual tables.
113+
// TODO(xinhaoz): Note that the sizes here intentionally exclude referenced
114+
// blob value sizes of virtual tables and the backing table. When we are able
115+
// to track referenced blob values for virtual tables more accurately we should
116+
// include those sizes in this estimation.
117+
func (bm *backingWithMetadata) referencedDataFraction() float64 {
118+
return float64(bm.virtualizedSize) / float64(bm.backing.Size)
114119
}
115120

116121
// AddAndRef adds a new backing to the set and takes a reference on it. Another
@@ -295,18 +300,21 @@ func (bv *VirtualBackings) DiskFileNums() []base.DiskFileNum {
295300
// referenced by virtual tables to total size, along with the list of virtual
296301
// tables that use the backing. If there are no backings in the set, nil is
297302
// returned.
298-
func (bv *VirtualBackings) ReplacementCandidate() (*TableBacking, [NumLevels][]*TableMetadata) {
303+
func (bv *VirtualBackings) ReplacementCandidate() (
304+
referencedDataFraction float64,
305+
backing *TableBacking,
306+
tables [NumLevels][]*TableMetadata,
307+
) {
299308
if bv.rewriteCandidates.Len() == 0 {
300-
return nil, [NumLevels][]*TableMetadata{}
309+
return 0, nil, [NumLevels][]*TableMetadata{}
301310
}
302311
v := bv.rewriteCandidates.items[0]
303-
var tables [NumLevels][]*TableMetadata
304312
tableNums := slices.Sorted(maps.Keys(v.virtualTables))
305313
for _, t := range tableNums {
306314
tl := v.virtualTables[t]
307315
tables[tl.level] = append(tables[tl.level], tl.meta)
308316
}
309-
return v.backing, tables
317+
return v.referencedDataFraction(), v.backing, tables
310318
}
311319

312320
func (bv *VirtualBackings) String() string {
@@ -332,7 +340,7 @@ func (bv *VirtualBackings) String() string {
332340
if len(bv.rewriteCandidates.items) > 0 {
333341
fmt.Fprint(&buf, "rewrite candidates heap: ")
334342
for _, v := range bv.rewriteCandidates.items {
335-
fmt.Fprintf(&buf, "%s(%.1f%%) ", v.backing.DiskFileNum, v.referencedDataPct()*100)
343+
fmt.Fprintf(&buf, "%s(%.1f%%) ", v.backing.DiskFileNum, v.referencedDataFraction()*100)
336344
}
337345
fmt.Fprintf(&buf, "\n")
338346
}
@@ -382,7 +390,7 @@ func (v *virtualBackingRewriteCandidatesHeap) Len() int {
382390
func (v *virtualBackingRewriteCandidatesHeap) Less(i, j int) bool {
383391
// We want to rewrite backings with a high percentage of garbage first,
384392
// so we order the heap by ratio of data referenced in virtual tables.
385-
return v.items[i].referencedDataPct() < v.items[j].referencedDataPct()
393+
return v.items[i].referencedDataFraction() < v.items[j].referencedDataFraction()
386394
}
387395

388396
func (v *virtualBackingRewriteCandidatesHeap) Swap(i, j int) {

options.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ import (
3838
)
3939

4040
const (
41-
cacheDefaultSize = 8 << 20 // 8 MB
42-
defaultLevelMultiplier = 10
41+
cacheDefaultSize = 8 << 20 // 8 MB
42+
defaultLevelMultiplier = 10
43+
defaultVirtualTableUnreferencedFraction = 0.3
4344
)
4445

4546
// FilterType exports the base.FilterType type.
@@ -782,6 +783,15 @@ type Options struct {
782783

783784
// SpanPolicyFunc is used to determine the SpanPolicy for a key region.
784785
SpanPolicyFunc SpanPolicyFunc
786+
787+
// VirtualTableRewriteUnreferencedFraction configures the minimum fraction of
788+
// unreferenced data in a backing table required to trigger a virtual table
789+
// rewrite compaction. This is calculated as the ratio of unreferenced
790+
// data size to total backing file size. A value of 0.0 triggers
791+
// rewrites for any amount of unreferenced data. A value of 1.0 disables
792+
// virtual table rewrite compactions entirely. The default value is 0.30
793+
// (rewrite when >= 30% of backing data is unreferenced).
794+
VirtualTableRewriteUnreferencedFraction func() float64
785795
}
786796

787797
// Filters is a map from filter policy name to filter policy. It is used for
@@ -1631,6 +1641,9 @@ func (o *Options) EnsureDefaults() {
16311641
if o.Experimental.SpanPolicyFunc == nil {
16321642
o.Experimental.SpanPolicyFunc = func(startKey []byte) (SpanPolicy, []byte, error) { return SpanPolicy{}, nil, nil }
16331643
}
1644+
if o.Experimental.VirtualTableRewriteUnreferencedFraction == nil {
1645+
o.Experimental.VirtualTableRewriteUnreferencedFraction = func() float64 { return defaultVirtualTableUnreferencedFraction }
1646+
}
16341647
// TODO(jackson): Enable value separation by default once we have confidence
16351648
// in a default policy.
16361649

testdata/compaction/virtual_rewrite

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,48 @@ COMPRESSION
204204
snappy | 296B (CR=1.23) |
205205
----
206206
----
207+
208+
# Test virtual table rewrite unreferenced fraction threshold (default 30%).
209+
# This test creates a backing with low unreferenced ratio and verifies
210+
# that virtual rewrite compaction does not trigger.
211+
212+
define auto-compactions=off
213+
----
214+
215+
# Create a backing table
216+
batch
217+
set a value_a
218+
set b value_b
219+
set c value_c
220+
set d value_d
221+
----
222+
223+
flush
224+
----
225+
L0.0:
226+
000005:[a#10,SET-d#13,SET] seqnums:[10-13] points:[a#10,SET-d#13,SET] size:695
227+
228+
compact a-d
229+
----
230+
L6:
231+
000005:[a#10,SET-d#13,SET] seqnums:[10-13] points:[a#10,SET-d#13,SET] size:695
232+
233+
# Excise only a small portion to create low unreferenced fraction (~0.1).
234+
excise a.5 b.5
235+
----
236+
L6:
237+
000006(000005):[a#10,SET-a#10,SET] seqnums:[10-13] points:[a#10,SET-a#10,SET] size:113(695)
238+
000007(000005):[c#12,SET-d#13,SET] seqnums:[10-13] points:[c#12,SET-d#13,SET] size:113(695)
239+
240+
virtual-backings
241+
----
242+
1 virtual backings, total size 695:
243+
000005: size=695 refBlobValueSize=0 useCount=2 protectionCount=0 virtualizedSize=226 tables: [000006 000007]
244+
rewrite candidates heap: 000005(32.5%)
245+
246+
# Should NOT trigger virtual rewrite (0.1 unreferenced fraction < default 0.3 threshold)
247+
run-virtual-rewrite-compaction
248+
----
249+
L6:
250+
000008:[a#0,SET-a#0,SET] seqnums:[0-0] points:[a#0,SET-a#0,SET] size:658
251+
000007(000005):[c#12,SET-d#13,SET] seqnums:[10-13] points:[c#12,SET-d#13,SET] size:113(695)

0 commit comments

Comments
 (0)