Upgrade versions of vendored libs
This commit is contained in:
142
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
142
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
@@ -534,7 +534,7 @@ func (i *blockIter) cacheEntry() {
|
||||
|
||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
|
||||
ikey := base.MakeSearchKey(key)
|
||||
@@ -629,25 +629,25 @@ func (i *blockIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []b
|
||||
// Iterate from that restart point to somewhere >= the key sought.
|
||||
for ; i.valid(); i.Next() {
|
||||
if base.InternalCompare(i.cmp, i.ikey, ikey) >= 0 {
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
|
||||
// pebble package.
|
||||
func (i *blockIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
// This should never be called as prefix iteration is handled by sstable.Iterator.
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
|
||||
ikey := base.MakeSearchKey(key)
|
||||
@@ -742,7 +742,7 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
// sought.
|
||||
i.offset = -1
|
||||
i.nextOffset = 0
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Iterate from that restart point to somewhere >= the key sought, then back
|
||||
@@ -761,7 +761,7 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
// loop will execute at least once with this if-block not being true, so
|
||||
// the key we are backing up to is the last one this loop cached.
|
||||
i.Prev()
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
if i.nextOffset >= targetOffset {
|
||||
@@ -777,30 +777,30 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) First() (*InternalKey, []byte) {
|
||||
func (i *blockIter) First() (*InternalKey, base.LazyValue) {
|
||||
i.offset = 0
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.clearCache()
|
||||
i.readEntry()
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Last implements internalIterator.Last, as documented in the pebble package.
|
||||
func (i *blockIter) Last() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Last() (*InternalKey, base.LazyValue) {
|
||||
// Seek forward from the last restart point.
|
||||
i.offset = int32(binary.LittleEndian.Uint32(i.data[i.restarts+4*(i.numRestarts-1):]))
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
i.readEntry()
|
||||
@@ -813,12 +813,12 @@ func (i *blockIter) Last() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Next implements internalIterator.Next, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if len(i.cachedBuf) > 0 {
|
||||
// We're switching from reverse iteration to forward iteration. We need to
|
||||
// populate i.fullKey with the current key we're positioned at so that
|
||||
@@ -836,7 +836,7 @@ func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
|
||||
i.offset = i.nextOffset
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.readEntry()
|
||||
// Manually inlined version of i.decodeInternalKey(i.key).
|
||||
@@ -850,12 +850,12 @@ func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
||||
i.ikey.UserKey = nil
|
||||
}
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Prev implements internalIterator.Prev, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if n := len(i.cached) - 1; n >= 0 {
|
||||
i.nextOffset = i.offset
|
||||
e := &i.cached[n]
|
||||
@@ -874,14 +874,14 @@ func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
i.ikey.UserKey = nil
|
||||
}
|
||||
i.cached = i.cached[:n]
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
i.clearCache()
|
||||
if i.offset <= 0 {
|
||||
i.offset = -1
|
||||
i.nextOffset = 0
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
targetOffset := i.offset
|
||||
@@ -921,7 +921,7 @@ func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Key implements internalIterator.Key, as documented in the pebble package.
|
||||
@@ -929,10 +929,8 @@ func (i *blockIter) Key() *InternalKey {
|
||||
return &i.ikey
|
||||
}
|
||||
|
||||
// Value implements internalIterator.Value, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Value() []byte {
|
||||
return i.val
|
||||
func (i *blockIter) value() base.LazyValue {
|
||||
return base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Error implements internalIterator.Error, as documented in the pebble
|
||||
@@ -987,6 +985,10 @@ type fragmentBlockIter struct {
|
||||
closeHook func(i keyspan.FragmentIterator) error
|
||||
}
|
||||
|
||||
func (i *fragmentBlockIter) resetForReuse() fragmentBlockIter {
|
||||
return fragmentBlockIter{blockIter: i.blockIter.resetForReuse()}
|
||||
}
|
||||
|
||||
func (i *fragmentBlockIter) decodeSpanKeys(k *InternalKey, internalValue []byte) {
|
||||
// TODO(jackson): The use of i.span.Keys to accumulate keys across multiple
|
||||
// calls to Decode is too confusing and subtle. Refactor to make it
|
||||
@@ -1017,10 +1019,10 @@ func (i *fragmentBlockIter) decodeSpanKeys(k *InternalKey, internalValue []byte)
|
||||
//
|
||||
// gatherForward iterates forward, re-combining the fragmented internal keys to
|
||||
// reconstruct a keyspan.Span that holds all the keys defined over the span.
|
||||
func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte) keyspan.Span {
|
||||
func (i *fragmentBlockIter) gatherForward(k *InternalKey, lazyValue base.LazyValue) *keyspan.Span {
|
||||
i.span = keyspan.Span{}
|
||||
if k == nil || !i.blockIter.valid() {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
i.err = nil
|
||||
// Use the i.keyBuf array to back the Keys slice to prevent an allocation
|
||||
@@ -1028,20 +1030,22 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
i.span.Keys = i.keyBuf[:0]
|
||||
|
||||
// Decode the span's end key and individual keys from the value.
|
||||
internalValue := lazyValue.InPlaceValue()
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
prevEnd := i.span.End
|
||||
|
||||
// There might exist additional internal keys with identical bounds encoded
|
||||
// within the block. Iterate forward, accumulating all the keys with
|
||||
// identical bounds to s.
|
||||
k, internalValue = i.blockIter.Next()
|
||||
k, lazyValue = i.blockIter.Next()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
for k != nil && i.blockIter.cmp(k.UserKey, i.span.Start) == 0 {
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
|
||||
// Since k indicates an equal start key, the encoded end key must
|
||||
@@ -1051,12 +1055,13 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
if i.blockIter.cmp(prevEnd, i.span.End) != 0 {
|
||||
i.err = base.CorruptionErrorf("pebble: corrupt keyspan fragmentation")
|
||||
i.span = keyspan.Span{}
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
k, internalValue = i.blockIter.Next()
|
||||
k, lazyValue = i.blockIter.Next()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
}
|
||||
// i.blockIter is positioned over the first internal key for the next span.
|
||||
return i.span
|
||||
return &i.span
|
||||
}
|
||||
|
||||
// gatherBackward gathers internal keys with identical bounds. Keys defined over
|
||||
@@ -1067,10 +1072,10 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
//
|
||||
// gatherBackward iterates backwards, re-combining the fragmented internal keys
|
||||
// to reconstruct a keyspan.Span that holds all the keys defined over the span.
|
||||
func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte) keyspan.Span {
|
||||
func (i *fragmentBlockIter) gatherBackward(k *InternalKey, lazyValue base.LazyValue) *keyspan.Span {
|
||||
i.span = keyspan.Span{}
|
||||
if k == nil || !i.blockIter.valid() {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
i.err = nil
|
||||
// Use the i.keyBuf array to back the Keys slice to prevent an allocation
|
||||
@@ -1078,20 +1083,22 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
i.span.Keys = i.keyBuf[:0]
|
||||
|
||||
// Decode the span's end key and individual keys from the value.
|
||||
internalValue := lazyValue.InPlaceValue()
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
prevEnd := i.span.End
|
||||
|
||||
// There might exist additional internal keys with identical bounds encoded
|
||||
// within the block. Iterate backward, accumulating all the keys with
|
||||
// identical bounds to s.
|
||||
k, internalValue = i.blockIter.Prev()
|
||||
k, lazyValue = i.blockIter.Prev()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
for k != nil && i.blockIter.cmp(k.UserKey, i.span.Start) == 0 {
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
|
||||
// Since k indicates an equal start key, the encoded end key must
|
||||
@@ -1101,27 +1108,26 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
if i.blockIter.cmp(prevEnd, i.span.End) != 0 {
|
||||
i.err = base.CorruptionErrorf("pebble: corrupt keyspan fragmentation")
|
||||
i.span = keyspan.Span{}
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
k, internalValue = i.blockIter.Prev()
|
||||
k, lazyValue = i.blockIter.Prev()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
}
|
||||
// i.blockIter is positioned over the last internal key for the previous
|
||||
// span.
|
||||
|
||||
// Backwards iteration encounters internal keys in the wrong order.
|
||||
keyspan.SortKeys(i.span.Keys)
|
||||
keyspan.SortKeysByTrailer(&i.span.Keys)
|
||||
|
||||
return i.span
|
||||
return &i.span
|
||||
}
|
||||
|
||||
// Error implements (base.InternalIterator).Error, as documented in the
|
||||
// internal/base package.
|
||||
// Error implements (keyspan.FragmentIterator).Error.
|
||||
func (i *fragmentBlockIter) Error() error {
|
||||
return i.err
|
||||
}
|
||||
|
||||
// Close implements (base.InternalIterator).Close, as documented in the
|
||||
// internal/base package.
|
||||
// Close implements (keyspan.FragmentIterator).Close.
|
||||
func (i *fragmentBlockIter) Close() error {
|
||||
var err error
|
||||
if i.closeHook != nil {
|
||||
@@ -1131,23 +1137,20 @@ func (i *fragmentBlockIter) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// First implements (base.InternalIterator).First, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) First() keyspan.Span {
|
||||
// First implements (keyspan.FragmentIterator).First
|
||||
func (i *fragmentBlockIter) First() *keyspan.Span {
|
||||
i.dir = +1
|
||||
return i.gatherForward(i.blockIter.First())
|
||||
}
|
||||
|
||||
// Last implements (base.InternalIterator).Last, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Last() keyspan.Span {
|
||||
// Last implements (keyspan.FragmentIterator).Last.
|
||||
func (i *fragmentBlockIter) Last() *keyspan.Span {
|
||||
i.dir = -1
|
||||
return i.gatherBackward(i.blockIter.Last())
|
||||
}
|
||||
|
||||
// Next implements (base.InternalIterator).Next, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Next() keyspan.Span {
|
||||
// Next implements (keyspan.FragmentIterator).Next.
|
||||
func (i *fragmentBlockIter) Next() *keyspan.Span {
|
||||
switch {
|
||||
case i.dir == -1 && !i.span.Valid():
|
||||
// Switching directions.
|
||||
@@ -1176,12 +1179,12 @@ func (i *fragmentBlockIter) Next() keyspan.Span {
|
||||
}
|
||||
i.dir = +1
|
||||
}
|
||||
return i.gatherForward(&i.blockIter.ikey, i.blockIter.val)
|
||||
// We know that this blockIter has in-place values.
|
||||
return i.gatherForward(&i.blockIter.ikey, base.MakeInPlaceValue(i.blockIter.val))
|
||||
}
|
||||
|
||||
// Prev implements (base.InternalIterator).Prev, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Prev() keyspan.Span {
|
||||
// Prev implements (keyspan.FragmentIterator).Prev.
|
||||
func (i *fragmentBlockIter) Prev() *keyspan.Span {
|
||||
switch {
|
||||
case i.dir == +1 && !i.span.Valid():
|
||||
// Switching directions.
|
||||
@@ -1210,21 +1213,20 @@ func (i *fragmentBlockIter) Prev() keyspan.Span {
|
||||
}
|
||||
i.dir = -1
|
||||
}
|
||||
return i.gatherBackward(&i.blockIter.ikey, i.blockIter.val)
|
||||
// We know that this blockIter has in-place values.
|
||||
return i.gatherBackward(&i.blockIter.ikey, base.MakeInPlaceValue(i.blockIter.val))
|
||||
}
|
||||
|
||||
// SeekGE implements (base.InternalIterator).SeekGE, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) SeekGE(k []byte) keyspan.Span {
|
||||
// SeekGE implements (keyspan.FragmentIterator).SeekGE.
|
||||
func (i *fragmentBlockIter) SeekGE(k []byte) *keyspan.Span {
|
||||
i.dir = +1
|
||||
return i.gatherForward(i.blockIter.SeekGE(k, false))
|
||||
return i.gatherForward(i.blockIter.SeekGE(k, base.SeekGEFlags(0)))
|
||||
}
|
||||
|
||||
// SeekLT implements (base.InternalIterator).SeekLT, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) SeekLT(k []byte) keyspan.Span {
|
||||
// SeekLT implements (keyspan.FragmentIterator).SeekLT.
|
||||
func (i *fragmentBlockIter) SeekLT(k []byte) *keyspan.Span {
|
||||
i.dir = -1
|
||||
return i.gatherBackward(i.blockIter.SeekLT(k))
|
||||
return i.gatherBackward(i.blockIter.SeekLT(k, base.SeekLTFlagsNone))
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
|
||||
279
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
279
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
@@ -46,6 +46,40 @@ import (
|
||||
// identical behavior to table properties by returning the nil slice from
|
||||
// FinishDataBlock and FinishIndexBlock, and interpret them as the universal
|
||||
// set in BlockPropertyFilter, and return a non-universal set in FinishTable.
|
||||
//
|
||||
// Block property filtering is nondeterministic because the separation of keys
|
||||
// into blocks is nondeterministic. Clients use block-property filters to
|
||||
// implement efficient application of a filter F that applies to key-value pairs
|
||||
// (abbreviated as kv-filter). Consider correctness defined as surfacing exactly
|
||||
// the same key-value pairs that would be surfaced if one applied the filter F
|
||||
// above normal iteration. With this correctness definition, block property
|
||||
// filtering may introduce two kinds of errors:
|
||||
//
|
||||
// a) Block property filtering that uses a kv-filter may produce additional
|
||||
// key-value pairs that don't satisfy the filter because of the separation
|
||||
// of keys into blocks. Clients may remove these extra key-value pairs by
|
||||
// re-applying the kv filter while reading results back from Pebble.
|
||||
//
|
||||
// b) Block property filtering may surface deleted key-value pairs if the
|
||||
// kv filter is not a strict function of the key's user key. A block
|
||||
// containing k.DEL may be filtered, while a block containing the deleted
|
||||
// key k.SET may not be filtered, if the kv filter applies to one but not
|
||||
// the other.
|
||||
//
|
||||
// This error may be avoided trivially by using a kv filter that is a pure
|
||||
// function of the user key. A filter that examines values or key kinds
|
||||
// requires care to ensure F(k.SET, <value>) = F(k.DEL) = F(k.SINGLEDEL).
|
||||
//
|
||||
// The combination of range deletions and filtering by table-level properties
|
||||
// add another opportunity for deleted point keys to be surfaced. The pebble
|
||||
// Iterator stack takes care to correctly apply filtered tables' range deletions
|
||||
// to lower tables, preventing this form of nondeterministic error.
|
||||
//
|
||||
// In addition to the non-determinism discussed in (b), which limits the use
|
||||
// of properties over values, we now have support for values that are not
|
||||
// stored together with the key, and may not even be retrieved during
|
||||
// compactions. If Pebble is configured with such value separation, block
|
||||
// properties must only apply to the key, and will be provided a nil value.
|
||||
|
||||
// BlockPropertyCollector is used when writing a sstable.
|
||||
// - All calls to Add are included in the next FinishDataBlock, after which
|
||||
@@ -112,6 +146,57 @@ type SuffixReplaceableBlockCollector interface {
|
||||
// be thread-safe.
|
||||
type BlockPropertyFilter = base.BlockPropertyFilter
|
||||
|
||||
// BoundLimitedBlockPropertyFilter implements the block-property filter but
|
||||
// imposes an additional constraint on its usage, requiring that only blocks
|
||||
// containing exclusively keys between its lower and upper bounds may be
|
||||
// filtered. The bounds may be change during iteration, so the filter doesn't
|
||||
// expose the bounds, instead implementing KeyIsWithin[Lower,Upper]Bound methods
|
||||
// for performing bound comparisons.
|
||||
//
|
||||
// To be used, a BoundLimitedBlockPropertyFilter must be supplied directly
|
||||
// through NewBlockPropertiesFilterer's dedicated parameter. If supplied through
|
||||
// the ordinary slice of block property filters, this filter's bounds will be
|
||||
// ignored.
|
||||
//
|
||||
// The current [lower,upper) bounds of the filter are unknown, because they may
|
||||
// be changing. During forward iteration the lower bound is externally
|
||||
// guaranteed, meaning Intersects only returns false if the sstable iterator is
|
||||
// already known to be positioned at a key ≥ lower. The sstable iterator is then
|
||||
// only responsible for ensuring filtered blocks also meet the upper bound, and
|
||||
// should only allow a block to be filtered if all its keys are < upper. The
|
||||
// sstable iterator may invoke KeyIsWithinUpperBound(key) to perform this check,
|
||||
// where key is an inclusive upper bound on the block's keys.
|
||||
//
|
||||
// During backward iteration the upper bound is externally guaranteed, and
|
||||
// Intersects only returns false if the sstable iterator is already known to be
|
||||
// positioned at a key < upper. The sstable iterator is responsible for ensuring
|
||||
// filtered blocks also meet the lower bound, enforcing that a block is only
|
||||
// filtered if all its keys are ≥ lower. This check is made through passing the
|
||||
// block's inclusive lower bound to KeyIsWithinLowerBound.
|
||||
//
|
||||
// Implementations may become active or inactive through implementing Intersects
|
||||
// to return true whenever the filter is disabled.
|
||||
//
|
||||
// Usage of BoundLimitedBlockPropertyFilter is subtle, and Pebble consumers
|
||||
// should not implement this interface directly. This interface is an internal
|
||||
// detail in the implementation of block-property range-key masking.
|
||||
type BoundLimitedBlockPropertyFilter interface {
|
||||
BlockPropertyFilter
|
||||
|
||||
// KeyIsWithinLowerBound tests whether the provided internal key falls
|
||||
// within the current lower bound of the filter. A true return value
|
||||
// indicates that the filter may be used to filter blocks that exclusively
|
||||
// contain keys ≥ `key`, so long as the blocks' keys also satisfy the upper
|
||||
// bound.
|
||||
KeyIsWithinLowerBound(key *InternalKey) bool
|
||||
// KeyIsWithinUpperBound tests whether the provided internal key falls
|
||||
// within the current upper bound of the filter. A true return value
|
||||
// indicates that the filter may be used to filter blocks that exclusively
|
||||
// contain keys ≤ `key`, so long as the blocks' keys also satisfy the lower
|
||||
// bound.
|
||||
KeyIsWithinUpperBound(key *InternalKey) bool
|
||||
}
|
||||
|
||||
// BlockIntervalCollector is a helper implementation of BlockPropertyCollector
|
||||
// for users who want to represent a set of the form [lower,upper) where both
|
||||
// lower and upper are uint64, and lower <= upper.
|
||||
@@ -326,32 +411,44 @@ func (w *suffixReplacementBlockCollectorWrapper) UpdateKeySuffixes(
|
||||
return w.BlockIntervalCollector.points.(SuffixReplaceableBlockCollector).UpdateKeySuffixes(oldProp, from, to)
|
||||
}
|
||||
|
||||
// blockIntervalFilter is an implementation of BlockPropertyFilter when the
|
||||
// BlockIntervalFilter is an implementation of BlockPropertyFilter when the
|
||||
// corresponding collector is a BlockIntervalCollector. That is, the set is of
|
||||
// the form [lower, upper).
|
||||
type blockIntervalFilter struct {
|
||||
type BlockIntervalFilter struct {
|
||||
name string
|
||||
filterInterval interval
|
||||
}
|
||||
|
||||
var _ BlockPropertyFilter = (*BlockIntervalFilter)(nil)
|
||||
|
||||
// NewBlockIntervalFilter constructs a BlockPropertyFilter that filters blocks
|
||||
// based on an interval property collected by BlockIntervalCollector and the
|
||||
// given [lower, upper) bounds. The given name specifies the
|
||||
// BlockIntervalCollector's properties to read.
|
||||
func NewBlockIntervalFilter(name string, lower uint64, upper uint64) BlockPropertyFilter {
|
||||
return &blockIntervalFilter{
|
||||
func NewBlockIntervalFilter(name string, lower uint64, upper uint64) *BlockIntervalFilter {
|
||||
b := new(BlockIntervalFilter)
|
||||
b.Init(name, lower, upper)
|
||||
return b
|
||||
}
|
||||
|
||||
// Init initializes (or re-initializes, clearing previous state) an existing
|
||||
// BLockPropertyFilter to filter blocks based on an interval property collected
|
||||
// by BlockIntervalCollector and the given [lower, upper) bounds. The given name
|
||||
// specifies the BlockIntervalCollector's properties to read.
|
||||
func (b *BlockIntervalFilter) Init(name string, lower, upper uint64) {
|
||||
*b = BlockIntervalFilter{
|
||||
name: name,
|
||||
filterInterval: interval{lower: lower, upper: upper},
|
||||
}
|
||||
}
|
||||
|
||||
// Name implements the BlockPropertyFilter interface.
|
||||
func (b *blockIntervalFilter) Name() string {
|
||||
func (b *BlockIntervalFilter) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
// Intersects implements the BlockPropertyFilter interface.
|
||||
func (b *blockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
func (b *BlockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
var i interval
|
||||
if err := i.decode(prop); err != nil {
|
||||
return false, err
|
||||
@@ -359,6 +456,14 @@ func (b *blockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
return i.intersects(b.filterInterval), nil
|
||||
}
|
||||
|
||||
// SetInterval adjusts the [lower, upper) bounds used by the filter. It is not
|
||||
// generally safe to alter the filter while it's in use, except as part of the
|
||||
// implementation of BlockPropertyFilterMask.SetSuffix used for range-key
|
||||
// masking.
|
||||
func (b *BlockIntervalFilter) SetInterval(lower, upper uint64) {
|
||||
b.filterInterval = interval{lower: lower, upper: upper}
|
||||
}
|
||||
|
||||
// When encoding block properties for each block, we cannot afford to encode
|
||||
// the name. Instead, the name is mapped to a shortID, in the scope of that
|
||||
// sstable, and the shortID is encoded. Since we use a uint8, there is a limit
|
||||
@@ -454,6 +559,25 @@ type BlockPropertiesFilterer struct {
|
||||
// has two filters, corresponding to shortIDs 2, 0, this would be:
|
||||
// len(shortIDToFiltersIndex)==3, 0=>1, 1=>-1, 2=>0.
|
||||
shortIDToFiltersIndex []int
|
||||
|
||||
// boundLimitedFilter, if non-nil, holds a single block-property filter with
|
||||
// additional constraints on its filtering. A boundLimitedFilter may only
|
||||
// filter blocks that are wholly contained within its bounds. During forward
|
||||
// iteration the lower bound (and during backward iteration the upper bound)
|
||||
// must be externally guaranteed, with Intersects only returning false if
|
||||
// that bound is met. The opposite bound is verified during iteration by the
|
||||
// sstable iterator.
|
||||
//
|
||||
// boundLimitedFilter is permitted to be defined on a property (`Name()`)
|
||||
// for which another filter exists in filters. In this case both filters
|
||||
// will be consulted, and either filter may exclude block(s). Only a single
|
||||
// bound-limited block-property filter may be set.
|
||||
//
|
||||
// The boundLimitedShortID field contains the shortID of the filter's
|
||||
// property within the sstable. It's set to -1 if the property was not
|
||||
// collected when the table was built.
|
||||
boundLimitedFilter BoundLimitedBlockPropertyFilter
|
||||
boundLimitedShortID int
|
||||
}
|
||||
|
||||
var blockPropertiesFiltererPool = sync.Pool{
|
||||
@@ -464,9 +588,16 @@ var blockPropertiesFiltererPool = sync.Pool{
|
||||
|
||||
// NewBlockPropertiesFilterer returns a partially initialized filterer. To complete
|
||||
// initialization, call IntersectsUserPropsAndFinishInit.
|
||||
func NewBlockPropertiesFilterer(filters []BlockPropertyFilter) *BlockPropertiesFilterer {
|
||||
func NewBlockPropertiesFilterer(
|
||||
filters []BlockPropertyFilter, limited BoundLimitedBlockPropertyFilter,
|
||||
) *BlockPropertiesFilterer {
|
||||
filterer := blockPropertiesFiltererPool.Get().(*BlockPropertiesFilterer)
|
||||
*filterer = BlockPropertiesFilterer{filters: filters}
|
||||
*filterer = BlockPropertiesFilterer{
|
||||
filters: filters,
|
||||
shortIDToFiltersIndex: filterer.shortIDToFiltersIndex[:0],
|
||||
boundLimitedFilter: limited,
|
||||
boundLimitedShortID: -1,
|
||||
}
|
||||
return filterer
|
||||
}
|
||||
|
||||
@@ -518,12 +649,75 @@ func (f *BlockPropertiesFilterer) IntersectsUserPropsAndFinishInit(
|
||||
}
|
||||
f.shortIDToFiltersIndex[shortID] = i
|
||||
}
|
||||
if f.boundLimitedFilter == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// There's a bound-limited filter. Find its shortID. It's possible that
|
||||
// there's an existing filter in f.filters on the same property. That's
|
||||
// okay. Both filters will be consulted whenever a relevant prop is decoded.
|
||||
props, ok := userProperties[f.boundLimitedFilter.Name()]
|
||||
if !ok {
|
||||
// The collector was not used when writing this file, so it's
|
||||
// intersecting. We leave f.boundLimitedShortID=-1, so the filter will
|
||||
// be unused within this file.
|
||||
return true, nil
|
||||
}
|
||||
byteProps := []byte(props)
|
||||
if len(byteProps) < 1 {
|
||||
return false, base.CorruptionErrorf(
|
||||
"block properties for %s is corrupted", f.boundLimitedFilter.Name())
|
||||
}
|
||||
f.boundLimitedShortID = int(byteProps[0])
|
||||
|
||||
// We don't check for table-level intersection for the bound-limited filter.
|
||||
// The bound-limited filter is treated as vacuously intersecting.
|
||||
//
|
||||
// NB: If a block-property filter needs to be toggled inactive/active, it
|
||||
// should be implemented within the Intersects implementation.
|
||||
//
|
||||
// TODO(jackson): We could filter at the table-level by threading the table
|
||||
// smallest and largest bounds here.
|
||||
|
||||
// The bound-limited filter isn't included in shortIDToFiltersIndex.
|
||||
//
|
||||
// When determining intersection, we decode props only up to the shortID
|
||||
// len(shortIDToFiltersIndex). If f.limitedShortID is greater than any of
|
||||
// the existing filters' shortIDs, we need to grow shortIDToFiltersIndex.
|
||||
// Growing the index with -1s ensures we're able to consult the index
|
||||
// without length checks.
|
||||
if n := len(f.shortIDToFiltersIndex); n <= f.boundLimitedShortID {
|
||||
if cap(f.shortIDToFiltersIndex) <= f.boundLimitedShortID {
|
||||
index := make([]int, f.boundLimitedShortID+1)
|
||||
copy(index, f.shortIDToFiltersIndex)
|
||||
f.shortIDToFiltersIndex = index
|
||||
} else {
|
||||
f.shortIDToFiltersIndex = f.shortIDToFiltersIndex[:f.boundLimitedShortID+1]
|
||||
}
|
||||
for j := n; j <= f.boundLimitedShortID; j++ {
|
||||
f.shortIDToFiltersIndex[j] = -1
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersects(props []byte) (bool, error) {
|
||||
type intersectsResult int8
|
||||
|
||||
const (
|
||||
blockIntersects intersectsResult = iota
|
||||
blockExcluded
|
||||
// blockMaybeExcluded is returned by BlockPropertiesFilterer.intersects when
|
||||
// no filters unconditionally exclude the block, but the bound-limited block
|
||||
// property filter will exclude it if the block's bounds fall within the
|
||||
// filter's current bounds. See the reader's
|
||||
// {single,two}LevelIterator.resolveMaybeExcluded methods.
|
||||
blockMaybeExcluded
|
||||
)
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersects(props []byte) (ret intersectsResult, err error) {
|
||||
i := 0
|
||||
decoder := blockPropertiesDecoder{props: props}
|
||||
ret = blockIntersects
|
||||
for i < len(f.shortIDToFiltersIndex) {
|
||||
var id int
|
||||
var prop []byte
|
||||
@@ -532,43 +726,68 @@ func (f *BlockPropertiesFilterer) intersects(props []byte) (bool, error) {
|
||||
var err error
|
||||
shortID, prop, err = decoder.next()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return ret, err
|
||||
}
|
||||
id = int(shortID)
|
||||
} else {
|
||||
id = math.MaxUint8 + 1
|
||||
}
|
||||
for i < len(f.shortIDToFiltersIndex) && id > i {
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
// There is a filter for this id, but the property for this id
|
||||
// is not encoded for this block.
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !intersects {
|
||||
return false, nil
|
||||
}
|
||||
// The property for this id is not encoded for this block, but there
|
||||
// may still be a filter for this id.
|
||||
if intersects, err := f.intersectsFilter(i, nil); err != nil {
|
||||
return ret, err
|
||||
} else if intersects == blockExcluded {
|
||||
return blockExcluded, nil
|
||||
} else if intersects == blockMaybeExcluded {
|
||||
ret = blockMaybeExcluded
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(f.shortIDToFiltersIndex) {
|
||||
return true, nil
|
||||
return ret, nil
|
||||
}
|
||||
// INVARIANT: id <= i. And since i is always incremented by 1, id==i.
|
||||
if id != i {
|
||||
panic(fmt.Sprintf("%d != %d", id, i))
|
||||
}
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(prop)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !intersects {
|
||||
return false, nil
|
||||
}
|
||||
if intersects, err := f.intersectsFilter(i, prop); err != nil {
|
||||
return ret, err
|
||||
} else if intersects == blockExcluded {
|
||||
return blockExcluded, nil
|
||||
} else if intersects == blockMaybeExcluded {
|
||||
ret = blockMaybeExcluded
|
||||
}
|
||||
i++
|
||||
}
|
||||
return true, nil
|
||||
// ret == blockIntersects || ret == blockMaybeExcluded
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersectsFilter(i int, prop []byte) (intersectsResult, error) {
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(prop)
|
||||
if err != nil {
|
||||
return blockIntersects, err
|
||||
}
|
||||
if !intersects {
|
||||
return blockExcluded, nil
|
||||
}
|
||||
}
|
||||
if i == f.boundLimitedShortID {
|
||||
// The bound-limited filter uses this id.
|
||||
//
|
||||
// The bound-limited filter only applies within a keyspan interval. We
|
||||
// expect the Intersects call to be cheaper than bounds checks. If
|
||||
// Intersects determines that there is no intersection, we return
|
||||
// `blockMaybeExcluded` if no other bpf unconditionally excludes the
|
||||
// block.
|
||||
intersects, err := f.boundLimitedFilter.Intersects(prop)
|
||||
if err != nil {
|
||||
return blockIntersects, err
|
||||
} else if !intersects {
|
||||
return blockMaybeExcluded, nil
|
||||
}
|
||||
}
|
||||
return blockIntersects, nil
|
||||
}
|
||||
|
||||
117
vendor/github.com/cockroachdb/pebble/sstable/block_property_test_utils.go
generated
vendored
Normal file
117
vendor/github.com/cockroachdb/pebble/sstable/block_property_test_utils.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2022 The LevelDB-Go and Pebble Authors. All rights reserved. Use
|
||||
// of this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package sstable
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/testkeys"
|
||||
)
|
||||
|
||||
// Code in this file contains utils for testing. It implements interval block
|
||||
// property collectors and filters on the suffixes of keys in the format used
|
||||
// by the testkeys package (eg, 'key@5').
|
||||
|
||||
const testKeysBlockPropertyName = `pebble.internal.testkeys.suffixes`
|
||||
|
||||
// NewTestKeysBlockPropertyCollector constructs a sstable property collector
|
||||
// over testkey suffixes.
|
||||
func NewTestKeysBlockPropertyCollector() BlockPropertyCollector {
|
||||
return NewBlockIntervalCollector(
|
||||
testKeysBlockPropertyName,
|
||||
&testKeysSuffixIntervalCollector{},
|
||||
nil)
|
||||
}
|
||||
|
||||
// NewTestKeysBlockPropertyFilter constructs a new block-property filter that excludes
|
||||
// blocks containing exclusively suffixed keys where all the suffixes fall
|
||||
// outside of the range [filterMin, filterMax).
|
||||
//
|
||||
// The filter only filters based on data derived from the key. The iteration
|
||||
// results of this block property filter are deterministic for unsuffixed keys
|
||||
// and keys with suffixes within the range [filterMin, filterMax). For keys with
|
||||
// suffixes outside the range, iteration is nondeterministic.
|
||||
func NewTestKeysBlockPropertyFilter(filterMin, filterMax uint64) *BlockIntervalFilter {
|
||||
return NewBlockIntervalFilter(testKeysBlockPropertyName, filterMin, filterMax)
|
||||
}
|
||||
|
||||
// NewTestKeysMaskingFilter constructs a TestKeysMaskingFilter that implements
|
||||
// pebble.BlockPropertyFilterMask for efficient range-key masking using the
|
||||
// testkeys block property filter. The masking filter wraps a block interval
|
||||
// filter, and modifies the configured interval when Pebble requests it.
|
||||
func NewTestKeysMaskingFilter() TestKeysMaskingFilter {
|
||||
return TestKeysMaskingFilter{BlockIntervalFilter: NewTestKeysBlockPropertyFilter(0, math.MaxUint64)}
|
||||
}
|
||||
|
||||
// TestKeysMaskingFilter implements BlockPropertyFilterMask and may be used to mask
|
||||
// point keys with the testkeys-style suffixes (eg, @4) that are masked by range
|
||||
// keys with testkeys-style suffixes.
|
||||
type TestKeysMaskingFilter struct {
|
||||
*BlockIntervalFilter
|
||||
}
|
||||
|
||||
// SetSuffix implements pebble.BlockPropertyFilterMask.
|
||||
func (f TestKeysMaskingFilter) SetSuffix(suffix []byte) error {
|
||||
ts, err := testkeys.ParseSuffix(suffix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.BlockIntervalFilter.SetInterval(uint64(ts), math.MaxUint64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Intersects implements the BlockPropertyFilter interface.
|
||||
func (f TestKeysMaskingFilter) Intersects(prop []byte) (bool, error) {
|
||||
return f.BlockIntervalFilter.Intersects(prop)
|
||||
}
|
||||
|
||||
var _ DataBlockIntervalCollector = (*testKeysSuffixIntervalCollector)(nil)
|
||||
|
||||
// testKeysSuffixIntervalCollector maintains an interval over the timestamps in
|
||||
// MVCC-like suffixes for keys (e.g. foo@123).
|
||||
type testKeysSuffixIntervalCollector struct {
|
||||
initialized bool
|
||||
lower, upper uint64
|
||||
}
|
||||
|
||||
// Add implements DataBlockIntervalCollector by adding the timestamp(s) in the
|
||||
// suffix(es) of this record to the current interval.
|
||||
//
|
||||
// Note that range sets and unsets may have multiple suffixes. Range key deletes
|
||||
// do not have a suffix. All other point keys have a single suffix.
|
||||
func (c *testKeysSuffixIntervalCollector) Add(key base.InternalKey, value []byte) error {
|
||||
i := testkeys.Comparer.Split(key.UserKey)
|
||||
if i == len(key.UserKey) {
|
||||
c.initialized = true
|
||||
c.lower, c.upper = 0, math.MaxUint64
|
||||
return nil
|
||||
}
|
||||
ts, err := testkeys.ParseSuffix(key.UserKey[i:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uts := uint64(ts)
|
||||
if !c.initialized {
|
||||
c.lower, c.upper = uts, uts+1
|
||||
c.initialized = true
|
||||
return nil
|
||||
}
|
||||
if uts < c.lower {
|
||||
c.lower = uts
|
||||
}
|
||||
if uts >= c.upper {
|
||||
c.upper = uts + 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FinishDataBlock implements DataBlockIntervalCollector.
|
||||
func (c *testKeysSuffixIntervalCollector) FinishDataBlock() (lower, upper uint64, err error) {
|
||||
l, u := c.lower, c.upper
|
||||
c.lower, c.upper = 0, 0
|
||||
c.initialized = false
|
||||
return l, u, nil
|
||||
}
|
||||
6
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
@@ -9,6 +9,12 @@ import "github.com/cockroachdb/pebble/internal/base"
|
||||
// InternalKeyKind exports the base.InternalKeyKind type.
|
||||
type InternalKeyKind = base.InternalKeyKind
|
||||
|
||||
// SeekGEFlags exports base.SeekGEFlags.
|
||||
type SeekGEFlags = base.SeekGEFlags
|
||||
|
||||
// SeekLTFlags exports base.SeekLTFlags.
|
||||
type SeekLTFlags = base.SeekLTFlags
|
||||
|
||||
// These constants are part of the file format, and should not be changed.
|
||||
const (
|
||||
InternalKeyKindDelete = base.InternalKeyKindDelete
|
||||
|
||||
16
vendor/github.com/cockroachdb/pebble/sstable/raw_block.go
generated
vendored
16
vendor/github.com/cockroachdb/pebble/sstable/raw_block.go
generated
vendored
@@ -33,7 +33,8 @@ func (w *rawBlockWriter) add(key InternalKey, value []byte) {
|
||||
// keys are stored in "raw" format (i.e. not as internal keys). Note that there
|
||||
// is significant similarity between this code and the code in blockIter. Yet
|
||||
// reducing duplication is difficult due to the blockIter being performance
|
||||
// critical.
|
||||
// critical. rawBlockIter must only be used for blocks where the value is
|
||||
// stored together with the key.
|
||||
type rawBlockIter struct {
|
||||
cmp Compare
|
||||
offset int32
|
||||
@@ -148,19 +149,6 @@ func (i *rawBlockIter) SeekGE(key []byte) bool {
|
||||
return i.Valid()
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
|
||||
// pebble package.
|
||||
func (i *rawBlockIter) SeekPrefixGE(key []byte) bool {
|
||||
// This should never be called as prefix iteration is never used with raw blocks.
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *rawBlockIter) SeekLT(key []byte) bool {
|
||||
panic("pebble/table: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *rawBlockIter) First() bool {
|
||||
|
||||
1142
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
1142
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
File diff suppressed because it is too large
Load Diff
78
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
78
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
@@ -9,13 +9,16 @@ import (
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
)
|
||||
|
||||
// RewriteKeySuffixes copies the content of the passed SSTable bytes to a new
|
||||
// sstable, written to `out`, in which the suffix `from` has is replaced with
|
||||
// `to` in every key. The input sstable must consist of only Sets and every key
|
||||
// must have `from` as its suffix as determined by the Split function of the
|
||||
// Comparer in the passed WriterOptions.
|
||||
// `to` in every key. The input sstable must consist of only Sets or RangeKeySets
|
||||
// and every key must have `from` as its suffix as determined by the Split
|
||||
// function of the Comparer in the passed WriterOptions. Range deletes must not
|
||||
// exist in this sstable, as they will be ignored.
|
||||
//
|
||||
// Data blocks are rewritten in parallel by `concurrency` workers and then
|
||||
// assembled into a final SST. Filters are copied from the original SST without
|
||||
@@ -73,12 +76,14 @@ func rewriteKeySuffixesInBlocks(
|
||||
return nil, errors.Wrap(err, "rewriting data blocks")
|
||||
}
|
||||
|
||||
// Copy over the range key block and replace suffixes in it if it exists.
|
||||
if err := rewriteRangeKeyBlockToWriter(r, w, from, to); err != nil {
|
||||
return nil, errors.Wrap(err, "rewriting range key blocks")
|
||||
}
|
||||
|
||||
// Copy over the filter block if it exists (rewriteDataBlocksToWriter will
|
||||
// already have ensured this is valid if it exists).
|
||||
if w.filter != nil {
|
||||
if l.Filter.Length == 0 {
|
||||
return nil, errors.New("input table has no filter")
|
||||
}
|
||||
if w.filter != nil && l.Filter.Length > 0 {
|
||||
filterBlock, _, err := readBlockBuf(r, l.Filter, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "reading filter")
|
||||
@@ -173,7 +178,8 @@ func rewriteBlocks(
|
||||
copy(scratch.UserKey, key.UserKey[:si])
|
||||
copy(scratch.UserKey[si:], to)
|
||||
|
||||
bw.add(scratch, val)
|
||||
v := val.InPlaceValue()
|
||||
bw.add(scratch, v)
|
||||
if output[i].start.UserKey == nil {
|
||||
keyAlloc, output[i].start = cloneKeyWithBuf(scratch, keyAlloc)
|
||||
}
|
||||
@@ -205,6 +211,10 @@ func rewriteDataBlocksToWriter(
|
||||
split Split,
|
||||
concurrency int,
|
||||
) error {
|
||||
if r.Properties.NumEntries == 0 {
|
||||
// No point keys.
|
||||
return nil
|
||||
}
|
||||
blocks := make([]blockWithSpan, len(data))
|
||||
|
||||
if w.filter != nil {
|
||||
@@ -313,8 +323,47 @@ func rewriteDataBlocksToWriter(
|
||||
w.props.NumEntries = r.Properties.NumEntries
|
||||
w.props.RawKeySize = r.Properties.RawKeySize
|
||||
w.props.RawValueSize = r.Properties.RawValueSize
|
||||
w.meta.SmallestPoint, w.meta.LargestPoint = blocks[0].start, blocks[len(blocks)-1].end
|
||||
w.meta.HasPointKeys = true
|
||||
w.meta.SetSmallestPointKey(blocks[0].start)
|
||||
w.meta.SetLargestPointKey(blocks[len(blocks)-1].end)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rewriteRangeKeyBlockToWriter(r *Reader, w *Writer, from, to []byte) error {
|
||||
iter, err := r.NewRawRangeKeyIter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if iter == nil {
|
||||
// No range keys.
|
||||
return nil
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for s := iter.First(); s != nil; s = iter.Next() {
|
||||
if !s.Valid() {
|
||||
break
|
||||
}
|
||||
for i := range s.Keys {
|
||||
if s.Keys[i].Kind() != base.InternalKeyKindRangeKeySet {
|
||||
return errBadKind
|
||||
}
|
||||
if !bytes.Equal(s.Keys[i].Suffix, from) {
|
||||
return errors.Errorf("key has suffix %q, expected %q", s.Keys[i].Suffix, from)
|
||||
}
|
||||
s.Keys[i].Suffix = to
|
||||
}
|
||||
|
||||
err := rangekey.Encode(s, func(k base.InternalKey, v []byte) error {
|
||||
// Calling AddRangeKey instead of addRangeKeySpan bypasses the fragmenter.
|
||||
// This is okay because the raw fragments off of `iter` are already
|
||||
// fragmented, and suffix replacement should not affect fragmentation.
|
||||
return w.AddRangeKey(k, v)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -362,11 +411,18 @@ func RewriteKeySuffixesViaWriter(
|
||||
scratch.UserKey = append(scratch.UserKey, to...)
|
||||
scratch.Trailer = k.Trailer
|
||||
|
||||
if w.addPoint(scratch, v); err != nil {
|
||||
val, _, err := v.Value(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if w.addPoint(scratch, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k, v = i.Next()
|
||||
}
|
||||
if err := rewriteRangeKeyBlockToWriter(r, w, from, to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
8
vendor/github.com/cockroachdb/pebble/sstable/write_queue.go
generated
vendored
8
vendor/github.com/cockroachdb/pebble/sstable/write_queue.go
generated
vendored
@@ -17,8 +17,6 @@ type writeTask struct {
|
||||
// currIndexBlock is the index block on which indexBlock.add must be called.
|
||||
currIndexBlock *indexBlockBuf
|
||||
indexEntrySep InternalKey
|
||||
// inflightSize is used to decrement Writer.coordination.sizeEstimate.inflightSize.
|
||||
inflightSize int
|
||||
// inflightIndexEntrySize is used to decrement Writer.indexBlock.sizeEstimate.inflightSize.
|
||||
indexInflightSize int
|
||||
// If the index block is finished, then we set the finishedIndexProps here.
|
||||
@@ -69,11 +67,6 @@ func (w *writeQueue) performWrite(task *writeTask) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the size estimates after writing the data block to disk.
|
||||
w.writer.coordination.sizeEstimate.dataBlockWritten(
|
||||
w.writer.meta.Size, task.inflightSize, int(bh.Length),
|
||||
)
|
||||
|
||||
bhp = BlockHandleWithProperties{BlockHandle: bh, Props: task.buf.dataBlockProps}
|
||||
if err = w.writer.addIndexEntry(
|
||||
task.indexEntrySep, bhp, task.buf.tmp[:], task.flushableIndexBlock, task.currIndexBlock,
|
||||
@@ -114,7 +107,6 @@ func (w *writeQueue) runWorker() {
|
||||
w.wg.Done()
|
||||
}
|
||||
|
||||
//lint:ignore U1000 - Will be used in a future pr.
|
||||
func (w *writeQueue) add(task *writeTask) {
|
||||
w.tasks <- task
|
||||
}
|
||||
|
||||
142
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
142
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
@@ -221,7 +221,9 @@ type coordinationState struct {
|
||||
|
||||
func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
|
||||
c.parallelismEnabled = parallelismEnabled
|
||||
c.sizeEstimate.useMutex = parallelismEnabled
|
||||
// useMutex is false regardless of parallelismEnabled, because we do not do
|
||||
// parallel compression yet.
|
||||
c.sizeEstimate.useMutex = false
|
||||
|
||||
// writeQueueSize determines the size of the write queue, or the number
|
||||
// of items which can be added to the queue without blocking. By default, we
|
||||
@@ -234,6 +236,47 @@ func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
|
||||
c.writeQueue = newWriteQueue(writeQueueSize, writer)
|
||||
}
|
||||
|
||||
// sizeEstimate is a general purpose helper for estimating two kinds of sizes:
|
||||
// A. The compressed sstable size, which is useful for deciding when to start
|
||||
// a new sstable during flushes or compactions. In practice, we use this in
|
||||
// estimating the data size (excluding the index).
|
||||
// B. The size of index blocks to decide when to start a new index block.
|
||||
//
|
||||
// There are some terminology peculiarities which are due to the origin of
|
||||
// sizeEstimate for use case A with parallel compression enabled (for which
|
||||
// the code has not been merged). Specifically this relates to the terms
|
||||
// "written" and "compressed".
|
||||
// - The notion of "written" for case A is sufficiently defined by saying that
|
||||
// the data block is compressed. Waiting for the actual data block write to
|
||||
// happen can result in unnecessary estimation, when we already know how big
|
||||
// it will be in compressed form. Additionally, with the forthcoming value
|
||||
// blocks containing older MVCC values, these compressed block will be held
|
||||
// in-memory until late in the sstable writing, and we do want to accurately
|
||||
// account for them without waiting for the actual write.
|
||||
// For case B, "written" means that the index entry has been fully
|
||||
// generated, and has been added to the uncompressed block buffer for that
|
||||
// index block. It does not include actually writing a potentially
|
||||
// compressed index block.
|
||||
// - The notion of "compressed" is to differentiate between a "inflight" size
|
||||
// and the actual size, and is handled via computing a compression ratio
|
||||
// observed so far (defaults to 1).
|
||||
// For case A, this is actual data block compression, so the "inflight" size
|
||||
// is uncompressed blocks (that are no longer being written to) and the
|
||||
// "compressed" size is after they have been compressed.
|
||||
// For case B the inflight size is for a key-value pair in the index for
|
||||
// which the value size (the encoded size of the BlockHandleWithProperties)
|
||||
// is not accurately known, while the compressed size is the size of that
|
||||
// entry when it has been added to the (in-progress) index ssblock.
|
||||
//
|
||||
// Usage: To update state, one can optionally provide an inflight write value
|
||||
// using addInflight (used for case B). When something is "written" the state
|
||||
// can be updated using either writtenWithDelta or writtenWithTotal, which
|
||||
// provide the actual delta size or the total size (latter must be
|
||||
// monotonically non-decreasing). If there were no calls to addInflight, there
|
||||
// isn't any real estimation happening here. So case A does not do any real
|
||||
// estimation. However, when we introduce parallel compression, there will be
|
||||
// estimation in that the client goroutine will call addInFlight and the
|
||||
// compression goroutines will call writtenWithDelta.
|
||||
type sizeEstimate struct {
|
||||
// emptySize is the size when there is no inflight data, and numEntries is 0.
|
||||
// emptySize is constant once set.
|
||||
@@ -300,18 +343,22 @@ func (s *sizeEstimate) addInflight(size int) {
|
||||
s.inflightSize += uint64(size)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) written(newTotalSize uint64, inflightSize int, finalEntrySize int) {
|
||||
s.inflightSize -= uint64(inflightSize)
|
||||
func (s *sizeEstimate) writtenWithTotal(newTotalSize uint64, inflightSize int) {
|
||||
finalEntrySize := int(newTotalSize - s.totalSize)
|
||||
s.writtenWithDelta(finalEntrySize, inflightSize)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) writtenWithDelta(finalEntrySize int, inflightSize int) {
|
||||
if inflightSize > 0 {
|
||||
// This entry was previously inflight, so we should decrement inflight
|
||||
// entries.
|
||||
// entries and update the "compression" stats for future estimation.
|
||||
s.numInflightEntries--
|
||||
s.inflightSize -= uint64(inflightSize)
|
||||
s.uncompressedSize += uint64(inflightSize)
|
||||
s.compressedSize += uint64(finalEntrySize)
|
||||
}
|
||||
s.numWrittenEntries++
|
||||
s.totalSize = newTotalSize
|
||||
|
||||
s.uncompressedSize += uint64(inflightSize)
|
||||
s.compressedSize += uint64(finalEntrySize)
|
||||
s.totalSize += uint64(finalEntrySize)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) clear() {
|
||||
@@ -368,7 +415,6 @@ func (i *indexBlockBuf) shouldFlush(
|
||||
defer i.size.mu.Unlock()
|
||||
}
|
||||
|
||||
// nEntries := i.size.estimate.numWrittenEntries + i.size.estimate.numInflightEntries
|
||||
nEntries := i.size.estimate.numTotalEntries()
|
||||
return shouldFlush(
|
||||
sep, valueLen, i.restartInterval, int(i.size.estimate.size()),
|
||||
@@ -382,10 +428,7 @@ func (i *indexBlockBuf) add(key InternalKey, value []byte, inflightSize int) {
|
||||
i.size.mu.Lock()
|
||||
defer i.size.mu.Unlock()
|
||||
}
|
||||
// Since, we're not compressing index entries when adding them to index blocks,
|
||||
// we assume that the size of entry written to the index block is equal to the
|
||||
// size of the inflight entry, giving us a compression ratio of 1.
|
||||
i.size.estimate.written(uint64(size), inflightSize, inflightSize)
|
||||
i.size.estimate.writtenWithTotal(uint64(size), inflightSize)
|
||||
}
|
||||
|
||||
func (i *indexBlockBuf) finish() []byte {
|
||||
@@ -424,11 +467,12 @@ func (i *indexBlockBuf) estimatedSize() uint64 {
|
||||
return i.size.estimate.size()
|
||||
}
|
||||
|
||||
// sizeEstimate is used for sstable size estimation. sizeEstimate can be accessed by
|
||||
// the Writer client, writeQueue, compressionQueue goroutines. Fields should only be
|
||||
// read/updated through the functions defined on the *sizeEstimate type.
|
||||
// sizeEstimate is used for sstable size estimation. sizeEstimate can be
|
||||
// accessed by the Writer client and compressionQueue goroutines. Fields
|
||||
// should only be read/updated through the functions defined on the
|
||||
// *sizeEstimate type.
|
||||
type dataBlockEstimates struct {
|
||||
// If we don't do block compression, block writes in parallel, then we don't need to take
|
||||
// If we don't do block compression in parallel, then we don't need to take
|
||||
// the performance hit of synchronizing using this mutex.
|
||||
useMutex bool
|
||||
mu sync.Mutex
|
||||
@@ -436,18 +480,16 @@ type dataBlockEstimates struct {
|
||||
estimate sizeEstimate
|
||||
}
|
||||
|
||||
// newTotalSize is the new w.meta.Size. inflightSize is the uncompressed block size estimate which
|
||||
// was previously added to sizeEstimate.inflightSize. writtenSize is the compressed size of the block
|
||||
// which was written to disk.
|
||||
func (d *dataBlockEstimates) dataBlockWritten(
|
||||
newTotalSize uint64, inflightSize int, writtenSize int,
|
||||
) {
|
||||
// inflightSize is the uncompressed block size estimate which has been
|
||||
// previously provided to addInflightDataBlock(). If addInflightDataBlock()
|
||||
// has not been called, this must be set to 0. compressedSize is the
|
||||
// compressed size of the block.
|
||||
func (d *dataBlockEstimates) dataBlockCompressed(compressedSize int, inflightSize int) {
|
||||
if d.useMutex {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
}
|
||||
|
||||
d.estimate.written(newTotalSize, inflightSize, writtenSize)
|
||||
d.estimate.writtenWithDelta(compressedSize+blockTrailerLen, inflightSize)
|
||||
}
|
||||
|
||||
// size is an estimated size of datablock data which has been written to disk.
|
||||
@@ -456,18 +498,19 @@ func (d *dataBlockEstimates) size() uint64 {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
}
|
||||
|
||||
// Use invariants to make sure that the size estimation works as expected
|
||||
// when parallelism is disabled.
|
||||
// If there is no parallel compression, there should not be any inflight bytes.
|
||||
if invariants.Enabled && !d.useMutex {
|
||||
if d.estimate.inflightSize != 0 {
|
||||
panic("unexpected inflight entry in data block size estimation")
|
||||
}
|
||||
}
|
||||
|
||||
return d.estimate.size()
|
||||
}
|
||||
|
||||
// Avoid linter unused error.
|
||||
var _ = (&dataBlockEstimates{}).addInflightDataBlock
|
||||
|
||||
// NB: unused since no parallel compression.
|
||||
func (d *dataBlockEstimates) addInflightDataBlock(size int) {
|
||||
if d.useMutex {
|
||||
d.mu.Lock()
|
||||
@@ -609,9 +652,10 @@ type indexBlockAndBlockProperties struct {
|
||||
block []byte
|
||||
}
|
||||
|
||||
// Set sets the value for the given key. The sequence number is set to
|
||||
// 0. Intended for use to externally construct an sstable before ingestion into
|
||||
// a DB.
|
||||
// Set sets the value for the given key. The sequence number is set to 0.
|
||||
// Intended for use to externally construct an sstable before ingestion into a
|
||||
// DB. For a given Writer, the keys passed to Set must be in strictly increasing
|
||||
// order.
|
||||
//
|
||||
// TODO(peter): untested
|
||||
func (w *Writer) Set(key, value []byte) error {
|
||||
@@ -708,8 +752,8 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
// 3.5% faster on BenchmarkWriter on go1.13. Remove if go1.14 or future
|
||||
// versions show this to not be a performance win.
|
||||
x := w.compare(largestPointKey.UserKey, key.UserKey)
|
||||
if x > 0 || (x == 0 && largestPointKey.Trailer < key.Trailer) {
|
||||
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
||||
if x > 0 || (x == 0 && largestPointKey.Trailer <= key.Trailer) {
|
||||
w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
largestPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
return w.err
|
||||
}
|
||||
@@ -790,7 +834,7 @@ func (w *Writer) addTombstone(key InternalKey, value []byte) error {
|
||||
return w.err
|
||||
}
|
||||
if prevKey.SeqNum() <= key.SeqNum() {
|
||||
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
||||
w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
prevKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
return w.err
|
||||
}
|
||||
@@ -938,7 +982,9 @@ func (w *Writer) addRangeKeySpan(span keyspan.Span) error {
|
||||
func (w *Writer) coalesceSpans(span keyspan.Span) {
|
||||
// This method is the emit function of the Fragmenter, so span.Keys is only
|
||||
// owned by this span and it's safe to mutate.
|
||||
err := rangekey.Coalesce(w.compare, span, &w.rangeKeyCoalesced)
|
||||
w.rangeKeyCoalesced.Start = span.Start
|
||||
w.rangeKeyCoalesced.End = span.End
|
||||
err := rangekey.Coalesce(w.compare, span.Keys, &w.rangeKeyCoalesced.Keys)
|
||||
if err != nil {
|
||||
w.err = errors.Newf("sstable: could not coalesce span: %s", err)
|
||||
return
|
||||
@@ -946,7 +992,7 @@ func (w *Writer) coalesceSpans(span keyspan.Span) {
|
||||
|
||||
// NB: The span only contains range keys and is internally consistent (eg,
|
||||
// no duplicate suffixes, no additional keys after a RANGEKEYDEL).
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(w.rangeKeyCoalesced))
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(&w.rangeKeyCoalesced))
|
||||
}
|
||||
|
||||
func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
|
||||
@@ -1080,19 +1126,16 @@ func (w *Writer) maybeAddToFilter(key []byte) {
|
||||
}
|
||||
|
||||
func (w *Writer) flush(key InternalKey) error {
|
||||
estimatedUncompressedSize := w.dataBlockBuf.dataBlock.estimatedSize()
|
||||
w.coordination.sizeEstimate.addInflightDataBlock(estimatedUncompressedSize)
|
||||
|
||||
var err error
|
||||
|
||||
// We're finishing a data block.
|
||||
err = w.finishDataBlockProps(w.dataBlockBuf)
|
||||
err := w.finishDataBlockProps(w.dataBlockBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.dataBlockBuf.finish()
|
||||
w.dataBlockBuf.compressAndChecksum(w.compression)
|
||||
// Since dataBlockEstimates.addInflightDataBlock was never called, the
|
||||
// inflightSize is set to 0.
|
||||
w.coordination.sizeEstimate.dataBlockCompressed(len(w.dataBlockBuf.compressed), 0)
|
||||
|
||||
// Determine if the index block should be flushed. Since we're accessing the
|
||||
// dataBlockBuf.dataBlock.curKey here, we have to make sure that once we start
|
||||
@@ -1139,7 +1182,6 @@ func (w *Writer) flush(key InternalKey) error {
|
||||
writeTask.compressionDone <- true
|
||||
writeTask.buf = w.dataBlockBuf
|
||||
writeTask.indexEntrySep = sep
|
||||
writeTask.inflightSize = estimatedUncompressedSize
|
||||
writeTask.currIndexBlock = w.indexBlock
|
||||
writeTask.indexInflightSize = sep.Size() + encodedBHPEstimatedSize
|
||||
writeTask.finishedIndexProps = indexProps
|
||||
@@ -1813,14 +1855,6 @@ func (w *Writer) Close() (err error) {
|
||||
// EstimatedSize returns the estimated size of the sstable being written if a
|
||||
// call to Finish() was made without adding additional keys.
|
||||
func (w *Writer) EstimatedSize() uint64 {
|
||||
if invariants.Enabled && !w.coordination.parallelismEnabled {
|
||||
// The w.meta.Size should only be accessed from the writeQueue goroutine
|
||||
// if parallelism is enabled, but since it isn't we break that invariant
|
||||
// here.
|
||||
if w.coordination.sizeEstimate.size() != w.meta.Size {
|
||||
panic("sstable size estimation sans parallelism is incorrect")
|
||||
}
|
||||
}
|
||||
return w.coordination.sizeEstimate.size() +
|
||||
uint64(w.dataBlockBuf.dataBlock.estimatedSize()) +
|
||||
w.indexBlock.estimatedSize()
|
||||
@@ -1838,7 +1872,7 @@ func (w *Writer) Metadata() (*WriterMetadata, error) {
|
||||
// WriterOption provide an interface to do work on Writer while it is being
|
||||
// opened.
|
||||
type WriterOption interface {
|
||||
// writerAPply is called on the writer during opening in order to set
|
||||
// writerApply is called on the writer during opening in order to set
|
||||
// internal parameters.
|
||||
writerApply(*Writer)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user