Skip to content

Commit a08918c

Browse files
committed
db: datadriven testing for span policy enforcer
1 parent 43300c4 commit a08918c

File tree

4 files changed

+224
-0
lines changed

4 files changed

+224
-0
lines changed

compaction_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,27 @@ 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+
enforcer := newSpanPolicyEnforcer(d, SpanPolicyEnforcerOptions{})
1516+
enforcer.scanOnce()
1517+
return ""
1518+
1519+
case "pending-policy-enforcement":
1520+
// Show files pending policy enforcement compaction.
1521+
d.mu.Lock()
1522+
count := d.mu.compact.policyEnforcementFiles.Count()
1523+
var buf strings.Builder
1524+
fmt.Fprintf(&buf, "pending: %d\n", count)
1525+
for f, level := range d.mu.compact.policyEnforcementFiles.Ascending() {
1526+
fmt.Fprintf(&buf, " L%d: %s\n", level, f.TableNum)
1527+
}
1528+
d.mu.Unlock()
1529+
return buf.String()
1530+
15101531
case "set-span-policies":
15111532
var spanPolicies []SpanAndPolicy
15121533
for line := range crstrings.LinesSeq(td.Input) {
@@ -1552,6 +1573,11 @@ func runCompactionTest(
15521573
td.Fatalf(t, "parsing minimum-mvcc-garbage-size: %s", err)
15531574
}
15541575
policy.ValueStoragePolicy.MinimumMVCCGarbageSize = int(size)
1576+
case "prefer-fast-compression":
1577+
if len(parts) != 1 {
1578+
td.Fatalf(t, "expected prefer-fast-compression with no value, got: %s", arg)
1579+
}
1580+
policy.PreferFastCompression = true
15551581
default:
15561582
td.Fatalf(t, "unknown span policy arg: %s", arg)
15571583
}
@@ -1660,6 +1686,11 @@ func TestCompaction(t *testing.T) {
16601686
maxVersion: FormatNewest,
16611687
verbose: true,
16621688
},
1689+
"policy_enforcement": {
1690+
minVersion: FormatNewest,
1691+
maxVersion: FormatNewest,
1692+
cmp: DefaultComparer,
1693+
},
16631694
}
16641695
datadriven.Walk(t, "testdata/compaction", func(t *testing.T, path string) {
16651696
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 {

span_policy_enforcer.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,29 @@ func (p *spanPolicyEnforcer) markForEnforcement(f *manifest.TableMetadata, level
373373
// Trigger compaction scheduling.
374374
p.db.maybeScheduleCompaction()
375375
}
376+
377+
// scanOnce performs a complete scan through all files in the LSM, checking for
378+
// policy violations and marking files for enforcement. This is useful for
379+
// testing where we want to run a full scan without timing concerns.
380+
func (p *spanPolicyEnforcer) scanOnce() {
381+
p.db.mu.Lock()
382+
vers := p.db.mu.versions.currentVersion()
383+
p.db.mu.Unlock()
384+
385+
// Reset cursor to start from the beginning.
386+
p.cursor = policyEnforcerCursor{level: 0}
387+
388+
for {
389+
nextFile, level := p.nextFile(vers, p.cursor)
390+
if nextFile == nil {
391+
// Reached end of scan.
392+
break
393+
}
394+
395+
if p.checkPolicyViolation(nextFile) {
396+
p.markForEnforcement(nextFile, level)
397+
}
398+
399+
p.cursor = makePolicyCursorAfterFile(nextFile, level)
400+
}
401+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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+
7+
define compression=zstd-force
8+
L1
9+
a#10,SET:a b#10,SET:b
10+
L1
11+
m#10,SET:m n#10,SET:n
12+
L2
13+
x#5,SET:x z#5,SET:z
14+
----
15+
L1:
16+
000004:[a#10,SET-b#10,SET]
17+
000005:[m#10,SET-n#10,SET]
18+
L2:
19+
000006:[x#5,SET-z#5,SET]
20+
21+
# Configure span policy: keys >= "m" require fast compression.
22+
set-span-policies
23+
m,zzz prefer-fast-compression
24+
----
25+
26+
# Initially no pending enforcement files.
27+
pending-policy-enforcement
28+
----
29+
pending: 0
30+
31+
# Run the enforcer scan to detect violations and mark files.
32+
# Files 000005 (m-n) and 000006 (x-z) are in the policy span and use Zstd.
33+
scan-policy-violations
34+
----
35+
36+
# Verify files are now pending enforcement. The enforcer's run loop would pause
37+
# here until these are processed.
38+
pending-policy-enforcement
39+
----
40+
pending: 2
41+
L2: 000006
42+
L1: 000005
43+
44+
# Run compaction. The scheduler should pick up policy enforcement compactions.
45+
auto-compact
46+
----
47+
L1:
48+
000004:[a#10,SET-b#10,SET]
49+
000005:[m#10,SET-n#10,SET]
50+
L2:
51+
000007:[x#0,SET-z#0,SET]
52+
53+
# After auto-compact processes enforcement compactions, pending should be cleared.
54+
# (auto-compact may process one or both files depending on scheduling)
55+
pending-policy-enforcement
56+
----
57+
pending: 0
58+
59+
# Scan again to check for any remaining violations.
60+
scan-policy-violations
61+
----
62+
63+
# One file may still violate policy if not recompacted in first pass.
64+
pending-policy-enforcement
65+
----
66+
pending: 1
67+
L1: 000005
68+
69+
auto-compact
70+
----
71+
L1:
72+
000004:[a#10,SET-b#10,SET]
73+
000008:[m#0,SET-n#0,SET]
74+
L2:
75+
000007:[x#0,SET-z#0,SET]
76+
77+
# All enforcement compactions complete.
78+
pending-policy-enforcement
79+
----
80+
pending: 0
81+
82+
# Test interaction with manual compaction: a file marked for enforcement
83+
# gets moved by a manual compaction. Since the move keeps the same file
84+
# reference, the enforcement mark remains valid.
85+
86+
define compression=zstd-force
87+
L1
88+
a#10,SET:a b#10,SET:b
89+
L2
90+
m#10,SET:m n#10,SET:n
91+
L3
92+
x#5,SET:x z#5,SET:z
93+
----
94+
L1:
95+
000004:[a#10,SET-b#10,SET]
96+
L2:
97+
000005:[m#10,SET-n#10,SET]
98+
L3:
99+
000006:[x#5,SET-z#5,SET]
100+
101+
# Set policy and scan to mark files.
102+
set-span-policies
103+
m,zzz prefer-fast-compression
104+
----
105+
106+
scan-policy-violations
107+
----
108+
109+
# Manually compact the L2 file. This moves 000005 to L3 but keeps the same
110+
# file reference, so the enforcement mark remains valid.
111+
compact m-o L2
112+
----
113+
L1:
114+
000004:[a#10,SET-b#10,SET]
115+
L3:
116+
000005:[m#10,SET-n#10,SET]
117+
000006:[x#5,SET-z#5,SET]
118+
119+
# Auto-compact picks up the enforcement compaction for one of the marked files.
120+
# File 000006 is outside the policy span (x-z < m), so only 000005 was marked.
121+
auto-compact
122+
----
123+
L1:
124+
000004:[a#10,SET-b#10,SET]
125+
L3:
126+
000005:[m#10,SET-n#10,SET]
127+
000007:[x#0,SET-z#0,SET]
128+
129+
# Scan and compact again - this should compact file 000005.
130+
scan-policy-violations
131+
----
132+
133+
auto-compact
134+
----
135+
L1:
136+
000004:[a#10,SET-b#10,SET]
137+
L3:
138+
000008:[m#0,SET-n#0,SET]
139+
000007:[x#0,SET-z#0,SET]

0 commit comments

Comments
 (0)