Skip to content

Commit 47ae8c1

Browse files
committed
db: datadriven testing for span policy enforcer
1 parent 178e206 commit 47ae8c1

5 files changed

Lines changed: 226 additions & 1 deletion

File tree

compaction_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,31 @@ func runCompactionTest(
15071507
s := blobRewriteLog.String()
15081508
return s
15091509

1510+
case "scan-policy-violations":
1511+
// Run the span policy enforcer's scan to detect violations and mark files.
1512+
if d.opts.Experimental.SpanPolicyFunc == nil {
1513+
return "no span policy configured"
1514+
}
1515+
// Wait for table stats to be loaded so that table properties
1516+
// are available for violation detection.
1517+
d.waitTableStats()
1518+
1519+
enforcer := newSpanPolicyEnforcer(d, SpanPolicyEnforcerOptions{})
1520+
enforcer.scanOnce()
1521+
return ""
1522+
1523+
case "pending-policy-enforcement":
1524+
// Show files pending policy enforcement compaction.
1525+
d.mu.Lock()
1526+
count := d.mu.compact.policyEnforcementFiles.Count()
1527+
var buf strings.Builder
1528+
fmt.Fprintf(&buf, "pending: %d\n", count)
1529+
for f, level := range d.mu.compact.policyEnforcementFiles.Ascending() {
1530+
fmt.Fprintf(&buf, " L%d: %s\n", level, f.TableNum)
1531+
}
1532+
d.mu.Unlock()
1533+
return buf.String()
1534+
15101535
case "set-span-policies":
15111536
var spanPolicies []SpanAndPolicy
15121537
for line := range crstrings.LinesSeq(td.Input) {
@@ -1552,6 +1577,11 @@ func runCompactionTest(
15521577
td.Fatalf(t, "parsing minimum-mvcc-garbage-size: %s", err)
15531578
}
15541579
policy.ValueStoragePolicy.MinimumMVCCGarbageSize = int(size)
1580+
case "prefer-fast-compression":
1581+
if len(parts) != 1 {
1582+
td.Fatalf(t, "expected prefer-fast-compression with no value, got: %s", arg)
1583+
}
1584+
policy.PreferFastCompression = true
15551585
default:
15561586
td.Fatalf(t, "unknown span policy arg: %s", arg)
15571587
}
@@ -1660,6 +1690,11 @@ func TestCompaction(t *testing.T) {
16601690
maxVersion: FormatNewest,
16611691
verbose: true,
16621692
},
1693+
"policy_enforcement": {
1694+
minVersion: FormatNewest,
1695+
maxVersion: FormatNewest,
1696+
cmp: DefaultComparer,
1697+
},
16631698
}
16641699
datadriven.Walk(t, "testdata/compaction", func(t *testing.T, path string) {
16651700
filename := filepath.Base(path)

data_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/cockroachdb/errors"
2626
"github.com/cockroachdb/pebble/internal/base"
2727
"github.com/cockroachdb/pebble/internal/blobtest"
28+
"github.com/cockroachdb/pebble/internal/compression"
2829
"github.com/cockroachdb/pebble/internal/humanize"
2930
"github.com/cockroachdb/pebble/internal/keyspan"
3031
"github.com/cockroachdb/pebble/internal/manifest"
@@ -36,6 +37,7 @@ import (
3637
"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
3738
"github.com/cockroachdb/pebble/objstorage/remote"
3839
"github.com/cockroachdb/pebble/sstable"
40+
"github.com/cockroachdb/pebble/sstable/block"
3941
"github.com/cockroachdb/pebble/sstable/block/blockkind"
4042
"github.com/cockroachdb/pebble/sstable/tablefilters/bloom"
4143
"github.com/cockroachdb/pebble/valsep"
@@ -1899,6 +1901,32 @@ func parseDBOptionsArgs(opts *Options, args []datadriven.CmdArg) error {
18991901
Secondary: wal.Dir{FS: opts.FS, Dirname: cmdArg.Vals[0]},
19001902
}
19011903
opts.WALFailover.EnsureDefaults()
1904+
case "compression":
1905+
var profile block.CompressionProfile
1906+
switch cmdArg.Vals[0] {
1907+
case "zstd":
1908+
profile = *block.ZstdCompression
1909+
case "snappy":
1910+
profile = *block.SnappyCompression
1911+
case "none":
1912+
profile = *block.NoCompression
1913+
case "zstd-force":
1914+
// For testing: Zstd with MinReductionPercent=0 so even small
1915+
// values are stored compressed.
1916+
profile = block.CompressionProfile{
1917+
Name: "test-zstd-force",
1918+
DataBlocks: block.SimpleCompressionSetting(compression.ZstdLevel3),
1919+
ValueBlocks: block.SimpleCompressionSetting(compression.ZstdLevel3),
1920+
OtherBlocks: compression.ZstdLevel3,
1921+
MinReductionPercent: 0,
1922+
}
1923+
default:
1924+
return errors.Newf("unrecognized compression %q", cmdArg.Vals[0])
1925+
}
1926+
for i := range opts.Levels {
1927+
p := profile
1928+
opts.Levels[i].Compression = func() *block.CompressionProfile { return &p }
1929+
}
19021930
}
19031931
}
19041932
if len(spanPolicies) > 0 {

options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ type Options struct {
789789
// scans the LSM for span policy violations. If nil, the policy enforcer
790790
// is disabled.
791791
//
792-
// Default: nil (disabled).
792+
// Default is nil (disabled).
793793
SpanPolicyEnforcerOptions *SpanPolicyEnforcerOptions
794794

795795
// VirtualTableRewriteUnreferencedFraction configures the minimum fraction of

span_policy_enforcer.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,25 @@ func (s *spanPolicyEnforcer) markForEnforcement(f *manifest.TableMetadata, level
326326
// Trigger compaction scheduling.
327327
s.db.maybeScheduleCompaction()
328328
}
329+
330+
// scanOnce performs a complete scan through all files in the LSM, checking for
331+
// policy violations and marking files for enforcement. This is used only for
332+
// testing to run a full scan without pacing concerns.
333+
func (s *spanPolicyEnforcer) scanOnce() {
334+
// Reset cursor to start from the beginning.
335+
s.cursor = lsmScanCursor{level: 0}
336+
337+
for {
338+
nextFile, level, endOfScan := s.getNextFile(false /* waitForPending */)
339+
if endOfScan {
340+
break
341+
}
342+
if nextFile == nil {
343+
continue
344+
}
345+
346+
if s.checkPolicyViolation(nextFile) {
347+
s.markForEnforcement(nextFile, level)
348+
}
349+
}
350+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Test span policy enforcer detecting compression violations and triggering
2+
# policy enforcement compactions.
3+
4+
# Create files with Zstd compression using zstd-force (MinReductionPercent=0).
5+
# The span policy requires fast compression for keys >= "m".
6+
# We use long repetitive values to ensure they compress with Zstd.
7+
8+
define compression=zstd-force
9+
L1
10+
a#10,SET:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b#10,SET:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
11+
L1
12+
m#10,SET:mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm n#10,SET:nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
13+
L2
14+
x#5,SET:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx z#5,SET:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
15+
----
16+
L1:
17+
000004:[a#10,SET-b#10,SET]
18+
000005:[m#10,SET-n#10,SET]
19+
L2:
20+
000006:[x#5,SET-z#5,SET]
21+
22+
# Configure span policy: keys >= "m" require fast compression.
23+
set-span-policies
24+
m,zzz prefer-fast-compression
25+
----
26+
27+
# Initially no pending enforcement files.
28+
pending-policy-enforcement
29+
----
30+
pending: 0
31+
32+
# Run the enforcer scan to detect violations and mark files.
33+
# Files 000005 (m-n) and 000006 (x-z) are in the policy span and use Zstd.
34+
scan-policy-violations
35+
----
36+
37+
# Verify files are now pending enforcement. The enforcer's run loop would pause
38+
# here until these are processed.
39+
pending-policy-enforcement
40+
----
41+
pending: 2
42+
L2: 000006
43+
L1: 000005
44+
45+
# Run compaction. The scheduler should pick up policy enforcement compactions.
46+
auto-compact
47+
----
48+
L1:
49+
000004:[a#10,SET-b#10,SET]
50+
000005:[m#10,SET-n#10,SET]
51+
L2:
52+
000007:[x#0,SET-z#0,SET]
53+
54+
# After auto-compact processes enforcement compactions, pending should be cleared.
55+
# (auto-compact may process one or both files depending on scheduling)
56+
pending-policy-enforcement
57+
----
58+
pending: 0
59+
60+
# Scan again to check for any remaining violations.
61+
scan-policy-violations
62+
----
63+
64+
# One file may still violate policy if not recompacted in first pass.
65+
pending-policy-enforcement
66+
----
67+
pending: 1
68+
L1: 000005
69+
70+
auto-compact
71+
----
72+
L1:
73+
000004:[a#10,SET-b#10,SET]
74+
000008:[m#0,SET-n#0,SET]
75+
L2:
76+
000007:[x#0,SET-z#0,SET]
77+
78+
# All enforcement compactions complete.
79+
pending-policy-enforcement
80+
----
81+
pending: 0
82+
83+
# Test interaction with manual compaction: a file marked for enforcement
84+
# gets moved by a manual compaction. Since the move keeps the same file
85+
# reference, the enforcement mark remains valid.
86+
87+
define compression=zstd-force
88+
L1
89+
a#10,SET:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b#10,SET:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
90+
L2
91+
m#10,SET:mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm n#10,SET:nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
92+
L3
93+
x#5,SET:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx z#5,SET:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
94+
----
95+
L1:
96+
000004:[a#10,SET-b#10,SET]
97+
L2:
98+
000005:[m#10,SET-n#10,SET]
99+
L3:
100+
000006:[x#5,SET-z#5,SET]
101+
102+
# Set policy and scan to mark files.
103+
set-span-policies
104+
m,zzz prefer-fast-compression
105+
----
106+
107+
scan-policy-violations
108+
----
109+
110+
# Manually compact the L2 file. This moves 000005 to L3 but keeps the same
111+
# file reference, so the enforcement mark remains valid.
112+
compact m-o L2
113+
----
114+
L1:
115+
000004:[a#10,SET-b#10,SET]
116+
L3:
117+
000005:[m#10,SET-n#10,SET]
118+
000006:[x#5,SET-z#5,SET]
119+
120+
# Auto-compact picks up the enforcement compaction for one of the marked files.
121+
# File 000006 is outside the policy span (x-z < m), so only 000005 was marked.
122+
auto-compact
123+
----
124+
L1:
125+
000004:[a#10,SET-b#10,SET]
126+
L3:
127+
000005:[m#10,SET-n#10,SET]
128+
000007:[x#0,SET-z#0,SET]
129+
130+
# Scan and compact again. This should compact file 000005.
131+
scan-policy-violations
132+
----
133+
134+
auto-compact
135+
----
136+
L1:
137+
000004:[a#10,SET-b#10,SET]
138+
L3:
139+
000008:[m#0,SET-n#0,SET]
140+
000007:[x#0,SET-z#0,SET]

0 commit comments

Comments
 (0)