Update dependencies
This commit is contained in:
36
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
36
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manual"
|
||||
@@ -400,10 +399,9 @@ type blockIter struct {
|
||||
// For a block encoded with a restart interval of 1, cached and cachedBuf
|
||||
// will not be used as there are no prefix compressed entries between the
|
||||
// restart points.
|
||||
cached []blockEntry
|
||||
cachedBuf []byte
|
||||
cacheHandle cache.Handle
|
||||
// The first user key in the block. This is used by the caller to set bounds
|
||||
cached []blockEntry
|
||||
cachedBuf []byte
|
||||
handle bufferHandle
|
||||
// for block iteration for already loaded blocks.
|
||||
firstUserKey []byte
|
||||
lazyValueHandling struct {
|
||||
@@ -458,10 +456,10 @@ func (i *blockIter) init(
|
||||
// ingested.
|
||||
// - Foreign sstable iteration: globalSeqNum is always set.
|
||||
func (i *blockIter) initHandle(
|
||||
cmp Compare, block cache.Handle, globalSeqNum uint64, hideObsoletePoints bool,
|
||||
cmp Compare, block bufferHandle, globalSeqNum uint64, hideObsoletePoints bool,
|
||||
) error {
|
||||
i.cacheHandle.Release()
|
||||
i.cacheHandle = block
|
||||
i.handle.Release()
|
||||
i.handle = block
|
||||
return i.init(cmp, block.Get(), globalSeqNum, hideObsoletePoints)
|
||||
}
|
||||
|
||||
@@ -690,8 +688,11 @@ func (i *blockIter) getFirstUserKey() []byte {
|
||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
if invariants.Enabled && i.isDataInvalidated() {
|
||||
panic(errors.AssertionFailedf("invalidated blockIter used"))
|
||||
}
|
||||
|
||||
i.clearCache()
|
||||
// Find the index of the smallest restart point whose key is > the key
|
||||
// sought; index will be numRestarts if there is no such restart point.
|
||||
i.offset = 0
|
||||
@@ -820,8 +821,11 @@ func (i *blockIter) SeekPrefixGE(
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
if invariants.Enabled && i.isDataInvalidated() {
|
||||
panic(errors.AssertionFailedf("invalidated blockIter used"))
|
||||
}
|
||||
|
||||
i.clearCache()
|
||||
// Find the index of the smallest restart point whose key is >= the key
|
||||
// sought; index will be numRestarts if there is no such restart point.
|
||||
i.offset = 0
|
||||
@@ -988,6 +992,10 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) First() (*InternalKey, base.LazyValue) {
|
||||
if invariants.Enabled && i.isDataInvalidated() {
|
||||
panic(errors.AssertionFailedf("invalidated blockIter used"))
|
||||
}
|
||||
|
||||
i.offset = 0
|
||||
if !i.valid() {
|
||||
return nil, base.LazyValue{}
|
||||
@@ -1017,6 +1025,10 @@ func decodeRestart(b []byte) int32 {
|
||||
|
||||
// Last implements internalIterator.Last, as documented in the pebble package.
|
||||
func (i *blockIter) Last() (*InternalKey, base.LazyValue) {
|
||||
if invariants.Enabled && i.isDataInvalidated() {
|
||||
panic(errors.AssertionFailedf("invalidated blockIter used"))
|
||||
}
|
||||
|
||||
// Seek forward from the last restart point.
|
||||
i.offset = decodeRestart(i.data[i.restarts+4*(i.numRestarts-1):])
|
||||
if !i.valid() {
|
||||
@@ -1515,8 +1527,8 @@ func (i *blockIter) Error() error {
|
||||
// Close implements internalIterator.Close, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Close() error {
|
||||
i.cacheHandle.Release()
|
||||
i.cacheHandle = cache.Handle{}
|
||||
i.handle.Release()
|
||||
i.handle = bufferHandle{}
|
||||
i.val = nil
|
||||
i.lazyValue = base.LazyValue{}
|
||||
i.lazyValueHandling.vbr = nil
|
||||
|
||||
28
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
28
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
@@ -189,13 +190,13 @@ type BoundLimitedBlockPropertyFilter interface {
|
||||
// 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
|
||||
KeyIsWithinLowerBound(key []byte) 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
|
||||
KeyIsWithinUpperBound(key []byte) bool
|
||||
}
|
||||
|
||||
// BlockIntervalCollector is a helper implementation of BlockPropertyCollector
|
||||
@@ -643,15 +644,21 @@ func (f *BlockPropertiesFilterer) intersectsUserPropsAndFinishInit(
|
||||
// considered intersecting.
|
||||
continue
|
||||
}
|
||||
byteProps := []byte(props)
|
||||
if len(byteProps) < 1 {
|
||||
if len(props) < 1 {
|
||||
return false, base.CorruptionErrorf(
|
||||
"block properties for %s is corrupted", f.filters[i].Name())
|
||||
}
|
||||
shortID := shortID(byteProps[0])
|
||||
intersects, err := f.filters[i].Intersects(byteProps[1:])
|
||||
if err != nil || !intersects {
|
||||
return false, err
|
||||
shortID := shortID(props[0])
|
||||
{
|
||||
// Use an unsafe conversion to avoid allocating. Intersects() is not
|
||||
// supposed to modify the given slice.
|
||||
// Note that unsafe.StringData only works if the string is not empty
|
||||
// (which we already checked).
|
||||
byteProps := unsafe.Slice(unsafe.StringData(props), len(props))
|
||||
intersects, err := f.filters[i].Intersects(byteProps[1:])
|
||||
if err != nil || !intersects {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
// Intersects the sstable, so need to use this filter when
|
||||
// deciding whether to read blocks.
|
||||
@@ -684,12 +691,11 @@ func (f *BlockPropertiesFilterer) intersectsUserPropsAndFinishInit(
|
||||
// be unused within this file.
|
||||
return true, nil
|
||||
}
|
||||
byteProps := []byte(props)
|
||||
if len(byteProps) < 1 {
|
||||
if len(props) < 1 {
|
||||
return false, base.CorruptionErrorf(
|
||||
"block properties for %s is corrupted", f.boundLimitedFilter.Name())
|
||||
}
|
||||
f.boundLimitedShortID = int(byteProps[0])
|
||||
f.boundLimitedShortID = int(props[0])
|
||||
|
||||
// We don't check for table-level intersection for the bound-limited filter.
|
||||
// The bound-limited filter is treated as vacuously intersecting.
|
||||
|
||||
148
vendor/github.com/cockroachdb/pebble/sstable/buffer_pool.go
generated
vendored
Normal file
148
vendor/github.com/cockroachdb/pebble/sstable/buffer_pool.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2023 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 (
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
)
|
||||
|
||||
// A bufferHandle is a handle to manually-managed memory. The handle may point
|
||||
// to a block in the block cache (h.Get() != nil), or a buffer that exists
|
||||
// outside the block cache allocated from a BufferPool (b.Valid()).
|
||||
type bufferHandle struct {
|
||||
h cache.Handle
|
||||
b Buf
|
||||
}
|
||||
|
||||
// Get retrieves the underlying buffer referenced by the handle.
|
||||
func (bh bufferHandle) Get() []byte {
|
||||
if v := bh.h.Get(); v != nil {
|
||||
return v
|
||||
} else if bh.b.p != nil {
|
||||
return bh.b.p.pool[bh.b.i].b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release releases the buffer, either back to the block cache or BufferPool.
|
||||
func (bh bufferHandle) Release() {
|
||||
bh.h.Release()
|
||||
bh.b.Release()
|
||||
}
|
||||
|
||||
// A BufferPool holds a pool of buffers for holding sstable blocks. An initial
|
||||
// size of the pool is provided on Init, but a BufferPool will grow to meet the
|
||||
// largest working set size. It'll never shrink. When a buffer is released, the
|
||||
// BufferPool recycles the buffer for future allocations.
|
||||
//
|
||||
// A BufferPool should only be used for short-lived allocations with
|
||||
// well-understood working set sizes to avoid excessive memory consumption.
|
||||
//
|
||||
// BufferPool is not thread-safe.
|
||||
type BufferPool struct {
|
||||
// pool contains all the buffers held by the pool, including buffers that
|
||||
// are in-use. For every i < len(pool): pool[i].v is non-nil.
|
||||
pool []allocedBuffer
|
||||
}
|
||||
|
||||
type allocedBuffer struct {
|
||||
v *cache.Value
|
||||
// b holds the current byte slice. It's backed by v, but may be a subslice
|
||||
// of v's memory while the buffer is in-use [ len(b) ≤ len(v.Buf()) ].
|
||||
//
|
||||
// If the buffer is not currently in-use, b is nil. When being recycled, the
|
||||
// BufferPool.Alloc will reset b to be a subslice of v.Buf().
|
||||
b []byte
|
||||
}
|
||||
|
||||
// Init initializes the pool with an initial working set buffer size of
|
||||
// `initialSize`.
|
||||
func (p *BufferPool) Init(initialSize int) {
|
||||
*p = BufferPool{
|
||||
pool: make([]allocedBuffer, 0, initialSize),
|
||||
}
|
||||
}
|
||||
|
||||
// initPreallocated is like Init but for internal sstable package use in
|
||||
// instances where a pre-allocated slice of []allocedBuffer already exists. It's
|
||||
// used to avoid an extra allocation initializing BufferPool.pool.
|
||||
func (p *BufferPool) initPreallocated(pool []allocedBuffer) {
|
||||
*p = BufferPool{
|
||||
pool: pool[:0],
|
||||
}
|
||||
}
|
||||
|
||||
// Release releases all buffers held by the pool and resets the pool to an
|
||||
// uninitialized state.
|
||||
func (p *BufferPool) Release() {
|
||||
for i := range p.pool {
|
||||
if p.pool[i].b != nil {
|
||||
panic(errors.AssertionFailedf("Release called on a BufferPool with in-use buffers"))
|
||||
}
|
||||
cache.Free(p.pool[i].v)
|
||||
}
|
||||
*p = BufferPool{}
|
||||
}
|
||||
|
||||
// Alloc allocates a new buffer of size n. If the pool already holds a buffer at
|
||||
// least as large as n, the pooled buffer is used instead.
|
||||
//
|
||||
// Alloc is O(MAX(N,M)) where N is the largest number of concurrently in-use
|
||||
// buffers allocated and M is the initialSize passed to Init.
|
||||
func (p *BufferPool) Alloc(n int) Buf {
|
||||
unusableBufferIdx := -1
|
||||
for i := 0; i < len(p.pool); i++ {
|
||||
if p.pool[i].b == nil {
|
||||
if len(p.pool[i].v.Buf()) >= n {
|
||||
p.pool[i].b = p.pool[i].v.Buf()[:n]
|
||||
return Buf{p: p, i: i}
|
||||
}
|
||||
unusableBufferIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
// If we would need to grow the size of the pool to allocate another buffer,
|
||||
// but there was a slot available occupied by a buffer that's just too
|
||||
// small, replace the too-small buffer.
|
||||
if len(p.pool) == cap(p.pool) && unusableBufferIdx >= 0 {
|
||||
i := unusableBufferIdx
|
||||
cache.Free(p.pool[i].v)
|
||||
p.pool[i].v = cache.Alloc(n)
|
||||
p.pool[i].b = p.pool[i].v.Buf()
|
||||
return Buf{p: p, i: i}
|
||||
}
|
||||
|
||||
// Allocate a new buffer.
|
||||
v := cache.Alloc(n)
|
||||
p.pool = append(p.pool, allocedBuffer{v: v, b: v.Buf()[:n]})
|
||||
return Buf{p: p, i: len(p.pool) - 1}
|
||||
}
|
||||
|
||||
// A Buf holds a reference to a manually-managed, pooled byte buffer.
|
||||
type Buf struct {
|
||||
p *BufferPool
|
||||
// i holds the index into p.pool where the buffer may be found. This scheme
|
||||
// avoids needing to allocate the handle to the buffer on the heap at the
|
||||
// cost of copying two words instead of one.
|
||||
i int
|
||||
}
|
||||
|
||||
// Valid returns true if the buf holds a valid buffer.
|
||||
func (b Buf) Valid() bool {
|
||||
return b.p != nil
|
||||
}
|
||||
|
||||
// Release releases the buffer back to the pool.
|
||||
func (b *Buf) Release() {
|
||||
if b.p == nil {
|
||||
return
|
||||
}
|
||||
// Clear the allocedBuffer's byte slice. This signals the allocated buffer
|
||||
// is no longer in use and a future call to BufferPool.Alloc may reuse this
|
||||
// buffer.
|
||||
b.p.pool[b.i].b = nil
|
||||
b.p = nil
|
||||
}
|
||||
11
vendor/github.com/cockroachdb/pebble/sstable/compression.go
generated
vendored
11
vendor/github.com/cockroachdb/pebble/sstable/compression.go
generated
vendored
@@ -52,8 +52,10 @@ func decompressInto(blockType blockType, compressed []byte, buf []byte) ([]byte,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// decompressBlock decompresses an SST block, with space allocated from a cache.
|
||||
func decompressBlock(cache *cache.Cache, blockType blockType, b []byte) (*cache.Value, error) {
|
||||
// decompressBlock decompresses an SST block, with manually-allocated space.
|
||||
// NB: If decompressBlock returns (nil, nil), no decompression was necessary and
|
||||
// the caller may use `b` directly.
|
||||
func decompressBlock(blockType blockType, b []byte) (*cache.Value, error) {
|
||||
if blockType == noCompressionBlockType {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -62,14 +64,13 @@ func decompressBlock(cache *cache.Cache, blockType blockType, b []byte) (*cache.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prefixLen != 0 {
|
||||
b = b[prefixLen:]
|
||||
}
|
||||
b = b[prefixLen:]
|
||||
// Allocate sufficient space from the cache.
|
||||
decoded := cache.Alloc(decodedLen)
|
||||
decodedBuf := decoded.Buf()
|
||||
if _, err := decompressInto(blockType, b, decodedBuf); err != nil {
|
||||
cache.Free(decoded)
|
||||
return nil, err
|
||||
}
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
6
vendor/github.com/cockroachdb/pebble/sstable/format.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/sstable/format.go
generated
vendored
@@ -25,8 +25,9 @@ const (
|
||||
TableFormatPebblev2 // Range keys.
|
||||
TableFormatPebblev3 // Value blocks.
|
||||
TableFormatPebblev4 // DELSIZED tombstones.
|
||||
NumTableFormats
|
||||
|
||||
TableFormatMax = TableFormatPebblev4
|
||||
TableFormatMax = NumTableFormats - 1
|
||||
)
|
||||
|
||||
// TableFormatPebblev4, in addition to DELSIZED, introduces the use of
|
||||
@@ -91,7 +92,8 @@ const (
|
||||
// obsolete, i.e., obsolete within the sstable, and we can guarantee that at
|
||||
// most one point will be exposed per user key. MERGE keys create more
|
||||
// complexity: MERGE followed by MERGE causes multiple keys to not be
|
||||
// obsolete. Same applies for MERGE followed by SET/SETWITHDEL. Note that:
|
||||
// obsolete. Same applies for MERGE followed by SET/SETWITHDEL/DEL*. Note
|
||||
// that:
|
||||
//
|
||||
// - For regular sst iteration, the obsolete marking is a performance
|
||||
// optimization, and multiple keys for the same userkey can be handled by
|
||||
|
||||
306
vendor/github.com/cockroachdb/pebble/sstable/layout.go
generated
vendored
Normal file
306
vendor/github.com/cockroachdb/pebble/sstable/layout.go
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
// Copyright 2011 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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
)
|
||||
|
||||
// Layout describes the block organization of an sstable.
|
||||
type Layout struct {
|
||||
// NOTE: changes to fields in this struct should also be reflected in
|
||||
// ValidateBlockChecksums, which validates a static list of BlockHandles
|
||||
// referenced in this struct.
|
||||
|
||||
Data []BlockHandleWithProperties
|
||||
Index []BlockHandle
|
||||
TopIndex BlockHandle
|
||||
Filter BlockHandle
|
||||
RangeDel BlockHandle
|
||||
RangeKey BlockHandle
|
||||
ValueBlock []BlockHandle
|
||||
ValueIndex BlockHandle
|
||||
Properties BlockHandle
|
||||
MetaIndex BlockHandle
|
||||
Footer BlockHandle
|
||||
Format TableFormat
|
||||
}
|
||||
|
||||
// Describe returns a description of the layout. If the verbose parameter is
|
||||
// true, details of the structure of each block are returned as well.
|
||||
func (l *Layout) Describe(
|
||||
w io.Writer, verbose bool, r *Reader, fmtRecord func(key *base.InternalKey, value []byte),
|
||||
) {
|
||||
ctx := context.TODO()
|
||||
type block struct {
|
||||
BlockHandle
|
||||
name string
|
||||
}
|
||||
var blocks []block
|
||||
|
||||
for i := range l.Data {
|
||||
blocks = append(blocks, block{l.Data[i].BlockHandle, "data"})
|
||||
}
|
||||
for i := range l.Index {
|
||||
blocks = append(blocks, block{l.Index[i], "index"})
|
||||
}
|
||||
if l.TopIndex.Length != 0 {
|
||||
blocks = append(blocks, block{l.TopIndex, "top-index"})
|
||||
}
|
||||
if l.Filter.Length != 0 {
|
||||
blocks = append(blocks, block{l.Filter, "filter"})
|
||||
}
|
||||
if l.RangeDel.Length != 0 {
|
||||
blocks = append(blocks, block{l.RangeDel, "range-del"})
|
||||
}
|
||||
if l.RangeKey.Length != 0 {
|
||||
blocks = append(blocks, block{l.RangeKey, "range-key"})
|
||||
}
|
||||
for i := range l.ValueBlock {
|
||||
blocks = append(blocks, block{l.ValueBlock[i], "value-block"})
|
||||
}
|
||||
if l.ValueIndex.Length != 0 {
|
||||
blocks = append(blocks, block{l.ValueIndex, "value-index"})
|
||||
}
|
||||
if l.Properties.Length != 0 {
|
||||
blocks = append(blocks, block{l.Properties, "properties"})
|
||||
}
|
||||
if l.MetaIndex.Length != 0 {
|
||||
blocks = append(blocks, block{l.MetaIndex, "meta-index"})
|
||||
}
|
||||
if l.Footer.Length != 0 {
|
||||
if l.Footer.Length == levelDBFooterLen {
|
||||
blocks = append(blocks, block{l.Footer, "leveldb-footer"})
|
||||
} else {
|
||||
blocks = append(blocks, block{l.Footer, "footer"})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(blocks, func(i, j int) bool {
|
||||
return blocks[i].Offset < blocks[j].Offset
|
||||
})
|
||||
|
||||
for i := range blocks {
|
||||
b := &blocks[i]
|
||||
fmt.Fprintf(w, "%10d %s (%d)\n", b.Offset, b.name, b.Length)
|
||||
|
||||
if !verbose {
|
||||
continue
|
||||
}
|
||||
if b.name == "filter" {
|
||||
continue
|
||||
}
|
||||
|
||||
if b.name == "footer" || b.name == "leveldb-footer" {
|
||||
trailer, offset := make([]byte, b.Length), b.Offset
|
||||
_ = r.readable.ReadAt(ctx, trailer, int64(offset))
|
||||
|
||||
if b.name == "footer" {
|
||||
checksumType := ChecksumType(trailer[0])
|
||||
fmt.Fprintf(w, "%10d checksum type: %s\n", offset, checksumType)
|
||||
trailer, offset = trailer[1:], offset+1
|
||||
}
|
||||
|
||||
metaHandle, n := binary.Uvarint(trailer)
|
||||
metaLen, m := binary.Uvarint(trailer[n:])
|
||||
fmt.Fprintf(w, "%10d meta: offset=%d, length=%d\n", offset, metaHandle, metaLen)
|
||||
trailer, offset = trailer[n+m:], offset+uint64(n+m)
|
||||
|
||||
indexHandle, n := binary.Uvarint(trailer)
|
||||
indexLen, m := binary.Uvarint(trailer[n:])
|
||||
fmt.Fprintf(w, "%10d index: offset=%d, length=%d\n", offset, indexHandle, indexLen)
|
||||
trailer, offset = trailer[n+m:], offset+uint64(n+m)
|
||||
|
||||
fmt.Fprintf(w, "%10d [padding]\n", offset)
|
||||
|
||||
trailing := 12
|
||||
if b.name == "leveldb-footer" {
|
||||
trailing = 8
|
||||
}
|
||||
|
||||
offset += uint64(len(trailer) - trailing)
|
||||
trailer = trailer[len(trailer)-trailing:]
|
||||
|
||||
if b.name == "footer" {
|
||||
version := trailer[:4]
|
||||
fmt.Fprintf(w, "%10d version: %d\n", offset, binary.LittleEndian.Uint32(version))
|
||||
trailer, offset = trailer[4:], offset+4
|
||||
}
|
||||
|
||||
magicNumber := trailer
|
||||
fmt.Fprintf(w, "%10d magic number: 0x%x\n", offset, magicNumber)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
h, err := r.readBlock(
|
||||
context.Background(), b.BlockHandle, nil /* transform */, nil /* readHandle */, nil /* stats */, nil /* buffer pool */)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, " [err: %s]\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
getRestart := func(data []byte, restarts, i int32) int32 {
|
||||
return decodeRestart(data[restarts+4*i:])
|
||||
}
|
||||
|
||||
formatIsRestart := func(data []byte, restarts, numRestarts, offset int32) {
|
||||
i := sort.Search(int(numRestarts), func(i int) bool {
|
||||
return getRestart(data, restarts, int32(i)) >= offset
|
||||
})
|
||||
if i < int(numRestarts) && getRestart(data, restarts, int32(i)) == offset {
|
||||
fmt.Fprintf(w, " [restart]\n")
|
||||
} else {
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
formatRestarts := func(data []byte, restarts, numRestarts int32) {
|
||||
for i := int32(0); i < numRestarts; i++ {
|
||||
offset := getRestart(data, restarts, i)
|
||||
fmt.Fprintf(w, "%10d [restart %d]\n",
|
||||
b.Offset+uint64(restarts+4*i), b.Offset+uint64(offset))
|
||||
}
|
||||
}
|
||||
|
||||
formatTrailer := func() {
|
||||
trailer := make([]byte, blockTrailerLen)
|
||||
offset := int64(b.Offset + b.Length)
|
||||
_ = r.readable.ReadAt(ctx, trailer, offset)
|
||||
bt := blockType(trailer[0])
|
||||
checksum := binary.LittleEndian.Uint32(trailer[1:])
|
||||
fmt.Fprintf(w, "%10d [trailer compression=%s checksum=0x%04x]\n", offset, bt, checksum)
|
||||
}
|
||||
|
||||
var lastKey InternalKey
|
||||
switch b.name {
|
||||
case "data", "range-del", "range-key":
|
||||
iter, _ := newBlockIter(r.Compare, h.Get())
|
||||
for key, value := iter.First(); key != nil; key, value = iter.Next() {
|
||||
ptr := unsafe.Pointer(uintptr(iter.ptr) + uintptr(iter.offset))
|
||||
shared, ptr := decodeVarint(ptr)
|
||||
unshared, ptr := decodeVarint(ptr)
|
||||
value2, _ := decodeVarint(ptr)
|
||||
|
||||
total := iter.nextOffset - iter.offset
|
||||
// The format of the numbers in the record line is:
|
||||
//
|
||||
// (<total> = <length> [<shared>] + <unshared> + <value>)
|
||||
//
|
||||
// <total> is the total number of bytes for the record.
|
||||
// <length> is the size of the 3 varint encoded integers for <shared>,
|
||||
// <unshared>, and <value>.
|
||||
// <shared> is the number of key bytes shared with the previous key.
|
||||
// <unshared> is the number of unshared key bytes.
|
||||
// <value> is the number of value bytes.
|
||||
fmt.Fprintf(w, "%10d record (%d = %d [%d] + %d + %d)",
|
||||
b.Offset+uint64(iter.offset), total,
|
||||
total-int32(unshared+value2), shared, unshared, value2)
|
||||
formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
|
||||
if fmtRecord != nil {
|
||||
fmt.Fprintf(w, " ")
|
||||
if l.Format < TableFormatPebblev3 {
|
||||
fmtRecord(key, value.InPlaceValue())
|
||||
} else {
|
||||
// InPlaceValue() will succeed even for data blocks where the
|
||||
// actual value is in a different location, since this value was
|
||||
// fetched from a blockIter which does not know about value
|
||||
// blocks.
|
||||
v := value.InPlaceValue()
|
||||
if base.TrailerKind(key.Trailer) != InternalKeyKindSet {
|
||||
fmtRecord(key, v)
|
||||
} else if !isValueHandle(valuePrefix(v[0])) {
|
||||
fmtRecord(key, v[1:])
|
||||
} else {
|
||||
vh := decodeValueHandle(v[1:])
|
||||
fmtRecord(key, []byte(fmt.Sprintf("value handle %+v", vh)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if base.InternalCompare(r.Compare, lastKey, *key) >= 0 {
|
||||
fmt.Fprintf(w, " WARNING: OUT OF ORDER KEYS!\n")
|
||||
}
|
||||
lastKey.Trailer = key.Trailer
|
||||
lastKey.UserKey = append(lastKey.UserKey[:0], key.UserKey...)
|
||||
}
|
||||
formatRestarts(iter.data, iter.restarts, iter.numRestarts)
|
||||
formatTrailer()
|
||||
case "index", "top-index":
|
||||
iter, _ := newBlockIter(r.Compare, h.Get())
|
||||
for key, value := iter.First(); key != nil; key, value = iter.Next() {
|
||||
bh, err := decodeBlockHandleWithProperties(value.InPlaceValue())
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%10d [err: %s]\n", b.Offset+uint64(iter.offset), err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "%10d block:%d/%d",
|
||||
b.Offset+uint64(iter.offset), bh.Offset, bh.Length)
|
||||
formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
|
||||
}
|
||||
formatRestarts(iter.data, iter.restarts, iter.numRestarts)
|
||||
formatTrailer()
|
||||
case "properties":
|
||||
iter, _ := newRawBlockIter(r.Compare, h.Get())
|
||||
for valid := iter.First(); valid; valid = iter.Next() {
|
||||
fmt.Fprintf(w, "%10d %s (%d)",
|
||||
b.Offset+uint64(iter.offset), iter.Key().UserKey, iter.nextOffset-iter.offset)
|
||||
formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
|
||||
}
|
||||
formatRestarts(iter.data, iter.restarts, iter.numRestarts)
|
||||
formatTrailer()
|
||||
case "meta-index":
|
||||
iter, _ := newRawBlockIter(r.Compare, h.Get())
|
||||
for valid := iter.First(); valid; valid = iter.Next() {
|
||||
value := iter.Value()
|
||||
var bh BlockHandle
|
||||
var n int
|
||||
var vbih valueBlocksIndexHandle
|
||||
isValueBlocksIndexHandle := false
|
||||
if bytes.Equal(iter.Key().UserKey, []byte(metaValueIndexName)) {
|
||||
vbih, n, err = decodeValueBlocksIndexHandle(value)
|
||||
bh = vbih.h
|
||||
isValueBlocksIndexHandle = true
|
||||
} else {
|
||||
bh, n = decodeBlockHandle(value)
|
||||
}
|
||||
if n == 0 || n != len(value) {
|
||||
fmt.Fprintf(w, "%10d [err: %s]\n", b.Offset+uint64(iter.offset), err)
|
||||
continue
|
||||
}
|
||||
var vbihStr string
|
||||
if isValueBlocksIndexHandle {
|
||||
vbihStr = fmt.Sprintf(" value-blocks-index-lengths: %d(num), %d(offset), %d(length)",
|
||||
vbih.blockNumByteLength, vbih.blockOffsetByteLength, vbih.blockLengthByteLength)
|
||||
}
|
||||
fmt.Fprintf(w, "%10d %s block:%d/%d%s",
|
||||
b.Offset+uint64(iter.offset), iter.Key().UserKey,
|
||||
bh.Offset, bh.Length, vbihStr)
|
||||
formatIsRestart(iter.data, iter.restarts, iter.numRestarts, iter.offset)
|
||||
}
|
||||
formatRestarts(iter.data, iter.restarts, iter.numRestarts)
|
||||
formatTrailer()
|
||||
case "value-block":
|
||||
// We don't peer into the value-block since it can't be interpreted
|
||||
// without the valueHandles.
|
||||
case "value-index":
|
||||
// We have already read the value-index to construct the list of
|
||||
// value-blocks, so no need to do it again.
|
||||
}
|
||||
|
||||
h.Release()
|
||||
}
|
||||
|
||||
last := blocks[len(blocks)-1]
|
||||
fmt.Fprintf(w, "%10d EOF\n", last.Offset+last.Length)
|
||||
}
|
||||
6
vendor/github.com/cockroachdb/pebble/sstable/options.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/sstable/options.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package sstable
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/fifo"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
)
|
||||
@@ -111,6 +112,11 @@ type ReaderOptions struct {
|
||||
// The default cache size is a zero-size cache.
|
||||
Cache *cache.Cache
|
||||
|
||||
// LoadBlockSema, if set, is used to limit the number of blocks that can be
|
||||
// loaded (i.e. read from the filesystem) in parallel. Each load acquires one
|
||||
// unit from the semaphore for the duration of the read.
|
||||
LoadBlockSema *fifo.Semaphore
|
||||
|
||||
// User properties specified in this map will not be added to sst.Properties.UserProperties.
|
||||
DeniedUserProperties map[string]struct{}
|
||||
|
||||
|
||||
139
vendor/github.com/cockroachdb/pebble/sstable/properties.go
generated
vendored
139
vendor/github.com/cockroachdb/pebble/sstable/properties.go
generated
vendored
@@ -25,10 +25,20 @@ var propBoolFalse = []byte{'0'}
|
||||
|
||||
var propOffsetTagMap = make(map[uintptr]string)
|
||||
|
||||
func init() {
|
||||
t := reflect.TypeOf(Properties{})
|
||||
func generateTagMaps(t reflect.Type, indexPrefix []int) {
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if f.Type.Kind() == reflect.Struct {
|
||||
if tag := f.Tag.Get("prop"); i == 0 && tag == "pebble.embbeded_common_properties" {
|
||||
// CommonProperties struct embedded in Properties. Note that since
|
||||
// CommonProperties is placed at the top of properties we can use
|
||||
// the offsets of the fields within CommonProperties to determine
|
||||
// the offsets of those fields within Properties.
|
||||
generateTagMaps(f.Type, []int{i})
|
||||
continue
|
||||
}
|
||||
panic("pebble: unknown struct type in Properties")
|
||||
}
|
||||
if tag := f.Tag.Get("prop"); tag != "" {
|
||||
switch f.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
@@ -38,16 +48,84 @@ func init() {
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported property field type: %s %s", f.Name, f.Type))
|
||||
}
|
||||
if len(indexPrefix) > 0 {
|
||||
// Prepend the index prefix so that we can use FieldByIndex on the top-level struct.
|
||||
f.Index = append(indexPrefix[:len(indexPrefix):len(indexPrefix)], f.Index...)
|
||||
}
|
||||
propTagMap[tag] = f
|
||||
propOffsetTagMap[f.Offset] = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
generateTagMaps(reflect.TypeOf(Properties{}), nil)
|
||||
}
|
||||
|
||||
// CommonProperties holds properties for either a virtual or a physical sstable. This
|
||||
// can be used by code which doesn't care to make the distinction between physical
|
||||
// and virtual sstables properties.
|
||||
//
|
||||
// For virtual sstables, fields are constructed through extrapolation upon virtual
|
||||
// reader construction. See MakeVirtualReader for implementation details.
|
||||
//
|
||||
// NB: The values of these properties can affect correctness. For example,
|
||||
// if NumRangeKeySets == 0, but the sstable actually contains range keys, then
|
||||
// the iterators will behave incorrectly.
|
||||
type CommonProperties struct {
|
||||
// The number of entries in this table.
|
||||
NumEntries uint64 `prop:"rocksdb.num.entries"`
|
||||
// Total raw key size.
|
||||
RawKeySize uint64 `prop:"rocksdb.raw.key.size"`
|
||||
// Total raw value size.
|
||||
RawValueSize uint64 `prop:"rocksdb.raw.value.size"`
|
||||
// Total raw key size of point deletion tombstones. This value is comparable
|
||||
// to RawKeySize.
|
||||
RawPointTombstoneKeySize uint64 `prop:"pebble.raw.point-tombstone.key.size"`
|
||||
// Sum of the raw value sizes carried by point deletion tombstones
|
||||
// containing size estimates. See the DeleteSized key kind. This value is
|
||||
// comparable to Raw{Key,Value}Size.
|
||||
RawPointTombstoneValueSize uint64 `prop:"pebble.raw.point-tombstone.value.size"`
|
||||
// The number of point deletion entries ("tombstones") in this table that
|
||||
// carry a size hint indicating the size of the value the tombstone deletes.
|
||||
NumSizedDeletions uint64 `prop:"pebble.num.deletions.sized"`
|
||||
// The number of deletion entries in this table, including both point and
|
||||
// range deletions.
|
||||
NumDeletions uint64 `prop:"rocksdb.deleted.keys"`
|
||||
// The number of range deletions in this table.
|
||||
NumRangeDeletions uint64 `prop:"rocksdb.num.range-deletions"`
|
||||
// The number of RANGEKEYDELs in this table.
|
||||
NumRangeKeyDels uint64 `prop:"pebble.num.range-key-dels"`
|
||||
// The number of RANGEKEYSETs in this table.
|
||||
NumRangeKeySets uint64 `prop:"pebble.num.range-key-sets"`
|
||||
// Total size of value blocks and value index block. Only serialized if > 0.
|
||||
ValueBlocksSize uint64 `prop:"pebble.value-blocks.size"`
|
||||
}
|
||||
|
||||
// String is only used for testing purposes.
|
||||
func (c *CommonProperties) String() string {
|
||||
var buf bytes.Buffer
|
||||
v := reflect.ValueOf(*c)
|
||||
loaded := make(map[uintptr]struct{})
|
||||
writeProperties(loaded, v, &buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// NumPointDeletions is the number of point deletions in the sstable. For virtual
|
||||
// sstables, this is an estimate.
|
||||
func (c *CommonProperties) NumPointDeletions() uint64 {
|
||||
return c.NumDeletions - c.NumRangeDeletions
|
||||
}
|
||||
|
||||
// Properties holds the sstable property values. The properties are
|
||||
// automatically populated during sstable creation and load from the properties
|
||||
// meta block when an sstable is opened.
|
||||
type Properties struct {
|
||||
// CommonProperties needs to be at the top of the Properties struct so that the
|
||||
// offsets of the fields in CommonProperties match the offsets of the embedded
|
||||
// fields of CommonProperties in Properties.
|
||||
CommonProperties `prop:"pebble.embbeded_common_properties"`
|
||||
|
||||
// The name of the comparer used in this table.
|
||||
ComparerName string `prop:"rocksdb.comparator"`
|
||||
// The compression algorithm used to compress blocks.
|
||||
@@ -81,22 +159,8 @@ type Properties struct {
|
||||
MergerName string `prop:"rocksdb.merge.operator"`
|
||||
// The number of blocks in this table.
|
||||
NumDataBlocks uint64 `prop:"rocksdb.num.data.blocks"`
|
||||
// The number of deletion entries in this table, including both point and
|
||||
// range deletions.
|
||||
NumDeletions uint64 `prop:"rocksdb.deleted.keys"`
|
||||
// The number of point deletion entries ("tombstones") in this table that
|
||||
// carry a size hint indicating the size of the value the tombstone deletes.
|
||||
NumSizedDeletions uint64 `prop:"pebble.num.deletions.sized"`
|
||||
// The number of entries in this table.
|
||||
NumEntries uint64 `prop:"rocksdb.num.entries"`
|
||||
// The number of merge operands in the table.
|
||||
NumMergeOperands uint64 `prop:"rocksdb.merge.operands"`
|
||||
// The number of range deletions in this table.
|
||||
NumRangeDeletions uint64 `prop:"rocksdb.num.range-deletions"`
|
||||
// The number of RANGEKEYDELs in this table.
|
||||
NumRangeKeyDels uint64 `prop:"pebble.num.range-key-dels"`
|
||||
// The number of RANGEKEYSETs in this table.
|
||||
NumRangeKeySets uint64 `prop:"pebble.num.range-key-sets"`
|
||||
// The number of RANGEKEYUNSETs in this table.
|
||||
NumRangeKeyUnsets uint64 `prop:"pebble.num.range-key-unsets"`
|
||||
// The number of value blocks in this table. Only serialized if > 0.
|
||||
@@ -111,21 +175,10 @@ type Properties struct {
|
||||
// A comma separated list of names of the property collectors used in this
|
||||
// table.
|
||||
PropertyCollectorNames string `prop:"rocksdb.property.collectors"`
|
||||
// Total raw key size.
|
||||
RawKeySize uint64 `prop:"rocksdb.raw.key.size"`
|
||||
// Total raw key size of point deletion tombstones. This value is comparable
|
||||
// to RawKeySize.
|
||||
RawPointTombstoneKeySize uint64 `prop:"pebble.raw.point-tombstone.key.size"`
|
||||
// Sum of the raw value sizes carried by point deletion tombstones
|
||||
// containing size estimates. See the DeleteSized key kind. This value is
|
||||
// comparable to Raw{Key,Value}Size.
|
||||
RawPointTombstoneValueSize uint64 `prop:"pebble.raw.point-tombstone.value.size"`
|
||||
// Total raw rangekey key size.
|
||||
RawRangeKeyKeySize uint64 `prop:"pebble.raw.range-key.key.size"`
|
||||
// Total raw rangekey value size.
|
||||
RawRangeKeyValueSize uint64 `prop:"pebble.raw.range-key.value.size"`
|
||||
// Total raw value size.
|
||||
RawValueSize uint64 `prop:"rocksdb.raw.value.size"`
|
||||
// The total number of keys in this table that were pinned by open snapshots.
|
||||
SnapshotPinnedKeys uint64 `prop:"pebble.num.snapshot-pinned-keys"`
|
||||
// The cumulative bytes of keys in this table that were pinned by
|
||||
@@ -138,8 +191,6 @@ type Properties struct {
|
||||
TopLevelIndexSize uint64 `prop:"rocksdb.top-level.index.size"`
|
||||
// User collected properties.
|
||||
UserProperties map[string]string
|
||||
// Total size of value blocks and value index block. Only serialized if > 0.
|
||||
ValueBlocksSize uint64 `prop:"pebble.value-blocks.size"`
|
||||
// If filtering is enabled, was the filter created on the whole key.
|
||||
WholeKeyFiltering bool `prop:"rocksdb.block.based.table.whole.key.filtering"`
|
||||
|
||||
@@ -160,12 +211,15 @@ func (p *Properties) NumRangeKeys() uint64 {
|
||||
return p.NumRangeKeyDels + p.NumRangeKeySets + p.NumRangeKeyUnsets
|
||||
}
|
||||
|
||||
func (p *Properties) String() string {
|
||||
var buf bytes.Buffer
|
||||
v := reflect.ValueOf(*p)
|
||||
func writeProperties(loaded map[uintptr]struct{}, v reflect.Value, buf *bytes.Buffer) {
|
||||
vt := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
ft := vt.Field(i)
|
||||
if ft.Type.Kind() == reflect.Struct {
|
||||
// Embedded struct within the properties.
|
||||
writeProperties(loaded, v.Field(i), buf)
|
||||
continue
|
||||
}
|
||||
tag := ft.Tag.Get("prop")
|
||||
if tag == "" {
|
||||
continue
|
||||
@@ -175,25 +229,33 @@ func (p *Properties) String() string {
|
||||
// TODO(peter): Use f.IsZero() when we can rely on go1.13.
|
||||
if zero := reflect.Zero(f.Type()); zero.Interface() == f.Interface() {
|
||||
// Skip printing of zero values which were not loaded from disk.
|
||||
if _, ok := p.Loaded[ft.Offset]; !ok {
|
||||
if _, ok := loaded[ft.Offset]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(&buf, "%s: ", tag)
|
||||
fmt.Fprintf(buf, "%s: ", tag)
|
||||
switch ft.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
fmt.Fprintf(&buf, "%t\n", f.Bool())
|
||||
fmt.Fprintf(buf, "%t\n", f.Bool())
|
||||
case reflect.Uint32:
|
||||
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
||||
fmt.Fprintf(buf, "%d\n", f.Uint())
|
||||
case reflect.Uint64:
|
||||
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
||||
fmt.Fprintf(buf, "%d\n", f.Uint())
|
||||
case reflect.String:
|
||||
fmt.Fprintf(&buf, "%s\n", f.String())
|
||||
fmt.Fprintf(buf, "%s\n", f.String())
|
||||
default:
|
||||
panic("not reached")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Properties) String() string {
|
||||
var buf bytes.Buffer
|
||||
v := reflect.ValueOf(*p)
|
||||
writeProperties(p.Loaded, v, &buf)
|
||||
|
||||
// Write the UserProperties.
|
||||
keys := make([]string, 0, len(p.UserProperties))
|
||||
for key := range p.UserProperties {
|
||||
keys = append(keys, key)
|
||||
@@ -214,6 +276,7 @@ func (p *Properties) load(
|
||||
}
|
||||
p.Loaded = make(map[uintptr]struct{})
|
||||
v := reflect.ValueOf(p).Elem()
|
||||
|
||||
for valid := i.First(); valid; valid = i.Next() {
|
||||
if f, ok := propTagMap[string(i.Key().UserKey)]; ok {
|
||||
p.Loaded[f.Offset] = struct{}{}
|
||||
|
||||
3366
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
3366
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
File diff suppressed because it is too large
Load Diff
291
vendor/github.com/cockroachdb/pebble/sstable/reader_iter.go
generated
vendored
Normal file
291
vendor/github.com/cockroachdb/pebble/sstable/reader_iter.go
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
// Copyright 2011 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
)
|
||||
|
||||
// Iterator iterates over an entire table of data.
|
||||
type Iterator interface {
|
||||
base.InternalIterator
|
||||
|
||||
// NextPrefix implements (base.InternalIterator).NextPrefix.
|
||||
NextPrefix(succKey []byte) (*InternalKey, base.LazyValue)
|
||||
|
||||
// MaybeFilteredKeys may be called when an iterator is exhausted to indicate
|
||||
// whether or not the last positioning method may have skipped any keys due
|
||||
// to block-property filters. This is used by the Pebble levelIter to
|
||||
// control when an iterator steps to the next sstable.
|
||||
//
|
||||
// MaybeFilteredKeys may always return false positives, that is it may
|
||||
// return true when no keys were filtered. It should only be called when the
|
||||
// iterator is exhausted. It must never return false negatives when the
|
||||
// iterator is exhausted.
|
||||
MaybeFilteredKeys() bool
|
||||
|
||||
SetCloseHook(fn func(i Iterator) error)
|
||||
}
|
||||
|
||||
// Iterator positioning optimizations and singleLevelIterator and
|
||||
// twoLevelIterator:
|
||||
//
|
||||
// An iterator is absolute positioned using one of the Seek or First or Last
|
||||
// calls. After absolute positioning, there can be relative positioning done
|
||||
// by stepping using Prev or Next.
|
||||
//
|
||||
// We implement optimizations below where an absolute positioning call can in
|
||||
// some cases use the current position to do less work. To understand these,
|
||||
// we first define some terms. An iterator is bounds-exhausted if the bounds
|
||||
// (upper of lower) have been reached. An iterator is data-exhausted if it has
|
||||
// the reached the end of the data (forward or reverse) in the sstable. A
|
||||
// singleLevelIterator only knows a local-data-exhausted property since when
|
||||
// it is used as part of a twoLevelIterator, the twoLevelIterator can step to
|
||||
// the next lower-level index block.
|
||||
//
|
||||
// The bounds-exhausted property is tracked by
|
||||
// singleLevelIterator.exhaustedBounds being +1 (upper bound reached) or -1
|
||||
// (lower bound reached). The same field is reused by twoLevelIterator. Either
|
||||
// may notice the exhaustion of the bound and set it. Note that if
|
||||
// singleLevelIterator sets this property, it is not a local property (since
|
||||
// the bound has been reached regardless of whether this is in the context of
|
||||
// the twoLevelIterator or not).
|
||||
//
|
||||
// The data-exhausted property is tracked in a more subtle manner. We define
|
||||
// two predicates:
|
||||
// - partial-local-data-exhausted (PLDE):
|
||||
// i.data.isDataInvalidated() || !i.data.valid()
|
||||
// - partial-global-data-exhausted (PGDE):
|
||||
// i.index.isDataInvalidated() || !i.index.valid() || i.data.isDataInvalidated() ||
|
||||
// !i.data.valid()
|
||||
//
|
||||
// PLDE is defined for a singleLevelIterator. PGDE is defined for a
|
||||
// twoLevelIterator. Oddly, in our code below the singleLevelIterator does not
|
||||
// know when it is part of a twoLevelIterator so it does not know when its
|
||||
// property is local or global.
|
||||
//
|
||||
// Now to define data-exhausted:
|
||||
// - Prerequisite: we must know that the iterator has been positioned and
|
||||
// i.err is nil.
|
||||
// - bounds-exhausted must not be true:
|
||||
// If bounds-exhausted is true, we have incomplete knowledge of
|
||||
// data-exhausted since PLDE or PGDE could be true because we could have
|
||||
// chosen not to load index block or data block and figured out that the
|
||||
// bound is exhausted (due to block property filters filtering out index and
|
||||
// data blocks and going past the bound on the top level index block). Note
|
||||
// that if we tried to separate out the BPF case from others we could
|
||||
// develop more knowledge here.
|
||||
// - PGDE is true for twoLevelIterator. PLDE is true if it is a standalone
|
||||
// singleLevelIterator. !PLDE or !PGDE of course imply that data-exhausted
|
||||
// is not true.
|
||||
//
|
||||
// An implication of the above is that if we are going to somehow utilize
|
||||
// knowledge of data-exhausted in an optimization, we must not forget the
|
||||
// existing value of bounds-exhausted since by forgetting the latter we can
|
||||
// erroneously think that data-exhausted is true. Bug #2036 was due to this
|
||||
// forgetting.
|
||||
//
|
||||
// Now to the two categories of optimizations we currently have:
|
||||
// - Monotonic bounds optimization that reuse prior iterator position when
|
||||
// doing seek: These only work with !data-exhausted. We could choose to make
|
||||
// these work with data-exhausted but have not bothered because in the
|
||||
// context of a DB if data-exhausted were true, the DB would move to the
|
||||
// next file in the level. Note that this behavior of moving to the next
|
||||
// file is not necessarily true for L0 files, so there could be some benefit
|
||||
// in the future in this optimization. See the WARNING-data-exhausted
|
||||
// comments if trying to optimize this in the future.
|
||||
// - TrySeekUsingNext optimizations: these work regardless of exhaustion
|
||||
// state.
|
||||
//
|
||||
// Implementation detail: In the code PLDE only checks that
|
||||
// i.data.isDataInvalidated(). This narrower check is safe, since this is a
|
||||
// subset of the set expressed by the OR expression. Also, it is not a
|
||||
// de-optimization since whenever we exhaust the iterator we explicitly call
|
||||
// i.data.invalidate(). PGDE checks i.index.isDataInvalidated() &&
|
||||
// i.data.isDataInvalidated(). Again, this narrower check is safe, and not a
|
||||
// de-optimization since whenever we exhaust the iterator we explicitly call
|
||||
// i.index.invalidate() and i.data.invalidate(). The && is questionable -- for
|
||||
// now this is a bit of defensive code. We should seriously consider removing
|
||||
// it, since defensive code suggests we are not confident about our invariants
|
||||
// (and if we are not confident, we need more invariant assertions, not
|
||||
// defensive code).
|
||||
//
|
||||
// TODO(sumeer): remove the aforementioned defensive code.
|
||||
|
||||
var singleLevelIterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
i := &singleLevelIterator{}
|
||||
// Note: this is a no-op if invariants are disabled or race is enabled.
|
||||
invariants.SetFinalizer(i, checkSingleLevelIterator)
|
||||
return i
|
||||
},
|
||||
}
|
||||
|
||||
var twoLevelIterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
i := &twoLevelIterator{}
|
||||
// Note: this is a no-op if invariants are disabled or race is enabled.
|
||||
invariants.SetFinalizer(i, checkTwoLevelIterator)
|
||||
return i
|
||||
},
|
||||
}
|
||||
|
||||
// TODO(jackson): rangedel fragmentBlockIters can't be pooled because of some
|
||||
// code paths that double Close the iters. Fix the double close and pool the
|
||||
// *fragmentBlockIter type directly.
|
||||
|
||||
var rangeKeyFragmentBlockIterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
i := &rangeKeyFragmentBlockIter{}
|
||||
// Note: this is a no-op if invariants are disabled or race is enabled.
|
||||
invariants.SetFinalizer(i, checkRangeKeyFragmentBlockIterator)
|
||||
return i
|
||||
},
|
||||
}
|
||||
|
||||
func checkSingleLevelIterator(obj interface{}) {
|
||||
i := obj.(*singleLevelIterator)
|
||||
if p := i.data.handle.Get(); p != nil {
|
||||
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %p\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
if p := i.index.handle.Get(); p != nil {
|
||||
fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %p\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func checkTwoLevelIterator(obj interface{}) {
|
||||
i := obj.(*twoLevelIterator)
|
||||
if p := i.data.handle.Get(); p != nil {
|
||||
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %p\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
if p := i.index.handle.Get(); p != nil {
|
||||
fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %p\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func checkRangeKeyFragmentBlockIterator(obj interface{}) {
|
||||
i := obj.(*rangeKeyFragmentBlockIter)
|
||||
if p := i.blockIter.handle.Get(); p != nil {
|
||||
fmt.Fprintf(os.Stderr, "fragmentBlockIter.blockIter.handle is not nil: %p\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// compactionIterator is similar to Iterator but it increments the number of
|
||||
// bytes that have been iterated through.
|
||||
type compactionIterator struct {
|
||||
*singleLevelIterator
|
||||
bytesIterated *uint64
|
||||
prevOffset uint64
|
||||
}
|
||||
|
||||
// compactionIterator implements the base.InternalIterator interface.
|
||||
var _ base.InternalIterator = (*compactionIterator)(nil)
|
||||
|
||||
func (i *compactionIterator) String() string {
|
||||
if i.vState != nil {
|
||||
return i.vState.fileNum.String()
|
||||
}
|
||||
return i.reader.fileNum.String()
|
||||
}
|
||||
|
||||
func (i *compactionIterator) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *compactionIterator) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *compactionIterator) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
func (i *compactionIterator) First() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
return i.skipForward(i.singleLevelIterator.First())
|
||||
}
|
||||
|
||||
func (i *compactionIterator) Last() (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: Last unimplemented")
|
||||
}
|
||||
|
||||
// Note: compactionIterator.Next mirrors the implementation of Iterator.Next
|
||||
// due to performance. Keep the two in sync.
|
||||
func (i *compactionIterator) Next() (*InternalKey, base.LazyValue) {
|
||||
if i.err != nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return i.skipForward(i.data.Next())
|
||||
}
|
||||
|
||||
func (i *compactionIterator) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: NextPrefix unimplemented")
|
||||
}
|
||||
|
||||
func (i *compactionIterator) Prev() (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: Prev unimplemented")
|
||||
}
|
||||
|
||||
func (i *compactionIterator) skipForward(
|
||||
key *InternalKey, val base.LazyValue,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if key == nil {
|
||||
for {
|
||||
if key, _ := i.index.Next(); key == nil {
|
||||
break
|
||||
}
|
||||
result := i.loadBlock(+1)
|
||||
if result != loadBlockOK {
|
||||
if i.err != nil {
|
||||
break
|
||||
}
|
||||
switch result {
|
||||
case loadBlockFailed:
|
||||
// We checked that i.index was at a valid entry, so
|
||||
// loadBlockFailed could not have happened due to to i.index
|
||||
// being exhausted, and must be due to an error.
|
||||
panic("loadBlock should not have failed with no error")
|
||||
case loadBlockIrrelevant:
|
||||
panic("compactionIter should not be using block intervals for skipping")
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected case %d", result))
|
||||
}
|
||||
}
|
||||
// result == loadBlockOK
|
||||
if key, val = i.data.First(); key != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curOffset := i.recordOffset()
|
||||
*i.bytesIterated += uint64(curOffset - i.prevOffset)
|
||||
i.prevOffset = curOffset
|
||||
|
||||
if i.vState != nil && key != nil {
|
||||
cmp := i.cmp(key.UserKey, i.vState.upper.UserKey)
|
||||
if cmp > 0 || (i.vState.upper.IsExclusiveSentinel() && cmp == 0) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
return key, val
|
||||
}
|
||||
1391
vendor/github.com/cockroachdb/pebble/sstable/reader_iter_single_lvl.go
generated
vendored
Normal file
1391
vendor/github.com/cockroachdb/pebble/sstable/reader_iter_single_lvl.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1085
vendor/github.com/cockroachdb/pebble/sstable/reader_iter_two_lvl.go
generated
vendored
Normal file
1085
vendor/github.com/cockroachdb/pebble/sstable/reader_iter_two_lvl.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
206
vendor/github.com/cockroachdb/pebble/sstable/reader_virtual.go
generated
vendored
Normal file
206
vendor/github.com/cockroachdb/pebble/sstable/reader_virtual.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright 2011 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 (
|
||||
"context"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
)
|
||||
|
||||
// VirtualReader wraps Reader. Its purpose is to restrict functionality of the
|
||||
// Reader which should be inaccessible to virtual sstables, and enforce bounds
|
||||
// invariants associated with virtual sstables. All reads on virtual sstables
|
||||
// should go through a VirtualReader.
|
||||
//
|
||||
// INVARIANT: Any iterators created through a virtual reader will guarantee that
|
||||
// they don't expose keys outside the virtual sstable bounds.
|
||||
type VirtualReader struct {
|
||||
vState virtualState
|
||||
reader *Reader
|
||||
Properties CommonProperties
|
||||
}
|
||||
|
||||
// Lightweight virtual sstable state which can be passed to sstable iterators.
|
||||
type virtualState struct {
|
||||
lower InternalKey
|
||||
upper InternalKey
|
||||
fileNum base.FileNum
|
||||
Compare Compare
|
||||
isForeign bool
|
||||
}
|
||||
|
||||
func ceilDiv(a, b uint64) uint64 {
|
||||
return (a + b - 1) / b
|
||||
}
|
||||
|
||||
// MakeVirtualReader is used to contruct a reader which can read from virtual
|
||||
// sstables.
|
||||
func MakeVirtualReader(
|
||||
reader *Reader, meta manifest.VirtualFileMeta, isForeign bool,
|
||||
) VirtualReader {
|
||||
if reader.fileNum != meta.FileBacking.DiskFileNum {
|
||||
panic("pebble: invalid call to MakeVirtualReader")
|
||||
}
|
||||
|
||||
vState := virtualState{
|
||||
lower: meta.Smallest,
|
||||
upper: meta.Largest,
|
||||
fileNum: meta.FileNum,
|
||||
Compare: reader.Compare,
|
||||
isForeign: isForeign,
|
||||
}
|
||||
v := VirtualReader{
|
||||
vState: vState,
|
||||
reader: reader,
|
||||
}
|
||||
|
||||
v.Properties.RawKeySize = ceilDiv(reader.Properties.RawKeySize*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.RawValueSize = ceilDiv(reader.Properties.RawValueSize*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.NumEntries = ceilDiv(reader.Properties.NumEntries*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.NumDeletions = ceilDiv(reader.Properties.NumDeletions*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.NumRangeDeletions = ceilDiv(reader.Properties.NumRangeDeletions*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.NumRangeKeyDels = ceilDiv(reader.Properties.NumRangeKeyDels*meta.Size, meta.FileBacking.Size)
|
||||
|
||||
// Note that we rely on NumRangeKeySets for correctness. If the sstable may
|
||||
// contain range keys, then NumRangeKeySets must be > 0. ceilDiv works because
|
||||
// meta.Size will not be 0 for virtual sstables.
|
||||
v.Properties.NumRangeKeySets = ceilDiv(reader.Properties.NumRangeKeySets*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.ValueBlocksSize = ceilDiv(reader.Properties.ValueBlocksSize*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.NumSizedDeletions = ceilDiv(reader.Properties.NumSizedDeletions*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.RawPointTombstoneKeySize = ceilDiv(reader.Properties.RawPointTombstoneKeySize*meta.Size, meta.FileBacking.Size)
|
||||
v.Properties.RawPointTombstoneValueSize = ceilDiv(reader.Properties.RawPointTombstoneValueSize*meta.Size, meta.FileBacking.Size)
|
||||
return v
|
||||
}
|
||||
|
||||
// NewCompactionIter is the compaction iterator function for virtual readers.
|
||||
func (v *VirtualReader) NewCompactionIter(
|
||||
bytesIterated *uint64, rp ReaderProvider, bufferPool *BufferPool,
|
||||
) (Iterator, error) {
|
||||
return v.reader.newCompactionIter(bytesIterated, rp, &v.vState, bufferPool)
|
||||
}
|
||||
|
||||
// NewIterWithBlockPropertyFiltersAndContextEtc wraps
|
||||
// Reader.NewIterWithBlockPropertyFiltersAndContext. We assume that the passed
|
||||
// in [lower, upper) bounds will have at least some overlap with the virtual
|
||||
// sstable bounds. No overlap is not currently supported in the iterator.
|
||||
func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||
ctx context.Context,
|
||||
lower, upper []byte,
|
||||
filterer *BlockPropertiesFilterer,
|
||||
hideObsoletePoints, useFilterBlock bool,
|
||||
stats *base.InternalIteratorStats,
|
||||
rp ReaderProvider,
|
||||
) (Iterator, error) {
|
||||
return v.reader.newIterWithBlockPropertyFiltersAndContext(
|
||||
ctx, lower, upper, filterer, hideObsoletePoints, useFilterBlock, stats, rp, &v.vState,
|
||||
)
|
||||
}
|
||||
|
||||
// ValidateBlockChecksumsOnBacking will call ValidateBlockChecksumsOnBacking on the underlying reader.
|
||||
// Note that block checksum validation is NOT restricted to virtual sstable bounds.
|
||||
func (v *VirtualReader) ValidateBlockChecksumsOnBacking() error {
|
||||
return v.reader.ValidateBlockChecksums()
|
||||
}
|
||||
|
||||
// NewRawRangeDelIter wraps Reader.NewRawRangeDelIter.
|
||||
func (v *VirtualReader) NewRawRangeDelIter() (keyspan.FragmentIterator, error) {
|
||||
iter, err := v.reader.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iter == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Truncation of spans isn't allowed at a user key that also contains points
|
||||
// in the same virtual sstable, as it would lead to covered points getting
|
||||
// uncovered. Set panicOnUpperTruncate to true if the file's upper bound
|
||||
// is not an exclusive sentinel.
|
||||
//
|
||||
// As an example, if an sstable contains a rangedel a-c and point keys at
|
||||
// a.SET.2 and b.SET.3, the file bounds [a#2,SET-b#RANGEDELSENTINEL] are
|
||||
// allowed (as they exclude b.SET.3), or [a#2,SET-c#RANGEDELSENTINEL] (as it
|
||||
// includes both point keys), but not [a#2,SET-b#3,SET] (as it would truncate
|
||||
// the rangedel at b and lead to the point being uncovered).
|
||||
return keyspan.Truncate(
|
||||
v.reader.Compare, iter, v.vState.lower.UserKey, v.vState.upper.UserKey,
|
||||
&v.vState.lower, &v.vState.upper, !v.vState.upper.IsExclusiveSentinel(), /* panicOnUpperTruncate */
|
||||
), nil
|
||||
}
|
||||
|
||||
// NewRawRangeKeyIter wraps Reader.NewRawRangeKeyIter.
|
||||
func (v *VirtualReader) NewRawRangeKeyIter() (keyspan.FragmentIterator, error) {
|
||||
iter, err := v.reader.NewRawRangeKeyIter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iter == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Truncation of spans isn't allowed at a user key that also contains points
|
||||
// in the same virtual sstable, as it would lead to covered points getting
|
||||
// uncovered. Set panicOnUpperTruncate to true if the file's upper bound
|
||||
// is not an exclusive sentinel.
|
||||
//
|
||||
// As an example, if an sstable contains a range key a-c and point keys at
|
||||
// a.SET.2 and b.SET.3, the file bounds [a#2,SET-b#RANGEKEYSENTINEL] are
|
||||
// allowed (as they exclude b.SET.3), or [a#2,SET-c#RANGEKEYSENTINEL] (as it
|
||||
// includes both point keys), but not [a#2,SET-b#3,SET] (as it would truncate
|
||||
// the range key at b and lead to the point being uncovered).
|
||||
return keyspan.Truncate(
|
||||
v.reader.Compare, iter, v.vState.lower.UserKey, v.vState.upper.UserKey,
|
||||
&v.vState.lower, &v.vState.upper, !v.vState.upper.IsExclusiveSentinel(), /* panicOnUpperTruncate */
|
||||
), nil
|
||||
}
|
||||
|
||||
// Constrain bounds will narrow the start, end bounds if they do not fit within
|
||||
// the virtual sstable. The function will return if the new end key is
|
||||
// inclusive.
|
||||
func (v *virtualState) constrainBounds(
|
||||
start, end []byte, endInclusive bool,
|
||||
) (lastKeyInclusive bool, first []byte, last []byte) {
|
||||
first = start
|
||||
if start == nil || v.Compare(start, v.lower.UserKey) < 0 {
|
||||
first = v.lower.UserKey
|
||||
}
|
||||
|
||||
// Note that we assume that start, end has some overlap with the virtual
|
||||
// sstable bounds.
|
||||
last = v.upper.UserKey
|
||||
lastKeyInclusive = !v.upper.IsExclusiveSentinel()
|
||||
if end != nil {
|
||||
cmp := v.Compare(end, v.upper.UserKey)
|
||||
switch {
|
||||
case cmp == 0:
|
||||
lastKeyInclusive = !v.upper.IsExclusiveSentinel() && endInclusive
|
||||
last = v.upper.UserKey
|
||||
case cmp > 0:
|
||||
lastKeyInclusive = !v.upper.IsExclusiveSentinel()
|
||||
last = v.upper.UserKey
|
||||
default:
|
||||
lastKeyInclusive = endInclusive
|
||||
last = end
|
||||
}
|
||||
}
|
||||
// TODO(bananabrick): What if someone passes in bounds completely outside of
|
||||
// virtual sstable bounds?
|
||||
return lastKeyInclusive, first, last
|
||||
}
|
||||
|
||||
// EstimateDiskUsage just calls VirtualReader.reader.EstimateDiskUsage after
|
||||
// enforcing the virtual sstable bounds.
|
||||
func (v *VirtualReader) EstimateDiskUsage(start, end []byte) (uint64, error) {
|
||||
_, f, l := v.vState.constrainBounds(start, end, true /* endInclusive */)
|
||||
return v.reader.EstimateDiskUsage(f, l)
|
||||
}
|
||||
|
||||
// CommonProperties implements the CommonReader interface.
|
||||
func (v *VirtualReader) CommonProperties() *CommonProperties {
|
||||
return &v.Properties
|
||||
}
|
||||
20
vendor/github.com/cockroachdb/pebble/sstable/value_block.go
generated
vendored
20
vendor/github.com/cockroachdb/pebble/sstable/value_block.go
generated
vendored
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
|
||||
"golang.org/x/exp/rand"
|
||||
@@ -728,7 +727,7 @@ func (ukb *UserKeyPrefixBound) IsEmpty() bool {
|
||||
type blockProviderWhenOpen interface {
|
||||
readBlockForVBR(
|
||||
ctx context.Context, h BlockHandle, stats *base.InternalIteratorStats,
|
||||
) (cache.Handle, error)
|
||||
) (bufferHandle, error)
|
||||
}
|
||||
|
||||
type blockProviderWhenClosed struct {
|
||||
@@ -749,9 +748,12 @@ func (bpwc *blockProviderWhenClosed) close() {
|
||||
|
||||
func (bpwc blockProviderWhenClosed) readBlockForVBR(
|
||||
ctx context.Context, h BlockHandle, stats *base.InternalIteratorStats,
|
||||
) (cache.Handle, error) {
|
||||
) (bufferHandle, error) {
|
||||
ctx = objiotracing.WithBlockType(ctx, objiotracing.ValueBlock)
|
||||
return bpwc.r.readBlock(ctx, h, nil, nil, stats)
|
||||
// TODO(jackson,sumeer): Consider whether to use a buffer pool in this case.
|
||||
// The bpwc is not allowed to outlive the iterator tree, so it cannot
|
||||
// outlive the buffer pool.
|
||||
return bpwc.r.readBlock(ctx, h, nil, nil, stats, nil /* buffer pool */)
|
||||
}
|
||||
|
||||
// ReaderProvider supports the implementation of blockProviderWhenClosed.
|
||||
@@ -790,16 +792,16 @@ type valueBlockReader struct {
|
||||
// The value blocks index is lazily retrieved the first time the reader
|
||||
// needs to read a value that resides in a value block.
|
||||
vbiBlock []byte
|
||||
vbiCache cache.Handle
|
||||
vbiCache bufferHandle
|
||||
// When sequentially iterating through all key-value pairs, the cost of
|
||||
// repeatedly getting a block that is already in the cache and releasing the
|
||||
// cache.Handle can be ~40% of the cpu overhead. So the reader remembers the
|
||||
// bufferHandle can be ~40% of the cpu overhead. So the reader remembers the
|
||||
// last value block it retrieved, in case there is locality of access, and
|
||||
// this value block can be used for the next value retrieval.
|
||||
valueBlockNum uint32
|
||||
valueBlock []byte
|
||||
valueBlockPtr unsafe.Pointer
|
||||
valueCache cache.Handle
|
||||
valueCache bufferHandle
|
||||
lazyFetcher base.LazyFetcher
|
||||
closed bool
|
||||
bufToMangle []byte
|
||||
@@ -833,12 +835,12 @@ func (r *valueBlockReader) close() {
|
||||
// we were to reopen this valueBlockReader and retrieve the same
|
||||
// Handle.value from the cache, we don't want to accidentally unref it when
|
||||
// attempting to unref the old handle.
|
||||
r.vbiCache = cache.Handle{}
|
||||
r.vbiCache = bufferHandle{}
|
||||
r.valueBlock = nil
|
||||
r.valueBlockPtr = nil
|
||||
r.valueCache.Release()
|
||||
// See comment above.
|
||||
r.valueCache = cache.Handle{}
|
||||
r.valueCache = bufferHandle{}
|
||||
r.closed = true
|
||||
// rp, vbih, stats remain valid, so that LazyFetcher.ValueFetcher can be
|
||||
// implemented.
|
||||
|
||||
42
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
42
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
@@ -852,32 +852,35 @@ func (w *Writer) makeAddPointDecisionV3(
|
||||
} else {
|
||||
cmpUser = w.compare(prevPointUserKey, key.UserKey)
|
||||
}
|
||||
keyKindIsSetOrMerge := keyKind == InternalKeyKindSet || keyKind == InternalKeyKindSetWithDelete ||
|
||||
keyKind == InternalKeyKindMerge
|
||||
keyKindIsPointDelete := keyKind == InternalKeyKindDelete ||
|
||||
keyKind == InternalKeyKindSingleDelete || keyKind == InternalKeyKindDeleteSized
|
||||
if !keyKindIsSetOrMerge && !keyKindIsPointDelete {
|
||||
// Ensure that no one adds a point key kind without considering the obsolete
|
||||
// handling for that kind.
|
||||
switch keyKind {
|
||||
case InternalKeyKindSet, InternalKeyKindSetWithDelete, InternalKeyKindMerge,
|
||||
InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||
default:
|
||||
panic(errors.AssertionFailedf("unexpected key kind %s", keyKind.String()))
|
||||
}
|
||||
// If same user key, then the current key is obsolete if any of the
|
||||
// following is true:
|
||||
// C1 The prev key was obsolete.
|
||||
// C2 The prev key was not a MERGE
|
||||
// C3 The current key is not a SET or SETWITHDELETE or MERGE, then it is
|
||||
// obsolete. This rule excludes SET, SETWITHDELETE, MERGE, since they
|
||||
// will be merged with the previous key.
|
||||
// C2 The prev key was not a MERGE. When the previous key is a MERGE we must
|
||||
// preserve SET* and MERGE since their values will be merged into the
|
||||
// previous key. We also must preserve DEL* since there may be an older
|
||||
// SET*/MERGE in a lower level that must not be merged with the MERGE --
|
||||
// if we omit the DEL* that lower SET*/MERGE will become visible.
|
||||
//
|
||||
// Regardless of whether it is the same user key or not
|
||||
// C4 The current key is some kind of point delete, and we are writing to
|
||||
// the lowest level, then it is also obsolete.
|
||||
// C3 The current key is some kind of point delete, and we are writing to
|
||||
// the lowest level, then it is also obsolete. The correctness of this
|
||||
// relies on the same user key not spanning multiple sstables in a level.
|
||||
//
|
||||
// C1 ensures that for a user key there is at most one transition from
|
||||
// !obsolete to obsolete. Consider a user key k, for which the first n keys
|
||||
// are not obsolete. We consider the various value of n:
|
||||
//
|
||||
// n = 0: This happens due to forceObsolete being set by the caller, or due
|
||||
// to C4. forceObsolete must only be set due a RANGEDEL, and that RANGEDEL
|
||||
// must also delete all the lower seqnums for the same user key. C4 triggers
|
||||
// to C3. forceObsolete must only be set due a RANGEDEL, and that RANGEDEL
|
||||
// must also delete all the lower seqnums for the same user key. C3 triggers
|
||||
// due to a point delete and that deletes all the lower seqnums for the same
|
||||
// user key.
|
||||
//
|
||||
@@ -885,12 +888,13 @@ func (w *Writer) makeAddPointDecisionV3(
|
||||
// MERGE, or the current key is some kind of point delete.
|
||||
//
|
||||
// n > 1: This is due to a sequence of MERGE keys, potentially followed by a
|
||||
// single SET or SETWITHDELETE.
|
||||
isObsolete = (cmpUser == 0 &&
|
||||
(prevKeyKind != InternalKeyKindMerge || prevPointKeyInfo.isObsolete || !keyKindIsSetOrMerge)) ||
|
||||
(w.writingToLowestLevel &&
|
||||
(keyKind == InternalKeyKindDelete || keyKind == InternalKeyKindSingleDelete ||
|
||||
keyKind == InternalKeyKindDeleteSized))
|
||||
// single non-MERGE key.
|
||||
isObsoleteC1AndC2 := cmpUser == 0 &&
|
||||
(prevPointKeyInfo.isObsolete || prevKeyKind != InternalKeyKindMerge)
|
||||
isObsoleteC3 := w.writingToLowestLevel &&
|
||||
(keyKind == InternalKeyKindDelete || keyKind == InternalKeyKindSingleDelete ||
|
||||
keyKind == InternalKeyKindDeleteSized)
|
||||
isObsolete = isObsoleteC1AndC2 || isObsoleteC3
|
||||
// TODO(sumeer): storing isObsolete SET and SETWITHDEL in value blocks is
|
||||
// possible, but requires some care in documenting and checking invariants.
|
||||
// There is code that assumes nothing in value blocks because of single MVCC
|
||||
|
||||
Reference in New Issue
Block a user