github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/sstable/value_block.go (about)

     1  // Copyright 2022 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package sstable
     6  
     7  import (
     8  	"context"
     9  	"encoding/binary"
    10  	"io"
    11  	"sync"
    12  	"unsafe"
    13  
    14  	"github.com/cockroachdb/errors"
    15  	"github.com/cockroachdb/pebble/internal/base"
    16  	"github.com/cockroachdb/pebble/internal/invariants"
    17  	"github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
    18  	"golang.org/x/exp/rand"
    19  )
    20  
    21  // Value blocks are supported in TableFormatPebblev3.
    22  //
    23  // 1. Motivation and overview
    24  //
    25  // Value blocks are a mechanism designed for sstables storing MVCC data, where
    26  // there can be many versions of a key that need to be kept, but only the
    27  // latest value is typically read (see the documentation for Comparer.Split
    28  // regarding MVCC keys). The goal is faster reads. Unlike Pebble versions,
    29  // which can be eagerly thrown away (except when there are snapshots), MVCC
    30  // versions are long-lived (e.g. default CockroachDB garbage collection
    31  // threshold for older versions is 24 hours) and can significantly slow down
    32  // reads. We have seen CockroachDB production workloads with very slow reads
    33  // due to:
    34  // - 100s of versions for each key in a table.
    35  //
    36  // - Tables with mostly MVCC garbage consisting of 2 versions per key -- a
    37  //   real key-value pair, followed by a key-value pair whose value (usually
    38  //   with zero byte length) indicates it is an MVCC tombstone.
    39  //
    40  // The value blocks mechanism attempts to improve read throughput in these
    41  // cases when the key size is smaller than the value sizes of older versions.
    42  // This is done by moving the value of an older version to a value block in a
    43  // different part of the sstable. This improves spatial locality of the data
    44  // being read by the workload, which increases caching effectiveness.
    45  //
    46  // Additionally, even when the key size is not smaller than the value of older
    47  // versions (e.g. secondary indexes in CockroachDB), TableFormatPebblev3
    48  // stores the result of key comparisons done at write time inside the sstable,
    49  // which makes stepping from one key prefix to the next prefix (i.e., skipping
    50  // over older versions of a MVCC key) more efficient by avoiding key
    51  // comparisons and key decoding. See the results in
    52  // https://github.com/cockroachdb/pebble/pull/2149 and more details in the
    53  // comment inside BenchmarkIteratorScanNextPrefix. These improvements are also
    54  // visible in end-to-end CockroachDB tests, as outlined in
    55  // https://github.com/cockroachdb/cockroach/pull/96652.
    56  //
    57  // In TableFormatPebblev3, each SET has a one byte value prefix that tells us
    58  // whether the value is in-place or in a value block. This 1 byte prefix
    59  // encodes additional information:
    60  //
    61  // - ShortAttribute: This is an attribute of the value. Currently, CockroachDB
    62  //   uses it to represent whether the value is a tombstone or not. This avoids
    63  //   the need to fetch a value from the value block if the caller only wants
    64  //   to figure out whether it is an MVCC tombstone. The length of the value is
    65  //   another attribute that the caller can be interested in, and it is also
    66  //   accessible without reading the value in the value block (see the value
    67  //   handle in the details section).
    68  //
    69  // - SET-same-prefix: this enables the aforementioned optimization when
    70  //   stepping from one key prefix to the next key prefix.
    71  //
    72  // We further optimize this iteration over prefixes by using the restart
    73  // points in a block to encode whether the SET at a restart point has the same
    74  // prefix since the last restart point. This allows us to skip over restart
    75  // points within the same block. See the comment in blockWriter, and how both
    76  // SET-same-prefix and the restart point information is used in
    77  // blockIter.nextPrefixV3.
    78  //
    79  // This flexibility of values that are in-place or in value blocks requires
    80  // flexibility in the iterator interface. The InternalIterator interface
    81  // returns a LazyValue instead of a byte slice. Additionally, pebble.Iterator
    82  // allows the caller to ask for a LazyValue. See lazy_value.go for details,
    83  // including the memory lifetime management.
    84  //
    85  // For historical discussions about this feature, see the issue
    86  // https://github.com/cockroachdb/pebble/issues/1170 and the prototype in
    87  // https://github.com/cockroachdb/pebble/pull/1443.
    88  //
    89  // The code in this file mainly covers value block and related encodings. We
    90  // discuss these in the next section.
    91  //
    92  // 2. Details
    93  //
    94  // Note that the notion of the latest value is local to the sstable. It is
    95  // possible that that latest value has been deleted by a sstable in a higher
    96  // level, and what is the latest value from the perspective of the whole LSM
    97  // is an older MVCC version. This only affects performance and not
    98  // correctness. This local knowledge is also why we continue to store these
    99  // older versions in the same sstable -- we need to be able to conveniently
   100  // read them. The code in this file is agnostic to the policy regarding what
   101  // should be stored in value blocks -- it allows even the latest MVCC version
   102  // to be stored in a value block. The policy decision in made in the
   103  // sstable.Writer. See Writer.makeAddPointDecisionV3.
   104  //
   105  // Data blocks contain two kinds of SET keys: those with in-place values and
   106  // those with a value handle. To distinguish these two cases we use a single
   107  // byte prefix (valuePrefix). This single byte prefix is split into multiple
   108  // parts, where nb represents information that is encoded in n bits.
   109  //
   110  // +---------------+--------------------+-----------+--------------------+
   111  // | value-kind 2b | SET-same-prefix 1b | unused 2b | short-attribute 3b |
   112  // +---------------+--------------------+-----------+--------------------+
   113  //
   114  // The 2 bit value-kind specifies whether this is an in-place value or a value
   115  // handle pointing to a value block. We use 2 bits here for future
   116  // representation of values that are in separate files. The 1 bit
   117  // SET-same-prefix is true if this key is a SET and is immediately preceded by
   118  // a SET that shares the same prefix. The 3 bit short-attribute is described
   119  // in base.ShortAttribute -- it stores user-defined attributes about the
   120  // value. It is unused for in-place values.
   121  //
   122  // Value Handle and Value Blocks:
   123  // valueHandles refer to values in value blocks. Value blocks are simpler than
   124  // normal data blocks (that contain key-value pairs, and allow for binary
   125  // search), which makes them cheap for value retrieval purposes. A valueHandle
   126  // is a tuple (valueLen, blockNum, offsetInBlock), where blockNum is the 0
   127  // indexed value block number and offsetInBlock is the byte offset in that
   128  // block containing the value. The valueHandle.valueLen is included since
   129  // there are multiple use cases in CockroachDB that need the value length but
   130  // not the value, for which we can avoid reading the value in the value block
   131  // (see
   132  // https://github.com/cockroachdb/pebble/issues/1170#issuecomment-958203245).
   133  //
   134  // A value block has a checksum like other blocks, and is optionally
   135  // compressed. An uncompressed value block is a sequence of values with no
   136  // separator or length (we rely on the valueHandle to demarcate). The
   137  // valueHandle.offsetInBlock points to the value, of length
   138  // valueHandle.valueLen. While writing a sstable, all the (possibly
   139  // compressed) value blocks need to be held in-memory until they can be
   140  // written. Value blocks are placed after the "meta rangedel" and "meta range
   141  // key" blocks since value blocks are considered less likely to be read.
   142  //
   143  // Meta Value Index Block:
   144  // Since the (key, valueHandle) pair are written before there is any knowledge
   145  // of the byte offset of the value block in the file, or its compressed
   146  // length, we need another lookup to map the valueHandle.blockNum to the
   147  // information needed to read it from the file. This information is provided
   148  // by the "value index block". The "value index block" is referred to by the
   149  // metaindex block. The design intentionally avoids making the "value index
   150  // block" a general purpose key-value block, since each caller wants to lookup
   151  // the information for a particular blockNum (there is no need for SeekGE
   152  // etc.). Instead, this index block stores a sequence of (blockNum,
   153  // blockOffset, blockLength) tuples, where the blockNums are consecutive
   154  // integers, and the tuples are encoded with a fixed width encoding. This
   155  // allows a reader to find the tuple for block K by looking at the offset
   156  // K*fixed-width. The fixed width for each field is decided by looking at the
   157  // maximum value of each of these fields. As a concrete example of a large
   158  // sstable with many value blocks, we constructed a 100MB sstable with many
   159  // versions and had 2475 value blocks (~32KB each). This sstable had this
   160  // tuple encoded using 2+4+2=8 bytes, which means the uncompressed value index
   161  // block was 2475*8=~19KB, which is modest. Therefore, we don't support more
   162  // than one value index block. Consider the example of 2 byte blockNum, 4 byte
   163  // blockOffset and 2 byte blockLen. The value index block will look like:
   164  //
   165  //   +---------------+------------------+---------------+
   166  //   | blockNum (2B) | blockOffset (4B) | blockLen (2B) |
   167  //   +---------------+------------------+---------------+
   168  //   |       0       |    7,123,456     |  30,000       |
   169  //   +---------------+------------------+---------------+
   170  //   |       1       |    7,153,456     |  20,000       |
   171  //   +---------------+------------------+---------------+
   172  //   |       2       |    7,173,456     |  25,567       |
   173  //   +---------------+------------------+---------------+
   174  //   |     ....      |      ...         |    ...        |
   175  //
   176  //
   177  // The metaindex block contains the valueBlocksIndexHandle which in addition
   178  // to the BlockHandle also specifies the widths of these tuple fields. In the
   179  // above example, the
   180  // valueBlockIndexHandle.{blockNumByteLength,blockOffsetByteLength,blockLengthByteLength}
   181  // will be (2,4,2).
   182  
   183  // valueHandle is stored with a key when the value is in a value block. This
   184  // handle is the pointer to that value.
   185  type valueHandle struct {
   186  	valueLen      uint32
   187  	blockNum      uint32
   188  	offsetInBlock uint32
   189  }
   190  
   191  // valuePrefix is the single byte prefix for either the in-place value or the
   192  // encoded valueHandle. It encoded multiple kinds of information.
   193  type valuePrefix byte
   194  
   195  const (
   196  	// 2 most-significant bits of valuePrefix encodes the value-kind.
   197  	valueKindMask           valuePrefix = '\xC0'
   198  	valueKindIsValueHandle  valuePrefix = '\x80'
   199  	valueKindIsInPlaceValue valuePrefix = '\x00'
   200  
   201  	// 1 bit indicates SET has same key prefix as immediately preceding key that
   202  	// is also a SET. If the immediately preceding key in the same block is a
   203  	// SET, AND this bit is 0, the prefix must have changed.
   204  	//
   205  	// Note that the current policy of only storing older MVCC versions in value
   206  	// blocks means that valueKindIsValueHandle => SET has same prefix. But no
   207  	// code should rely on this behavior. Also, SET has same prefix does *not*
   208  	// imply valueKindIsValueHandle.
   209  	setHasSameKeyPrefixMask valuePrefix = '\x20'
   210  
   211  	// 3 least-significant bits for the user-defined base.ShortAttribute.
   212  	// Undefined for valueKindIsInPlaceValue.
   213  	userDefinedShortAttributeMask valuePrefix = '\x07'
   214  )
   215  
   216  // valueHandle fields are varint encoded, so maximum 5 bytes each, plus 1 byte
   217  // for the valuePrefix. This could alternatively be group varint encoded, but
   218  // experiments were inconclusive
   219  // (https://github.com/cockroachdb/pebble/pull/1443#issuecomment-1270298802).
   220  const valueHandleMaxLen = 5*3 + 1
   221  
   222  // Assert blockHandleLikelyMaxLen >= valueHandleMaxLen.
   223  const _ = uint(blockHandleLikelyMaxLen - valueHandleMaxLen)
   224  
   225  func encodeValueHandle(dst []byte, v valueHandle) int {
   226  	n := 0
   227  	n += binary.PutUvarint(dst[n:], uint64(v.valueLen))
   228  	n += binary.PutUvarint(dst[n:], uint64(v.blockNum))
   229  	n += binary.PutUvarint(dst[n:], uint64(v.offsetInBlock))
   230  	return n
   231  }
   232  
   233  func makePrefixForValueHandle(setHasSameKeyPrefix bool, attribute base.ShortAttribute) valuePrefix {
   234  	prefix := valueKindIsValueHandle | valuePrefix(attribute)
   235  	if setHasSameKeyPrefix {
   236  		prefix = prefix | setHasSameKeyPrefixMask
   237  	}
   238  	return prefix
   239  }
   240  
   241  func makePrefixForInPlaceValue(setHasSameKeyPrefix bool) valuePrefix {
   242  	prefix := valueKindIsInPlaceValue
   243  	if setHasSameKeyPrefix {
   244  		prefix = prefix | setHasSameKeyPrefixMask
   245  	}
   246  	return prefix
   247  }
   248  
   249  func isValueHandle(b valuePrefix) bool {
   250  	return b&valueKindMask == valueKindIsValueHandle
   251  }
   252  
   253  // REQUIRES: isValueHandle(b)
   254  func getShortAttribute(b valuePrefix) base.ShortAttribute {
   255  	return base.ShortAttribute(b & userDefinedShortAttributeMask)
   256  }
   257  
   258  func setHasSamePrefix(b valuePrefix) bool {
   259  	return b&setHasSameKeyPrefixMask == setHasSameKeyPrefixMask
   260  }
   261  
   262  func decodeLenFromValueHandle(src []byte) (uint32, []byte) {
   263  	ptr := unsafe.Pointer(&src[0])
   264  	var v uint32
   265  	if a := *((*uint8)(ptr)); a < 128 {
   266  		v = uint32(a)
   267  		src = src[1:]
   268  	} else if a, b := a&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 1))); b < 128 {
   269  		v = uint32(b)<<7 | uint32(a)
   270  		src = src[2:]
   271  	} else if b, c := b&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 2))); c < 128 {
   272  		v = uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   273  		src = src[3:]
   274  	} else if c, d := c&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 3))); d < 128 {
   275  		v = uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   276  		src = src[4:]
   277  	} else {
   278  		d, e := d&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 4)))
   279  		v = uint32(e)<<28 | uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   280  		src = src[5:]
   281  	}
   282  	return v, src
   283  }
   284  
   285  func decodeRemainingValueHandle(src []byte) valueHandle {
   286  	var vh valueHandle
   287  	ptr := unsafe.Pointer(&src[0])
   288  	// Manually inlined uvarint decoding. Saves ~25% in benchmarks. Unrolling
   289  	// a loop for i:=0; i<2; i++, saves ~6%.
   290  	var v uint32
   291  	if a := *((*uint8)(ptr)); a < 128 {
   292  		v = uint32(a)
   293  		ptr = unsafe.Pointer(uintptr(ptr) + 1)
   294  	} else if a, b := a&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 1))); b < 128 {
   295  		v = uint32(b)<<7 | uint32(a)
   296  		ptr = unsafe.Pointer(uintptr(ptr) + 2)
   297  	} else if b, c := b&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 2))); c < 128 {
   298  		v = uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   299  		ptr = unsafe.Pointer(uintptr(ptr) + 3)
   300  	} else if c, d := c&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 3))); d < 128 {
   301  		v = uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   302  		ptr = unsafe.Pointer(uintptr(ptr) + 4)
   303  	} else {
   304  		d, e := d&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 4)))
   305  		v = uint32(e)<<28 | uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   306  		ptr = unsafe.Pointer(uintptr(ptr) + 5)
   307  	}
   308  	vh.blockNum = v
   309  
   310  	if a := *((*uint8)(ptr)); a < 128 {
   311  		v = uint32(a)
   312  	} else if a, b := a&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 1))); b < 128 {
   313  		v = uint32(b)<<7 | uint32(a)
   314  	} else if b, c := b&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 2))); c < 128 {
   315  		v = uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   316  	} else if c, d := c&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 3))); d < 128 {
   317  		v = uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   318  	} else {
   319  		d, e := d&0x7f, *((*uint8)(unsafe.Pointer(uintptr(ptr) + 4)))
   320  		v = uint32(e)<<28 | uint32(d)<<21 | uint32(c)<<14 | uint32(b)<<7 | uint32(a)
   321  	}
   322  	vh.offsetInBlock = v
   323  
   324  	return vh
   325  }
   326  
   327  func decodeValueHandle(src []byte) valueHandle {
   328  	valLen, src := decodeLenFromValueHandle(src)
   329  	vh := decodeRemainingValueHandle(src)
   330  	vh.valueLen = valLen
   331  	return vh
   332  }
   333  
   334  // valueBlocksIndexHandle is placed in the metaindex if there are any value
   335  // blocks. If there are no value blocks, there is no value blocks index, and
   336  // no entry in the metaindex. Note that the lack of entry in the metaindex
   337  // should not be used to ascertain whether the values are prefixed, since the
   338  // former is an emergent property of the data that was written and not known
   339  // until all the key-value pairs in the sstable are written.
   340  type valueBlocksIndexHandle struct {
   341  	h                     BlockHandle
   342  	blockNumByteLength    uint8
   343  	blockOffsetByteLength uint8
   344  	blockLengthByteLength uint8
   345  }
   346  
   347  const valueBlocksIndexHandleMaxLen = blockHandleMaxLenWithoutProperties + 3
   348  
   349  // Assert blockHandleLikelyMaxLen >= valueBlocksIndexHandleMaxLen.
   350  const _ = uint(blockHandleLikelyMaxLen - valueBlocksIndexHandleMaxLen)
   351  
   352  func encodeValueBlocksIndexHandle(dst []byte, v valueBlocksIndexHandle) int {
   353  	n := encodeBlockHandle(dst, v.h)
   354  	dst[n] = v.blockNumByteLength
   355  	n++
   356  	dst[n] = v.blockOffsetByteLength
   357  	n++
   358  	dst[n] = v.blockLengthByteLength
   359  	n++
   360  	return n
   361  }
   362  
   363  func decodeValueBlocksIndexHandle(src []byte) (valueBlocksIndexHandle, int, error) {
   364  	var vbih valueBlocksIndexHandle
   365  	var n int
   366  	vbih.h, n = decodeBlockHandle(src)
   367  	if n <= 0 {
   368  		return vbih, 0, errors.Errorf("bad BlockHandle %x", src)
   369  	}
   370  	if len(src) != n+3 {
   371  		return vbih, 0, errors.Errorf("bad BlockHandle %x", src)
   372  	}
   373  	vbih.blockNumByteLength = src[n]
   374  	vbih.blockOffsetByteLength = src[n+1]
   375  	vbih.blockLengthByteLength = src[n+2]
   376  	return vbih, n + 3, nil
   377  }
   378  
   379  type valueBlocksAndIndexStats struct {
   380  	numValueBlocks         uint64
   381  	numValuesInValueBlocks uint64
   382  	// Includes both value blocks and value index block.
   383  	valueBlocksAndIndexSize uint64
   384  }
   385  
   386  // valueBlockWriter writes a sequence of value blocks, and the value blocks
   387  // index, for a sstable.
   388  type valueBlockWriter struct {
   389  	// The configured uncompressed block size and size threshold
   390  	blockSize, blockSizeThreshold int
   391  	// Configured compression.
   392  	compression Compression
   393  	// checksummer with configured checksum type.
   394  	checksummer checksummer
   395  	// Block finished callback.
   396  	blockFinishedFunc func(compressedSize int)
   397  
   398  	// buf is the current block being written to (uncompressed).
   399  	buf *blockBuffer
   400  	// compressedBuf is used for compressing the block.
   401  	compressedBuf *blockBuffer
   402  	// Sequence of blocks that are finished.
   403  	blocks []blockAndHandle
   404  	// Cumulative value block bytes written so far.
   405  	totalBlockBytes uint64
   406  	numValues       uint64
   407  }
   408  
   409  type blockAndHandle struct {
   410  	block      *blockBuffer
   411  	handle     BlockHandle
   412  	compressed bool
   413  }
   414  
   415  type blockBuffer struct {
   416  	b []byte
   417  }
   418  
   419  // Pool of block buffers that should be roughly the blockSize.
   420  var uncompressedValueBlockBufPool = sync.Pool{
   421  	New: func() interface{} {
   422  		return &blockBuffer{}
   423  	},
   424  }
   425  
   426  // Pool of block buffers for compressed value blocks. These may widely vary in
   427  // size based on compression ratios.
   428  var compressedValueBlockBufPool = sync.Pool{
   429  	New: func() interface{} {
   430  		return &blockBuffer{}
   431  	},
   432  }
   433  
   434  func releaseToValueBlockBufPool(pool *sync.Pool, b *blockBuffer) {
   435  	// Don't pool buffers larger than 128KB, in case we had some rare large
   436  	// values.
   437  	if len(b.b) > 128*1024 {
   438  		return
   439  	}
   440  	if invariants.Enabled {
   441  		// Set the bytes to a random value. Cap the number of bytes being
   442  		// randomized to prevent test timeouts.
   443  		length := cap(b.b)
   444  		if length > 1000 {
   445  			length = 1000
   446  		}
   447  		b.b = b.b[:length:length]
   448  		rand.Read(b.b)
   449  	}
   450  	pool.Put(b)
   451  }
   452  
   453  var valueBlockWriterPool = sync.Pool{
   454  	New: func() interface{} {
   455  		return &valueBlockWriter{}
   456  	},
   457  }
   458  
   459  func newValueBlockWriter(
   460  	blockSize int,
   461  	blockSizeThreshold int,
   462  	compression Compression,
   463  	checksumType ChecksumType,
   464  	// compressedSize should exclude the block trailer.
   465  	blockFinishedFunc func(compressedSize int),
   466  ) *valueBlockWriter {
   467  	w := valueBlockWriterPool.Get().(*valueBlockWriter)
   468  	*w = valueBlockWriter{
   469  		blockSize:          blockSize,
   470  		blockSizeThreshold: blockSizeThreshold,
   471  		compression:        compression,
   472  		checksummer: checksummer{
   473  			checksumType: checksumType,
   474  		},
   475  		blockFinishedFunc: blockFinishedFunc,
   476  		buf:               uncompressedValueBlockBufPool.Get().(*blockBuffer),
   477  		compressedBuf:     compressedValueBlockBufPool.Get().(*blockBuffer),
   478  		blocks:            w.blocks[:0],
   479  	}
   480  	w.buf.b = w.buf.b[:0]
   481  	w.compressedBuf.b = w.compressedBuf.b[:0]
   482  	return w
   483  }
   484  
   485  func releaseValueBlockWriter(w *valueBlockWriter) {
   486  	for i := range w.blocks {
   487  		if w.blocks[i].compressed {
   488  			releaseToValueBlockBufPool(&compressedValueBlockBufPool, w.blocks[i].block)
   489  		} else {
   490  			releaseToValueBlockBufPool(&uncompressedValueBlockBufPool, w.blocks[i].block)
   491  		}
   492  		w.blocks[i].block = nil
   493  	}
   494  	if w.buf != nil {
   495  		releaseToValueBlockBufPool(&uncompressedValueBlockBufPool, w.buf)
   496  	}
   497  	if w.compressedBuf != nil {
   498  		releaseToValueBlockBufPool(&compressedValueBlockBufPool, w.compressedBuf)
   499  	}
   500  	*w = valueBlockWriter{
   501  		blocks: w.blocks[:0],
   502  	}
   503  	valueBlockWriterPool.Put(w)
   504  }
   505  
   506  func (w *valueBlockWriter) addValue(v []byte) (valueHandle, error) {
   507  	if invariants.Enabled && len(v) == 0 {
   508  		return valueHandle{}, errors.Errorf("cannot write empty value to value block")
   509  	}
   510  	w.numValues++
   511  	blockLen := len(w.buf.b)
   512  	valueLen := len(v)
   513  	if blockLen >= w.blockSize ||
   514  		(blockLen > w.blockSizeThreshold && blockLen+valueLen > w.blockSize) {
   515  		// Block is not currently empty and adding this value will become too big,
   516  		// so finish this block.
   517  		w.compressAndFlush()
   518  		blockLen = len(w.buf.b)
   519  		if invariants.Enabled && blockLen != 0 {
   520  			panic("blockLen of new block should be 0")
   521  		}
   522  	}
   523  	vh := valueHandle{
   524  		valueLen:      uint32(valueLen),
   525  		blockNum:      uint32(len(w.blocks)),
   526  		offsetInBlock: uint32(blockLen),
   527  	}
   528  	blockLen = int(vh.offsetInBlock + vh.valueLen)
   529  	if cap(w.buf.b) < blockLen {
   530  		size := 2 * cap(w.buf.b)
   531  		if size < 1024 {
   532  			size = 1024
   533  		}
   534  		for size < blockLen {
   535  			size *= 2
   536  		}
   537  		buf := make([]byte, blockLen, size)
   538  		_ = copy(buf, w.buf.b)
   539  		w.buf.b = buf
   540  	} else {
   541  		w.buf.b = w.buf.b[:blockLen]
   542  	}
   543  	buf := w.buf.b[vh.offsetInBlock:]
   544  	n := copy(buf, v)
   545  	if n != len(buf) {
   546  		panic("incorrect length computation")
   547  	}
   548  	return vh, nil
   549  }
   550  
   551  func (w *valueBlockWriter) compressAndFlush() {
   552  	// Compress the buffer, discarding the result if the improvement isn't at
   553  	// least 12.5%.
   554  	blockType := noCompressionBlockType
   555  	b := w.buf
   556  	if w.compression != NoCompression {
   557  		blockType, w.compressedBuf.b =
   558  			compressBlock(w.compression, w.buf.b, w.compressedBuf.b[:cap(w.compressedBuf.b)])
   559  		if len(w.compressedBuf.b) < len(w.buf.b)-len(w.buf.b)/8 {
   560  			b = w.compressedBuf
   561  		} else {
   562  			blockType = noCompressionBlockType
   563  		}
   564  	}
   565  	n := len(b.b)
   566  	if n+blockTrailerLen > cap(b.b) {
   567  		block := make([]byte, n+blockTrailerLen)
   568  		copy(block, b.b)
   569  		b.b = block
   570  	} else {
   571  		b.b = b.b[:n+blockTrailerLen]
   572  	}
   573  	b.b[n] = byte(blockType)
   574  	w.computeChecksum(b.b)
   575  	bh := BlockHandle{Offset: w.totalBlockBytes, Length: uint64(n)}
   576  	w.totalBlockBytes += uint64(len(b.b))
   577  	// blockFinishedFunc length excludes the block trailer.
   578  	w.blockFinishedFunc(n)
   579  	compressed := blockType != noCompressionBlockType
   580  	w.blocks = append(w.blocks, blockAndHandle{
   581  		block:      b,
   582  		handle:     bh,
   583  		compressed: compressed,
   584  	})
   585  	// Handed off a buffer to w.blocks, so need get a new one.
   586  	if compressed {
   587  		w.compressedBuf = compressedValueBlockBufPool.Get().(*blockBuffer)
   588  	} else {
   589  		w.buf = uncompressedValueBlockBufPool.Get().(*blockBuffer)
   590  	}
   591  	w.buf.b = w.buf.b[:0]
   592  }
   593  
   594  func (w *valueBlockWriter) computeChecksum(block []byte) {
   595  	n := len(block) - blockTrailerLen
   596  	checksum := w.checksummer.checksum(block[:n], block[n:n+1])
   597  	binary.LittleEndian.PutUint32(block[n+1:], checksum)
   598  }
   599  
   600  func (w *valueBlockWriter) finish(
   601  	writer io.Writer, fileOffset uint64,
   602  ) (valueBlocksIndexHandle, valueBlocksAndIndexStats, error) {
   603  	if len(w.buf.b) > 0 {
   604  		w.compressAndFlush()
   605  	}
   606  	n := len(w.blocks)
   607  	if n == 0 {
   608  		return valueBlocksIndexHandle{}, valueBlocksAndIndexStats{}, nil
   609  	}
   610  	largestOffset := uint64(0)
   611  	largestLength := uint64(0)
   612  	for i := range w.blocks {
   613  		_, err := writer.Write(w.blocks[i].block.b)
   614  		if err != nil {
   615  			return valueBlocksIndexHandle{}, valueBlocksAndIndexStats{}, err
   616  		}
   617  		w.blocks[i].handle.Offset += fileOffset
   618  		largestOffset = w.blocks[i].handle.Offset
   619  		if largestLength < w.blocks[i].handle.Length {
   620  			largestLength = w.blocks[i].handle.Length
   621  		}
   622  	}
   623  	vbihOffset := fileOffset + w.totalBlockBytes
   624  
   625  	vbih := valueBlocksIndexHandle{
   626  		h: BlockHandle{
   627  			Offset: vbihOffset,
   628  		},
   629  		blockNumByteLength:    uint8(lenLittleEndian(uint64(n - 1))),
   630  		blockOffsetByteLength: uint8(lenLittleEndian(largestOffset)),
   631  		blockLengthByteLength: uint8(lenLittleEndian(largestLength)),
   632  	}
   633  	var err error
   634  	if vbih, err = w.writeValueBlocksIndex(writer, vbih); err != nil {
   635  		return valueBlocksIndexHandle{}, valueBlocksAndIndexStats{}, err
   636  	}
   637  	stats := valueBlocksAndIndexStats{
   638  		numValueBlocks:          uint64(n),
   639  		numValuesInValueBlocks:  w.numValues,
   640  		valueBlocksAndIndexSize: w.totalBlockBytes + vbih.h.Length + blockTrailerLen,
   641  	}
   642  	return vbih, stats, err
   643  }
   644  
   645  func (w *valueBlockWriter) writeValueBlocksIndex(
   646  	writer io.Writer, h valueBlocksIndexHandle,
   647  ) (valueBlocksIndexHandle, error) {
   648  	blockLen :=
   649  		int(h.blockNumByteLength+h.blockOffsetByteLength+h.blockLengthByteLength) * len(w.blocks)
   650  	h.h.Length = uint64(blockLen)
   651  	blockLen += blockTrailerLen
   652  	var buf []byte
   653  	if cap(w.buf.b) < blockLen {
   654  		buf = make([]byte, blockLen)
   655  		w.buf.b = buf
   656  	} else {
   657  		buf = w.buf.b[:blockLen]
   658  	}
   659  	b := buf
   660  	for i := range w.blocks {
   661  		littleEndianPut(uint64(i), b, int(h.blockNumByteLength))
   662  		b = b[int(h.blockNumByteLength):]
   663  		littleEndianPut(w.blocks[i].handle.Offset, b, int(h.blockOffsetByteLength))
   664  		b = b[int(h.blockOffsetByteLength):]
   665  		littleEndianPut(w.blocks[i].handle.Length, b, int(h.blockLengthByteLength))
   666  		b = b[int(h.blockLengthByteLength):]
   667  	}
   668  	if len(b) != blockTrailerLen {
   669  		panic("incorrect length calculation")
   670  	}
   671  	b[0] = byte(noCompressionBlockType)
   672  	w.computeChecksum(buf)
   673  	if _, err := writer.Write(buf); err != nil {
   674  		return valueBlocksIndexHandle{}, err
   675  	}
   676  	return h, nil
   677  }
   678  
   679  // littleEndianPut writes v to b using little endian encoding, under the
   680  // assumption that v can be represented using n bytes.
   681  func littleEndianPut(v uint64, b []byte, n int) {
   682  	_ = b[n-1] // bounds check
   683  	for i := 0; i < n; i++ {
   684  		b[i] = byte(v)
   685  		v = v >> 8
   686  	}
   687  }
   688  
   689  // lenLittleEndian returns the minimum number of bytes needed to encode v
   690  // using little endian encoding.
   691  func lenLittleEndian(v uint64) int {
   692  	n := 0
   693  	for i := 0; i < 8; i++ {
   694  		n++
   695  		v = v >> 8
   696  		if v == 0 {
   697  			break
   698  		}
   699  	}
   700  	return n
   701  }
   702  
   703  func littleEndianGet(b []byte, n int) uint64 {
   704  	_ = b[n-1] // bounds check
   705  	v := uint64(b[0])
   706  	for i := 1; i < n; i++ {
   707  		v |= uint64(b[i]) << (8 * i)
   708  	}
   709  	return v
   710  }
   711  
   712  // UserKeyPrefixBound represents a [Lower,Upper) bound of user key prefixes.
   713  // If both are nil, there is no bound specified. Else, Compare(Lower,Upper)
   714  // must be < 0.
   715  type UserKeyPrefixBound struct {
   716  	// Lower is a lower bound user key prefix.
   717  	Lower []byte
   718  	// Upper is an upper bound user key prefix.
   719  	Upper []byte
   720  }
   721  
   722  // IsEmpty returns true iff the bound is empty.
   723  func (ukb *UserKeyPrefixBound) IsEmpty() bool {
   724  	return len(ukb.Lower) == 0 && len(ukb.Upper) == 0
   725  }
   726  
   727  type blockProviderWhenOpen interface {
   728  	readBlockForVBR(
   729  		ctx context.Context, h BlockHandle, stats *base.InternalIteratorStats,
   730  	) (bufferHandle, error)
   731  }
   732  
   733  type blockProviderWhenClosed struct {
   734  	rp ReaderProvider
   735  	r  *Reader
   736  }
   737  
   738  func (bpwc *blockProviderWhenClosed) open() error {
   739  	var err error
   740  	bpwc.r, err = bpwc.rp.GetReader()
   741  	return err
   742  }
   743  
   744  func (bpwc *blockProviderWhenClosed) close() {
   745  	bpwc.rp.Close()
   746  	bpwc.r = nil
   747  }
   748  
   749  func (bpwc blockProviderWhenClosed) readBlockForVBR(
   750  	ctx context.Context, h BlockHandle, stats *base.InternalIteratorStats,
   751  ) (bufferHandle, error) {
   752  	ctx = objiotracing.WithBlockType(ctx, objiotracing.ValueBlock)
   753  	// TODO(jackson,sumeer): Consider whether to use a buffer pool in this case.
   754  	// The bpwc is not allowed to outlive the iterator tree, so it cannot
   755  	// outlive the buffer pool.
   756  	return bpwc.r.readBlock(ctx, h, nil, nil, stats, nil /* buffer pool */)
   757  }
   758  
   759  // ReaderProvider supports the implementation of blockProviderWhenClosed.
   760  // GetReader and Close can be called multiple times in pairs.
   761  type ReaderProvider interface {
   762  	GetReader() (r *Reader, err error)
   763  	Close()
   764  }
   765  
   766  // TrivialReaderProvider implements ReaderProvider for a Reader that will
   767  // outlive the top-level iterator in the iterator tree.
   768  type TrivialReaderProvider struct {
   769  	*Reader
   770  }
   771  
   772  var _ ReaderProvider = TrivialReaderProvider{}
   773  
   774  // GetReader implements ReaderProvider.
   775  func (trp TrivialReaderProvider) GetReader() (*Reader, error) {
   776  	return trp.Reader, nil
   777  }
   778  
   779  // Close implements ReaderProvider.
   780  func (trp TrivialReaderProvider) Close() {}
   781  
   782  // valueBlockReader is used to retrieve values in value
   783  // blocks. It is used when the sstable was written with
   784  // Properties.ValueBlocksAreEnabled.
   785  type valueBlockReader struct {
   786  	ctx    context.Context
   787  	bpOpen blockProviderWhenOpen
   788  	rp     ReaderProvider
   789  	vbih   valueBlocksIndexHandle
   790  	stats  *base.InternalIteratorStats
   791  
   792  	// The value blocks index is lazily retrieved the first time the reader
   793  	// needs to read a value that resides in a value block.
   794  	vbiBlock []byte
   795  	vbiCache bufferHandle
   796  	// When sequentially iterating through all key-value pairs, the cost of
   797  	// repeatedly getting a block that is already in the cache and releasing the
   798  	// bufferHandle can be ~40% of the cpu overhead. So the reader remembers the
   799  	// last value block it retrieved, in case there is locality of access, and
   800  	// this value block can be used for the next value retrieval.
   801  	valueBlockNum uint32
   802  	valueBlock    []byte
   803  	valueBlockPtr unsafe.Pointer
   804  	valueCache    bufferHandle
   805  	lazyFetcher   base.LazyFetcher
   806  	closed        bool
   807  	bufToMangle   []byte
   808  }
   809  
   810  func (r *valueBlockReader) getLazyValueForPrefixAndValueHandle(handle []byte) base.LazyValue {
   811  	fetcher := &r.lazyFetcher
   812  	valLen, h := decodeLenFromValueHandle(handle[1:])
   813  	*fetcher = base.LazyFetcher{
   814  		Fetcher: r,
   815  		Attribute: base.AttributeAndLen{
   816  			ValueLen:       int32(valLen),
   817  			ShortAttribute: getShortAttribute(valuePrefix(handle[0])),
   818  		},
   819  	}
   820  	if r.stats != nil {
   821  		r.stats.SeparatedPointValue.Count++
   822  		r.stats.SeparatedPointValue.ValueBytes += uint64(valLen)
   823  	}
   824  	return base.LazyValue{
   825  		ValueOrHandle: h,
   826  		Fetcher:       fetcher,
   827  	}
   828  }
   829  
   830  func (r *valueBlockReader) close() {
   831  	r.bpOpen = nil
   832  	r.vbiBlock = nil
   833  	r.vbiCache.Release()
   834  	// Set the handle to empty since Release does not nil the Handle.value. If
   835  	// we were to reopen this valueBlockReader and retrieve the same
   836  	// Handle.value from the cache, we don't want to accidentally unref it when
   837  	// attempting to unref the old handle.
   838  	r.vbiCache = bufferHandle{}
   839  	r.valueBlock = nil
   840  	r.valueBlockPtr = nil
   841  	r.valueCache.Release()
   842  	// See comment above.
   843  	r.valueCache = bufferHandle{}
   844  	r.closed = true
   845  	// rp, vbih, stats remain valid, so that LazyFetcher.ValueFetcher can be
   846  	// implemented.
   847  }
   848  
   849  // Fetch implements base.ValueFetcher.
   850  func (r *valueBlockReader) Fetch(
   851  	handle []byte, valLen int32, buf []byte,
   852  ) (val []byte, callerOwned bool, err error) {
   853  	if !r.closed {
   854  		val, err := r.getValueInternal(handle, valLen)
   855  		if invariants.Enabled {
   856  			val = r.doValueMangling(val)
   857  		}
   858  		return val, false, err
   859  	}
   860  
   861  	bp := blockProviderWhenClosed{rp: r.rp}
   862  	err = bp.open()
   863  	if err != nil {
   864  		return nil, false, err
   865  	}
   866  	defer bp.close()
   867  	defer r.close()
   868  	r.bpOpen = bp
   869  	var v []byte
   870  	v, err = r.getValueInternal(handle, valLen)
   871  	if err != nil {
   872  		return nil, false, err
   873  	}
   874  	buf = append(buf[:0], v...)
   875  	return buf, true, nil
   876  }
   877  
   878  // doValueMangling attempts to uncover violations of the contract listed in
   879  // the declaration comment of LazyValue. It is expensive, hence only called
   880  // when invariants.Enabled.
   881  func (r *valueBlockReader) doValueMangling(v []byte) []byte {
   882  	// Randomly set the bytes in the previous retrieved value to 0, since
   883  	// property P1 only requires the valueBlockReader to maintain the memory of
   884  	// one fetched value.
   885  	if rand.Intn(2) == 0 {
   886  		for i := range r.bufToMangle {
   887  			r.bufToMangle[i] = 0
   888  		}
   889  	}
   890  	// Store the current value in a new buffer for future mangling.
   891  	r.bufToMangle = append([]byte(nil), v...)
   892  	return r.bufToMangle
   893  }
   894  
   895  func (r *valueBlockReader) getValueInternal(handle []byte, valLen int32) (val []byte, err error) {
   896  	vh := decodeRemainingValueHandle(handle)
   897  	vh.valueLen = uint32(valLen)
   898  	if r.vbiBlock == nil {
   899  		ch, err := r.bpOpen.readBlockForVBR(r.ctx, r.vbih.h, r.stats)
   900  		if err != nil {
   901  			return nil, err
   902  		}
   903  		r.vbiCache = ch
   904  		r.vbiBlock = ch.Get()
   905  	}
   906  	if r.valueBlock == nil || r.valueBlockNum != vh.blockNum {
   907  		vbh, err := r.getBlockHandle(vh.blockNum)
   908  		if err != nil {
   909  			return nil, err
   910  		}
   911  		vbCacheHandle, err := r.bpOpen.readBlockForVBR(r.ctx, vbh, r.stats)
   912  		if err != nil {
   913  			return nil, err
   914  		}
   915  		r.valueBlockNum = vh.blockNum
   916  		r.valueCache.Release()
   917  		r.valueCache = vbCacheHandle
   918  		r.valueBlock = vbCacheHandle.Get()
   919  		r.valueBlockPtr = unsafe.Pointer(&r.valueBlock[0])
   920  	}
   921  	if r.stats != nil {
   922  		r.stats.SeparatedPointValue.ValueBytesFetched += uint64(valLen)
   923  	}
   924  	return r.valueBlock[vh.offsetInBlock : vh.offsetInBlock+vh.valueLen], nil
   925  }
   926  
   927  func (r *valueBlockReader) getBlockHandle(blockNum uint32) (BlockHandle, error) {
   928  	indexEntryLen :=
   929  		int(r.vbih.blockNumByteLength + r.vbih.blockOffsetByteLength + r.vbih.blockLengthByteLength)
   930  	offsetInIndex := indexEntryLen * int(blockNum)
   931  	if len(r.vbiBlock) < offsetInIndex+indexEntryLen {
   932  		return BlockHandle{}, errors.Errorf(
   933  			"cannot read at offset %d and length %d from block of length %d",
   934  			offsetInIndex, indexEntryLen, len(r.vbiBlock))
   935  	}
   936  	b := r.vbiBlock[offsetInIndex : offsetInIndex+indexEntryLen]
   937  	n := int(r.vbih.blockNumByteLength)
   938  	bn := littleEndianGet(b, n)
   939  	if uint32(bn) != blockNum {
   940  		return BlockHandle{},
   941  			errors.Errorf("expected block num %d but found %d", blockNum, bn)
   942  	}
   943  	b = b[n:]
   944  	n = int(r.vbih.blockOffsetByteLength)
   945  	blockOffset := littleEndianGet(b, n)
   946  	b = b[n:]
   947  	n = int(r.vbih.blockLengthByteLength)
   948  	blockLen := littleEndianGet(b, n)
   949  	return BlockHandle{Offset: blockOffset, Length: blockLen}, nil
   950  }