Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sstable: teach pebble about suffix replacement #3240

Merged
merged 4 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion external_iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func TestIterRandomizedMaybeFilteredKeys(t *testing.T) {
defer r.Close()

filter := sstable.NewTestKeysBlockPropertyFilter(uint64(tsSeparator), math.MaxUint64)
filterer, err := sstable.IntersectsTable([]BlockPropertyFilter{filter}, nil, r.Properties.UserProperties)
filterer, err := sstable.IntersectsTable([]BlockPropertyFilter{filter}, nil, r.Properties.UserProperties, nil)
require.NoError(t, err)
require.NotNil(t, filterer)

Expand Down
2 changes: 2 additions & 0 deletions format_major_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ const (
// requires a format major version.
FormatSyntheticPrefixes

// TODO(msbutler): add major version for synthetic suffixes

// -- Add new versions here --

// FormatNewest is the most recent format major version.
Expand Down
5 changes: 5 additions & 0 deletions ingest.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ func ingestLoad1External(
SyntheticPrefix: e.SyntheticPrefix,
}
}
meta.SyntheticSuffix = e.SyntheticSuffix

if err := meta.Validate(opts.Comparer.Compare, opts.Comparer.FormatKey); err != nil {
return nil, err
Expand Down Expand Up @@ -1125,6 +1126,10 @@ type ExternalFile struct {
// is accessed as if those keys all instead have prefix SyntheticPrefix.
// SyntheticPrefix must be a prefix of both SmallestUserKey and LargestUserKey.
ContentPrefix, SyntheticPrefix []byte
// SyntheticSuffix will replace the suffix of every key in the file during
// iteration. Note that the file itself is not modifed, rather, every key
// returned by an iterator will have the synthetic suffix.
SyntheticSuffix []byte
}

// IngestWithStats does the same as Ingest, and additionally returns
Expand Down
5 changes: 5 additions & 0 deletions internal/base/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ type BlockPropertyFilter interface {
// Intersects returns true if the set represented by prop intersects with
// the set in the filter.
Intersects(prop []byte) (bool, error)
// SyntheticSuffixIntersects runs Intersects, but only after using the passed in
// suffix arg to modify a decoded copy of the passed in prop. This method only
// needs to be implemented for filters which that will be used with suffix
// replacement.
SyntheticSuffixIntersects(prop []byte, suffix []byte) (bool, error)
}
7 changes: 7 additions & 0 deletions internal/manifest/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ type FileMetadata struct {
// PrefixReplacement is used for virtual files where the backing file has a
// different prefix on its keys than the span in which it is being exposed.
PrefixReplacement *PrefixReplacement
SyntheticSuffix []byte
}

// InternalKeyBounds returns the set of overall table bounds.
Expand Down Expand Up @@ -900,6 +901,12 @@ func (m *FileMetadata) Validate(cmp Compare, formatKey base.FormatKey) error {
}
}

if m.SyntheticSuffix != nil {
if !m.Virtual {
return base.CorruptionErrorf("suffix replacement rule set with non-virtual file")
}
}

return nil
}

Expand Down
12 changes: 12 additions & 0 deletions internal/manifest/version_edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const (
customTagNonSafeIgnoreMask = 1 << 6
customTagVirtual = 66
customTagPrefixRewrite = 67
customTagSuffixRewrite = 68
)

// DeletedFileEntry holds the state for a file deletion from a level. The file
Expand Down Expand Up @@ -338,6 +339,7 @@ func (v *VersionEdit) Decode(r io.Reader) error {
backingFileNum uint64
}{}
var virtualPrefix *PrefixReplacement
var syntheticSuffix []byte
if tag == tagNewFile4 || tag == tagNewFile5 {
for {
customTag, err := d.readUvarint()
Expand Down Expand Up @@ -368,6 +370,11 @@ func (v *VersionEdit) Decode(r io.Reader) error {
SyntheticPrefix: synthetic,
}
continue
} else if customTag == customTagSuffixRewrite {
syntheticSuffix, err = d.readBytes()
if err != nil {
return err
}
}

field, err := d.readBytes()
Expand Down Expand Up @@ -407,6 +414,7 @@ func (v *VersionEdit) Decode(r io.Reader) error {
MarkedForCompaction: markedForCompaction,
Virtual: virtualState.virtual,
PrefixReplacement: virtualPrefix,
SyntheticSuffix: syntheticSuffix,
}
if tag != tagNewFile5 { // no range keys present
m.SmallestPointKey = base.DecodeInternalKey(smallestPointKey)
Expand Down Expand Up @@ -628,6 +636,10 @@ func (v *VersionEdit) Encode(w io.Writer) error {
e.writeBytes(x.Meta.PrefixReplacement.ContentPrefix)
e.writeBytes(x.Meta.PrefixReplacement.SyntheticPrefix)
}
if x.Meta.SyntheticSuffix != nil {
e.writeUvarint(customTagSuffixRewrite)
e.writeBytes(x.Meta.SyntheticSuffix)
}
e.writeUvarint(customTagTerminate)
}
}
Expand Down
13 changes: 7 additions & 6 deletions iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ func (f *minSeqNumFilter) Intersects(prop []byte) (bool, error) {
return minSeqNum < f.seqNumUpperBound, nil
}

func (f *minSeqNumFilter) SyntheticSuffixIntersects(prop []byte, suffix []byte) (bool, error) {
panic("unimplemented")
}

func TestReadSampling(t *testing.T) {
var d *DB
defer func() {
Expand Down Expand Up @@ -1254,8 +1258,7 @@ func TestIteratorBlockIntervalFilter(t *testing.T) {
return err.Error()
}
opts.PointKeyFilters = append(opts.PointKeyFilters,
sstable.NewBlockIntervalFilter(fmt.Sprintf("%d", id),
uint64(lower), uint64(upper)))
sstable.NewBlockIntervalFilter(fmt.Sprintf("%d", id), uint64(lower), uint64(upper), nil))
default:
return fmt.Sprintf("unknown key: %s", arg.Key)
}
Expand Down Expand Up @@ -1347,8 +1350,7 @@ func TestIteratorRandomizedBlockIntervalFilter(t *testing.T) {

var iterOpts IterOptions
iterOpts.PointKeyFilters = []BlockPropertyFilter{
sstable.NewBlockIntervalFilter("0",
uint64(lower), uint64(upper)),
sstable.NewBlockIntervalFilter("0", uint64(lower), uint64(upper), nil),
}
iter, _ := d.NewIter(&iterOpts)
defer func() {
Expand Down Expand Up @@ -2217,8 +2219,7 @@ func BenchmarkBlockPropertyFilter(b *testing.B) {
var iterOpts IterOptions
if filter {
iterOpts.PointKeyFilters = []BlockPropertyFilter{
sstable.NewBlockIntervalFilter("0",
uint64(0), uint64(1)),
sstable.NewBlockIntervalFilter("0", uint64(0), uint64(1), nil),
}
}
iter, _ := d.NewIter(&iterOpts)
Expand Down
8 changes: 8 additions & 0 deletions range_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,14 @@ func (m *rangeKeyMasking) Intersects(prop []byte) (bool, error) {
return m.filter.Intersects(prop)
}

func (m *rangeKeyMasking) SyntheticSuffixIntersects(prop []byte, suffix []byte) (bool, error) {
if m.maskSpan == nil {
// No span is actively masking.
return true, nil
}
return m.filter.SyntheticSuffixIntersects(prop, suffix)
}

// KeyIsWithinLowerBound implements the limitedBlockPropertyFilter interface
// defined in the sstable package. It's used to restrict the masking block
// property filter to only applying within the bounds of the active range key.
Expand Down
Loading
Loading