Skip to content

Commit df18f0e

Browse files
committed
options: change SpanPolicyFunc to take UserKeyBounds
If the span policy function is non-trivial, it might have to do a lot of work to find the end key at which e.g. the empty policy doesn't apply anymore. We switch to passing `UserKeyBounds`, since we only care about the policy up to the compaction end bound. We also add `SpanPolicy.IsDefault` and improve sanity checks in `MakeStaticSpanPolicyFunc`, so we can start using it in CRBD for the local key space.
1 parent 97dec88 commit df18f0e

File tree

4 files changed

+59
-23
lines changed

4 files changed

+59
-23
lines changed

compaction.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3389,7 +3389,10 @@ func (d *DB) compactAndWrite(
33893389
(len(spanPolicy.KeyRange.End) > 0 && d.cmp(firstKey, spanPolicy.KeyRange.End) >= 0) {
33903390
var err error
33913391
if d.opts.Experimental.SpanPolicyFunc != nil {
3392-
spanPolicy, err = d.opts.Experimental.SpanPolicyFunc(firstKey)
3392+
spanPolicy, err = d.opts.Experimental.SpanPolicyFunc(base.UserKeyBounds{
3393+
Start: firstKey,
3394+
End: c.bounds.End,
3395+
})
33933396
if err != nil {
33943397
return runner.Finish().WithError(err)
33953398
}

internal.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ type InternalKey = base.InternalKey
4242
// KeyRange exports the base.KeyRange type.
4343
type KeyRange = base.KeyRange
4444

45+
// UserKeyBounds exports the base.UserKeyBounds type.
46+
type UserKeyBounds = base.UserKeyBounds
47+
4548
// MakeInternalKey constructs an internal key from a specified user key,
4649
// sequence number and kind.
4750
func MakeInternalKey(userKey []byte, seqNum SeqNum, kind InternalKeyKind) InternalKey {

options.go

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,15 @@ type ValueSeparationPolicy struct {
12101210
// SpanPolicy contains policies that can vary by key range. The zero value for
12111211
// all fields, other than the KeyRange, is the default policy.
12121212
type SpanPolicy struct {
1213-
// KeyRange defines the key range for which this policy is valid. The end
1214-
// key can be empty, in which case the policy is valid for the entire
1215-
// keyspace after KeyRange.Start. The Start and End keys are not required to
1216-
// encompass the whole KeyRange over which this policy applies, i.e., they
1217-
// should be interpreted as a subset of the real interval for the policy.
1213+
// KeyRange defines the key range for which this policy is valid.
1214+
//
1215+
// If Start is empty, the policy is valid for the entire keyspace up to End.
1216+
// If End is empty, the policy is valid for the entire keyspace after Start.
1217+
// If both are empty, this is the only policy across the entire keyspace.
1218+
//
1219+
// The Start and End keys are not required to encompass the whole KeyRange over
1220+
// which this policy applies, i.e., they should be interpreted as a subset of
1221+
// the real interval for the policy.
12181222
KeyRange KeyRange
12191223

12201224
// Prefer a faster compression algorithm for the keys in this span.
@@ -1254,6 +1258,17 @@ type SpanPolicy struct {
12541258
// Pebble will remember the set of (KeyRange, SpanID) pairs that it has seen
12551259
// in its history, for error checking the aforementioned invariant.
12561260
TieringPolicy TieringPolicyAndExtractor
1261+
1262+
// NOTE: update the IsDefault() method if you add new fields to this struct.
1263+
}
1264+
1265+
// IsDefault returns true if the SpanPolicy is the default policy, i.e. none of
1266+
// the fields other than KeyRange are set.
1267+
func (p *SpanPolicy) IsDefault() bool {
1268+
return !p.PreferFastCompression &&
1269+
!p.DisableValueSeparationBySuffix &&
1270+
p.ValueStoragePolicy == ValueStorageDefault &&
1271+
p.TieringPolicy == nil
12571272
}
12581273

12591274
// String returns a string representation of the SpanPolicy.
@@ -1299,12 +1314,21 @@ const (
12991314

13001315
// SpanPolicyFunc is used to determine the SpanPolicy for a key region.
13011316
//
1302-
// The returned policy is valid over the interval in SpanPolicy.KeyRange,
1303-
// which must include the startKey specified by the caller.
1317+
// The returned policy is valid over the interval in policy.KeyRange, which
1318+
// must include the bounds.Start key specified by the caller. Specifically,
1319+
// policy.KeyRange.Start is empty or is <= bounds.Start.
1320+
// If policy.KeyRange.End is before bounds.End, then the policy is only valid up
1321+
// to that point.
13041322
//
13051323
// A flush or compaction will call this function once for the first key to be
1306-
// output. If the compaction reaches the end key, the current output sst is
1307-
// finished and the function is called again.
1324+
// output. If the compaction reaches policy.KeyRange.End, the current output sst
1325+
// is finished and the function is called again (with the first key >=
1326+
// policy.KeyRange.End).
1327+
//
1328+
// Correctness must never depend on having a specific span policy. The function
1329+
// is allowed to change the returned policy arbitrarily.
1330+
//
1331+
// If this function returns an error, the flush or compaction will be aborted.
13081332
//
13091333
// TODO(sumeer): since there is a single TieringPolicy per SpanPolicy, we will
13101334
// split sstables at tiering policy boundaries. Historically, SpanPolicys have
@@ -1315,20 +1339,23 @@ const (
13151339
// apply to the sstable as a whole (e.g. PreferFastCompression) to determine
13161340
// whether to split at the end. This will require some restructuring of the
13171341
// compact.Runner interface, so we will do this later.
1318-
type SpanPolicyFunc func(startKey []byte) (policy SpanPolicy, err error)
1319-
1320-
// MakeStaticSpanPolicyFunc returns a SpanPolicyFunc that applies a given
1321-
// policy to the given span (and the default policy outside the span). The
1322-
// supplied policies must be non-overlapping in key range. This method must
1323-
// not be called with inputPolicies that have an empty KeyRange.End to signify
1324-
// extending to the end of the keyspace. The empty slice is assumed to be a
1325-
// valid key that sorts before all other keys.
1342+
type SpanPolicyFunc func(bounds base.UserKeyBounds) (policy SpanPolicy, err error)
1343+
1344+
// MakeStaticSpanPolicyFunc returns a SpanPolicyFunc that applies a given policy
1345+
// to the given span (and the default policy outside the span). The supplied
1346+
// policies must be non-overlapping in key range. This method must not be called
1347+
// with inputPolicies that have an empty KeyRange.Start or End to signify
1348+
// extending to the start/end of the keyspace.
13261349
func MakeStaticSpanPolicyFunc(cmp base.Compare, inputPolicies ...SpanPolicy) SpanPolicyFunc {
13271350
// Collect all the boundaries of the input policies, sort and deduplicate them.
13281351
uniqueKeys := make([][]byte, 0, 2*len(inputPolicies))
13291352
for i := range inputPolicies {
1330-
uniqueKeys = append(uniqueKeys, inputPolicies[i].KeyRange.Start)
1331-
uniqueKeys = append(uniqueKeys, inputPolicies[i].KeyRange.End)
1353+
r := inputPolicies[i].KeyRange
1354+
if len(r.Start) == 0 || len(r.End) == 0 || cmp(r.Start, r.End) >= 0 {
1355+
panic("invalid key range in input policy")
1356+
}
1357+
uniqueKeys = append(uniqueKeys, r.Start)
1358+
uniqueKeys = append(uniqueKeys, r.End)
13321359
}
13331360
slices.SortFunc(uniqueKeys, cmp)
13341361
uniqueKeys = slices.CompactFunc(uniqueKeys, func(a, b []byte) bool { return cmp(a, b) == 0 })
@@ -1343,13 +1370,16 @@ func MakeStaticSpanPolicyFunc(cmp base.Compare, inputPolicies ...SpanPolicy) Spa
13431370
// Populate the non-default policies.
13441371
for _, p := range inputPolicies {
13451372
idx, _ := slices.BinarySearchFunc(uniqueKeys, p.KeyRange.Start, cmp)
1373+
if cmp(p.KeyRange.End, uniqueKeys[idx+1]) != 0 {
1374+
panic("overlapping key ranges in input policies")
1375+
}
13461376
policies[idx] = p
13471377
policies[idx].KeyRange = KeyRange{Start: uniqueKeys[idx], End: uniqueKeys[idx+1]}
13481378
}
13491379

1350-
return func(startKey []byte) (_ SpanPolicy, _ error) {
1380+
return func(bounds base.UserKeyBounds) (_ SpanPolicy, _ error) {
13511381
// Find the policy that applies to the start key.
1352-
idx, eq := slices.BinarySearchFunc(uniqueKeys, startKey, cmp)
1382+
idx, eq := slices.BinarySearchFunc(uniqueKeys, bounds.Start, cmp)
13531383
switch idx {
13541384
case len(uniqueKeys):
13551385
// The start key is after the last policy.

options_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ func TestStaticSpanPolicyFunc(t *testing.T) {
628628

629629
spf := MakeStaticSpanPolicyFunc(testkeys.Comparer.Compare, inputSpanPolicies...)
630630
for _, key := range strings.Fields(td.Input) {
631-
policy, err := spf([]byte(key))
631+
policy, err := spf(UserKeyBounds{Start: []byte(key)})
632632
require.NoError(t, err)
633633
// TODO(sumeer): also output KeyRange.Start.
634634
if policy.KeyRange.End == nil {

0 commit comments

Comments
 (0)