Skip to content

Commit 2f4ff3d

Browse files
committed
db: datadriven testing for span policy enforcer
1 parent 2404e1b commit 2f4ff3d

File tree

5 files changed

+226
-1
lines changed

5 files changed

+226
-1
lines changed

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 []SpanPolicy
15121537
for line := range crstrings.LinesSeq(td.Input) {
@@ -1553,6 +1578,11 @@ func runCompactionTest(
15531578
td.Fatalf(t, "parsing minimum-mvcc-garbage-size: %s", err)
15541579
}
15551580
policy.ValueStoragePolicy.MinimumMVCCGarbageSize = int(size)
1581+
case "prefer-fast-compression":
1582+
if len(parts) != 1 {
1583+
td.Fatalf(t, "expected prefer-fast-compression with no value, got: %s", arg)
1584+
}
1585+
policy.PreferFastCompression = true
15561586
default:
15571587
td.Fatalf(t, "unknown span policy arg: %s", arg)
15581588
}
@@ -1658,6 +1688,11 @@ func TestCompaction(t *testing.T) {
16581688
maxVersion: FormatNewest,
16591689
verbose: true,
16601690
},
1691+
"policy_enforcement": {
1692+
minVersion: FormatNewest,
1693+
maxVersion: FormatNewest,
1694+
cmp: DefaultComparer,
1695+
},
16611696
}
16621697
datadriven.Walk(t, "testdata/compaction", func(t *testing.T, path string) {
16631698
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"
@@ -1895,6 +1897,32 @@ func parseDBOptionsArgs(opts *Options, args []datadriven.CmdArg) error {
18951897
Secondary: wal.Dir{FS: opts.FS, Dirname: cmdArg.Vals[0]},
18961898
}
18971899
opts.WALFailover.EnsureDefaults()
1900+
case "compression":
1901+
var profile block.CompressionProfile
1902+
switch cmdArg.Vals[0] {
1903+
case "zstd":
1904+
profile = *block.ZstdCompression
1905+
case "snappy":
1906+
profile = *block.SnappyCompression
1907+
case "none":
1908+
profile = *block.NoCompression
1909+
case "zstd-force":
1910+
// For testing: Zstd with MinReductionPercent=0 so even small
1911+
// values are stored compressed.
1912+
profile = block.CompressionProfile{
1913+
Name: "test-zstd-force",
1914+
DataBlocks: block.SimpleCompressionSetting(compression.ZstdLevel3),
1915+
ValueBlocks: block.SimpleCompressionSetting(compression.ZstdLevel3),
1916+
OtherBlocks: compression.ZstdLevel3,
1917+
MinReductionPercent: 0,
1918+
}
1919+
default:
1920+
return errors.Newf("unrecognized compression %q", cmdArg.Vals[0])
1921+
}
1922+
for i := range opts.Levels {
1923+
p := profile
1924+
opts.Levels[i].Compression = func() *block.CompressionProfile { return &p }
1925+
}
18981926
}
18991927
}
19001928
if len(spanPolicies) > 0 {

options.go

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

799799
// VirtualTableRewriteUnreferencedFraction configures the minimum fraction of

span_policy_enforcer.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,25 @@ func (s *spanPolicyEnforcer) markForEnforcement(f *manifest.TableMetadata, level
322322
// Trigger compaction scheduling.
323323
s.db.maybeScheduleCompaction()
324324
}
325+
326+
// scanOnce performs a complete scan through all files in the LSM, checking for
327+
// policy violations and marking files for enforcement. This is used only for
328+
// testing to run a full scan without pacing concerns.
329+
func (s *spanPolicyEnforcer) scanOnce() {
330+
// Reset cursor to start from the beginning.
331+
s.cursor = manifest.ScanCursor{Level: 0}
332+
333+
for {
334+
nextFile, level, endOfScan := s.getNextFile(false /* waitForPending */)
335+
if endOfScan {
336+
break
337+
}
338+
if nextFile == nil {
339+
continue
340+
}
341+
342+
if s.checkPolicyViolation(nextFile) {
343+
s.markForEnforcement(nextFile, level)
344+
}
345+
}
346+
}
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)