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

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