Upgrade versions of vendored libs
This commit is contained in:
597
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
597
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
@@ -5,24 +5,25 @@
|
||||
package sstable
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/bytealloc"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
"github.com/cockroachdb/pebble/internal/crc"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/private"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
"github.com/cockroachdb/pebble/objstorage"
|
||||
)
|
||||
|
||||
// encodedBHPEstimatedSize estimates the size of the encoded BlockHandleWithProperties.
|
||||
@@ -109,27 +110,16 @@ func (m *WriterMetadata) updateSeqNum(seqNum uint64) {
|
||||
}
|
||||
}
|
||||
|
||||
type flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
type writeCloseSyncer interface {
|
||||
io.WriteCloser
|
||||
Sync() error
|
||||
}
|
||||
|
||||
// Writer is a table writer.
|
||||
type Writer struct {
|
||||
writer io.Writer
|
||||
bufWriter *bufio.Writer
|
||||
syncer writeCloseSyncer
|
||||
meta WriterMetadata
|
||||
err error
|
||||
writable objstorage.Writable
|
||||
meta WriterMetadata
|
||||
err error
|
||||
// cacheID and fileNum are used to remove blocks written to the sstable from
|
||||
// the cache, providing a defense in depth against bugs which cause cache
|
||||
// collisions.
|
||||
cacheID uint64
|
||||
fileNum base.FileNum
|
||||
fileNum base.DiskFileNum
|
||||
// The following fields are copied from Options.
|
||||
blockSize int
|
||||
blockSizeThreshold int
|
||||
@@ -186,9 +176,9 @@ type Writer struct {
|
||||
// indexBlockAlloc is used to bulk-allocate byte slices used to store index
|
||||
// blocks in indexPartitions. These live until the index finishes.
|
||||
indexBlockAlloc []byte
|
||||
// indexSepAlloc is used to bulk-allocate index block seperator slices stored
|
||||
// indexSepAlloc is used to bulk-allocate index block separator slices stored
|
||||
// in indexPartitions. These live until the index finishes.
|
||||
indexSepAlloc []byte
|
||||
indexSepAlloc bytealloc.A
|
||||
|
||||
// To allow potentially overlapping (i.e. un-fragmented) range keys spans to
|
||||
// be added to the Writer, a keyspan.Fragmenter is used to retain the keys
|
||||
@@ -196,7 +186,8 @@ type Writer struct {
|
||||
// keys must be added in order of their start user-key.
|
||||
fragmenter keyspan.Fragmenter
|
||||
rangeKeyEncoder rangekey.Encoder
|
||||
rangeKeyCoalesced keyspan.Span
|
||||
rangeKeysBySuffix keyspan.KeysBySuffix
|
||||
rangeKeySpan keyspan.Span
|
||||
rkBuf []byte
|
||||
// dataBlockBuf consists of the state which is currently owned by and used by
|
||||
// the Writer client goroutine. This state can be handed off to other goroutines.
|
||||
@@ -206,6 +197,24 @@ type Writer struct {
|
||||
blockBuf blockBuf
|
||||
|
||||
coordination coordinationState
|
||||
|
||||
// Information (other than the byte slice) about the last point key, to
|
||||
// avoid extracting it again.
|
||||
lastPointKeyInfo pointKeyInfo
|
||||
|
||||
// For value blocks.
|
||||
shortAttributeExtractor base.ShortAttributeExtractor
|
||||
requiredInPlaceValueBound UserKeyPrefixBound
|
||||
valueBlockWriter *valueBlockWriter
|
||||
}
|
||||
|
||||
type pointKeyInfo struct {
|
||||
trailer uint64
|
||||
// Only computed when w.valueBlockWriter is not nil.
|
||||
userKeyLen int
|
||||
// prefixLen uses w.split, if not nil. Only computed when w.valueBlockWriter
|
||||
// is not nil.
|
||||
prefixLen int
|
||||
}
|
||||
|
||||
type coordinationState struct {
|
||||
@@ -238,35 +247,37 @@ func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
|
||||
|
||||
// sizeEstimate is a general purpose helper for estimating two kinds of sizes:
|
||||
// A. The compressed sstable size, which is useful for deciding when to start
|
||||
// a new sstable during flushes or compactions. In practice, we use this in
|
||||
// estimating the data size (excluding the index).
|
||||
//
|
||||
// a new sstable during flushes or compactions. In practice, we use this in
|
||||
// estimating the data size (excluding the index).
|
||||
//
|
||||
// B. The size of index blocks to decide when to start a new index block.
|
||||
//
|
||||
// There are some terminology peculiarities which are due to the origin of
|
||||
// sizeEstimate for use case A with parallel compression enabled (for which
|
||||
// the code has not been merged). Specifically this relates to the terms
|
||||
// "written" and "compressed".
|
||||
// - The notion of "written" for case A is sufficiently defined by saying that
|
||||
// the data block is compressed. Waiting for the actual data block write to
|
||||
// happen can result in unnecessary estimation, when we already know how big
|
||||
// it will be in compressed form. Additionally, with the forthcoming value
|
||||
// blocks containing older MVCC values, these compressed block will be held
|
||||
// in-memory until late in the sstable writing, and we do want to accurately
|
||||
// account for them without waiting for the actual write.
|
||||
// For case B, "written" means that the index entry has been fully
|
||||
// generated, and has been added to the uncompressed block buffer for that
|
||||
// index block. It does not include actually writing a potentially
|
||||
// compressed index block.
|
||||
// - The notion of "compressed" is to differentiate between a "inflight" size
|
||||
// and the actual size, and is handled via computing a compression ratio
|
||||
// observed so far (defaults to 1).
|
||||
// For case A, this is actual data block compression, so the "inflight" size
|
||||
// is uncompressed blocks (that are no longer being written to) and the
|
||||
// "compressed" size is after they have been compressed.
|
||||
// For case B the inflight size is for a key-value pair in the index for
|
||||
// which the value size (the encoded size of the BlockHandleWithProperties)
|
||||
// is not accurately known, while the compressed size is the size of that
|
||||
// entry when it has been added to the (in-progress) index ssblock.
|
||||
// - The notion of "written" for case A is sufficiently defined by saying that
|
||||
// the data block is compressed. Waiting for the actual data block write to
|
||||
// happen can result in unnecessary estimation, when we already know how big
|
||||
// it will be in compressed form. Additionally, with the forthcoming value
|
||||
// blocks containing older MVCC values, these compressed block will be held
|
||||
// in-memory until late in the sstable writing, and we do want to accurately
|
||||
// account for them without waiting for the actual write.
|
||||
// For case B, "written" means that the index entry has been fully
|
||||
// generated, and has been added to the uncompressed block buffer for that
|
||||
// index block. It does not include actually writing a potentially
|
||||
// compressed index block.
|
||||
// - The notion of "compressed" is to differentiate between a "inflight" size
|
||||
// and the actual size, and is handled via computing a compression ratio
|
||||
// observed so far (defaults to 1).
|
||||
// For case A, this is actual data block compression, so the "inflight" size
|
||||
// is uncompressed blocks (that are no longer being written to) and the
|
||||
// "compressed" size is after they have been compressed.
|
||||
// For case B the inflight size is for a key-value pair in the index for
|
||||
// which the value size (the encoded size of the BlockHandleWithProperties)
|
||||
// is not accurately known, while the compressed size is the size of that
|
||||
// entry when it has been added to the (in-progress) index ssblock.
|
||||
//
|
||||
// Usage: To update state, one can optionally provide an inflight write value
|
||||
// using addInflight (used for case B). When something is "written" the state
|
||||
@@ -727,40 +738,153 @@ func (w *Writer) Add(key InternalKey, value []byte) error {
|
||||
return w.addPoint(key, value)
|
||||
}
|
||||
|
||||
func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
if !w.disableKeyOrderChecks && w.dataBlockBuf.dataBlock.nEntries >= 1 {
|
||||
// curKey is guaranteed to be the last point key which was added to the Writer.
|
||||
// Inlining base.DecodeInternalKey has a 2-3% improve in the BenchmarkWriter
|
||||
// benchmark.
|
||||
encodedKey := w.dataBlockBuf.dataBlock.curKey
|
||||
n := len(encodedKey) - base.InternalTrailerLen
|
||||
var trailer uint64
|
||||
if n >= 0 {
|
||||
trailer = binary.LittleEndian.Uint64(encodedKey[n:])
|
||||
encodedKey = encodedKey[:n:n]
|
||||
} else {
|
||||
trailer = uint64(InternalKeyKindInvalid)
|
||||
encodedKey = nil
|
||||
func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
|
||||
prevTrailer := w.lastPointKeyInfo.trailer
|
||||
w.lastPointKeyInfo.trailer = key.Trailer
|
||||
if w.dataBlockBuf.dataBlock.nEntries == 0 {
|
||||
return nil
|
||||
}
|
||||
if !w.disableKeyOrderChecks {
|
||||
prevPointUserKey := w.dataBlockBuf.dataBlock.curKey
|
||||
n := len(prevPointUserKey) - base.InternalTrailerLen
|
||||
if n < 0 {
|
||||
panic("corrupt key in blockWriter buffer")
|
||||
}
|
||||
largestPointKey := InternalKey{
|
||||
UserKey: encodedKey,
|
||||
Trailer: trailer,
|
||||
}
|
||||
|
||||
if largestPointKey.UserKey != nil {
|
||||
// TODO(peter): Manually inlined version of base.InternalCompare(). This is
|
||||
// 3.5% faster on BenchmarkWriter on go1.13. Remove if go1.14 or future
|
||||
// versions show this to not be a performance win.
|
||||
x := w.compare(largestPointKey.UserKey, key.UserKey)
|
||||
if x > 0 || (x == 0 && largestPointKey.Trailer <= key.Trailer) {
|
||||
w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
largestPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
return w.err
|
||||
}
|
||||
prevPointUserKey = prevPointUserKey[:n:n]
|
||||
cmpUser := w.compare(prevPointUserKey, key.UserKey)
|
||||
if cmpUser > 0 || (cmpUser == 0 && prevTrailer <= key.Trailer) {
|
||||
return errors.Errorf(
|
||||
"pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
InternalKey{UserKey: prevPointUserKey, Trailer: prevTrailer}.Pretty(w.formatKey),
|
||||
key.Pretty(w.formatKey))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := w.maybeFlush(key, value); err != nil {
|
||||
func (w *Writer) makeAddPointDecisionV3(
|
||||
key InternalKey, valueLen int,
|
||||
) (setHasSamePrefix bool, writeToValueBlock bool, err error) {
|
||||
prevPointKeyInfo := w.lastPointKeyInfo
|
||||
w.lastPointKeyInfo.userKeyLen = len(key.UserKey)
|
||||
w.lastPointKeyInfo.prefixLen = w.lastPointKeyInfo.userKeyLen
|
||||
if w.split != nil {
|
||||
w.lastPointKeyInfo.prefixLen = w.split(key.UserKey)
|
||||
}
|
||||
w.lastPointKeyInfo.trailer = key.Trailer
|
||||
if w.dataBlockBuf.dataBlock.nEntries == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
prevPointUserKey := w.dataBlockBuf.dataBlock.curKey
|
||||
n := len(prevPointUserKey) - base.InternalTrailerLen
|
||||
if n < 0 {
|
||||
panic("corrupt key in blockWriter buffer")
|
||||
}
|
||||
prevPointUserKey = prevPointUserKey[:n:n]
|
||||
prevPointKey := InternalKey{UserKey: prevPointUserKey, Trailer: prevPointKeyInfo.trailer}
|
||||
considerWriteToValueBlock := base.TrailerKind(prevPointKeyInfo.trailer) == InternalKeyKindSet &&
|
||||
base.TrailerKind(key.Trailer) == InternalKeyKindSet
|
||||
if considerWriteToValueBlock && !w.requiredInPlaceValueBound.IsEmpty() {
|
||||
keyPrefix := key.UserKey[:w.lastPointKeyInfo.prefixLen]
|
||||
cmpUpper := w.compare(
|
||||
w.requiredInPlaceValueBound.Upper, keyPrefix)
|
||||
if cmpUpper <= 0 {
|
||||
// Common case for CockroachDB. Make it empty since all future keys in
|
||||
// this sstable will also have cmpUpper <= 0.
|
||||
w.requiredInPlaceValueBound = UserKeyPrefixBound{}
|
||||
} else if w.compare(keyPrefix, w.requiredInPlaceValueBound.Lower) >= 0 {
|
||||
considerWriteToValueBlock = false
|
||||
}
|
||||
}
|
||||
// cmpPrefix is initialized iff considerWriteToValueBlock.
|
||||
var cmpPrefix int
|
||||
var cmpUser int
|
||||
if considerWriteToValueBlock {
|
||||
// Compare the prefixes.
|
||||
cmpPrefix = w.compare(prevPointUserKey[:prevPointKeyInfo.prefixLen],
|
||||
key.UserKey[:w.lastPointKeyInfo.prefixLen])
|
||||
cmpUser = cmpPrefix
|
||||
if cmpPrefix == 0 {
|
||||
// Need to compare suffixes to compute cmpUser.
|
||||
cmpUser = w.compare(prevPointUserKey[prevPointKeyInfo.prefixLen:],
|
||||
key.UserKey[w.lastPointKeyInfo.prefixLen:])
|
||||
}
|
||||
} else {
|
||||
cmpUser = w.compare(prevPointUserKey, key.UserKey)
|
||||
}
|
||||
if !w.disableKeyOrderChecks &&
|
||||
(cmpUser > 0 || (cmpUser == 0 && prevPointKeyInfo.trailer <= key.Trailer)) {
|
||||
return false, false, errors.Errorf(
|
||||
"pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
prevPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
}
|
||||
if !considerWriteToValueBlock {
|
||||
return false, false, nil
|
||||
}
|
||||
// NB: it is possible that cmpUser == 0, i.e., these two SETs have identical
|
||||
// user keys (because of an open snapshot). This should be the rare case.
|
||||
setHasSamePrefix = cmpPrefix == 0
|
||||
considerWriteToValueBlock = setHasSamePrefix
|
||||
// Use of 0 here is somewhat arbitrary. Given the minimum 3 byte encoding of
|
||||
// valueHandle, this should be > 3. But tiny values are common in test and
|
||||
// unlikely in production, so we use 0 here for better test coverage.
|
||||
const tinyValueThreshold = 0
|
||||
if considerWriteToValueBlock && valueLen <= tinyValueThreshold {
|
||||
considerWriteToValueBlock = false
|
||||
}
|
||||
return setHasSamePrefix, considerWriteToValueBlock, nil
|
||||
}
|
||||
|
||||
func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
var err error
|
||||
var setHasSameKeyPrefix, writeToValueBlock, addPrefixToValueStoredWithKey bool
|
||||
maxSharedKeyLen := len(key.UserKey)
|
||||
if w.valueBlockWriter != nil {
|
||||
// maxSharedKeyLen is limited to the prefix of the preceding key. If the
|
||||
// preceding key was in a different block, then the blockWriter will
|
||||
// ignore this maxSharedKeyLen.
|
||||
maxSharedKeyLen = w.lastPointKeyInfo.prefixLen
|
||||
setHasSameKeyPrefix, writeToValueBlock, err = w.makeAddPointDecisionV3(key, len(value))
|
||||
addPrefixToValueStoredWithKey = base.TrailerKind(key.Trailer) == InternalKeyKindSet
|
||||
} else {
|
||||
err = w.makeAddPointDecisionV2(key)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var valueStoredWithKey []byte
|
||||
var prefix valuePrefix
|
||||
var valueStoredWithKeyLen int
|
||||
if writeToValueBlock {
|
||||
vh, err := w.valueBlockWriter.addValue(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := encodeValueHandle(w.blockBuf.tmp[:], vh)
|
||||
valueStoredWithKey = w.blockBuf.tmp[:n]
|
||||
valueStoredWithKeyLen = len(valueStoredWithKey) + 1
|
||||
var attribute base.ShortAttribute
|
||||
if w.shortAttributeExtractor != nil {
|
||||
// TODO(sumeer): for compactions, it is possible that the input sstable
|
||||
// already has this value in the value section and so we have already
|
||||
// extracted the ShortAttribute. Avoid extracting it again. This will
|
||||
// require changing the Writer.Add interface.
|
||||
if attribute, err = w.shortAttributeExtractor(
|
||||
key.UserKey, w.lastPointKeyInfo.prefixLen, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prefix = makePrefixForValueHandle(setHasSameKeyPrefix, attribute)
|
||||
} else {
|
||||
valueStoredWithKey = value
|
||||
valueStoredWithKeyLen = len(value)
|
||||
if addPrefixToValueStoredWithKey {
|
||||
valueStoredWithKeyLen++
|
||||
}
|
||||
prefix = makePrefixForInPlaceValue(setHasSameKeyPrefix)
|
||||
}
|
||||
|
||||
if err := w.maybeFlush(key, valueStoredWithKeyLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -771,14 +895,23 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
}
|
||||
}
|
||||
for i := range w.blockPropCollectors {
|
||||
if err := w.blockPropCollectors[i].Add(key, value); err != nil {
|
||||
v := value
|
||||
if addPrefixToValueStoredWithKey {
|
||||
// Values for SET are not required to be in-place, and in the future may
|
||||
// not even be read by the compaction, so pass nil values. Block
|
||||
// property collectors in such Pebble DB's must not look at the value.
|
||||
v = nil
|
||||
}
|
||||
if err := w.blockPropCollectors[i].Add(key, v); err != nil {
|
||||
w.err = err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w.maybeAddToFilter(key.UserKey)
|
||||
w.dataBlockBuf.dataBlock.add(key, value)
|
||||
w.dataBlockBuf.dataBlock.addWithOptionalValuePrefix(
|
||||
key, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix,
|
||||
setHasSameKeyPrefix)
|
||||
|
||||
w.meta.updateSeqNum(key.SeqNum())
|
||||
|
||||
@@ -797,8 +930,9 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
|
||||
w.props.NumEntries++
|
||||
switch key.Kind() {
|
||||
case InternalKeyKindDelete:
|
||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
||||
w.props.NumDeletions++
|
||||
w.props.RawPointTombstoneKeySize += uint64(key.Size())
|
||||
case InternalKeyKindMerge:
|
||||
w.props.NumMergeOperands++
|
||||
}
|
||||
@@ -903,10 +1037,15 @@ func (w *Writer) addTombstone(key InternalKey, value []byte) error {
|
||||
}
|
||||
|
||||
// RangeKeySet sets a range between start (inclusive) and end (exclusive) with
|
||||
// the given suffix to the given value.
|
||||
// the given suffix to the given value. The resulting range key is given the
|
||||
// sequence number zero, with the expectation that the resulting sstable will be
|
||||
// ingested.
|
||||
//
|
||||
// Keys must be added to the table in increasing order of start key. Spans are
|
||||
// not required to be fragmented.
|
||||
// not required to be fragmented. The same suffix may not be set or unset twice
|
||||
// over the same keyspan, because it would result in inconsistent state. Both
|
||||
// the Set and Unset would share the zero sequence number, and a key cannot be
|
||||
// both simultaneously set and unset.
|
||||
func (w *Writer) RangeKeySet(start, end, suffix, value []byte) error {
|
||||
return w.addRangeKeySpan(keyspan.Span{
|
||||
Start: w.tempRangeKeyCopy(start),
|
||||
@@ -922,10 +1061,15 @@ func (w *Writer) RangeKeySet(start, end, suffix, value []byte) error {
|
||||
}
|
||||
|
||||
// RangeKeyUnset un-sets a range between start (inclusive) and end (exclusive)
|
||||
// with the given suffix.
|
||||
// with the given suffix. The resulting range key is given the
|
||||
// sequence number zero, with the expectation that the resulting sstable will be
|
||||
// ingested.
|
||||
//
|
||||
// Keys must be added to the table in increasing order of start key. Spans are
|
||||
// not required to be fragmented.
|
||||
// not required to be fragmented. The same suffix may not be set or unset twice
|
||||
// over the same keyspan, because it would result in inconsistent state. Both
|
||||
// the Set and Unset would share the zero sequence number, and a key cannot be
|
||||
// both simultaneously set and unset.
|
||||
func (w *Writer) RangeKeyUnset(start, end, suffix []byte) error {
|
||||
return w.addRangeKeySpan(keyspan.Span{
|
||||
Start: w.tempRangeKeyCopy(start),
|
||||
@@ -970,6 +1114,11 @@ func (w *Writer) AddRangeKey(key InternalKey, value []byte) error {
|
||||
}
|
||||
|
||||
func (w *Writer) addRangeKeySpan(span keyspan.Span) error {
|
||||
if w.compare(span.Start, span.End) >= 0 {
|
||||
return errors.Errorf(
|
||||
"pebble: start key must be strictly less than end key",
|
||||
)
|
||||
}
|
||||
if w.fragmenter.Start() != nil && w.compare(w.fragmenter.Start(), span.Start) > 0 {
|
||||
return errors.Errorf("pebble: spans must be added in order: %s > %s",
|
||||
w.formatKey(w.fragmenter.Start()), w.formatKey(span.Start))
|
||||
@@ -979,20 +1128,23 @@ func (w *Writer) addRangeKeySpan(span keyspan.Span) error {
|
||||
return w.err
|
||||
}
|
||||
|
||||
func (w *Writer) coalesceSpans(span keyspan.Span) {
|
||||
// This method is the emit function of the Fragmenter, so span.Keys is only
|
||||
// owned by this span and it's safe to mutate.
|
||||
w.rangeKeyCoalesced.Start = span.Start
|
||||
w.rangeKeyCoalesced.End = span.End
|
||||
err := rangekey.Coalesce(w.compare, span.Keys, &w.rangeKeyCoalesced.Keys)
|
||||
if err != nil {
|
||||
w.err = errors.Newf("sstable: could not coalesce span: %s", err)
|
||||
return
|
||||
}
|
||||
func (w *Writer) encodeRangeKeySpan(span keyspan.Span) {
|
||||
// This method is the emit function of the Fragmenter.
|
||||
//
|
||||
// NB: The span should only contain range keys and be internally consistent
|
||||
// (eg, no duplicate suffixes, no additional keys after a RANGEKEYDEL).
|
||||
//
|
||||
// We use w.rangeKeysBySuffix and w.rangeKeySpan to avoid allocations.
|
||||
|
||||
// NB: The span only contains range keys and is internally consistent (eg,
|
||||
// no duplicate suffixes, no additional keys after a RANGEKEYDEL).
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(&w.rangeKeyCoalesced))
|
||||
// Sort the keys by suffix. Iteration doesn't *currently* depend on it, but
|
||||
// we may want to in the future.
|
||||
w.rangeKeysBySuffix.Cmp = w.compare
|
||||
w.rangeKeysBySuffix.Keys = span.Keys
|
||||
sort.Sort(&w.rangeKeysBySuffix)
|
||||
|
||||
w.rangeKeySpan = span
|
||||
w.rangeKeySpan.Keys = w.rangeKeysBySuffix.Keys
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(&w.rangeKeySpan))
|
||||
}
|
||||
|
||||
func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
|
||||
@@ -1201,8 +1353,8 @@ func (w *Writer) flush(key InternalKey) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Writer) maybeFlush(key InternalKey, value []byte) error {
|
||||
if !w.dataBlockBuf.shouldFlush(key, len(value), w.blockSize, w.blockSizeThreshold) {
|
||||
func (w *Writer) maybeFlush(key InternalKey, valueLen int) error {
|
||||
if !w.dataBlockBuf.shouldFlush(key, valueLen, w.blockSize, w.blockSizeThreshold) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1274,11 +1426,11 @@ func (w *Writer) indexEntrySep(prevKey, key InternalKey, dataBlockBuf *dataBlock
|
||||
// they're used when the index block is finished.
|
||||
//
|
||||
// Invariant:
|
||||
// 1. addIndexEntry must not store references to the sep InternalKey, the tmp
|
||||
// byte slice, bhp.Props. That is, these must be either deep copied or
|
||||
// encoded.
|
||||
// 2. addIndexEntry must not hold references to the flushIndexBuf, and the writeTo
|
||||
// indexBlockBufs.
|
||||
// 1. addIndexEntry must not store references to the sep InternalKey, the tmp
|
||||
// byte slice, bhp.Props. That is, these must be either deep copied or
|
||||
// encoded.
|
||||
// 2. addIndexEntry must not hold references to the flushIndexBuf, and the writeTo
|
||||
// indexBlockBufs.
|
||||
func (w *Writer) addIndexEntry(
|
||||
sep InternalKey,
|
||||
bhp BlockHandleWithProperties,
|
||||
@@ -1323,8 +1475,8 @@ func (w *Writer) addPrevDataBlockToIndexBlockProps() {
|
||||
// aren't being written asynchronously.
|
||||
//
|
||||
// Invariant:
|
||||
// 1. addIndexEntrySync must not store references to the prevKey, key InternalKey's,
|
||||
// the tmp byte slice. That is, these must be either deep copied or encoded.
|
||||
// 1. addIndexEntrySync must not store references to the prevKey, key InternalKey's,
|
||||
// the tmp byte slice. That is, these must be either deep copied or encoded.
|
||||
func (w *Writer) addIndexEntrySync(
|
||||
prevKey, key InternalKey, bhp BlockHandleWithProperties, tmp []byte,
|
||||
) error {
|
||||
@@ -1388,25 +1540,22 @@ func shouldFlush(
|
||||
return newSize > targetBlockSize
|
||||
}
|
||||
|
||||
const keyAllocSize = 256 << 10
|
||||
|
||||
func cloneKeyWithBuf(k InternalKey, buf []byte) ([]byte, InternalKey) {
|
||||
func cloneKeyWithBuf(k InternalKey, a bytealloc.A) (bytealloc.A, InternalKey) {
|
||||
if len(k.UserKey) == 0 {
|
||||
return buf, k
|
||||
return a, k
|
||||
}
|
||||
if len(buf) < len(k.UserKey) {
|
||||
buf = make([]byte, len(k.UserKey)+keyAllocSize)
|
||||
}
|
||||
n := copy(buf, k.UserKey)
|
||||
return buf[n:], InternalKey{UserKey: buf[:n:n], Trailer: k.Trailer}
|
||||
a, keyCopy := a.Copy(k.UserKey)
|
||||
return a, InternalKey{UserKey: keyCopy, Trailer: k.Trailer}
|
||||
}
|
||||
|
||||
// Invariants: The byte slice returned by finishIndexBlockProps is heap-allocated
|
||||
// and has its own lifetime, independent of the Writer and the blockPropsEncoder,
|
||||
//
|
||||
// and has its own lifetime, independent of the Writer and the blockPropsEncoder,
|
||||
//
|
||||
// and it is safe to:
|
||||
// 1. Reuse w.blockPropsEncoder without first encoding the byte slice returned.
|
||||
// 2. Store the byte slice in the Writer since it is a copy and not supported by
|
||||
// an underlying buffer.
|
||||
// 1. Reuse w.blockPropsEncoder without first encoding the byte slice returned.
|
||||
// 2. Store the byte slice in the Writer since it is a copy and not supported by
|
||||
// an underlying buffer.
|
||||
func (w *Writer) finishIndexBlockProps() ([]byte, error) {
|
||||
w.blockPropsEncoder.resetProps()
|
||||
for i := range w.blockPropCollectors {
|
||||
@@ -1426,11 +1575,11 @@ func (w *Writer) finishIndexBlockProps() ([]byte, error) {
|
||||
// level index block. This is only used when two level indexes are enabled.
|
||||
//
|
||||
// Invariants:
|
||||
// 1. The props slice passed into finishedIndexBlock must not be a
|
||||
// owned by any other struct, since it will be stored in the Writer.indexPartitions
|
||||
// slice.
|
||||
// 2. None of the buffers owned by indexBuf will be shallow copied and stored elsewhere.
|
||||
// That is, it must be safe to reuse indexBuf after finishIndexBlock has been called.
|
||||
// 1. The props slice passed into finishedIndexBlock must not be a
|
||||
// owned by any other struct, since it will be stored in the Writer.indexPartitions
|
||||
// slice.
|
||||
// 2. None of the buffers owned by indexBuf will be shallow copied and stored elsewhere.
|
||||
// That is, it must be safe to reuse indexBuf after finishIndexBlock has been called.
|
||||
func (w *Writer) finishIndexBlock(indexBuf *indexBlockBuf, props []byte) error {
|
||||
part := indexBlockAndBlockProperties{
|
||||
nEntries: indexBuf.block.nEntries, properties: props,
|
||||
@@ -1512,7 +1661,7 @@ func compressAndChecksum(b []byte, compression Compression, blockBuf *blockBuf)
|
||||
func (w *Writer) writeCompressedBlock(block []byte, blockTrailerBuf []byte) (BlockHandle, error) {
|
||||
bh := BlockHandle{Offset: w.meta.Size, Length: uint64(len(block))}
|
||||
|
||||
if w.cacheID != 0 && w.fileNum != 0 {
|
||||
if w.cacheID != 0 && w.fileNum.FileNum() != 0 {
|
||||
// Remove the block being written from the cache. This provides defense in
|
||||
// depth against bugs which cause cache collisions.
|
||||
//
|
||||
@@ -1522,20 +1671,38 @@ func (w *Writer) writeCompressedBlock(block []byte, blockTrailerBuf []byte) (Blo
|
||||
}
|
||||
|
||||
// Write the bytes to the file.
|
||||
n, err := w.writer.Write(block)
|
||||
if err != nil {
|
||||
if err := w.writable.Write(block); err != nil {
|
||||
return BlockHandle{}, err
|
||||
}
|
||||
w.meta.Size += uint64(n)
|
||||
n, err = w.writer.Write(blockTrailerBuf[:blockTrailerLen])
|
||||
if err != nil {
|
||||
w.meta.Size += uint64(len(block))
|
||||
if err := w.writable.Write(blockTrailerBuf[:blockTrailerLen]); err != nil {
|
||||
return BlockHandle{}, err
|
||||
}
|
||||
w.meta.Size += uint64(n)
|
||||
w.meta.Size += blockTrailerLen
|
||||
|
||||
return bh, nil
|
||||
}
|
||||
|
||||
// Write implements io.Writer. This is analogous to writeCompressedBlock for
|
||||
// blocks that already incorporate the trailer, and don't need the callee to
|
||||
// return a BlockHandle.
|
||||
func (w *Writer) Write(blockWithTrailer []byte) (n int, err error) {
|
||||
offset := w.meta.Size
|
||||
if w.cacheID != 0 && w.fileNum.FileNum() != 0 {
|
||||
// Remove the block being written from the cache. This provides defense in
|
||||
// depth against bugs which cause cache collisions.
|
||||
//
|
||||
// TODO(peter): Alternatively, we could add the uncompressed value to the
|
||||
// cache.
|
||||
w.cache.Delete(w.cacheID, w.fileNum, offset)
|
||||
}
|
||||
w.meta.Size += uint64(len(blockWithTrailer))
|
||||
if err := w.writable.Write(blockWithTrailer); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(blockWithTrailer), nil
|
||||
}
|
||||
|
||||
func (w *Writer) writeBlock(
|
||||
b []byte, compression Compression, blockBuf *blockBuf,
|
||||
) (BlockHandle, error) {
|
||||
@@ -1562,6 +1729,12 @@ func (w *Writer) assertFormatCompatibility() error {
|
||||
)
|
||||
}
|
||||
|
||||
if (w.props.NumValueBlocks > 0 || w.props.NumValuesInValueBlocks > 0 ||
|
||||
w.props.ValueBlocksSize > 0) && w.tableFormat < TableFormatPebblev3 {
|
||||
return errors.Newf(
|
||||
"table format version %s is less than the minimum required version %s for value blocks",
|
||||
w.tableFormat, TableFormatPebblev3)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1569,21 +1742,28 @@ func (w *Writer) assertFormatCompatibility() error {
|
||||
// table was written to.
|
||||
func (w *Writer) Close() (err error) {
|
||||
defer func() {
|
||||
if w.syncer == nil {
|
||||
return
|
||||
if w.valueBlockWriter != nil {
|
||||
releaseValueBlockWriter(w.valueBlockWriter)
|
||||
// Defensive code in case Close gets called again. We don't want to put
|
||||
// the same object to a sync.Pool.
|
||||
w.valueBlockWriter = nil
|
||||
}
|
||||
err1 := w.syncer.Close()
|
||||
if err == nil {
|
||||
err = err1
|
||||
if w.writable != nil {
|
||||
w.writable.Abort()
|
||||
w.writable = nil
|
||||
}
|
||||
// Record any error in the writer (so we can exit early if Close is called
|
||||
// again).
|
||||
if err != nil {
|
||||
w.err = err
|
||||
}
|
||||
w.syncer = nil
|
||||
}()
|
||||
|
||||
// finish must be called before we check for an error, because finish will
|
||||
// block until every single task added to the writeQueue has been processed,
|
||||
// and an error could be encountered while any of those tasks are processed.
|
||||
if err = w.coordination.writeQueue.finish(); err != nil {
|
||||
w.err = err
|
||||
if err := w.coordination.writeQueue.finish(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if w.err != nil {
|
||||
@@ -1609,17 +1789,14 @@ func (w *Writer) Close() (err error) {
|
||||
if w.dataBlockBuf.dataBlock.nEntries > 0 || w.indexBlock.block.nEntries == 0 {
|
||||
bh, err := w.writeBlock(w.dataBlockBuf.dataBlock.finish(), w.compression, &w.dataBlockBuf.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
var bhp BlockHandleWithProperties
|
||||
if bhp, err = w.maybeAddBlockPropertiesToBlockHandle(bh); err != nil {
|
||||
w.err = err
|
||||
bhp, err := w.maybeAddBlockPropertiesToBlockHandle(bh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prevKey := base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey)
|
||||
if err = w.addIndexEntrySync(prevKey, InternalKey{}, bhp, w.dataBlockBuf.tmp[:]); err != nil {
|
||||
w.err = err
|
||||
if err := w.addIndexEntrySync(prevKey, InternalKey{}, bhp, w.dataBlockBuf.tmp[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1631,13 +1808,11 @@ func (w *Writer) Close() (err error) {
|
||||
if w.filter != nil {
|
||||
b, err := w.filter.finish()
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
bh, err := w.writeBlock(b, NoCompression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
n := encodeBlockHandle(w.blockBuf.tmp[:], bh)
|
||||
metaindex.add(InternalKey{UserKey: []byte(w.filter.metaName())}, w.blockBuf.tmp[:n])
|
||||
@@ -1651,8 +1826,7 @@ func (w *Writer) Close() (err error) {
|
||||
// Write the two level index block.
|
||||
indexBH, err = w.writeTwoLevelIndex()
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
w.props.IndexType = binarySearchIndex
|
||||
@@ -1665,8 +1839,7 @@ func (w *Writer) Close() (err error) {
|
||||
// Write the single level index block.
|
||||
indexBH, err = w.writeBlock(w.indexBlock.finish(), w.compression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1691,8 +1864,7 @@ func (w *Writer) Close() (err error) {
|
||||
}
|
||||
rangeDelBH, err = w.writeBlock(w.rangeDelBlock.finish(), NoCompression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1706,8 +1878,7 @@ func (w *Writer) Close() (err error) {
|
||||
kind := key.Kind()
|
||||
endKey, _, ok := rangekey.DecodeEndKey(kind, w.rangeKeyBlock.curValue)
|
||||
if !ok {
|
||||
w.err = errors.Newf("invalid end key: %s", w.rangeKeyBlock.curValue)
|
||||
return w.err
|
||||
return errors.Newf("invalid end key: %s", w.rangeKeyBlock.curValue)
|
||||
}
|
||||
k := base.MakeExclusiveSentinelKey(kind, endKey).Clone()
|
||||
w.meta.SetLargestRangeKey(k)
|
||||
@@ -1716,8 +1887,21 @@ func (w *Writer) Close() (err error) {
|
||||
// enable compression on this block.
|
||||
rangeKeyBH, err = w.writeBlock(w.rangeKeyBlock.finish(), NoCompression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if w.valueBlockWriter != nil {
|
||||
vbiHandle, vbStats, err := w.valueBlockWriter.finish(w, w.meta.Size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.props.NumValueBlocks = vbStats.numValueBlocks
|
||||
w.props.NumValuesInValueBlocks = vbStats.numValuesInValueBlocks
|
||||
w.props.ValueBlocksSize = vbStats.valueBlocksAndIndexSize
|
||||
if vbStats.numValueBlocks > 0 {
|
||||
n := encodeValueBlocksIndexHandle(w.blockBuf.tmp[:], vbiHandle)
|
||||
metaindex.add(InternalKey{UserKey: []byte(metaValueIndexName)}, w.blockBuf.tmp[:n])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1734,7 +1918,6 @@ func (w *Writer) Close() (err error) {
|
||||
userProps := make(map[string]string)
|
||||
for i := range w.propCollectors {
|
||||
if err := w.propCollectors[i].Finish(userProps); err != nil {
|
||||
w.err = err
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1742,10 +1925,8 @@ func (w *Writer) Close() (err error) {
|
||||
scratch := w.blockPropsEncoder.getScratchForProp()
|
||||
// Place the shortID in the first byte.
|
||||
scratch = append(scratch, byte(i))
|
||||
buf, err :=
|
||||
w.blockPropCollectors[i].FinishTable(scratch)
|
||||
buf, err := w.blockPropCollectors[i].FinishTable(scratch)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return err
|
||||
}
|
||||
var prop string
|
||||
@@ -1768,11 +1949,10 @@ func (w *Writer) Close() (err error) {
|
||||
// reduces table size without a significant impact on performance.
|
||||
raw.restartInterval = propertiesBlockRestartInterval
|
||||
w.props.CompressionOptions = rocksDBCompressionOptions
|
||||
w.props.save(&raw)
|
||||
w.props.save(w.tableFormat, &raw)
|
||||
bh, err := w.writeBlock(raw.finish(), NoCompression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
n := encodeBlockHandle(w.blockBuf.tmp[:], bh)
|
||||
metaindex.add(InternalKey{UserKey: []byte(metaPropertiesName)}, w.blockBuf.tmp[:n])
|
||||
@@ -1798,8 +1978,7 @@ func (w *Writer) Close() (err error) {
|
||||
// expect the meta-index block to not be compressed.
|
||||
metaindexBH, err := w.writeBlock(metaindex.blockWriter.finish(), NoCompression, &w.blockBuf)
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the table footer.
|
||||
@@ -1809,34 +1988,25 @@ func (w *Writer) Close() (err error) {
|
||||
metaindexBH: metaindexBH,
|
||||
indexBH: indexBH,
|
||||
}
|
||||
var n int
|
||||
if n, err = w.writer.Write(footer.encode(w.blockBuf.tmp[:])); err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
encoded := footer.encode(w.blockBuf.tmp[:])
|
||||
if err := w.writable.Write(footer.encode(w.blockBuf.tmp[:])); err != nil {
|
||||
return err
|
||||
}
|
||||
w.meta.Size += uint64(n)
|
||||
w.meta.Size += uint64(len(encoded))
|
||||
w.meta.Properties = w.props
|
||||
|
||||
// Flush the buffer.
|
||||
if w.bufWriter != nil {
|
||||
if err := w.bufWriter.Flush(); err != nil {
|
||||
w.err = err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the features present in the table are compatible with the format
|
||||
// configured for the table.
|
||||
if err = w.assertFormatCompatibility(); err != nil {
|
||||
w.err = err
|
||||
return w.err
|
||||
}
|
||||
|
||||
if err := w.syncer.Sync(); err != nil {
|
||||
w.err = err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writable.Finish(); err != nil {
|
||||
w.writable = nil
|
||||
return err
|
||||
}
|
||||
w.writable = nil
|
||||
|
||||
w.dataBlockBuf.clear()
|
||||
dataBlockBufPool.Put(w.dataBlockBuf)
|
||||
w.dataBlockBuf = nil
|
||||
@@ -1845,9 +2015,6 @@ func (w *Writer) Close() (err error) {
|
||||
w.indexBlock = nil
|
||||
|
||||
// Make any future calls to Set or Close return an error.
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
w.err = errWriterClosed
|
||||
return nil
|
||||
}
|
||||
@@ -1863,7 +2030,7 @@ func (w *Writer) EstimatedSize() uint64 {
|
||||
// Metadata returns the metadata for the finished sstable. Only valid to call
|
||||
// after the sstable has been finished.
|
||||
func (w *Writer) Metadata() (*WriterMetadata, error) {
|
||||
if w.syncer != nil {
|
||||
if w.writable != nil {
|
||||
return nil, errors.New("pebble: writer is not closed")
|
||||
}
|
||||
return &w.meta, nil
|
||||
@@ -1905,23 +2072,12 @@ func (o *PreviousPointKeyOpt) writerApply(w *Writer) {
|
||||
o.w = w
|
||||
}
|
||||
|
||||
// internalTableOpt is a WriterOption that sets properties for sstables being
|
||||
// created by the db itself (i.e. through flushes and compactions), as opposed
|
||||
// to those meant for ingestion.
|
||||
type internalTableOpt struct{}
|
||||
|
||||
func (i internalTableOpt) writerApply(w *Writer) {
|
||||
// Set the external sst version to 0. This is what RocksDB expects for
|
||||
// db-internal sstables; otherwise, it could apply a global sequence number.
|
||||
w.props.ExternalFormatVersion = 0
|
||||
}
|
||||
|
||||
// NewWriter returns a new table writer for the file. Closing the writer will
|
||||
// close the file.
|
||||
func NewWriter(f writeCloseSyncer, o WriterOptions, extraOpts ...WriterOption) *Writer {
|
||||
func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...WriterOption) *Writer {
|
||||
o = o.ensureDefaults()
|
||||
w := &Writer{
|
||||
syncer: f,
|
||||
writable: writable,
|
||||
meta: WriterMetadata{
|
||||
SmallestSeqNum: math.MaxUint64,
|
||||
},
|
||||
@@ -1954,6 +2110,14 @@ func NewWriter(f writeCloseSyncer, o WriterOptions, extraOpts ...WriterOption) *
|
||||
Format: o.Comparer.FormatKey,
|
||||
},
|
||||
}
|
||||
if w.tableFormat >= TableFormatPebblev3 {
|
||||
w.shortAttributeExtractor = o.ShortAttributeExtractor
|
||||
w.requiredInPlaceValueBound = o.RequiredInPlaceValueBound
|
||||
w.valueBlockWriter = newValueBlockWriter(
|
||||
w.blockSize, w.blockSizeThreshold, w.compression, w.checksumType, func(compressedSize int) {
|
||||
w.coordination.sizeEstimate.dataBlockCompressed(compressedSize, 0)
|
||||
})
|
||||
}
|
||||
|
||||
w.dataBlockBuf = newDataBlockBuf(w.restartInterval, w.checksumType)
|
||||
|
||||
@@ -1963,13 +2127,13 @@ func NewWriter(f writeCloseSyncer, o WriterOptions, extraOpts ...WriterOption) *
|
||||
|
||||
w.coordination.init(o.Parallelism, w)
|
||||
|
||||
if f == nil {
|
||||
w.err = errors.New("pebble: nil file")
|
||||
if writable == nil {
|
||||
w.err = errors.New("pebble: nil writable")
|
||||
return w
|
||||
}
|
||||
|
||||
// Note that WriterOptions are applied in two places; the ones with a
|
||||
// preApply() method are applied here, and the rest are applied after
|
||||
// preApply() method are applied here. The rest are applied down below after
|
||||
// default properties are set.
|
||||
type preApply interface{ preApply() }
|
||||
for _, opt := range extraOpts {
|
||||
@@ -2038,29 +2202,30 @@ func NewWriter(f writeCloseSyncer, o WriterOptions, extraOpts ...WriterOption) *
|
||||
|
||||
// Apply the remaining WriterOptions that do not have a preApply() method.
|
||||
for _, opt := range extraOpts {
|
||||
if _, ok := opt.(preApply); !ok {
|
||||
opt.writerApply(w)
|
||||
if _, ok := opt.(preApply); ok {
|
||||
continue
|
||||
}
|
||||
opt.writerApply(w)
|
||||
}
|
||||
|
||||
// Initialize the range key fragmenter and encoder.
|
||||
w.fragmenter.Emit = w.coalesceSpans
|
||||
w.fragmenter.Emit = w.encodeRangeKeySpan
|
||||
w.rangeKeyEncoder.Emit = w.addRangeKey
|
||||
|
||||
// If f does not have a Flush method, do our own buffering.
|
||||
if _, ok := f.(flusher); ok {
|
||||
w.writer = f
|
||||
} else {
|
||||
w.bufWriter = bufio.NewWriter(f)
|
||||
w.writer = w.bufWriter
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// internalGetProperties is a private, internal-use-only function that takes a
|
||||
// Writer and returns a pointer to its Properties, allowing direct mutation.
|
||||
// It's used by internal Pebble flushes and compactions to set internal
|
||||
// properties. It gets installed in private.
|
||||
func internalGetProperties(w *Writer) *Properties {
|
||||
return &w.props
|
||||
}
|
||||
|
||||
func init() {
|
||||
private.SSTableWriterDisableKeyOrderChecks = func(i interface{}) {
|
||||
w := i.(*Writer)
|
||||
w.disableKeyOrderChecks = true
|
||||
}
|
||||
private.SSTableInternalTableOpt = internalTableOpt{}
|
||||
private.SSTableInternalProperties = internalGetProperties
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user