Upgrade versions of vendored libs
This commit is contained in:
25
vendor/github.com/cockroachdb/pebble/internal/base/comparer.go
generated
vendored
25
vendor/github.com/cockroachdb/pebble/internal/base/comparer.go
generated
vendored
@@ -70,11 +70,11 @@ type Successor func(dst, a []byte) []byte
|
||||
// returns the smallest key that is larger than the given prefix a.
|
||||
// ImmediateSuccessor must return a prefix key k such that:
|
||||
//
|
||||
// Split(k) == len(k) and Compare(k, a) > 0
|
||||
// Split(k) == len(k) and Compare(k, a) > 0
|
||||
//
|
||||
// and there exists no representable k2 such that:
|
||||
//
|
||||
// Split(k2) == len(k2) and Compare(k2, a) > 0 and Compare(k2, k) < 0
|
||||
// Split(k2) == len(k2) and Compare(k2, a) > 0 and Compare(k2, k) < 0
|
||||
//
|
||||
// As an example, an implementation built on the natural byte ordering using
|
||||
// bytes.Compare could append a `\0` to `a`.
|
||||
@@ -98,24 +98,23 @@ type ImmediateSuccessor func(dst, a []byte) []byte
|
||||
//
|
||||
// The returned prefix must have the following properties:
|
||||
//
|
||||
// 1) The prefix must be a byte prefix:
|
||||
// 1. The prefix must be a byte prefix:
|
||||
//
|
||||
// bytes.HasPrefix(a, prefix(a))
|
||||
// bytes.HasPrefix(a, prefix(a))
|
||||
//
|
||||
// 2) A key consisting of just a prefix must sort before all other keys with
|
||||
// that prefix:
|
||||
// 2. A key consisting of just a prefix must sort before all other keys with
|
||||
// that prefix:
|
||||
//
|
||||
// Compare(prefix(a), a) < 0 if len(suffix(a)) > 0
|
||||
// Compare(prefix(a), a) < 0 if len(suffix(a)) > 0
|
||||
//
|
||||
// 3) Prefixes must be used to order keys before suffixes:
|
||||
// 3. Prefixes must be used to order keys before suffixes:
|
||||
//
|
||||
// If Compare(a, b) <= 0, then Compare(prefix(a), prefix(b)) <= 0
|
||||
// If Compare(a, b) <= 0, then Compare(prefix(a), prefix(b)) <= 0
|
||||
//
|
||||
// 4) Suffixes themselves must be valid keys and comparable, respecting the same
|
||||
// ordering as within a key.
|
||||
//
|
||||
// If Compare(prefix(a), prefix(b)) == 0, then Compare(suffix(a), suffix(b)) == Compare(a, b)
|
||||
// 4. Suffixes themselves must be valid keys and comparable, respecting the same
|
||||
// ordering as within a key.
|
||||
//
|
||||
// If Compare(prefix(a), prefix(b)) == 0, then Compare(suffix(a), suffix(b)) == Compare(a, b)
|
||||
type Split func(a []byte) int
|
||||
|
||||
// Comparer defines a total ordering over the space of []byte keys: a 'less
|
||||
|
||||
79
vendor/github.com/cockroachdb/pebble/internal/base/filenames.go
generated
vendored
79
vendor/github.com/cockroachdb/pebble/internal/base/filenames.go
generated
vendored
@@ -20,6 +20,31 @@ type FileNum uint64
|
||||
// String returns a string representation of the file number.
|
||||
func (fn FileNum) String() string { return fmt.Sprintf("%06d", fn) }
|
||||
|
||||
// DiskFileNum converts a FileNum to a DiskFileNum. DiskFileNum should only be
|
||||
// called if the caller can ensure that the FileNum belongs to a physical file
|
||||
// on disk. These could be manifests, log files, physical sstables on disk, the
|
||||
// options file, but not virtual sstables.
|
||||
func (fn FileNum) DiskFileNum() DiskFileNum {
|
||||
return DiskFileNum{fn}
|
||||
}
|
||||
|
||||
// A DiskFileNum is just a FileNum belonging to a file which exists on disk.
|
||||
// Note that a FileNum is an internal DB identifier and it could belong to files
|
||||
// which don't exist on disk. An example would be virtual sstable FileNums.
|
||||
// Converting a DiskFileNum to a FileNum is always valid, whereas converting a
|
||||
// FileNum to DiskFileNum may not be valid and care should be taken to prove
|
||||
// that the FileNum actually exists on disk.
|
||||
type DiskFileNum struct {
|
||||
fn FileNum
|
||||
}
|
||||
|
||||
func (dfn DiskFileNum) String() string { return dfn.fn.String() }
|
||||
|
||||
// FileNum converts a DiskFileNum to a FileNum. This conversion is always valid.
|
||||
func (dfn DiskFileNum) FileNum() FileNum {
|
||||
return dfn.fn
|
||||
}
|
||||
|
||||
// FileType enumerates the types of files found in a DB.
|
||||
type FileType int
|
||||
|
||||
@@ -36,92 +61,92 @@ const (
|
||||
)
|
||||
|
||||
// MakeFilename builds a filename from components.
|
||||
func MakeFilename(fileType FileType, fileNum FileNum) string {
|
||||
func MakeFilename(fileType FileType, dfn DiskFileNum) string {
|
||||
switch fileType {
|
||||
case FileTypeLog:
|
||||
return fmt.Sprintf("%s.log", fileNum)
|
||||
return fmt.Sprintf("%s.log", dfn)
|
||||
case FileTypeLock:
|
||||
return "LOCK"
|
||||
case FileTypeTable:
|
||||
return fmt.Sprintf("%s.sst", fileNum)
|
||||
return fmt.Sprintf("%s.sst", dfn)
|
||||
case FileTypeManifest:
|
||||
return fmt.Sprintf("MANIFEST-%s", fileNum)
|
||||
return fmt.Sprintf("MANIFEST-%s", dfn)
|
||||
case FileTypeCurrent:
|
||||
return "CURRENT"
|
||||
case FileTypeOptions:
|
||||
return fmt.Sprintf("OPTIONS-%s", fileNum)
|
||||
return fmt.Sprintf("OPTIONS-%s", dfn)
|
||||
case FileTypeOldTemp:
|
||||
return fmt.Sprintf("CURRENT.%s.dbtmp", fileNum)
|
||||
return fmt.Sprintf("CURRENT.%s.dbtmp", dfn)
|
||||
case FileTypeTemp:
|
||||
return fmt.Sprintf("temporary.%s.dbtmp", fileNum)
|
||||
return fmt.Sprintf("temporary.%s.dbtmp", dfn)
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// MakeFilepath builds a filepath from components.
|
||||
func MakeFilepath(fs vfs.FS, dirname string, fileType FileType, fileNum FileNum) string {
|
||||
return fs.PathJoin(dirname, MakeFilename(fileType, fileNum))
|
||||
func MakeFilepath(fs vfs.FS, dirname string, fileType FileType, dfn DiskFileNum) string {
|
||||
return fs.PathJoin(dirname, MakeFilename(fileType, dfn))
|
||||
}
|
||||
|
||||
// ParseFilename parses the components from a filename.
|
||||
func ParseFilename(fs vfs.FS, filename string) (fileType FileType, fileNum FileNum, ok bool) {
|
||||
func ParseFilename(fs vfs.FS, filename string) (fileType FileType, dfn DiskFileNum, ok bool) {
|
||||
filename = fs.PathBase(filename)
|
||||
switch {
|
||||
case filename == "CURRENT":
|
||||
return FileTypeCurrent, 0, true
|
||||
return FileTypeCurrent, DiskFileNum{0}, true
|
||||
case filename == "LOCK":
|
||||
return FileTypeLock, 0, true
|
||||
return FileTypeLock, DiskFileNum{0}, true
|
||||
case strings.HasPrefix(filename, "MANIFEST-"):
|
||||
fileNum, ok = parseFileNum(filename[len("MANIFEST-"):])
|
||||
dfn, ok = parseDiskFileNum(filename[len("MANIFEST-"):])
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return FileTypeManifest, fileNum, true
|
||||
return FileTypeManifest, dfn, true
|
||||
case strings.HasPrefix(filename, "OPTIONS-"):
|
||||
fileNum, ok = parseFileNum(filename[len("OPTIONS-"):])
|
||||
dfn, ok = parseDiskFileNum(filename[len("OPTIONS-"):])
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return FileTypeOptions, fileNum, ok
|
||||
return FileTypeOptions, dfn, ok
|
||||
case strings.HasPrefix(filename, "CURRENT.") && strings.HasSuffix(filename, ".dbtmp"):
|
||||
s := strings.TrimSuffix(filename[len("CURRENT."):], ".dbtmp")
|
||||
fileNum, ok = parseFileNum(s)
|
||||
dfn, ok = parseDiskFileNum(s)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return FileTypeOldTemp, fileNum, ok
|
||||
return FileTypeOldTemp, dfn, ok
|
||||
case strings.HasPrefix(filename, "temporary.") && strings.HasSuffix(filename, ".dbtmp"):
|
||||
s := strings.TrimSuffix(filename[len("temporary."):], ".dbtmp")
|
||||
fileNum, ok = parseFileNum(s)
|
||||
dfn, ok = parseDiskFileNum(s)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return FileTypeTemp, fileNum, ok
|
||||
return FileTypeTemp, dfn, ok
|
||||
default:
|
||||
i := strings.IndexByte(filename, '.')
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
fileNum, ok = parseFileNum(filename[:i])
|
||||
dfn, ok = parseDiskFileNum(filename[:i])
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch filename[i+1:] {
|
||||
case "sst":
|
||||
return FileTypeTable, fileNum, true
|
||||
return FileTypeTable, dfn, true
|
||||
case "log":
|
||||
return FileTypeLog, fileNum, true
|
||||
return FileTypeLog, dfn, true
|
||||
}
|
||||
}
|
||||
return 0, fileNum, false
|
||||
return 0, dfn, false
|
||||
}
|
||||
|
||||
func parseFileNum(s string) (fileNum FileNum, ok bool) {
|
||||
func parseDiskFileNum(s string) (dfn DiskFileNum, ok bool) {
|
||||
u, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return fileNum, false
|
||||
return dfn, false
|
||||
}
|
||||
return FileNum(u), true
|
||||
return DiskFileNum{FileNum(u)}, true
|
||||
}
|
||||
|
||||
// A Fataler fatals a process with a message when called.
|
||||
|
||||
40
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
40
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
@@ -60,6 +60,12 @@ const (
|
||||
InternalKeyKindRangeKeyUnset InternalKeyKind = 20
|
||||
InternalKeyKindRangeKeySet InternalKeyKind = 21
|
||||
|
||||
// InternalKeyKindIngestSST is used to distinguish a batch that corresponds to
|
||||
// the WAL entry for ingested sstables that are added to the flushable
|
||||
// queue. This InternalKeyKind cannot appear, amongst other key kinds in a
|
||||
// batch, or in an sstable.
|
||||
InternalKeyKindIngestSST InternalKeyKind = 22
|
||||
|
||||
// This maximum value isn't part of the file format. It's unlikely,
|
||||
// but future extensions may increase this value.
|
||||
//
|
||||
@@ -69,7 +75,7 @@ const (
|
||||
// which sorts 'less than or equal to' any other valid internalKeyKind, when
|
||||
// searching for any kind of internal key formed by a certain user key and
|
||||
// seqNum.
|
||||
InternalKeyKindMax InternalKeyKind = 21
|
||||
InternalKeyKindMax InternalKeyKind = 22
|
||||
|
||||
// InternalKeyZeroSeqnumMaxTrailer is the largest trailer with a
|
||||
// zero sequence number.
|
||||
@@ -111,6 +117,7 @@ var internalKeyKindNames = []string{
|
||||
InternalKeyKindRangeKeySet: "RANGEKEYSET",
|
||||
InternalKeyKindRangeKeyUnset: "RANGEKEYUNSET",
|
||||
InternalKeyKindRangeKeyDelete: "RANGEKEYDEL",
|
||||
InternalKeyKindIngestSST: "INGESTSST",
|
||||
InternalKeyKindInvalid: "INVALID",
|
||||
}
|
||||
|
||||
@@ -187,6 +194,7 @@ var kindsMap = map[string]InternalKeyKind{
|
||||
"DEL": InternalKeyKindDelete,
|
||||
"SINGLEDEL": InternalKeyKindSingleDelete,
|
||||
"RANGEDEL": InternalKeyKindRangeDelete,
|
||||
"LOGDATA": InternalKeyKindLogData,
|
||||
"SET": InternalKeyKindSet,
|
||||
"MERGE": InternalKeyKindMerge,
|
||||
"INVALID": InternalKeyKindInvalid,
|
||||
@@ -195,6 +203,7 @@ var kindsMap = map[string]InternalKeyKind{
|
||||
"RANGEKEYSET": InternalKeyKindRangeKeySet,
|
||||
"RANGEKEYUNSET": InternalKeyKindRangeKeyUnset,
|
||||
"RANGEKEYDEL": InternalKeyKindRangeKeyDelete,
|
||||
"INGESTSST": InternalKeyKindIngestSST,
|
||||
}
|
||||
|
||||
// ParseInternalKey parses the string representation of an internal key. The
|
||||
@@ -362,9 +371,14 @@ func (k *InternalKey) SetKind(kind InternalKeyKind) {
|
||||
k.Trailer = (k.Trailer &^ 0xff) | uint64(kind)
|
||||
}
|
||||
|
||||
// Kind returns the kind compoment of the key.
|
||||
// Kind returns the kind component of the key.
|
||||
func (k InternalKey) Kind() InternalKeyKind {
|
||||
return InternalKeyKind(k.Trailer & 0xff)
|
||||
return TrailerKind(k.Trailer)
|
||||
}
|
||||
|
||||
// TrailerKind returns the key kind of the key trailer.
|
||||
func TrailerKind(trailer uint64) InternalKeyKind {
|
||||
return InternalKeyKind(trailer & 0xff)
|
||||
}
|
||||
|
||||
// Valid returns true if the key has a valid kind.
|
||||
@@ -383,6 +397,13 @@ func (k InternalKey) Clone() InternalKey {
|
||||
}
|
||||
}
|
||||
|
||||
// CopyFrom converts this InternalKey into a clone of the passed-in InternalKey,
|
||||
// reusing any space already used for the current UserKey.
|
||||
func (k *InternalKey) CopyFrom(k2 InternalKey) {
|
||||
k.UserKey = append(k.UserKey[:0], k2.UserKey...)
|
||||
k.Trailer = k2.Trailer
|
||||
}
|
||||
|
||||
// String returns a string representation of the key.
|
||||
func (k InternalKey) String() string {
|
||||
return fmt.Sprintf("%s#%d,%d", FormatBytes(k.UserKey), k.SeqNum(), k.Kind())
|
||||
@@ -413,7 +434,11 @@ type prettyInternalKey struct {
|
||||
}
|
||||
|
||||
func (k prettyInternalKey) Format(s fmt.State, c rune) {
|
||||
fmt.Fprintf(s, "%s#%d,%s", k.formatKey(k.UserKey), k.SeqNum(), k.Kind())
|
||||
if seqNum := k.SeqNum(); seqNum == InternalKeySeqNumMax {
|
||||
fmt.Fprintf(s, "%s#inf,%s", k.formatKey(k.UserKey), k.Kind())
|
||||
} else {
|
||||
fmt.Fprintf(s, "%s#%d,%s", k.formatKey(k.UserKey), k.SeqNum(), k.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// ParsePrettyInternalKey parses the pretty string representation of an
|
||||
@@ -425,6 +450,11 @@ func ParsePrettyInternalKey(s string) InternalKey {
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown kind: %q", x[2]))
|
||||
}
|
||||
seqNum, _ := strconv.ParseUint(x[1], 10, 64)
|
||||
var seqNum uint64
|
||||
if x[1] == "max" || x[1] == "inf" {
|
||||
seqNum = InternalKeySeqNumMax
|
||||
} else {
|
||||
seqNum, _ = strconv.ParseUint(x[1], 10, 64)
|
||||
}
|
||||
return MakeInternalKey([]byte(ukey), seqNum, kind)
|
||||
}
|
||||
|
||||
57
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
57
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
@@ -4,7 +4,10 @@
|
||||
|
||||
package base
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// InternalIterator iterates over a DB's key/value pairs in key order. Unlike
|
||||
// the Iterator interface, the returned keys are InternalKeys composed of the
|
||||
@@ -40,7 +43,9 @@ import "fmt"
|
||||
// context is not a strict byte prefix, but defined by byte equality for the
|
||||
// result of the Comparer.Split method. An InternalIterator is not required to
|
||||
// support prefix iteration mode, and can implement SeekPrefixGE by forwarding
|
||||
// to SeekGE.
|
||||
// to SeekGE. When the iteration prefix is exhausted, it is not valid to call
|
||||
// Next on an internal iterator that's already returned (nil,nilv) or a key
|
||||
// beyond the prefix.
|
||||
//
|
||||
// Bounds, [lower, upper), can be set on iterators, either using the SetBounds()
|
||||
// function in the interface, or in implementation specific ways during iterator
|
||||
@@ -152,6 +157,22 @@ type InternalIterator interface {
|
||||
// SeekPrefixGE or Next returned (nil, nilv).
|
||||
Next() (*InternalKey, LazyValue)
|
||||
|
||||
// NextPrefix moves the iterator to the next key/value pair with a different
|
||||
// prefix than the key at the current iterator position. Returns the key and
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nil)
|
||||
// otherwise. Note that NextPrefix only checks the upper bound. It is up to
|
||||
// the caller to ensure that key is greater than or equal to the lower
|
||||
// bound.
|
||||
//
|
||||
// NextPrefix is passed the immediate successor to the current prefix key. A
|
||||
// valid implementation of NextPrefix is to call SeekGE with succKey.
|
||||
//
|
||||
// It is not allowed to call NextPrefix when the previous call was a reverse
|
||||
// positioning operation or a call to a forward positioning method that
|
||||
// returned (nil, nilv). It is also not allowed to call NextPrefix when the
|
||||
// iterator is in prefix iteration mode.
|
||||
NextPrefix(succKey []byte) (*InternalKey, LazyValue)
|
||||
|
||||
// Prev moves the iterator to the previous key/value pair. Returns the key
|
||||
// and value if the iterator is pointing at a valid entry, and (nil, nilv)
|
||||
// otherwise. Note that Prev only checks the lower bound. It is up to the
|
||||
@@ -330,7 +351,11 @@ type InternalIteratorStats struct {
|
||||
BlockBytes uint64
|
||||
// Subset of BlockBytes that were in the block cache.
|
||||
BlockBytesInCache uint64
|
||||
|
||||
// BlockReadDuration accumulates the duration spent fetching blocks
|
||||
// due to block cache misses.
|
||||
// TODO(sumeer): this currently excludes the time spent in Reader creation,
|
||||
// and in reading the rangedel and rangekey blocks. Fix that.
|
||||
BlockReadDuration time.Duration
|
||||
// The following can repeatedly count the same points if they are iterated
|
||||
// over multiple times. Additionally, they may count a point twice when
|
||||
// switching directions. The latter could be improved if needed.
|
||||
@@ -339,7 +364,7 @@ type InternalIteratorStats struct {
|
||||
// included.
|
||||
KeyBytes uint64
|
||||
// Bytes in values that were iterated over. Currently, only point values are
|
||||
// included.
|
||||
// included. For separated values, this is the size of the handle.
|
||||
ValueBytes uint64
|
||||
// The count of points iterated over.
|
||||
PointCount uint64
|
||||
@@ -347,14 +372,38 @@ type InternalIteratorStats struct {
|
||||
// can be useful for discovering instances of
|
||||
// https://github.com/cockroachdb/pebble/issues/1070.
|
||||
PointsCoveredByRangeTombstones uint64
|
||||
|
||||
// Stats related to points in value blocks encountered during iteration.
|
||||
// These are useful to understand outliers, since typical user facing
|
||||
// iteration should tend to only look at the latest point, and hence have
|
||||
// the following stats close to 0.
|
||||
SeparatedPointValue struct {
|
||||
// Count is a count of points that were in value blocks. This is not a
|
||||
// subset of PointCount: PointCount is produced by mergingIter and if
|
||||
// positioned once, and successful in returning a point, will have a
|
||||
// PointCount of 1, regardless of how many sstables (and memtables etc.)
|
||||
// in the heap got positioned. The count here includes every sstable
|
||||
// iterator that got positioned in the heap.
|
||||
Count uint64
|
||||
// ValueBytes represent the total byte length of the values (in value
|
||||
// blocks) of the points corresponding to Count.
|
||||
ValueBytes uint64
|
||||
// ValueBytesFetched is the total byte length of the values (in value
|
||||
// blocks) that were retrieved.
|
||||
ValueBytesFetched uint64
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges the stats in from into the given stats.
|
||||
func (s *InternalIteratorStats) Merge(from InternalIteratorStats) {
|
||||
s.BlockBytes += from.BlockBytes
|
||||
s.BlockBytesInCache += from.BlockBytesInCache
|
||||
s.BlockReadDuration += from.BlockReadDuration
|
||||
s.KeyBytes += from.KeyBytes
|
||||
s.ValueBytes += from.ValueBytes
|
||||
s.PointCount += from.PointCount
|
||||
s.PointsCoveredByRangeTombstones += from.PointsCoveredByRangeTombstones
|
||||
s.SeparatedPointValue.Count += from.SeparatedPointValue.Count
|
||||
s.SeparatedPointValue.ValueBytes += from.SeparatedPointValue.ValueBytes
|
||||
s.SeparatedPointValue.ValueBytesFetched += from.SeparatedPointValue.ValueBytesFetched
|
||||
}
|
||||
|
||||
175
vendor/github.com/cockroachdb/pebble/internal/base/lazy_value.go
generated
vendored
175
vendor/github.com/cockroachdb/pebble/internal/base/lazy_value.go
generated
vendored
@@ -16,12 +16,8 @@ import "github.com/cockroachdb/pebble/internal/invariants"
|
||||
// it can call the ShortAttributeExtractor to extract the attribute and store
|
||||
// it together with the key. This allows for cheap retrieval of
|
||||
// AttributeAndLen on the read-path, without doing a more expensive retrieval
|
||||
// of the value.
|
||||
//
|
||||
// In general, the extraction code may want to also look at the key to decide
|
||||
// how to treat the value. Our current needs can be satisfied by configuring a
|
||||
// keyspan for which the ShortAttributeExtractor should be used, so we do not
|
||||
// expose the key to the extractor.
|
||||
// of the value. In general, the extraction code may want to also look at the
|
||||
// key to decide how to treat the value, hence the key* parameters.
|
||||
//
|
||||
// Write path performance: The ShortAttributeExtractor func cannot be inlined,
|
||||
// so we will pay the cost of this function call. However, we will only pay
|
||||
@@ -38,7 +34,8 @@ const MaxShortAttribute = 7
|
||||
|
||||
// ShortAttributeExtractor is an extractor that given the value, will return
|
||||
// the ShortAttribute.
|
||||
type ShortAttributeExtractor func(value []byte) (ShortAttribute, error)
|
||||
type ShortAttributeExtractor func(
|
||||
key []byte, keyPrefixLen int, value []byte) (ShortAttribute, error)
|
||||
|
||||
// AttributeAndLen represents the pair of value length and the short
|
||||
// attribute.
|
||||
@@ -70,24 +67,26 @@ type AttributeAndLen struct {
|
||||
// Memory management:
|
||||
// This is subtle, but important for performance.
|
||||
//
|
||||
// - A LazyValue returned by an InternalIterator or Iterator is unstable in
|
||||
// that repositioning the iterator will invalidate the memory inside it. A
|
||||
// caller wishing to maintain that LazyValue needs to call
|
||||
// LazyValue.Clone(). Note that this does not fetch the value if it is not
|
||||
// in-place. Additionally, Clone() *must* only be called if
|
||||
// LazyValue.Value() has *not* been called, since (a) it only makes a
|
||||
// promise about stability of LazyValue and not the underlying value, (b) if
|
||||
// LazyValue.Value() has been called already, the caller has the value
|
||||
// []byte slice so it is unnecessary to fiddle around with LazyValue.
|
||||
// A LazyValue returned by an InternalIterator or Iterator is unstable in that
|
||||
// repositioning the iterator will invalidate the memory inside it. A caller
|
||||
// wishing to maintain that LazyValue needs to call LazyValue.Clone(). Note
|
||||
// that this does not fetch the value if it is not in-place. Clone() should
|
||||
// ideally not be called if LazyValue.Value() has been called, since the
|
||||
// cloned LazyValue will forget the extracted/fetched value, and calling
|
||||
// Value() on this clone will cause the value to be extracted again. That is,
|
||||
// Clone() does not make any promise about the memory stability of the
|
||||
// underlying value.
|
||||
//
|
||||
// - A user of an iterator that calls LazyValue.Value() wants as much as
|
||||
// possible for the returned value []byte to point to iterator owned memory.
|
||||
// - [P1] The underlying iterator that owns that memory also needs a promise
|
||||
// A user of an iterator that calls LazyValue.Value() wants as much as
|
||||
// possible for the returned value []byte to point to iterator owned memory.
|
||||
//
|
||||
// 1. [P1] The underlying iterator that owns that memory also needs a promise
|
||||
// from that user that at any time there is at most one value []byte slice
|
||||
// that the caller is expecting it to maintain. Otherwise, the underlying
|
||||
// iterator has to maintain multiple such []byte slices which results in
|
||||
// more complicated and inefficient code.
|
||||
// - [P2] The underlying iterator, in order to make the promise that it is
|
||||
//
|
||||
// 2. [P2] The underlying iterator, in order to make the promise that it is
|
||||
// maintaining the one value []byte slice, also needs a way to know when
|
||||
// it is relieved of that promise. One way it is relieved of that promise
|
||||
// is by being told that it is being repositioned. Typically, the owner of
|
||||
@@ -105,28 +104,31 @@ type AttributeAndLen struct {
|
||||
// the caller. This will be true if the callee attempted to use buf and
|
||||
// either successfully used it or allocated a new []byte slice.
|
||||
//
|
||||
// To ground the above in reality, we consider three examples of callers of
|
||||
// LazyValue.Value():
|
||||
// - Iterator: it calls LazyValue.Value for its own use when merging values.
|
||||
// When merging during reverse iteration, it may have cloned the LazyValue.
|
||||
// In this case it calls LazyValue.Value() on the cloned value, merges it,
|
||||
// and then calls LazyValue.Value() on the current iterator position and
|
||||
// merges it. So it is honoring P1.
|
||||
// - Iterator on behalf of Iterator clients: The Iterator.Value() method
|
||||
// needs to call LazyValue.Value(). The client of Iterator is satisfying P1
|
||||
// because of the inherent Iterator interface constraint, i.e., it is calling
|
||||
// Iterator.Value() on the current Iterator position. It is possible that
|
||||
// the Iterator has cloned this LazyValue (for the reverse iteration case),
|
||||
// which the client is unaware of, so the underlying sstable iterator may
|
||||
// not be able to satisfy P2. This is ok because Iterator will call
|
||||
// LazyValue.Value with its (reusable) owned buffer.
|
||||
// - CockroachDB's pebbleMVCCScanner: This will use LazyValues from Iterator
|
||||
// since during reverse iteration in order to find the highest version that
|
||||
// satisfies a read it needs to clone the LazyValue, step back the iterator
|
||||
// and then decide whether it needs the value from the previously cloned
|
||||
// LazyValue. The pebbleMVCCScanner will satisfy P1. The P2 story is
|
||||
// similar to the previous case in that it will call LazyValue.Value with
|
||||
// its (reusable) owned buffer.
|
||||
// To ground the above in reality, we consider three examples of callers of
|
||||
// LazyValue.Value():
|
||||
//
|
||||
// - Iterator: it calls LazyValue.Value for its own use when merging values.
|
||||
// When merging during reverse iteration, it may have cloned the LazyValue.
|
||||
// In this case it calls LazyValue.Value() on the cloned value, merges it,
|
||||
// and then calls LazyValue.Value() on the current iterator position and
|
||||
// merges it. So it is honoring P1.
|
||||
//
|
||||
// - Iterator on behalf of Iterator clients: The Iterator.Value() method
|
||||
// needs to call LazyValue.Value(). The client of Iterator is satisfying P1
|
||||
// because of the inherent Iterator interface constraint, i.e., it is calling
|
||||
// Iterator.Value() on the current Iterator position. It is possible that
|
||||
// the Iterator has cloned this LazyValue (for the reverse iteration case),
|
||||
// which the client is unaware of, so the underlying sstable iterator may
|
||||
// not be able to satisfy P2. This is ok because Iterator will call
|
||||
// LazyValue.Value with its (reusable) owned buffer.
|
||||
//
|
||||
// - CockroachDB's pebbleMVCCScanner: This will use LazyValues from Iterator
|
||||
// since during reverse iteration in order to find the highest version that
|
||||
// satisfies a read it needs to clone the LazyValue, step back the iterator
|
||||
// and then decide whether it needs the value from the previously cloned
|
||||
// LazyValue. The pebbleMVCCScanner will satisfy P1. The P2 story is
|
||||
// similar to the previous case in that it will call LazyValue.Value with
|
||||
// its (reusable) owned buffer.
|
||||
//
|
||||
// Corollary: callers that directly use InternalIterator can know that they
|
||||
// have done nothing to interfere with promise P2 can pass in a nil buf and be
|
||||
@@ -154,9 +156,24 @@ type LazyValue struct {
|
||||
}
|
||||
|
||||
// LazyFetcher supports fetching a lazy value.
|
||||
//
|
||||
// Fetcher and Attribute are to be initialized at creation time. The fields
|
||||
// are arranged to reduce the sizeof this struct.
|
||||
type LazyFetcher struct {
|
||||
// ValueFetcher, given a handle, returns the value. It is acceptable to call
|
||||
// the ValueFetcher as long as the DB is open. However, one should assume
|
||||
// Fetcher, given a handle, returns the value.
|
||||
Fetcher ValueFetcher
|
||||
err error
|
||||
value []byte
|
||||
// Attribute includes the short attribute and value length.
|
||||
Attribute AttributeAndLen
|
||||
fetched bool
|
||||
callerOwned bool
|
||||
}
|
||||
|
||||
// ValueFetcher is an interface for fetching a value.
|
||||
type ValueFetcher interface {
|
||||
// Fetch returns the value, given the handle. It is acceptable to call the
|
||||
// ValueFetcher.Fetch as long as the DB is open. However, one should assume
|
||||
// there is a fast-path when the iterator tree has not moved off the sstable
|
||||
// iterator that initially provided this LazyValue. Hence, to utilize this
|
||||
// fast-path the caller should try to decide whether it needs the value or
|
||||
@@ -166,13 +183,8 @@ type LazyFetcher struct {
|
||||
// If the fetcher attempted to use buf *and* len(buf) was insufficient, it
|
||||
// will allocate a new slice for the value. In either case it will set
|
||||
// callerOwned to true.
|
||||
ValueFetcher func(handle []byte, buf []byte) (val []byte, callerOwned bool, err error)
|
||||
// Attribute includes the short attribute and value length.
|
||||
Attribute AttributeAndLen
|
||||
fetched bool
|
||||
value []byte
|
||||
callerOwned bool
|
||||
err error
|
||||
Fetch(
|
||||
handle []byte, valLen int32, buf []byte) (val []byte, callerOwned bool, err error)
|
||||
}
|
||||
|
||||
// Value returns the underlying value.
|
||||
@@ -197,7 +209,8 @@ func (lv *LazyValue) fetchValue(buf []byte) (val []byte, callerOwned bool, err e
|
||||
f := lv.Fetcher
|
||||
if !f.fetched {
|
||||
f.fetched = true
|
||||
f.value, f.callerOwned, f.err = lv.Fetcher.ValueFetcher(lv.ValueOrHandle, buf)
|
||||
f.value, f.callerOwned, f.err = f.Fetcher.Fetch(
|
||||
lv.ValueOrHandle, lv.Fetcher.Attribute.ValueLen, buf)
|
||||
}
|
||||
return f.value, f.callerOwned, f.err
|
||||
}
|
||||
@@ -219,18 +232,46 @@ func (lv *LazyValue) Len() int {
|
||||
return int(lv.Fetcher.Attribute.ValueLen)
|
||||
}
|
||||
|
||||
// Clone creates a stable copy of the LazyValue, by appending bytes to buf.
|
||||
// REQUIRES: LazyValue.Value() has not been called.
|
||||
// Ensure that this is only called before LazyValue.Value is called. We don't
|
||||
// actually care if it was in-place but we don't want confusing things about whether
|
||||
// we need to clone the value inside the fetcher.
|
||||
func (lv *LazyValue) Clone(buf []byte) (LazyValue, []byte) {
|
||||
if invariants.Enabled && lv.Fetcher != nil && lv.Fetcher.fetched {
|
||||
panic("value has already been fetched")
|
||||
// TryGetShortAttribute returns the ShortAttribute and a bool indicating
|
||||
// whether the ShortAttribute was populated.
|
||||
func (lv *LazyValue) TryGetShortAttribute() (ShortAttribute, bool) {
|
||||
if lv.Fetcher == nil {
|
||||
return 0, false
|
||||
}
|
||||
return lv.Fetcher.Attribute.ShortAttribute, true
|
||||
}
|
||||
|
||||
// Clone creates a stable copy of the LazyValue, by appending bytes to buf.
|
||||
// The fetcher parameter must be non-nil and may be over-written and used
|
||||
// inside the returned LazyValue -- this is needed to avoid an allocation.
|
||||
// Most callers have at most K cloned LazyValues, where K is hard-coded, so
|
||||
// they can have a pool of exactly K LazyFetcher structs they can reuse in
|
||||
// these calls. The alternative of allocating LazyFetchers from a sync.Pool is
|
||||
// not viable since we have no code trigger for returning to the pool
|
||||
// (LazyValues are simply GC'd).
|
||||
//
|
||||
// NB: It is highly preferable that LazyValue.Value() has not been called,
|
||||
// since the Clone will forget any previously extracted value, and a future
|
||||
// call to Value will cause it to be fetched again. We do this since we don't
|
||||
// want to reason about whether or not to clone an already extracted value
|
||||
// inside the Fetcher (we don't). Property P1 applies here too: if lv1.Value()
|
||||
// has been called, and then lv2 is created as a clone of lv1, then calling
|
||||
// lv2.Value() can invalidate any backing memory maintained inside the fetcher
|
||||
// for lv1 (even though these are the same values). We initially prohibited
|
||||
// calling LazyValue.Clone() if LazyValue.Value() has been called, but there
|
||||
// is at least one complex caller (pebbleMVCCScanner inside CockroachDB) where
|
||||
// it is not easy to prove this invariant.
|
||||
func (lv *LazyValue) Clone(buf []byte, fetcher *LazyFetcher) (LazyValue, []byte) {
|
||||
var lvCopy LazyValue
|
||||
if lv.Fetcher != nil {
|
||||
*fetcher = LazyFetcher{
|
||||
Fetcher: lv.Fetcher.Fetcher,
|
||||
Attribute: lv.Fetcher.Attribute,
|
||||
// Not copying anything that has been extracted.
|
||||
}
|
||||
lvCopy.Fetcher = fetcher
|
||||
}
|
||||
// INVARIANT: LazyFetcher.value is nil, so there is nothing to copy there.
|
||||
vLen := len(lv.ValueOrHandle)
|
||||
lvCopy := LazyValue{Fetcher: lv.Fetcher}
|
||||
if vLen == 0 {
|
||||
return lvCopy, buf
|
||||
}
|
||||
@@ -244,13 +285,3 @@ func (lv *LazyValue) Clone(buf []byte) (LazyValue, []byte) {
|
||||
func MakeInPlaceValue(val []byte) LazyValue {
|
||||
return LazyValue{ValueOrHandle: val}
|
||||
}
|
||||
|
||||
// TODO(sumeer): test that callers are adhering to promise P1. In the sstable
|
||||
// iterators, when invariants are enabled, create a testingFetcher struct that
|
||||
// contains the sstable name and the []byte backing for the last fetched
|
||||
// value. The sstable iterator, instead of returning an in-place value, will
|
||||
// return a copy of the key in ValueOrHandle, and a ValueFetcher implemented
|
||||
// by testingFetcher.valueFetcher. Each time this method is called, it will
|
||||
// mangle the last fetched value []byte slice, allocate a new one, open the
|
||||
// sstable, seek to the key, and copy the value into this new byte slice and
|
||||
// return.
|
||||
|
||||
144
vendor/github.com/cockroachdb/pebble/internal/base/logger.go
generated
vendored
Normal file
144
vendor/github.com/cockroachdb/pebble/internal/base/logger.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// 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 base
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
)
|
||||
|
||||
// Logger defines an interface for writing log messages.
|
||||
type Logger interface {
|
||||
Infof(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
type defaultLogger struct{}
|
||||
|
||||
// DefaultLogger logs to the Go stdlib logs.
|
||||
var DefaultLogger defaultLogger
|
||||
|
||||
var _ Logger = DefaultLogger
|
||||
|
||||
// Infof implements the Logger.Infof interface.
|
||||
func (defaultLogger) Infof(format string, args ...interface{}) {
|
||||
_ = log.Output(2, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf implements the Logger.Fatalf interface.
|
||||
func (defaultLogger) Fatalf(format string, args ...interface{}) {
|
||||
_ = log.Output(2, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// InMemLogger implements Logger using an in-memory buffer (used for testing).
|
||||
// The buffer can be read via String() and cleared via Reset().
|
||||
type InMemLogger struct {
|
||||
mu struct {
|
||||
sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
}
|
||||
|
||||
var _ Logger = (*InMemLogger)(nil)
|
||||
|
||||
// Reset clears the internal buffer.
|
||||
func (b *InMemLogger) Reset() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.mu.buf.Reset()
|
||||
}
|
||||
|
||||
// String returns the current internal buffer.
|
||||
func (b *InMemLogger) String() string {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.mu.buf.String()
|
||||
}
|
||||
|
||||
// Infof is part of the Logger interface.
|
||||
func (b *InMemLogger) Infof(format string, args ...interface{}) {
|
||||
s := fmt.Sprintf(format, args...)
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.mu.buf.Write([]byte(s))
|
||||
if n := len(s); n == 0 || s[n-1] != '\n' {
|
||||
b.mu.buf.Write([]byte("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf is part of the Logger interface.
|
||||
func (b *InMemLogger) Fatalf(format string, args ...interface{}) {
|
||||
b.Infof(format, args...)
|
||||
runtime.Goexit()
|
||||
}
|
||||
|
||||
// LoggerAndTracer defines an interface for logging and tracing.
|
||||
type LoggerAndTracer interface {
|
||||
Logger
|
||||
// Eventf formats and emits a tracing log, if tracing is enabled in the
|
||||
// current context.
|
||||
Eventf(ctx context.Context, format string, args ...interface{})
|
||||
// IsTracingEnabled returns true if tracing is enabled. It can be used as an
|
||||
// optimization to avoid calling Eventf (which will be a noop when tracing
|
||||
// is not enabled) to avoid the overhead of boxing the args.
|
||||
IsTracingEnabled(ctx context.Context) bool
|
||||
}
|
||||
|
||||
// LoggerWithNoopTracer wraps a logger and does no tracing.
|
||||
type LoggerWithNoopTracer struct {
|
||||
Logger
|
||||
}
|
||||
|
||||
var _ LoggerAndTracer = &LoggerWithNoopTracer{}
|
||||
|
||||
// Eventf implements LoggerAndTracer.
|
||||
func (*LoggerWithNoopTracer) Eventf(ctx context.Context, format string, args ...interface{}) {
|
||||
if invariants.Enabled && ctx == nil {
|
||||
panic("Eventf context is nil")
|
||||
}
|
||||
}
|
||||
|
||||
// IsTracingEnabled implements LoggerAndTracer.
|
||||
func (*LoggerWithNoopTracer) IsTracingEnabled(ctx context.Context) bool {
|
||||
if invariants.Enabled && ctx == nil {
|
||||
panic("IsTracingEnabled ctx is nil")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NoopLoggerAndTracer does no logging and tracing. Remember that struct{} is
|
||||
// special cased in Go and does not incur an allocation when it backs the
|
||||
// interface LoggerAndTracer.
|
||||
type NoopLoggerAndTracer struct{}
|
||||
|
||||
var _ LoggerAndTracer = NoopLoggerAndTracer{}
|
||||
|
||||
// Infof implements LoggerAndTracer.
|
||||
func (l NoopLoggerAndTracer) Infof(format string, args ...interface{}) {}
|
||||
|
||||
// Fatalf implements LoggerAndTracer.
|
||||
func (l NoopLoggerAndTracer) Fatalf(format string, args ...interface{}) {}
|
||||
|
||||
// Eventf implements LoggerAndTracer.
|
||||
func (l NoopLoggerAndTracer) Eventf(ctx context.Context, format string, args ...interface{}) {
|
||||
if invariants.Enabled && ctx == nil {
|
||||
panic("Eventf context is nil")
|
||||
}
|
||||
}
|
||||
|
||||
// IsTracingEnabled implements LoggerAndTracer.
|
||||
func (l NoopLoggerAndTracer) IsTracingEnabled(ctx context.Context) bool {
|
||||
if invariants.Enabled && ctx == nil {
|
||||
panic("IsTracingEnabled ctx is nil")
|
||||
}
|
||||
return false
|
||||
}
|
||||
2
vendor/github.com/cockroachdb/pebble/internal/base/merger.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/internal/base/merger.go
generated
vendored
@@ -23,7 +23,7 @@ type Merge func(key, value []byte) (ValueMerger, error)
|
||||
//
|
||||
// The merge operation must be associative. That is, for the values A, B, C:
|
||||
//
|
||||
// Merge(A).MergeOlder(B).MergeOlder(C) == Merge(C).MergeNewer(B).MergeNewer(A)
|
||||
// Merge(A).MergeOlder(B).MergeOlder(C) == Merge(C).MergeNewer(B).MergeNewer(A)
|
||||
//
|
||||
// Examples of merge operators are integer addition, list append, and string
|
||||
// concatenation.
|
||||
|
||||
54
vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go
generated
vendored
Normal file
54
vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 base
|
||||
|
||||
// This file defines sequence numbers that are reserved for foreign keys i.e.
|
||||
// internal keys coming from other Pebble instances and existing in shared
|
||||
// storage, as those will "slot below" any internal keys added by our own Pebble
|
||||
// instance. Any keys created by this Pebble instance need to be greater than
|
||||
// all reserved sequence numbers (i.e. >= SeqNumStart).
|
||||
const (
|
||||
// SeqNumZero is the zero sequence number, set by compactions if they can
|
||||
// guarantee there are no keys underneath an internal key.
|
||||
SeqNumZero = uint64(0)
|
||||
|
||||
// SeqNumL6Point is the sequence number reserved for foreign point keys in L6.
|
||||
// This sequence number must be lower than SeqNumL6RangeKey for range key
|
||||
// masking to work correctly.
|
||||
SeqNumL6Point = uint64(1)
|
||||
|
||||
// SeqNumL6RangeKey is the sequence number reserved for foreign range keys in
|
||||
// L6. Only RangeKeySets are expected at this level.
|
||||
SeqNumL6RangeKey = uint64(2)
|
||||
|
||||
// SeqNumL5RangeDel is the sequence number reserved for foreign range deletes
|
||||
// in L5. These keys could delete L6 points.
|
||||
SeqNumL5RangeDel = uint64(3)
|
||||
|
||||
// SeqNumL5Point is the sequence number reserved for foreign point keys in L5.
|
||||
// Any sst-local range deletions would have already been applied to these keys,
|
||||
// so they can safely get a sequence number higher than SeqNumL5RangeDel.
|
||||
// However they must have a sequence number lower than SeqNumL5RangeKey* for
|
||||
// range key masking to work correctly.
|
||||
SeqNumL5Point = uint64(4)
|
||||
|
||||
// SeqNumL5RangeKeyUnsetDel is the sequence number reserved for foreign
|
||||
// range key unsets/deletes in L5. These operations could apply to L6
|
||||
// RangeKeySets, so this sequence number must be > SeqNumL6RangeKey.
|
||||
SeqNumL5RangeKeyUnsetDel = uint64(5)
|
||||
|
||||
// SeqNumL5RangeKeySet is the sequence number reserved for foreign range key
|
||||
// Sets in L5. These operations could apply to L6 RangeKeySets, so this
|
||||
// sequence number must be > SeqNumL6RangeKey. Any SST-local rangekey
|
||||
// unsets/dels have already been applied to them, so their sequence number must
|
||||
// be > SeqNumL5RangeKeyUnsetDel.
|
||||
SeqNumL5RangeKeySet = uint64(6)
|
||||
|
||||
// Sequence numbers 7-9 are reserved for future use.
|
||||
|
||||
// SeqNumStart is the first sequence number assigned to a key written by
|
||||
// ourselves.
|
||||
SeqNumStart = uint64(10)
|
||||
)
|
||||
Reference in New Issue
Block a user