Upgrade versions of vendored libs

This commit is contained in:
eleksir
2022-11-04 16:29:14 +03:00
parent fba732cc24
commit a7ae372459
941 changed files with 219674 additions and 23611 deletions

View File

@@ -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.

View File

@@ -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
}

View 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
}

View File

@@ -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

View File

@@ -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 {

File diff suppressed because it is too large Load Diff

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}