Line data Source code
1 : // Copyright 2011 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 : "bytes"
9 : "encoding/binary"
10 : "fmt"
11 : "math"
12 : "runtime"
13 : "sort"
14 : "sync"
15 :
16 : "github.com/cespare/xxhash/v2"
17 : "github.com/cockroachdb/errors"
18 : "github.com/cockroachdb/pebble/internal/base"
19 : "github.com/cockroachdb/pebble/internal/bytealloc"
20 : "github.com/cockroachdb/pebble/internal/cache"
21 : "github.com/cockroachdb/pebble/internal/crc"
22 : "github.com/cockroachdb/pebble/internal/invariants"
23 : "github.com/cockroachdb/pebble/internal/keyspan"
24 : "github.com/cockroachdb/pebble/internal/private"
25 : "github.com/cockroachdb/pebble/internal/rangekey"
26 : "github.com/cockroachdb/pebble/objstorage"
27 : )
28 :
29 : // encodedBHPEstimatedSize estimates the size of the encoded BlockHandleWithProperties.
30 : // It would also be nice to account for the length of the data block properties here,
31 : // but isn't necessary since this is an estimate.
32 : const encodedBHPEstimatedSize = binary.MaxVarintLen64 * 2
33 :
34 : var errWriterClosed = errors.New("pebble: writer is closed")
35 :
36 : // WriterMetadata holds info about a finished sstable.
37 : type WriterMetadata struct {
38 : Size uint64
39 : SmallestPoint InternalKey
40 : // LargestPoint, LargestRangeKey, LargestRangeDel should not be accessed
41 : // before Writer.Close is called, because they may only be set on
42 : // Writer.Close.
43 : LargestPoint InternalKey
44 : SmallestRangeDel InternalKey
45 : LargestRangeDel InternalKey
46 : SmallestRangeKey InternalKey
47 : LargestRangeKey InternalKey
48 : HasPointKeys bool
49 : HasRangeDelKeys bool
50 : HasRangeKeys bool
51 : SmallestSeqNum uint64
52 : LargestSeqNum uint64
53 : Properties Properties
54 : }
55 :
56 : // SetSmallestPointKey sets the smallest point key to the given key.
57 : // NB: this method set the "absolute" smallest point key. Any existing key is
58 : // overridden.
59 2 : func (m *WriterMetadata) SetSmallestPointKey(k InternalKey) {
60 2 : m.SmallestPoint = k
61 2 : m.HasPointKeys = true
62 2 : }
63 :
64 : // SetSmallestRangeDelKey sets the smallest rangedel key to the given key.
65 : // NB: this method set the "absolute" smallest rangedel key. Any existing key is
66 : // overridden.
67 2 : func (m *WriterMetadata) SetSmallestRangeDelKey(k InternalKey) {
68 2 : m.SmallestRangeDel = k
69 2 : m.HasRangeDelKeys = true
70 2 : }
71 :
72 : // SetSmallestRangeKey sets the smallest range key to the given key.
73 : // NB: this method set the "absolute" smallest range key. Any existing key is
74 : // overridden.
75 2 : func (m *WriterMetadata) SetSmallestRangeKey(k InternalKey) {
76 2 : m.SmallestRangeKey = k
77 2 : m.HasRangeKeys = true
78 2 : }
79 :
80 : // SetLargestPointKey sets the largest point key to the given key.
81 : // NB: this method set the "absolute" largest point key. Any existing key is
82 : // overridden.
83 2 : func (m *WriterMetadata) SetLargestPointKey(k InternalKey) {
84 2 : m.LargestPoint = k
85 2 : m.HasPointKeys = true
86 2 : }
87 :
88 : // SetLargestRangeDelKey sets the largest rangedel key to the given key.
89 : // NB: this method set the "absolute" largest rangedel key. Any existing key is
90 : // overridden.
91 2 : func (m *WriterMetadata) SetLargestRangeDelKey(k InternalKey) {
92 2 : m.LargestRangeDel = k
93 2 : m.HasRangeDelKeys = true
94 2 : }
95 :
96 : // SetLargestRangeKey sets the largest range key to the given key.
97 : // NB: this method set the "absolute" largest range key. Any existing key is
98 : // overridden.
99 2 : func (m *WriterMetadata) SetLargestRangeKey(k InternalKey) {
100 2 : m.LargestRangeKey = k
101 2 : m.HasRangeKeys = true
102 2 : }
103 :
104 2 : func (m *WriterMetadata) updateSeqNum(seqNum uint64) {
105 2 : if m.SmallestSeqNum > seqNum {
106 2 : m.SmallestSeqNum = seqNum
107 2 : }
108 2 : if m.LargestSeqNum < seqNum {
109 2 : m.LargestSeqNum = seqNum
110 2 : }
111 : }
112 :
113 : // Writer is a table writer.
114 : type Writer struct {
115 : writable objstorage.Writable
116 : meta WriterMetadata
117 : err error
118 : // cacheID and fileNum are used to remove blocks written to the sstable from
119 : // the cache, providing a defense in depth against bugs which cause cache
120 : // collisions.
121 : cacheID uint64
122 : fileNum base.DiskFileNum
123 : // The following fields are copied from Options.
124 : blockSize int
125 : blockSizeThreshold int
126 : indexBlockSize int
127 : indexBlockSizeThreshold int
128 : compare Compare
129 : split Split
130 : formatKey base.FormatKey
131 : compression Compression
132 : separator Separator
133 : successor Successor
134 : tableFormat TableFormat
135 : isStrictObsolete bool
136 : writingToLowestLevel bool
137 : cache *cache.Cache
138 : restartInterval int
139 : checksumType ChecksumType
140 : // disableKeyOrderChecks disables the checks that keys are added to an
141 : // sstable in order. It is intended for internal use only in the construction
142 : // of invalid sstables for testing. See tool/make_test_sstables.go.
143 : disableKeyOrderChecks bool
144 : // With two level indexes, the index/filter of a SST file is partitioned into
145 : // smaller blocks with an additional top-level index on them. When reading an
146 : // index/filter, only the top-level index is loaded into memory. The two level
147 : // index/filter then uses the top-level index to load on demand into the block
148 : // cache the partitions that are required to perform the index/filter query.
149 : //
150 : // Two level indexes are enabled automatically when there is more than one
151 : // index block.
152 : //
153 : // This is useful when there are very large index blocks, which generally occurs
154 : // with the usage of large keys. With large index blocks, the index blocks fight
155 : // the data blocks for block cache space and the index blocks are likely to be
156 : // re-read many times from the disk. The top level index, which has a much
157 : // smaller memory footprint, can be used to prevent the entire index block from
158 : // being loaded into the block cache.
159 : twoLevelIndex bool
160 : // Internal flag to allow creation of range-del-v1 format blocks. Only used
161 : // for testing. Note that v2 format blocks are backwards compatible with v1
162 : // format blocks.
163 : rangeDelV1Format bool
164 : indexBlock *indexBlockBuf
165 : rangeDelBlock blockWriter
166 : rangeKeyBlock blockWriter
167 : topLevelIndexBlock blockWriter
168 : props Properties
169 : propCollectors []TablePropertyCollector
170 : blockPropCollectors []BlockPropertyCollector
171 : obsoleteCollector obsoleteKeyBlockPropertyCollector
172 : blockPropsEncoder blockPropertiesEncoder
173 : // filter accumulates the filter block. If populated, the filter ingests
174 : // either the output of w.split (i.e. a prefix extractor) if w.split is not
175 : // nil, or the full keys otherwise.
176 : filter filterWriter
177 : indexPartitions []indexBlockAndBlockProperties
178 :
179 : // indexBlockAlloc is used to bulk-allocate byte slices used to store index
180 : // blocks in indexPartitions. These live until the index finishes.
181 : indexBlockAlloc []byte
182 : // indexSepAlloc is used to bulk-allocate index block separator slices stored
183 : // in indexPartitions. These live until the index finishes.
184 : indexSepAlloc bytealloc.A
185 :
186 : // To allow potentially overlapping (i.e. un-fragmented) range keys spans to
187 : // be added to the Writer, a keyspan.Fragmenter is used to retain the keys
188 : // and values, emitting fragmented, coalesced spans as appropriate. Range
189 : // keys must be added in order of their start user-key.
190 : fragmenter keyspan.Fragmenter
191 : rangeKeyEncoder rangekey.Encoder
192 : rangeKeysBySuffix keyspan.KeysBySuffix
193 : rangeKeySpan keyspan.Span
194 : rkBuf []byte
195 : // dataBlockBuf consists of the state which is currently owned by and used by
196 : // the Writer client goroutine. This state can be handed off to other goroutines.
197 : dataBlockBuf *dataBlockBuf
198 : // blockBuf consists of the state which is owned by and used by the Writer client
199 : // goroutine.
200 : blockBuf blockBuf
201 :
202 : coordination coordinationState
203 :
204 : // Information (other than the byte slice) about the last point key, to
205 : // avoid extracting it again.
206 : lastPointKeyInfo pointKeyInfo
207 :
208 : // For value blocks.
209 : shortAttributeExtractor base.ShortAttributeExtractor
210 : requiredInPlaceValueBound UserKeyPrefixBound
211 : valueBlockWriter *valueBlockWriter
212 : }
213 :
214 : type pointKeyInfo struct {
215 : trailer uint64
216 : // Only computed when w.valueBlockWriter is not nil.
217 : userKeyLen int
218 : // prefixLen uses w.split, if not nil. Only computed when w.valueBlockWriter
219 : // is not nil.
220 : prefixLen int
221 : // True iff the point was marked obsolete.
222 : isObsolete bool
223 : }
224 :
225 : type coordinationState struct {
226 : parallelismEnabled bool
227 :
228 : // writeQueue is used to write data blocks to disk. The writeQueue is primarily
229 : // used to maintain the order in which data blocks must be written to disk. For
230 : // this reason, every single data block write must be done through the writeQueue.
231 : writeQueue *writeQueue
232 :
233 : sizeEstimate dataBlockEstimates
234 : }
235 :
236 2 : func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
237 2 : c.parallelismEnabled = parallelismEnabled
238 2 : // useMutex is false regardless of parallelismEnabled, because we do not do
239 2 : // parallel compression yet.
240 2 : c.sizeEstimate.useMutex = false
241 2 :
242 2 : // writeQueueSize determines the size of the write queue, or the number
243 2 : // of items which can be added to the queue without blocking. By default, we
244 2 : // use a writeQueue size of 0, since we won't be doing any block writes in
245 2 : // parallel.
246 2 : writeQueueSize := 0
247 2 : if parallelismEnabled {
248 2 : writeQueueSize = runtime.GOMAXPROCS(0)
249 2 : }
250 2 : c.writeQueue = newWriteQueue(writeQueueSize, writer)
251 : }
252 :
253 : // sizeEstimate is a general purpose helper for estimating two kinds of sizes:
254 : // A. The compressed sstable size, which is useful for deciding when to start
255 : //
256 : // a new sstable during flushes or compactions. In practice, we use this in
257 : // estimating the data size (excluding the index).
258 : //
259 : // B. The size of index blocks to decide when to start a new index block.
260 : //
261 : // There are some terminology peculiarities which are due to the origin of
262 : // sizeEstimate for use case A with parallel compression enabled (for which
263 : // the code has not been merged). Specifically this relates to the terms
264 : // "written" and "compressed".
265 : // - The notion of "written" for case A is sufficiently defined by saying that
266 : // the data block is compressed. Waiting for the actual data block write to
267 : // happen can result in unnecessary estimation, when we already know how big
268 : // it will be in compressed form. Additionally, with the forthcoming value
269 : // blocks containing older MVCC values, these compressed block will be held
270 : // in-memory until late in the sstable writing, and we do want to accurately
271 : // account for them without waiting for the actual write.
272 : // For case B, "written" means that the index entry has been fully
273 : // generated, and has been added to the uncompressed block buffer for that
274 : // index block. It does not include actually writing a potentially
275 : // compressed index block.
276 : // - The notion of "compressed" is to differentiate between a "inflight" size
277 : // and the actual size, and is handled via computing a compression ratio
278 : // observed so far (defaults to 1).
279 : // For case A, this is actual data block compression, so the "inflight" size
280 : // is uncompressed blocks (that are no longer being written to) and the
281 : // "compressed" size is after they have been compressed.
282 : // For case B the inflight size is for a key-value pair in the index for
283 : // which the value size (the encoded size of the BlockHandleWithProperties)
284 : // is not accurately known, while the compressed size is the size of that
285 : // entry when it has been added to the (in-progress) index ssblock.
286 : //
287 : // Usage: To update state, one can optionally provide an inflight write value
288 : // using addInflight (used for case B). When something is "written" the state
289 : // can be updated using either writtenWithDelta or writtenWithTotal, which
290 : // provide the actual delta size or the total size (latter must be
291 : // monotonically non-decreasing). If there were no calls to addInflight, there
292 : // isn't any real estimation happening here. So case A does not do any real
293 : // estimation. However, when we introduce parallel compression, there will be
294 : // estimation in that the client goroutine will call addInFlight and the
295 : // compression goroutines will call writtenWithDelta.
296 : type sizeEstimate struct {
297 : // emptySize is the size when there is no inflight data, and numEntries is 0.
298 : // emptySize is constant once set.
299 : emptySize uint64
300 :
301 : // inflightSize is the estimated size of some inflight data which hasn't
302 : // been written yet.
303 : inflightSize uint64
304 :
305 : // totalSize is the total size of the data which has already been written.
306 : totalSize uint64
307 :
308 : // numWrittenEntries is the total number of entries which have already been
309 : // written.
310 : numWrittenEntries uint64
311 : // numInflightEntries is the total number of entries which are inflight, and
312 : // haven't been written.
313 : numInflightEntries uint64
314 :
315 : // maxEstimatedSize stores the maximum result returned from sizeEstimate.size.
316 : // It ensures that values returned from subsequent calls to Writer.EstimatedSize
317 : // never decrease.
318 : maxEstimatedSize uint64
319 :
320 : // We assume that the entries added to the sizeEstimate can be compressed.
321 : // For this reason, we keep track of a compressedSize and an uncompressedSize
322 : // to compute a compression ratio for the inflight entries. If the entries
323 : // aren't being compressed, then compressedSize and uncompressedSize must be
324 : // equal.
325 : compressedSize uint64
326 : uncompressedSize uint64
327 : }
328 :
329 2 : func (s *sizeEstimate) init(emptySize uint64) {
330 2 : s.emptySize = emptySize
331 2 : }
332 :
333 2 : func (s *sizeEstimate) size() uint64 {
334 2 : ratio := float64(1)
335 2 : if s.uncompressedSize > 0 {
336 2 : ratio = float64(s.compressedSize) / float64(s.uncompressedSize)
337 2 : }
338 2 : estimatedInflightSize := uint64(float64(s.inflightSize) * ratio)
339 2 : total := s.totalSize + estimatedInflightSize
340 2 : if total > s.maxEstimatedSize {
341 2 : s.maxEstimatedSize = total
342 2 : } else {
343 2 : total = s.maxEstimatedSize
344 2 : }
345 :
346 2 : if total == 0 {
347 2 : return s.emptySize
348 2 : }
349 :
350 2 : return total
351 : }
352 :
353 2 : func (s *sizeEstimate) numTotalEntries() uint64 {
354 2 : return s.numWrittenEntries + s.numInflightEntries
355 2 : }
356 :
357 2 : func (s *sizeEstimate) addInflight(size int) {
358 2 : s.numInflightEntries++
359 2 : s.inflightSize += uint64(size)
360 2 : }
361 :
362 2 : func (s *sizeEstimate) writtenWithTotal(newTotalSize uint64, inflightSize int) {
363 2 : finalEntrySize := int(newTotalSize - s.totalSize)
364 2 : s.writtenWithDelta(finalEntrySize, inflightSize)
365 2 : }
366 :
367 2 : func (s *sizeEstimate) writtenWithDelta(finalEntrySize int, inflightSize int) {
368 2 : if inflightSize > 0 {
369 2 : // This entry was previously inflight, so we should decrement inflight
370 2 : // entries and update the "compression" stats for future estimation.
371 2 : s.numInflightEntries--
372 2 : s.inflightSize -= uint64(inflightSize)
373 2 : s.uncompressedSize += uint64(inflightSize)
374 2 : s.compressedSize += uint64(finalEntrySize)
375 2 : }
376 2 : s.numWrittenEntries++
377 2 : s.totalSize += uint64(finalEntrySize)
378 : }
379 :
380 2 : func (s *sizeEstimate) clear() {
381 2 : *s = sizeEstimate{emptySize: s.emptySize}
382 2 : }
383 :
384 : type indexBlockBuf struct {
385 : // block will only be accessed from the writeQueue.
386 : block blockWriter
387 :
388 : size struct {
389 : useMutex bool
390 : mu sync.Mutex
391 : estimate sizeEstimate
392 : }
393 :
394 : // restartInterval matches indexBlockBuf.block.restartInterval. We store it twice, because the `block`
395 : // must only be accessed from the writeQueue goroutine.
396 : restartInterval int
397 : }
398 :
399 2 : func (i *indexBlockBuf) clear() {
400 2 : i.block.clear()
401 2 : if i.size.useMutex {
402 2 : i.size.mu.Lock()
403 2 : defer i.size.mu.Unlock()
404 2 : }
405 2 : i.size.estimate.clear()
406 2 : i.restartInterval = 0
407 : }
408 :
409 : var indexBlockBufPool = sync.Pool{
410 2 : New: func() interface{} {
411 2 : return &indexBlockBuf{}
412 2 : },
413 : }
414 :
415 : const indexBlockRestartInterval = 1
416 :
417 2 : func newIndexBlockBuf(useMutex bool) *indexBlockBuf {
418 2 : i := indexBlockBufPool.Get().(*indexBlockBuf)
419 2 : i.size.useMutex = useMutex
420 2 : i.restartInterval = indexBlockRestartInterval
421 2 : i.block.restartInterval = indexBlockRestartInterval
422 2 : i.size.estimate.init(emptyBlockSize)
423 2 : return i
424 2 : }
425 :
426 : func (i *indexBlockBuf) shouldFlush(
427 : sep InternalKey, valueLen, targetBlockSize, sizeThreshold int,
428 2 : ) bool {
429 2 : if i.size.useMutex {
430 2 : i.size.mu.Lock()
431 2 : defer i.size.mu.Unlock()
432 2 : }
433 :
434 2 : nEntries := i.size.estimate.numTotalEntries()
435 2 : return shouldFlush(
436 2 : sep, valueLen, i.restartInterval, int(i.size.estimate.size()),
437 2 : int(nEntries), targetBlockSize, sizeThreshold)
438 : }
439 :
440 2 : func (i *indexBlockBuf) add(key InternalKey, value []byte, inflightSize int) {
441 2 : i.block.add(key, value)
442 2 : size := i.block.estimatedSize()
443 2 : if i.size.useMutex {
444 2 : i.size.mu.Lock()
445 2 : defer i.size.mu.Unlock()
446 2 : }
447 2 : i.size.estimate.writtenWithTotal(uint64(size), inflightSize)
448 : }
449 :
450 2 : func (i *indexBlockBuf) finish() []byte {
451 2 : b := i.block.finish()
452 2 : return b
453 2 : }
454 :
455 2 : func (i *indexBlockBuf) addInflight(inflightSize int) {
456 2 : if i.size.useMutex {
457 2 : i.size.mu.Lock()
458 2 : defer i.size.mu.Unlock()
459 2 : }
460 2 : i.size.estimate.addInflight(inflightSize)
461 : }
462 :
463 2 : func (i *indexBlockBuf) estimatedSize() uint64 {
464 2 : if i.size.useMutex {
465 2 : i.size.mu.Lock()
466 2 : defer i.size.mu.Unlock()
467 2 : }
468 :
469 : // Make sure that the size estimation works as expected when parallelism
470 : // is disabled.
471 2 : if invariants.Enabled && !i.size.useMutex {
472 2 : if i.size.estimate.inflightSize != 0 {
473 0 : panic("unexpected inflight entry in index block size estimation")
474 : }
475 :
476 : // NB: The i.block should only be accessed from the writeQueue goroutine,
477 : // when parallelism is enabled. We break that invariant here, but that's
478 : // okay since parallelism is disabled.
479 2 : if i.size.estimate.size() != uint64(i.block.estimatedSize()) {
480 0 : panic("index block size estimation sans parallelism is incorrect")
481 : }
482 : }
483 2 : return i.size.estimate.size()
484 : }
485 :
486 : // sizeEstimate is used for sstable size estimation. sizeEstimate can be
487 : // accessed by the Writer client and compressionQueue goroutines. Fields
488 : // should only be read/updated through the functions defined on the
489 : // *sizeEstimate type.
490 : type dataBlockEstimates struct {
491 : // If we don't do block compression in parallel, then we don't need to take
492 : // the performance hit of synchronizing using this mutex.
493 : useMutex bool
494 : mu sync.Mutex
495 :
496 : estimate sizeEstimate
497 : }
498 :
499 : // inflightSize is the uncompressed block size estimate which has been
500 : // previously provided to addInflightDataBlock(). If addInflightDataBlock()
501 : // has not been called, this must be set to 0. compressedSize is the
502 : // compressed size of the block.
503 2 : func (d *dataBlockEstimates) dataBlockCompressed(compressedSize int, inflightSize int) {
504 2 : if d.useMutex {
505 0 : d.mu.Lock()
506 0 : defer d.mu.Unlock()
507 0 : }
508 2 : d.estimate.writtenWithDelta(compressedSize+blockTrailerLen, inflightSize)
509 : }
510 :
511 : // size is an estimated size of datablock data which has been written to disk.
512 2 : func (d *dataBlockEstimates) size() uint64 {
513 2 : if d.useMutex {
514 0 : d.mu.Lock()
515 0 : defer d.mu.Unlock()
516 0 : }
517 : // If there is no parallel compression, there should not be any inflight bytes.
518 2 : if invariants.Enabled && !d.useMutex {
519 2 : if d.estimate.inflightSize != 0 {
520 0 : panic("unexpected inflight entry in data block size estimation")
521 : }
522 : }
523 2 : return d.estimate.size()
524 : }
525 :
526 : // Avoid linter unused error.
527 : var _ = (&dataBlockEstimates{}).addInflightDataBlock
528 :
529 : // NB: unused since no parallel compression.
530 0 : func (d *dataBlockEstimates) addInflightDataBlock(size int) {
531 0 : if d.useMutex {
532 0 : d.mu.Lock()
533 0 : defer d.mu.Unlock()
534 0 : }
535 :
536 0 : d.estimate.addInflight(size)
537 : }
538 :
539 : var writeTaskPool = sync.Pool{
540 2 : New: func() interface{} {
541 2 : t := &writeTask{}
542 2 : t.compressionDone = make(chan bool, 1)
543 2 : return t
544 2 : },
545 : }
546 :
547 : type checksummer struct {
548 : checksumType ChecksumType
549 : xxHasher *xxhash.Digest
550 : }
551 :
552 2 : func (c *checksummer) checksum(block []byte, blockType []byte) (checksum uint32) {
553 2 : // Calculate the checksum.
554 2 : switch c.checksumType {
555 2 : case ChecksumTypeCRC32c:
556 2 : checksum = crc.New(block).Update(blockType).Value()
557 1 : case ChecksumTypeXXHash64:
558 1 : if c.xxHasher == nil {
559 1 : c.xxHasher = xxhash.New()
560 1 : } else {
561 1 : c.xxHasher.Reset()
562 1 : }
563 1 : c.xxHasher.Write(block)
564 1 : c.xxHasher.Write(blockType)
565 1 : checksum = uint32(c.xxHasher.Sum64())
566 0 : default:
567 0 : panic(errors.Newf("unsupported checksum type: %d", c.checksumType))
568 : }
569 2 : return checksum
570 : }
571 :
572 : type blockBuf struct {
573 : // tmp is a scratch buffer, large enough to hold either footerLen bytes,
574 : // blockTrailerLen bytes, (5 * binary.MaxVarintLen64) bytes, and most
575 : // likely large enough for a block handle with properties.
576 : tmp [blockHandleLikelyMaxLen]byte
577 : // compressedBuf is the destination buffer for compression. It is re-used over the
578 : // lifetime of the blockBuf, avoiding the allocation of a temporary buffer for each block.
579 : compressedBuf []byte
580 : checksummer checksummer
581 : }
582 :
583 2 : func (b *blockBuf) clear() {
584 2 : // We can't assign b.compressedBuf[:0] to compressedBuf because snappy relies
585 2 : // on the length of the buffer, and not the capacity to determine if it needs
586 2 : // to make an allocation.
587 2 : *b = blockBuf{
588 2 : compressedBuf: b.compressedBuf, checksummer: b.checksummer,
589 2 : }
590 2 : }
591 :
592 : // A dataBlockBuf holds all the state required to compress and write a data block to disk.
593 : // A dataBlockBuf begins its lifecycle owned by the Writer client goroutine. The Writer
594 : // client goroutine adds keys to the sstable, writing directly into a dataBlockBuf's blockWriter
595 : // until the block is full. Once a dataBlockBuf's block is full, the dataBlockBuf may be passed
596 : // to other goroutines for compression and file I/O.
597 : type dataBlockBuf struct {
598 : blockBuf
599 : dataBlock blockWriter
600 :
601 : // uncompressed is a reference to a byte slice which is owned by the dataBlockBuf. It is the
602 : // next byte slice to be compressed. The uncompressed byte slice will be backed by the
603 : // dataBlock.buf.
604 : uncompressed []byte
605 : // compressed is a reference to a byte slice which is owned by the dataBlockBuf. It is the
606 : // compressed byte slice which must be written to disk. The compressed byte slice may be
607 : // backed by the dataBlock.buf, or the dataBlockBuf.compressedBuf, depending on whether
608 : // we use the result of the compression.
609 : compressed []byte
610 :
611 : // We're making calls to BlockPropertyCollectors from the Writer client goroutine. We need to
612 : // pass the encoded block properties over to the write queue. To prevent copies, and allocations,
613 : // we give each dataBlockBuf, a blockPropertiesEncoder.
614 : blockPropsEncoder blockPropertiesEncoder
615 : // dataBlockProps is set when Writer.finishDataBlockProps is called. The dataBlockProps slice is
616 : // a shallow copy of the internal buffer of the dataBlockBuf.blockPropsEncoder.
617 : dataBlockProps []byte
618 :
619 : // sepScratch is reusable scratch space for computing separator keys.
620 : sepScratch []byte
621 : }
622 :
623 2 : func (d *dataBlockBuf) clear() {
624 2 : d.blockBuf.clear()
625 2 : d.dataBlock.clear()
626 2 :
627 2 : d.uncompressed = nil
628 2 : d.compressed = nil
629 2 : d.dataBlockProps = nil
630 2 : d.sepScratch = d.sepScratch[:0]
631 2 : }
632 :
633 : var dataBlockBufPool = sync.Pool{
634 2 : New: func() interface{} {
635 2 : return &dataBlockBuf{}
636 2 : },
637 : }
638 :
639 2 : func newDataBlockBuf(restartInterval int, checksumType ChecksumType) *dataBlockBuf {
640 2 : d := dataBlockBufPool.Get().(*dataBlockBuf)
641 2 : d.dataBlock.restartInterval = restartInterval
642 2 : d.checksummer.checksumType = checksumType
643 2 : return d
644 2 : }
645 :
646 2 : func (d *dataBlockBuf) finish() {
647 2 : d.uncompressed = d.dataBlock.finish()
648 2 : }
649 :
650 2 : func (d *dataBlockBuf) compressAndChecksum(c Compression) {
651 2 : d.compressed = compressAndChecksum(d.uncompressed, c, &d.blockBuf)
652 2 : }
653 :
654 : func (d *dataBlockBuf) shouldFlush(
655 : key InternalKey, valueLen, targetBlockSize, sizeThreshold int,
656 2 : ) bool {
657 2 : return shouldFlush(
658 2 : key, valueLen, d.dataBlock.restartInterval, d.dataBlock.estimatedSize(),
659 2 : d.dataBlock.nEntries, targetBlockSize, sizeThreshold)
660 2 : }
661 :
662 : type indexBlockAndBlockProperties struct {
663 : nEntries int
664 : // sep is the last key added to this block, for computing a separator later.
665 : sep InternalKey
666 : properties []byte
667 : // block is the encoded block produced by blockWriter.finish.
668 : block []byte
669 : }
670 :
671 : // Set sets the value for the given key. The sequence number is set to 0.
672 : // Intended for use to externally construct an sstable before ingestion into a
673 : // DB. For a given Writer, the keys passed to Set must be in strictly increasing
674 : // order.
675 : //
676 : // TODO(peter): untested
677 2 : func (w *Writer) Set(key, value []byte) error {
678 2 : if w.err != nil {
679 0 : return w.err
680 0 : }
681 2 : if w.isStrictObsolete {
682 0 : return errors.Errorf("use AddWithForceObsolete")
683 0 : }
684 : // forceObsolete is false based on the assumption that no RANGEDELs in the
685 : // sstable delete the added points.
686 2 : return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindSet), value, false)
687 : }
688 :
689 : // Delete deletes the value for the given key. The sequence number is set to
690 : // 0. Intended for use to externally construct an sstable before ingestion into
691 : // a DB.
692 : //
693 : // TODO(peter): untested
694 1 : func (w *Writer) Delete(key []byte) error {
695 1 : if w.err != nil {
696 0 : return w.err
697 0 : }
698 1 : if w.isStrictObsolete {
699 0 : return errors.Errorf("use AddWithForceObsolete")
700 0 : }
701 : // forceObsolete is false based on the assumption that no RANGEDELs in the
702 : // sstable delete the added points.
703 1 : return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindDelete), nil, false)
704 : }
705 :
706 : // DeleteRange deletes all of the keys (and values) in the range [start,end)
707 : // (inclusive on start, exclusive on end). The sequence number is set to
708 : // 0. Intended for use to externally construct an sstable before ingestion into
709 : // a DB.
710 : //
711 : // TODO(peter): untested
712 2 : func (w *Writer) DeleteRange(start, end []byte) error {
713 2 : if w.err != nil {
714 0 : return w.err
715 0 : }
716 2 : return w.addTombstone(base.MakeInternalKey(start, 0, InternalKeyKindRangeDelete), end)
717 : }
718 :
719 : // Merge adds an action to the DB that merges the value at key with the new
720 : // value. The details of the merge are dependent upon the configured merge
721 : // operator. The sequence number is set to 0. Intended for use to externally
722 : // construct an sstable before ingestion into a DB.
723 : //
724 : // TODO(peter): untested
725 1 : func (w *Writer) Merge(key, value []byte) error {
726 1 : if w.err != nil {
727 0 : return w.err
728 0 : }
729 1 : if w.isStrictObsolete {
730 0 : return errors.Errorf("use AddWithForceObsolete")
731 0 : }
732 : // forceObsolete is false based on the assumption that no RANGEDELs in the
733 : // sstable that delete the added points. If the user configured this writer
734 : // to be strict-obsolete, addPoint will reject the addition of this MERGE.
735 1 : return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindMerge), value, false)
736 : }
737 :
738 : // Add adds a key/value pair to the table being written. For a given Writer,
739 : // the keys passed to Add must be in increasing order. The exception to this
740 : // rule is range deletion tombstones. Range deletion tombstones need to be
741 : // added ordered by their start key, but they can be added out of order from
742 : // point entries. Additionally, range deletion tombstones must be fragmented
743 : // (i.e. by keyspan.Fragmenter).
744 2 : func (w *Writer) Add(key InternalKey, value []byte) error {
745 2 : if w.isStrictObsolete {
746 0 : return errors.Errorf("use AddWithForceObsolete")
747 0 : }
748 2 : return w.AddWithForceObsolete(key, value, false)
749 : }
750 :
751 : // AddWithForceObsolete must be used when writing a strict-obsolete sstable.
752 : //
753 : // forceObsolete indicates whether the caller has determined that this key is
754 : // obsolete even though it may be the latest point key for this userkey. This
755 : // should be set to true for keys obsoleted by RANGEDELs, and is required for
756 : // strict-obsolete sstables.
757 : //
758 : // Note that there are two properties, S1 and S2 (see comment in format.go)
759 : // that strict-obsolete ssts must satisfy. S2, due to RANGEDELs, is solely the
760 : // responsibility of the caller. S1 is solely the responsibility of the
761 : // callee.
762 2 : func (w *Writer) AddWithForceObsolete(key InternalKey, value []byte, forceObsolete bool) error {
763 2 : if w.err != nil {
764 0 : return w.err
765 0 : }
766 :
767 2 : switch key.Kind() {
768 2 : case InternalKeyKindRangeDelete:
769 2 : return w.addTombstone(key, value)
770 : case base.InternalKeyKindRangeKeyDelete,
771 : base.InternalKeyKindRangeKeySet,
772 0 : base.InternalKeyKindRangeKeyUnset:
773 0 : w.err = errors.Errorf(
774 0 : "pebble: range keys must be added via one of the RangeKey* functions")
775 0 : return w.err
776 : }
777 2 : return w.addPoint(key, value, forceObsolete)
778 : }
779 :
780 2 : func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
781 2 : prevTrailer := w.lastPointKeyInfo.trailer
782 2 : w.lastPointKeyInfo.trailer = key.Trailer
783 2 : if w.dataBlockBuf.dataBlock.nEntries == 0 {
784 2 : return nil
785 2 : }
786 2 : if !w.disableKeyOrderChecks {
787 2 : prevPointUserKey := w.dataBlockBuf.dataBlock.getCurUserKey()
788 2 : cmpUser := w.compare(prevPointUserKey, key.UserKey)
789 2 : if cmpUser > 0 || (cmpUser == 0 && prevTrailer <= key.Trailer) {
790 1 : return errors.Errorf(
791 1 : "pebble: keys must be added in strictly increasing order: %s, %s",
792 1 : InternalKey{UserKey: prevPointUserKey, Trailer: prevTrailer}.Pretty(w.formatKey),
793 1 : key.Pretty(w.formatKey))
794 1 : }
795 : }
796 2 : return nil
797 : }
798 :
799 : // REQUIRES: at least one point has been written to the Writer.
800 2 : func (w *Writer) getLastPointUserKey() []byte {
801 2 : if w.dataBlockBuf.dataBlock.nEntries == 0 {
802 0 : panic(errors.AssertionFailedf("no point keys added to writer"))
803 : }
804 2 : return w.dataBlockBuf.dataBlock.getCurUserKey()
805 : }
806 :
807 : func (w *Writer) makeAddPointDecisionV3(
808 : key InternalKey, valueLen int,
809 2 : ) (setHasSamePrefix bool, writeToValueBlock bool, isObsolete bool, err error) {
810 2 : prevPointKeyInfo := w.lastPointKeyInfo
811 2 : w.lastPointKeyInfo.userKeyLen = len(key.UserKey)
812 2 : w.lastPointKeyInfo.prefixLen = w.lastPointKeyInfo.userKeyLen
813 2 : if w.split != nil {
814 2 : w.lastPointKeyInfo.prefixLen = w.split(key.UserKey)
815 2 : }
816 2 : w.lastPointKeyInfo.trailer = key.Trailer
817 2 : w.lastPointKeyInfo.isObsolete = false
818 2 : if !w.meta.HasPointKeys {
819 2 : return false, false, false, nil
820 2 : }
821 2 : keyKind := base.TrailerKind(key.Trailer)
822 2 : prevPointUserKey := w.getLastPointUserKey()
823 2 : prevPointKey := InternalKey{UserKey: prevPointUserKey, Trailer: prevPointKeyInfo.trailer}
824 2 : prevKeyKind := base.TrailerKind(prevPointKeyInfo.trailer)
825 2 : considerWriteToValueBlock := prevKeyKind == InternalKeyKindSet &&
826 2 : keyKind == InternalKeyKindSet
827 2 : if considerWriteToValueBlock && !w.requiredInPlaceValueBound.IsEmpty() {
828 1 : keyPrefix := key.UserKey[:w.lastPointKeyInfo.prefixLen]
829 1 : cmpUpper := w.compare(
830 1 : w.requiredInPlaceValueBound.Upper, keyPrefix)
831 1 : if cmpUpper <= 0 {
832 1 : // Common case for CockroachDB. Make it empty since all future keys in
833 1 : // this sstable will also have cmpUpper <= 0.
834 1 : w.requiredInPlaceValueBound = UserKeyPrefixBound{}
835 1 : } else if w.compare(keyPrefix, w.requiredInPlaceValueBound.Lower) >= 0 {
836 1 : considerWriteToValueBlock = false
837 1 : }
838 : }
839 : // cmpPrefix is initialized iff considerWriteToValueBlock.
840 2 : var cmpPrefix int
841 2 : var cmpUser int
842 2 : if considerWriteToValueBlock {
843 2 : // Compare the prefixes.
844 2 : cmpPrefix = w.compare(prevPointUserKey[:prevPointKeyInfo.prefixLen],
845 2 : key.UserKey[:w.lastPointKeyInfo.prefixLen])
846 2 : cmpUser = cmpPrefix
847 2 : if cmpPrefix == 0 {
848 2 : // Need to compare suffixes to compute cmpUser.
849 2 : cmpUser = w.compare(prevPointUserKey[prevPointKeyInfo.prefixLen:],
850 2 : key.UserKey[w.lastPointKeyInfo.prefixLen:])
851 2 : }
852 2 : } else {
853 2 : cmpUser = w.compare(prevPointUserKey, key.UserKey)
854 2 : }
855 : // Ensure that no one adds a point key kind without considering the obsolete
856 : // handling for that kind.
857 2 : switch keyKind {
858 : case InternalKeyKindSet, InternalKeyKindSetWithDelete, InternalKeyKindMerge,
859 2 : InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
860 0 : default:
861 0 : panic(errors.AssertionFailedf("unexpected key kind %s", keyKind.String()))
862 : }
863 : // If same user key, then the current key is obsolete if any of the
864 : // following is true:
865 : // C1 The prev key was obsolete.
866 : // C2 The prev key was not a MERGE. When the previous key is a MERGE we must
867 : // preserve SET* and MERGE since their values will be merged into the
868 : // previous key. We also must preserve DEL* since there may be an older
869 : // SET*/MERGE in a lower level that must not be merged with the MERGE --
870 : // if we omit the DEL* that lower SET*/MERGE will become visible.
871 : //
872 : // Regardless of whether it is the same user key or not
873 : // C3 The current key is some kind of point delete, and we are writing to
874 : // the lowest level, then it is also obsolete. The correctness of this
875 : // relies on the same user key not spanning multiple sstables in a level.
876 : //
877 : // C1 ensures that for a user key there is at most one transition from
878 : // !obsolete to obsolete. Consider a user key k, for which the first n keys
879 : // are not obsolete. We consider the various value of n:
880 : //
881 : // n = 0: This happens due to forceObsolete being set by the caller, or due
882 : // to C3. forceObsolete must only be set due a RANGEDEL, and that RANGEDEL
883 : // must also delete all the lower seqnums for the same user key. C3 triggers
884 : // due to a point delete and that deletes all the lower seqnums for the same
885 : // user key.
886 : //
887 : // n = 1: This is the common case. It happens when the first key is not a
888 : // MERGE, or the current key is some kind of point delete.
889 : //
890 : // n > 1: This is due to a sequence of MERGE keys, potentially followed by a
891 : // single non-MERGE key.
892 2 : isObsoleteC1AndC2 := cmpUser == 0 &&
893 2 : (prevPointKeyInfo.isObsolete || prevKeyKind != InternalKeyKindMerge)
894 2 : isObsoleteC3 := w.writingToLowestLevel &&
895 2 : (keyKind == InternalKeyKindDelete || keyKind == InternalKeyKindSingleDelete ||
896 2 : keyKind == InternalKeyKindDeleteSized)
897 2 : isObsolete = isObsoleteC1AndC2 || isObsoleteC3
898 2 : // TODO(sumeer): storing isObsolete SET and SETWITHDEL in value blocks is
899 2 : // possible, but requires some care in documenting and checking invariants.
900 2 : // There is code that assumes nothing in value blocks because of single MVCC
901 2 : // version (those should be ok). We have to ensure setHasSamePrefix is
902 2 : // correctly initialized here etc.
903 2 :
904 2 : if !w.disableKeyOrderChecks &&
905 2 : (cmpUser > 0 || (cmpUser == 0 && prevPointKeyInfo.trailer <= key.Trailer)) {
906 1 : return false, false, false, errors.Errorf(
907 1 : "pebble: keys must be added in strictly increasing order: %s, %s",
908 1 : prevPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
909 1 : }
910 2 : if !considerWriteToValueBlock {
911 2 : return false, false, isObsolete, nil
912 2 : }
913 : // NB: it is possible that cmpUser == 0, i.e., these two SETs have identical
914 : // user keys (because of an open snapshot). This should be the rare case.
915 2 : setHasSamePrefix = cmpPrefix == 0
916 2 : considerWriteToValueBlock = setHasSamePrefix
917 2 : // Use of 0 here is somewhat arbitrary. Given the minimum 3 byte encoding of
918 2 : // valueHandle, this should be > 3. But tiny values are common in test and
919 2 : // unlikely in production, so we use 0 here for better test coverage.
920 2 : const tinyValueThreshold = 0
921 2 : if considerWriteToValueBlock && valueLen <= tinyValueThreshold {
922 2 : considerWriteToValueBlock = false
923 2 : }
924 2 : return setHasSamePrefix, considerWriteToValueBlock, isObsolete, nil
925 : }
926 :
927 2 : func (w *Writer) addPoint(key InternalKey, value []byte, forceObsolete bool) error {
928 2 : if w.isStrictObsolete && key.Kind() == InternalKeyKindMerge {
929 1 : return errors.Errorf("MERGE not supported in a strict-obsolete sstable")
930 1 : }
931 2 : var err error
932 2 : var setHasSameKeyPrefix, writeToValueBlock, addPrefixToValueStoredWithKey bool
933 2 : var isObsolete bool
934 2 : maxSharedKeyLen := len(key.UserKey)
935 2 : if w.valueBlockWriter != nil {
936 2 : // maxSharedKeyLen is limited to the prefix of the preceding key. If the
937 2 : // preceding key was in a different block, then the blockWriter will
938 2 : // ignore this maxSharedKeyLen.
939 2 : maxSharedKeyLen = w.lastPointKeyInfo.prefixLen
940 2 : setHasSameKeyPrefix, writeToValueBlock, isObsolete, err =
941 2 : w.makeAddPointDecisionV3(key, len(value))
942 2 : addPrefixToValueStoredWithKey = base.TrailerKind(key.Trailer) == InternalKeyKindSet
943 2 : } else {
944 2 : err = w.makeAddPointDecisionV2(key)
945 2 : }
946 2 : if err != nil {
947 1 : return err
948 1 : }
949 2 : isObsolete = w.tableFormat >= TableFormatPebblev4 && (isObsolete || forceObsolete)
950 2 : w.lastPointKeyInfo.isObsolete = isObsolete
951 2 : var valueStoredWithKey []byte
952 2 : var prefix valuePrefix
953 2 : var valueStoredWithKeyLen int
954 2 : if writeToValueBlock {
955 2 : vh, err := w.valueBlockWriter.addValue(value)
956 2 : if err != nil {
957 0 : return err
958 0 : }
959 2 : n := encodeValueHandle(w.blockBuf.tmp[:], vh)
960 2 : valueStoredWithKey = w.blockBuf.tmp[:n]
961 2 : valueStoredWithKeyLen = len(valueStoredWithKey) + 1
962 2 : var attribute base.ShortAttribute
963 2 : if w.shortAttributeExtractor != nil {
964 1 : // TODO(sumeer): for compactions, it is possible that the input sstable
965 1 : // already has this value in the value section and so we have already
966 1 : // extracted the ShortAttribute. Avoid extracting it again. This will
967 1 : // require changing the Writer.Add interface.
968 1 : if attribute, err = w.shortAttributeExtractor(
969 1 : key.UserKey, w.lastPointKeyInfo.prefixLen, value); err != nil {
970 0 : return err
971 0 : }
972 : }
973 2 : prefix = makePrefixForValueHandle(setHasSameKeyPrefix, attribute)
974 2 : } else {
975 2 : valueStoredWithKey = value
976 2 : valueStoredWithKeyLen = len(value)
977 2 : if addPrefixToValueStoredWithKey {
978 2 : valueStoredWithKeyLen++
979 2 : }
980 2 : prefix = makePrefixForInPlaceValue(setHasSameKeyPrefix)
981 : }
982 :
983 2 : if err := w.maybeFlush(key, valueStoredWithKeyLen); err != nil {
984 1 : return err
985 1 : }
986 :
987 2 : for i := range w.propCollectors {
988 1 : if err := w.propCollectors[i].Add(key, value); err != nil {
989 1 : w.err = err
990 1 : return err
991 1 : }
992 : }
993 2 : for i := range w.blockPropCollectors {
994 2 : v := value
995 2 : if addPrefixToValueStoredWithKey {
996 2 : // Values for SET are not required to be in-place, and in the future may
997 2 : // not even be read by the compaction, so pass nil values. Block
998 2 : // property collectors in such Pebble DB's must not look at the value.
999 2 : v = nil
1000 2 : }
1001 2 : if err := w.blockPropCollectors[i].Add(key, v); err != nil {
1002 1 : w.err = err
1003 1 : return err
1004 1 : }
1005 : }
1006 2 : if w.tableFormat >= TableFormatPebblev4 {
1007 2 : w.obsoleteCollector.AddPoint(isObsolete)
1008 2 : }
1009 :
1010 2 : w.maybeAddToFilter(key.UserKey)
1011 2 : w.dataBlockBuf.dataBlock.addWithOptionalValuePrefix(
1012 2 : key, isObsolete, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix,
1013 2 : setHasSameKeyPrefix)
1014 2 :
1015 2 : w.meta.updateSeqNum(key.SeqNum())
1016 2 :
1017 2 : if !w.meta.HasPointKeys {
1018 2 : k := w.dataBlockBuf.dataBlock.getCurKey()
1019 2 : // NB: We need to ensure that SmallestPoint.UserKey is set, so we create
1020 2 : // an InternalKey which is semantically identical to the key, but won't
1021 2 : // have a nil UserKey. We do this, because key.UserKey could be nil, and
1022 2 : // we don't want SmallestPoint.UserKey to be nil.
1023 2 : //
1024 2 : // todo(bananabrick): Determine if it's okay to have a nil SmallestPoint
1025 2 : // .UserKey now that we don't rely on a nil UserKey to determine if the
1026 2 : // key has been set or not.
1027 2 : w.meta.SetSmallestPointKey(k.Clone())
1028 2 : }
1029 :
1030 2 : w.props.NumEntries++
1031 2 : switch key.Kind() {
1032 2 : case InternalKeyKindDelete, InternalKeyKindSingleDelete:
1033 2 : w.props.NumDeletions++
1034 2 : w.props.RawPointTombstoneKeySize += uint64(len(key.UserKey))
1035 2 : case InternalKeyKindDeleteSized:
1036 2 : var size uint64
1037 2 : if len(value) > 0 {
1038 2 : var n int
1039 2 : size, n = binary.Uvarint(value)
1040 2 : if n <= 0 {
1041 0 : w.err = errors.Newf("%s key's value (%x) does not parse as uvarint",
1042 0 : errors.Safe(key.Kind().String()), value)
1043 0 : return w.err
1044 0 : }
1045 : }
1046 2 : w.props.NumDeletions++
1047 2 : w.props.NumSizedDeletions++
1048 2 : w.props.RawPointTombstoneKeySize += uint64(len(key.UserKey))
1049 2 : w.props.RawPointTombstoneValueSize += size
1050 2 : case InternalKeyKindMerge:
1051 2 : w.props.NumMergeOperands++
1052 : }
1053 2 : w.props.RawKeySize += uint64(key.Size())
1054 2 : w.props.RawValueSize += uint64(len(value))
1055 2 : return nil
1056 : }
1057 :
1058 1 : func (w *Writer) prettyTombstone(k InternalKey, value []byte) fmt.Formatter {
1059 1 : return keyspan.Span{
1060 1 : Start: k.UserKey,
1061 1 : End: value,
1062 1 : Keys: []keyspan.Key{{Trailer: k.Trailer}},
1063 1 : }.Pretty(w.formatKey)
1064 1 : }
1065 :
1066 2 : func (w *Writer) addTombstone(key InternalKey, value []byte) error {
1067 2 : if !w.disableKeyOrderChecks && !w.rangeDelV1Format && w.rangeDelBlock.nEntries > 0 {
1068 2 : // Check that tombstones are being added in fragmented order. If the two
1069 2 : // tombstones overlap, their start and end keys must be identical.
1070 2 : prevKey := w.rangeDelBlock.getCurKey()
1071 2 : switch c := w.compare(prevKey.UserKey, key.UserKey); {
1072 0 : case c > 0:
1073 0 : w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
1074 0 : prevKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
1075 0 : return w.err
1076 2 : case c == 0:
1077 2 : prevValue := w.rangeDelBlock.curValue
1078 2 : if w.compare(prevValue, value) != 0 {
1079 1 : w.err = errors.Errorf("pebble: overlapping tombstones must be fragmented: %s vs %s",
1080 1 : w.prettyTombstone(prevKey, prevValue),
1081 1 : w.prettyTombstone(key, value))
1082 1 : return w.err
1083 1 : }
1084 2 : if prevKey.SeqNum() <= key.SeqNum() {
1085 1 : w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
1086 1 : prevKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
1087 1 : return w.err
1088 1 : }
1089 2 : default:
1090 2 : prevValue := w.rangeDelBlock.curValue
1091 2 : if w.compare(prevValue, key.UserKey) > 0 {
1092 1 : w.err = errors.Errorf("pebble: overlapping tombstones must be fragmented: %s vs %s",
1093 1 : w.prettyTombstone(prevKey, prevValue),
1094 1 : w.prettyTombstone(key, value))
1095 1 : return w.err
1096 1 : }
1097 : }
1098 : }
1099 :
1100 2 : if key.Trailer == InternalKeyRangeDeleteSentinel {
1101 0 : w.err = errors.Errorf("pebble: cannot add range delete sentinel: %s", key.Pretty(w.formatKey))
1102 0 : return w.err
1103 0 : }
1104 :
1105 2 : for i := range w.propCollectors {
1106 1 : if err := w.propCollectors[i].Add(key, value); err != nil {
1107 1 : w.err = err
1108 1 : return err
1109 1 : }
1110 : }
1111 :
1112 2 : w.meta.updateSeqNum(key.SeqNum())
1113 2 :
1114 2 : switch {
1115 1 : case w.rangeDelV1Format:
1116 1 : // Range tombstones are not fragmented in the v1 (i.e. RocksDB) range
1117 1 : // deletion block format, so we need to track the largest range tombstone
1118 1 : // end key as every range tombstone is added.
1119 1 : //
1120 1 : // Note that writing the v1 format is only supported for tests.
1121 1 : if w.props.NumRangeDeletions == 0 {
1122 1 : w.meta.SetSmallestRangeDelKey(key.Clone())
1123 1 : w.meta.SetLargestRangeDelKey(base.MakeRangeDeleteSentinelKey(value).Clone())
1124 1 : } else {
1125 1 : if base.InternalCompare(w.compare, w.meta.SmallestRangeDel, key) > 0 {
1126 1 : w.meta.SetSmallestRangeDelKey(key.Clone())
1127 1 : }
1128 1 : end := base.MakeRangeDeleteSentinelKey(value)
1129 1 : if base.InternalCompare(w.compare, w.meta.LargestRangeDel, end) < 0 {
1130 1 : w.meta.SetLargestRangeDelKey(end.Clone())
1131 1 : }
1132 : }
1133 :
1134 2 : default:
1135 2 : // Range tombstones are fragmented in the v2 range deletion block format,
1136 2 : // so the start key of the first range tombstone added will be the smallest
1137 2 : // range tombstone key. The largest range tombstone key will be determined
1138 2 : // in Writer.Close() as the end key of the last range tombstone added.
1139 2 : if w.props.NumRangeDeletions == 0 {
1140 2 : w.meta.SetSmallestRangeDelKey(key.Clone())
1141 2 : }
1142 : }
1143 :
1144 2 : w.props.NumEntries++
1145 2 : w.props.NumDeletions++
1146 2 : w.props.NumRangeDeletions++
1147 2 : w.props.RawKeySize += uint64(key.Size())
1148 2 : w.props.RawValueSize += uint64(len(value))
1149 2 : w.rangeDelBlock.add(key, value)
1150 2 : return nil
1151 : }
1152 :
1153 : // RangeKeySet sets a range between start (inclusive) and end (exclusive) with
1154 : // the given suffix to the given value. The resulting range key is given the
1155 : // sequence number zero, with the expectation that the resulting sstable will be
1156 : // ingested.
1157 : //
1158 : // Keys must be added to the table in increasing order of start key. Spans are
1159 : // not required to be fragmented. The same suffix may not be set or unset twice
1160 : // over the same keyspan, because it would result in inconsistent state. Both
1161 : // the Set and Unset would share the zero sequence number, and a key cannot be
1162 : // both simultaneously set and unset.
1163 1 : func (w *Writer) RangeKeySet(start, end, suffix, value []byte) error {
1164 1 : return w.addRangeKeySpan(keyspan.Span{
1165 1 : Start: w.tempRangeKeyCopy(start),
1166 1 : End: w.tempRangeKeyCopy(end),
1167 1 : Keys: []keyspan.Key{
1168 1 : {
1169 1 : Trailer: base.MakeTrailer(0, base.InternalKeyKindRangeKeySet),
1170 1 : Suffix: w.tempRangeKeyCopy(suffix),
1171 1 : Value: w.tempRangeKeyCopy(value),
1172 1 : },
1173 1 : },
1174 1 : })
1175 1 : }
1176 :
1177 : // RangeKeyUnset un-sets a range between start (inclusive) and end (exclusive)
1178 : // with the given suffix. The resulting range key is given the
1179 : // sequence number zero, with the expectation that the resulting sstable will be
1180 : // ingested.
1181 : //
1182 : // Keys must be added to the table in increasing order of start key. Spans are
1183 : // not required to be fragmented. The same suffix may not be set or unset twice
1184 : // over the same keyspan, because it would result in inconsistent state. Both
1185 : // the Set and Unset would share the zero sequence number, and a key cannot be
1186 : // both simultaneously set and unset.
1187 1 : func (w *Writer) RangeKeyUnset(start, end, suffix []byte) error {
1188 1 : return w.addRangeKeySpan(keyspan.Span{
1189 1 : Start: w.tempRangeKeyCopy(start),
1190 1 : End: w.tempRangeKeyCopy(end),
1191 1 : Keys: []keyspan.Key{
1192 1 : {
1193 1 : Trailer: base.MakeTrailer(0, base.InternalKeyKindRangeKeyUnset),
1194 1 : Suffix: w.tempRangeKeyCopy(suffix),
1195 1 : },
1196 1 : },
1197 1 : })
1198 1 : }
1199 :
1200 : // RangeKeyDelete deletes a range between start (inclusive) and end (exclusive).
1201 : //
1202 : // Keys must be added to the table in increasing order of start key. Spans are
1203 : // not required to be fragmented.
1204 1 : func (w *Writer) RangeKeyDelete(start, end []byte) error {
1205 1 : return w.addRangeKeySpan(keyspan.Span{
1206 1 : Start: w.tempRangeKeyCopy(start),
1207 1 : End: w.tempRangeKeyCopy(end),
1208 1 : Keys: []keyspan.Key{
1209 1 : {Trailer: base.MakeTrailer(0, base.InternalKeyKindRangeKeyDelete)},
1210 1 : },
1211 1 : })
1212 1 : }
1213 :
1214 : // AddRangeKey adds a range key set, unset, or delete key/value pair to the
1215 : // table being written.
1216 : //
1217 : // Range keys must be supplied in strictly ascending order of start key (i.e.
1218 : // user key ascending, sequence number descending, and key type descending).
1219 : // Ranges added must also be supplied in fragmented span order - i.e. other than
1220 : // spans that are perfectly aligned (same start and end keys), spans may not
1221 : // overlap. Range keys may be added out of order relative to point keys and
1222 : // range deletions.
1223 2 : func (w *Writer) AddRangeKey(key InternalKey, value []byte) error {
1224 2 : if w.err != nil {
1225 0 : return w.err
1226 0 : }
1227 2 : return w.addRangeKey(key, value)
1228 : }
1229 :
1230 1 : func (w *Writer) addRangeKeySpan(span keyspan.Span) error {
1231 1 : if w.compare(span.Start, span.End) >= 0 {
1232 0 : return errors.Errorf(
1233 0 : "pebble: start key must be strictly less than end key",
1234 0 : )
1235 0 : }
1236 1 : if w.fragmenter.Start() != nil && w.compare(w.fragmenter.Start(), span.Start) > 0 {
1237 1 : return errors.Errorf("pebble: spans must be added in order: %s > %s",
1238 1 : w.formatKey(w.fragmenter.Start()), w.formatKey(span.Start))
1239 1 : }
1240 : // Add this span to the fragmenter.
1241 1 : w.fragmenter.Add(span)
1242 1 : return w.err
1243 : }
1244 :
1245 1 : func (w *Writer) encodeRangeKeySpan(span keyspan.Span) {
1246 1 : // This method is the emit function of the Fragmenter.
1247 1 : //
1248 1 : // NB: The span should only contain range keys and be internally consistent
1249 1 : // (eg, no duplicate suffixes, no additional keys after a RANGEKEYDEL).
1250 1 : //
1251 1 : // We use w.rangeKeysBySuffix and w.rangeKeySpan to avoid allocations.
1252 1 :
1253 1 : // Sort the keys by suffix. Iteration doesn't *currently* depend on it, but
1254 1 : // we may want to in the future.
1255 1 : w.rangeKeysBySuffix.Cmp = w.compare
1256 1 : w.rangeKeysBySuffix.Keys = span.Keys
1257 1 : sort.Sort(&w.rangeKeysBySuffix)
1258 1 :
1259 1 : w.rangeKeySpan = span
1260 1 : w.rangeKeySpan.Keys = w.rangeKeysBySuffix.Keys
1261 1 : w.err = firstError(w.err, w.rangeKeyEncoder.Encode(&w.rangeKeySpan))
1262 1 : }
1263 :
1264 2 : func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
1265 2 : if !w.disableKeyOrderChecks && w.rangeKeyBlock.nEntries > 0 {
1266 2 : prevStartKey := w.rangeKeyBlock.getCurKey()
1267 2 : prevEndKey, _, ok := rangekey.DecodeEndKey(prevStartKey.Kind(), w.rangeKeyBlock.curValue)
1268 2 : if !ok {
1269 0 : // We panic here as we should have previously decoded and validated this
1270 0 : // key and value when it was first added to the range key block.
1271 0 : panic(errors.Errorf("pebble: invalid end key for span: %s",
1272 0 : prevStartKey.Pretty(w.formatKey)))
1273 : }
1274 :
1275 2 : curStartKey := key
1276 2 : curEndKey, _, ok := rangekey.DecodeEndKey(curStartKey.Kind(), value)
1277 2 : if !ok {
1278 0 : w.err = errors.Errorf("pebble: invalid end key for span: %s",
1279 0 : curStartKey.Pretty(w.formatKey))
1280 0 : return w.err
1281 0 : }
1282 :
1283 : // Start keys must be strictly increasing.
1284 2 : if base.InternalCompare(w.compare, prevStartKey, curStartKey) >= 0 {
1285 1 : w.err = errors.Errorf(
1286 1 : "pebble: range keys starts must be added in increasing order: %s, %s",
1287 1 : prevStartKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
1288 1 : return w.err
1289 1 : }
1290 :
1291 : // Start keys are increasing. If the start user keys are equal, the
1292 : // end keys must be equal (i.e. aligned spans).
1293 2 : if w.compare(prevStartKey.UserKey, curStartKey.UserKey) == 0 {
1294 2 : if w.compare(prevEndKey, curEndKey) != 0 {
1295 1 : w.err = errors.Errorf("pebble: overlapping range keys must be fragmented: %s, %s",
1296 1 : prevStartKey.Pretty(w.formatKey),
1297 1 : curStartKey.Pretty(w.formatKey))
1298 1 : return w.err
1299 1 : }
1300 2 : } else if w.compare(prevEndKey, curStartKey.UserKey) > 0 {
1301 1 : // If the start user keys are NOT equal, the spans must be disjoint (i.e.
1302 1 : // no overlap).
1303 1 : // NOTE: the inequality excludes zero, as we allow the end key of the
1304 1 : // lower span be the same as the start key of the upper span, because
1305 1 : // the range end key is considered an exclusive bound.
1306 1 : w.err = errors.Errorf("pebble: overlapping range keys must be fragmented: %s, %s",
1307 1 : prevStartKey.Pretty(w.formatKey),
1308 1 : curStartKey.Pretty(w.formatKey))
1309 1 : return w.err
1310 1 : }
1311 : }
1312 :
1313 : // TODO(travers): Add an invariant-gated check to ensure that suffix-values
1314 : // are sorted within coalesced spans.
1315 :
1316 : // Range-keys and point-keys are intended to live in "parallel" keyspaces.
1317 : // However, we track a single seqnum in the table metadata that spans both of
1318 : // these keyspaces.
1319 : // TODO(travers): Consider tracking range key seqnums separately.
1320 2 : w.meta.updateSeqNum(key.SeqNum())
1321 2 :
1322 2 : // Range tombstones are fragmented, so the start key of the first range key
1323 2 : // added will be the smallest. The largest range key is determined in
1324 2 : // Writer.Close() as the end key of the last range key added to the block.
1325 2 : if w.props.NumRangeKeys() == 0 {
1326 2 : w.meta.SetSmallestRangeKey(key.Clone())
1327 2 : }
1328 :
1329 : // Update block properties.
1330 2 : w.props.RawRangeKeyKeySize += uint64(key.Size())
1331 2 : w.props.RawRangeKeyValueSize += uint64(len(value))
1332 2 : switch key.Kind() {
1333 2 : case base.InternalKeyKindRangeKeyDelete:
1334 2 : w.props.NumRangeKeyDels++
1335 2 : case base.InternalKeyKindRangeKeySet:
1336 2 : w.props.NumRangeKeySets++
1337 2 : case base.InternalKeyKindRangeKeyUnset:
1338 2 : w.props.NumRangeKeyUnsets++
1339 0 : default:
1340 0 : panic(errors.Errorf("pebble: invalid range key type: %s", key.Kind()))
1341 : }
1342 :
1343 2 : for i := range w.blockPropCollectors {
1344 2 : if err := w.blockPropCollectors[i].Add(key, value); err != nil {
1345 0 : return err
1346 0 : }
1347 : }
1348 :
1349 : // Add the key to the block.
1350 2 : w.rangeKeyBlock.add(key, value)
1351 2 : return nil
1352 : }
1353 :
1354 : // tempRangeKeyBuf returns a slice of length n from the Writer's rkBuf byte
1355 : // slice. Any byte written to the returned slice is retained for the lifetime of
1356 : // the Writer.
1357 1 : func (w *Writer) tempRangeKeyBuf(n int) []byte {
1358 1 : if cap(w.rkBuf)-len(w.rkBuf) < n {
1359 1 : size := len(w.rkBuf) + 2*n
1360 1 : if size < 2*cap(w.rkBuf) {
1361 1 : size = 2 * cap(w.rkBuf)
1362 1 : }
1363 1 : buf := make([]byte, len(w.rkBuf), size)
1364 1 : copy(buf, w.rkBuf)
1365 1 : w.rkBuf = buf
1366 : }
1367 1 : b := w.rkBuf[len(w.rkBuf) : len(w.rkBuf)+n]
1368 1 : w.rkBuf = w.rkBuf[:len(w.rkBuf)+n]
1369 1 : return b
1370 : }
1371 :
1372 : // tempRangeKeyCopy returns a copy of the provided slice, stored in the Writer's
1373 : // range key buffer.
1374 1 : func (w *Writer) tempRangeKeyCopy(k []byte) []byte {
1375 1 : if len(k) == 0 {
1376 1 : return nil
1377 1 : }
1378 1 : buf := w.tempRangeKeyBuf(len(k))
1379 1 : copy(buf, k)
1380 1 : return buf
1381 : }
1382 :
1383 2 : func (w *Writer) maybeAddToFilter(key []byte) {
1384 2 : if w.filter != nil {
1385 2 : if w.split != nil {
1386 2 : prefix := key[:w.split(key)]
1387 2 : w.filter.addKey(prefix)
1388 2 : } else {
1389 1 : w.filter.addKey(key)
1390 1 : }
1391 : }
1392 : }
1393 :
1394 2 : func (w *Writer) flush(key InternalKey) error {
1395 2 : // We're finishing a data block.
1396 2 : err := w.finishDataBlockProps(w.dataBlockBuf)
1397 2 : if err != nil {
1398 1 : return err
1399 1 : }
1400 2 : w.dataBlockBuf.finish()
1401 2 : w.dataBlockBuf.compressAndChecksum(w.compression)
1402 2 : // Since dataBlockEstimates.addInflightDataBlock was never called, the
1403 2 : // inflightSize is set to 0.
1404 2 : w.coordination.sizeEstimate.dataBlockCompressed(len(w.dataBlockBuf.compressed), 0)
1405 2 :
1406 2 : // Determine if the index block should be flushed. Since we're accessing the
1407 2 : // dataBlockBuf.dataBlock.curKey here, we have to make sure that once we start
1408 2 : // to pool the dataBlockBufs, the curKey isn't used by the Writer once the
1409 2 : // dataBlockBuf is added back to a sync.Pool. In this particular case, the
1410 2 : // byte slice which supports "sep" will eventually be copied when "sep" is
1411 2 : // added to the index block.
1412 2 : prevKey := w.dataBlockBuf.dataBlock.getCurKey()
1413 2 : sep := w.indexEntrySep(prevKey, key, w.dataBlockBuf)
1414 2 : // We determine that we should flush an index block from the Writer client
1415 2 : // goroutine, but we actually finish the index block from the writeQueue.
1416 2 : // When we determine that an index block should be flushed, we need to call
1417 2 : // BlockPropertyCollector.FinishIndexBlock. But block property collector
1418 2 : // calls must happen sequentially from the Writer client. Therefore, we need
1419 2 : // to determine that we are going to flush the index block from the Writer
1420 2 : // client.
1421 2 : shouldFlushIndexBlock := supportsTwoLevelIndex(w.tableFormat) && w.indexBlock.shouldFlush(
1422 2 : sep, encodedBHPEstimatedSize, w.indexBlockSize, w.indexBlockSizeThreshold,
1423 2 : )
1424 2 :
1425 2 : var indexProps []byte
1426 2 : var flushableIndexBlock *indexBlockBuf
1427 2 : if shouldFlushIndexBlock {
1428 2 : flushableIndexBlock = w.indexBlock
1429 2 : w.indexBlock = newIndexBlockBuf(w.coordination.parallelismEnabled)
1430 2 : // Call BlockPropertyCollector.FinishIndexBlock, since we've decided to
1431 2 : // flush the index block.
1432 2 : indexProps, err = w.finishIndexBlockProps()
1433 2 : if err != nil {
1434 1 : return err
1435 1 : }
1436 : }
1437 :
1438 : // We've called BlockPropertyCollector.FinishDataBlock, and, if necessary,
1439 : // BlockPropertyCollector.FinishIndexBlock. Since we've decided to finish
1440 : // the data block, we can call
1441 : // BlockPropertyCollector.AddPrevDataBlockToIndexBlock.
1442 2 : w.addPrevDataBlockToIndexBlockProps()
1443 2 :
1444 2 : // Schedule a write.
1445 2 : writeTask := writeTaskPool.Get().(*writeTask)
1446 2 : // We're setting compressionDone to indicate that compression of this block
1447 2 : // has already been completed.
1448 2 : writeTask.compressionDone <- true
1449 2 : writeTask.buf = w.dataBlockBuf
1450 2 : writeTask.indexEntrySep = sep
1451 2 : writeTask.currIndexBlock = w.indexBlock
1452 2 : writeTask.indexInflightSize = sep.Size() + encodedBHPEstimatedSize
1453 2 : writeTask.finishedIndexProps = indexProps
1454 2 : writeTask.flushableIndexBlock = flushableIndexBlock
1455 2 :
1456 2 : // The writeTask corresponds to an unwritten index entry.
1457 2 : w.indexBlock.addInflight(writeTask.indexInflightSize)
1458 2 :
1459 2 : w.dataBlockBuf = nil
1460 2 : if w.coordination.parallelismEnabled {
1461 2 : w.coordination.writeQueue.add(writeTask)
1462 2 : } else {
1463 2 : err = w.coordination.writeQueue.addSync(writeTask)
1464 2 : }
1465 2 : w.dataBlockBuf = newDataBlockBuf(w.restartInterval, w.checksumType)
1466 2 :
1467 2 : return err
1468 : }
1469 :
1470 2 : func (w *Writer) maybeFlush(key InternalKey, valueLen int) error {
1471 2 : if !w.dataBlockBuf.shouldFlush(key, valueLen, w.blockSize, w.blockSizeThreshold) {
1472 2 : return nil
1473 2 : }
1474 :
1475 2 : err := w.flush(key)
1476 2 :
1477 2 : if err != nil {
1478 1 : w.err = err
1479 1 : return err
1480 1 : }
1481 :
1482 2 : return nil
1483 : }
1484 :
1485 : // dataBlockBuf.dataBlockProps set by this method must be encoded before any future use of the
1486 : // dataBlockBuf.blockPropsEncoder, since the properties slice will get reused by the
1487 : // blockPropsEncoder.
1488 2 : func (w *Writer) finishDataBlockProps(buf *dataBlockBuf) error {
1489 2 : if len(w.blockPropCollectors) == 0 {
1490 2 : return nil
1491 2 : }
1492 2 : var err error
1493 2 : buf.blockPropsEncoder.resetProps()
1494 2 : for i := range w.blockPropCollectors {
1495 2 : scratch := buf.blockPropsEncoder.getScratchForProp()
1496 2 : if scratch, err = w.blockPropCollectors[i].FinishDataBlock(scratch); err != nil {
1497 1 : return err
1498 1 : }
1499 2 : if len(scratch) > 0 {
1500 2 : buf.blockPropsEncoder.addProp(shortID(i), scratch)
1501 2 : }
1502 : }
1503 :
1504 2 : buf.dataBlockProps = buf.blockPropsEncoder.unsafeProps()
1505 2 : return nil
1506 : }
1507 :
1508 : // The BlockHandleWithProperties returned by this method must be encoded before any future use of
1509 : // the Writer.blockPropsEncoder, since the properties slice will get reused by the blockPropsEncoder.
1510 : // maybeAddBlockPropertiesToBlockHandle should only be called if block is being written synchronously
1511 : // with the Writer client.
1512 : func (w *Writer) maybeAddBlockPropertiesToBlockHandle(
1513 : bh BlockHandle,
1514 2 : ) (BlockHandleWithProperties, error) {
1515 2 : err := w.finishDataBlockProps(w.dataBlockBuf)
1516 2 : if err != nil {
1517 0 : return BlockHandleWithProperties{}, err
1518 0 : }
1519 2 : return BlockHandleWithProperties{BlockHandle: bh, Props: w.dataBlockBuf.dataBlockProps}, nil
1520 : }
1521 :
1522 2 : func (w *Writer) indexEntrySep(prevKey, key InternalKey, dataBlockBuf *dataBlockBuf) InternalKey {
1523 2 : // Make a rough guess that we want key-sized scratch to compute the separator.
1524 2 : if cap(dataBlockBuf.sepScratch) < key.Size() {
1525 2 : dataBlockBuf.sepScratch = make([]byte, 0, key.Size()*2)
1526 2 : }
1527 :
1528 2 : var sep InternalKey
1529 2 : if key.UserKey == nil && key.Trailer == 0 {
1530 2 : sep = prevKey.Successor(w.compare, w.successor, dataBlockBuf.sepScratch[:0])
1531 2 : } else {
1532 2 : sep = prevKey.Separator(w.compare, w.separator, dataBlockBuf.sepScratch[:0], key)
1533 2 : }
1534 2 : return sep
1535 : }
1536 :
1537 : // addIndexEntry adds an index entry for the specified key and block handle.
1538 : // addIndexEntry can be called from both the Writer client goroutine, and the
1539 : // writeQueue goroutine. If the flushIndexBuf != nil, then the indexProps, as
1540 : // they're used when the index block is finished.
1541 : //
1542 : // Invariant:
1543 : // 1. addIndexEntry must not store references to the sep InternalKey, the tmp
1544 : // byte slice, bhp.Props. That is, these must be either deep copied or
1545 : // encoded.
1546 : // 2. addIndexEntry must not hold references to the flushIndexBuf, and the writeTo
1547 : // indexBlockBufs.
1548 : func (w *Writer) addIndexEntry(
1549 : sep InternalKey,
1550 : bhp BlockHandleWithProperties,
1551 : tmp []byte,
1552 : flushIndexBuf *indexBlockBuf,
1553 : writeTo *indexBlockBuf,
1554 : inflightSize int,
1555 : indexProps []byte,
1556 2 : ) error {
1557 2 : if bhp.Length == 0 {
1558 0 : // A valid blockHandle must be non-zero.
1559 0 : // In particular, it must have a non-zero length.
1560 0 : return nil
1561 0 : }
1562 :
1563 2 : encoded := encodeBlockHandleWithProperties(tmp, bhp)
1564 2 :
1565 2 : if flushIndexBuf != nil {
1566 2 : if cap(w.indexPartitions) == 0 {
1567 2 : w.indexPartitions = make([]indexBlockAndBlockProperties, 0, 32)
1568 2 : }
1569 : // Enable two level indexes if there is more than one index block.
1570 2 : w.twoLevelIndex = true
1571 2 : if err := w.finishIndexBlock(flushIndexBuf, indexProps); err != nil {
1572 0 : return err
1573 0 : }
1574 : }
1575 :
1576 2 : writeTo.add(sep, encoded, inflightSize)
1577 2 : return nil
1578 : }
1579 :
1580 2 : func (w *Writer) addPrevDataBlockToIndexBlockProps() {
1581 2 : for i := range w.blockPropCollectors {
1582 2 : w.blockPropCollectors[i].AddPrevDataBlockToIndexBlock()
1583 2 : }
1584 : }
1585 :
1586 : // addIndexEntrySync adds an index entry for the specified key and block handle.
1587 : // Writer.addIndexEntry is only called synchronously once Writer.Close is called.
1588 : // addIndexEntrySync should only be called if we're sure that index entries
1589 : // aren't being written asynchronously.
1590 : //
1591 : // Invariant:
1592 : // 1. addIndexEntrySync must not store references to the prevKey, key InternalKey's,
1593 : // the tmp byte slice. That is, these must be either deep copied or encoded.
1594 : func (w *Writer) addIndexEntrySync(
1595 : prevKey, key InternalKey, bhp BlockHandleWithProperties, tmp []byte,
1596 2 : ) error {
1597 2 : sep := w.indexEntrySep(prevKey, key, w.dataBlockBuf)
1598 2 : shouldFlush := supportsTwoLevelIndex(
1599 2 : w.tableFormat) && w.indexBlock.shouldFlush(
1600 2 : sep, encodedBHPEstimatedSize, w.indexBlockSize, w.indexBlockSizeThreshold,
1601 2 : )
1602 2 : var flushableIndexBlock *indexBlockBuf
1603 2 : var props []byte
1604 2 : var err error
1605 2 : if shouldFlush {
1606 2 : flushableIndexBlock = w.indexBlock
1607 2 : w.indexBlock = newIndexBlockBuf(w.coordination.parallelismEnabled)
1608 2 :
1609 2 : // Call BlockPropertyCollector.FinishIndexBlock, since we've decided to
1610 2 : // flush the index block.
1611 2 : props, err = w.finishIndexBlockProps()
1612 2 : if err != nil {
1613 0 : return err
1614 0 : }
1615 : }
1616 :
1617 2 : err = w.addIndexEntry(sep, bhp, tmp, flushableIndexBlock, w.indexBlock, 0, props)
1618 2 : if flushableIndexBlock != nil {
1619 2 : flushableIndexBlock.clear()
1620 2 : indexBlockBufPool.Put(flushableIndexBlock)
1621 2 : }
1622 2 : w.addPrevDataBlockToIndexBlockProps()
1623 2 : return err
1624 : }
1625 :
1626 : func shouldFlush(
1627 : key InternalKey,
1628 : valueLen int,
1629 : restartInterval, estimatedBlockSize, numEntries, targetBlockSize, sizeThreshold int,
1630 2 : ) bool {
1631 2 : if numEntries == 0 {
1632 2 : return false
1633 2 : }
1634 :
1635 2 : if estimatedBlockSize >= targetBlockSize {
1636 2 : return true
1637 2 : }
1638 :
1639 : // The block is currently smaller than the target size.
1640 2 : if estimatedBlockSize <= sizeThreshold {
1641 2 : // The block is smaller than the threshold size at which we'll consider
1642 2 : // flushing it.
1643 2 : return false
1644 2 : }
1645 :
1646 2 : newSize := estimatedBlockSize + key.Size() + valueLen
1647 2 : if numEntries%restartInterval == 0 {
1648 2 : newSize += 4
1649 2 : }
1650 2 : newSize += 4 // varint for shared prefix length
1651 2 : newSize += uvarintLen(uint32(key.Size())) // varint for unshared key bytes
1652 2 : newSize += uvarintLen(uint32(valueLen)) // varint for value size
1653 2 : // Flush if the block plus the new entry is larger than the target size.
1654 2 : return newSize > targetBlockSize
1655 : }
1656 :
1657 2 : func cloneKeyWithBuf(k InternalKey, a bytealloc.A) (bytealloc.A, InternalKey) {
1658 2 : if len(k.UserKey) == 0 {
1659 0 : return a, k
1660 0 : }
1661 2 : a, keyCopy := a.Copy(k.UserKey)
1662 2 : return a, InternalKey{UserKey: keyCopy, Trailer: k.Trailer}
1663 : }
1664 :
1665 : // Invariants: The byte slice returned by finishIndexBlockProps is heap-allocated
1666 : //
1667 : // and has its own lifetime, independent of the Writer and the blockPropsEncoder,
1668 : //
1669 : // and it is safe to:
1670 : // 1. Reuse w.blockPropsEncoder without first encoding the byte slice returned.
1671 : // 2. Store the byte slice in the Writer since it is a copy and not supported by
1672 : // an underlying buffer.
1673 2 : func (w *Writer) finishIndexBlockProps() ([]byte, error) {
1674 2 : w.blockPropsEncoder.resetProps()
1675 2 : for i := range w.blockPropCollectors {
1676 2 : scratch := w.blockPropsEncoder.getScratchForProp()
1677 2 : var err error
1678 2 : if scratch, err = w.blockPropCollectors[i].FinishIndexBlock(scratch); err != nil {
1679 1 : return nil, err
1680 1 : }
1681 2 : if len(scratch) > 0 {
1682 2 : w.blockPropsEncoder.addProp(shortID(i), scratch)
1683 2 : }
1684 : }
1685 2 : return w.blockPropsEncoder.props(), nil
1686 : }
1687 :
1688 : // finishIndexBlock finishes the current index block and adds it to the top
1689 : // level index block. This is only used when two level indexes are enabled.
1690 : //
1691 : // Invariants:
1692 : // 1. The props slice passed into finishedIndexBlock must not be a
1693 : // owned by any other struct, since it will be stored in the Writer.indexPartitions
1694 : // slice.
1695 : // 2. None of the buffers owned by indexBuf will be shallow copied and stored elsewhere.
1696 : // That is, it must be safe to reuse indexBuf after finishIndexBlock has been called.
1697 2 : func (w *Writer) finishIndexBlock(indexBuf *indexBlockBuf, props []byte) error {
1698 2 : part := indexBlockAndBlockProperties{
1699 2 : nEntries: indexBuf.block.nEntries, properties: props,
1700 2 : }
1701 2 : w.indexSepAlloc, part.sep = cloneKeyWithBuf(
1702 2 : indexBuf.block.getCurKey(), w.indexSepAlloc,
1703 2 : )
1704 2 : bk := indexBuf.finish()
1705 2 : if len(w.indexBlockAlloc) < len(bk) {
1706 2 : // Allocate enough bytes for approximately 16 index blocks.
1707 2 : w.indexBlockAlloc = make([]byte, len(bk)*16)
1708 2 : }
1709 2 : n := copy(w.indexBlockAlloc, bk)
1710 2 : part.block = w.indexBlockAlloc[:n:n]
1711 2 : w.indexBlockAlloc = w.indexBlockAlloc[n:]
1712 2 : w.indexPartitions = append(w.indexPartitions, part)
1713 2 : return nil
1714 : }
1715 :
1716 2 : func (w *Writer) writeTwoLevelIndex() (BlockHandle, error) {
1717 2 : props, err := w.finishIndexBlockProps()
1718 2 : if err != nil {
1719 0 : return BlockHandle{}, err
1720 0 : }
1721 : // Add the final unfinished index.
1722 2 : if err = w.finishIndexBlock(w.indexBlock, props); err != nil {
1723 0 : return BlockHandle{}, err
1724 0 : }
1725 :
1726 2 : for i := range w.indexPartitions {
1727 2 : b := &w.indexPartitions[i]
1728 2 : w.props.NumDataBlocks += uint64(b.nEntries)
1729 2 :
1730 2 : data := b.block
1731 2 : w.props.IndexSize += uint64(len(data))
1732 2 : bh, err := w.writeBlock(data, w.compression, &w.blockBuf)
1733 2 : if err != nil {
1734 0 : return BlockHandle{}, err
1735 0 : }
1736 2 : bhp := BlockHandleWithProperties{
1737 2 : BlockHandle: bh,
1738 2 : Props: b.properties,
1739 2 : }
1740 2 : encoded := encodeBlockHandleWithProperties(w.blockBuf.tmp[:], bhp)
1741 2 : w.topLevelIndexBlock.add(b.sep, encoded)
1742 : }
1743 :
1744 : // NB: RocksDB includes the block trailer length in the index size
1745 : // property, though it doesn't include the trailer in the top level
1746 : // index size property.
1747 2 : w.props.IndexPartitions = uint64(len(w.indexPartitions))
1748 2 : w.props.TopLevelIndexSize = uint64(w.topLevelIndexBlock.estimatedSize())
1749 2 : w.props.IndexSize += w.props.TopLevelIndexSize + blockTrailerLen
1750 2 :
1751 2 : return w.writeBlock(w.topLevelIndexBlock.finish(), w.compression, &w.blockBuf)
1752 : }
1753 :
1754 2 : func compressAndChecksum(b []byte, compression Compression, blockBuf *blockBuf) []byte {
1755 2 : // Compress the buffer, discarding the result if the improvement isn't at
1756 2 : // least 12.5%.
1757 2 : blockType, compressed := compressBlock(compression, b, blockBuf.compressedBuf)
1758 2 : if blockType != noCompressionBlockType && cap(compressed) > cap(blockBuf.compressedBuf) {
1759 2 : blockBuf.compressedBuf = compressed[:cap(compressed)]
1760 2 : }
1761 2 : if len(compressed) < len(b)-len(b)/8 {
1762 2 : b = compressed
1763 2 : } else {
1764 2 : blockType = noCompressionBlockType
1765 2 : }
1766 :
1767 2 : blockBuf.tmp[0] = byte(blockType)
1768 2 :
1769 2 : // Calculate the checksum.
1770 2 : checksum := blockBuf.checksummer.checksum(b, blockBuf.tmp[:1])
1771 2 : binary.LittleEndian.PutUint32(blockBuf.tmp[1:5], checksum)
1772 2 : return b
1773 : }
1774 :
1775 2 : func (w *Writer) writeCompressedBlock(block []byte, blockTrailerBuf []byte) (BlockHandle, error) {
1776 2 : bh := BlockHandle{Offset: w.meta.Size, Length: uint64(len(block))}
1777 2 :
1778 2 : if w.cacheID != 0 && w.fileNum.FileNum() != 0 {
1779 2 : // Remove the block being written from the cache. This provides defense in
1780 2 : // depth against bugs which cause cache collisions.
1781 2 : //
1782 2 : // TODO(peter): Alternatively, we could add the uncompressed value to the
1783 2 : // cache.
1784 2 : w.cache.Delete(w.cacheID, w.fileNum, bh.Offset)
1785 2 : }
1786 :
1787 : // Write the bytes to the file.
1788 2 : if err := w.writable.Write(block); err != nil {
1789 0 : return BlockHandle{}, err
1790 0 : }
1791 2 : w.meta.Size += uint64(len(block))
1792 2 : if err := w.writable.Write(blockTrailerBuf[:blockTrailerLen]); err != nil {
1793 0 : return BlockHandle{}, err
1794 0 : }
1795 2 : w.meta.Size += blockTrailerLen
1796 2 :
1797 2 : return bh, nil
1798 : }
1799 :
1800 : // Write implements io.Writer. This is analogous to writeCompressedBlock for
1801 : // blocks that already incorporate the trailer, and don't need the callee to
1802 : // return a BlockHandle.
1803 2 : func (w *Writer) Write(blockWithTrailer []byte) (n int, err error) {
1804 2 : offset := w.meta.Size
1805 2 : if w.cacheID != 0 && w.fileNum.FileNum() != 0 {
1806 2 : // Remove the block being written from the cache. This provides defense in
1807 2 : // depth against bugs which cause cache collisions.
1808 2 : //
1809 2 : // TODO(peter): Alternatively, we could add the uncompressed value to the
1810 2 : // cache.
1811 2 : w.cache.Delete(w.cacheID, w.fileNum, offset)
1812 2 : }
1813 2 : w.meta.Size += uint64(len(blockWithTrailer))
1814 2 : if err := w.writable.Write(blockWithTrailer); err != nil {
1815 0 : return 0, err
1816 0 : }
1817 2 : return len(blockWithTrailer), nil
1818 : }
1819 :
1820 : func (w *Writer) writeBlock(
1821 : b []byte, compression Compression, blockBuf *blockBuf,
1822 2 : ) (BlockHandle, error) {
1823 2 : b = compressAndChecksum(b, compression, blockBuf)
1824 2 : return w.writeCompressedBlock(b, blockBuf.tmp[:])
1825 2 : }
1826 :
1827 : // assertFormatCompatibility ensures that the features present on the table are
1828 : // compatible with the table format version.
1829 2 : func (w *Writer) assertFormatCompatibility() error {
1830 2 : // PebbleDBv1: block properties.
1831 2 : if len(w.blockPropCollectors) > 0 && w.tableFormat < TableFormatPebblev1 {
1832 1 : return errors.Newf(
1833 1 : "table format version %s is less than the minimum required version %s for block properties",
1834 1 : w.tableFormat, TableFormatPebblev1,
1835 1 : )
1836 1 : }
1837 :
1838 : // PebbleDBv2: range keys.
1839 2 : if w.props.NumRangeKeys() > 0 && w.tableFormat < TableFormatPebblev2 {
1840 1 : return errors.Newf(
1841 1 : "table format version %s is less than the minimum required version %s for range keys",
1842 1 : w.tableFormat, TableFormatPebblev2,
1843 1 : )
1844 1 : }
1845 :
1846 : // PebbleDBv3: value blocks.
1847 2 : if (w.props.NumValueBlocks > 0 || w.props.NumValuesInValueBlocks > 0 ||
1848 2 : w.props.ValueBlocksSize > 0) && w.tableFormat < TableFormatPebblev3 {
1849 0 : return errors.Newf(
1850 0 : "table format version %s is less than the minimum required version %s for value blocks",
1851 0 : w.tableFormat, TableFormatPebblev3)
1852 0 : }
1853 :
1854 : // PebbleDBv4: DELSIZED tombstones.
1855 2 : if w.props.NumSizedDeletions > 0 && w.tableFormat < TableFormatPebblev4 {
1856 0 : return errors.Newf(
1857 0 : "table format version %s is less than the minimum required version %s for sized deletion tombstones",
1858 0 : w.tableFormat, TableFormatPebblev4)
1859 0 : }
1860 2 : return nil
1861 : }
1862 :
1863 : // Close finishes writing the table and closes the underlying file that the
1864 : // table was written to.
1865 2 : func (w *Writer) Close() (err error) {
1866 2 : defer func() {
1867 2 : if w.valueBlockWriter != nil {
1868 2 : releaseValueBlockWriter(w.valueBlockWriter)
1869 2 : // Defensive code in case Close gets called again. We don't want to put
1870 2 : // the same object to a sync.Pool.
1871 2 : w.valueBlockWriter = nil
1872 2 : }
1873 2 : if w.writable != nil {
1874 1 : w.writable.Abort()
1875 1 : w.writable = nil
1876 1 : }
1877 : // Record any error in the writer (so we can exit early if Close is called
1878 : // again).
1879 2 : if err != nil {
1880 1 : w.err = err
1881 1 : }
1882 : }()
1883 :
1884 : // finish must be called before we check for an error, because finish will
1885 : // block until every single task added to the writeQueue has been processed,
1886 : // and an error could be encountered while any of those tasks are processed.
1887 2 : if err := w.coordination.writeQueue.finish(); err != nil {
1888 1 : return err
1889 1 : }
1890 :
1891 2 : if w.err != nil {
1892 1 : return w.err
1893 1 : }
1894 :
1895 : // The w.meta.LargestPointKey is only used once the Writer is closed, so it is safe to set it
1896 : // when the Writer is closed.
1897 : //
1898 : // The following invariants ensure that setting the largest key at this point of a Writer close
1899 : // is correct:
1900 : // 1. Keys must only be added to the Writer in an increasing order.
1901 : // 2. The current w.dataBlockBuf is guaranteed to have the latest key added to the Writer. This
1902 : // must be true, because a w.dataBlockBuf is only switched out when a dataBlock is flushed,
1903 : // however, if a dataBlock is flushed, then we add a key to the new w.dataBlockBuf in the
1904 : // addPoint function after the flush occurs.
1905 2 : if w.dataBlockBuf.dataBlock.nEntries >= 1 {
1906 2 : w.meta.SetLargestPointKey(w.dataBlockBuf.dataBlock.getCurKey().Clone())
1907 2 : }
1908 :
1909 : // Finish the last data block, or force an empty data block if there
1910 : // aren't any data blocks at all.
1911 2 : if w.dataBlockBuf.dataBlock.nEntries > 0 || w.indexBlock.block.nEntries == 0 {
1912 2 : bh, err := w.writeBlock(w.dataBlockBuf.dataBlock.finish(), w.compression, &w.dataBlockBuf.blockBuf)
1913 2 : if err != nil {
1914 0 : return err
1915 0 : }
1916 2 : bhp, err := w.maybeAddBlockPropertiesToBlockHandle(bh)
1917 2 : if err != nil {
1918 0 : return err
1919 0 : }
1920 2 : prevKey := w.dataBlockBuf.dataBlock.getCurKey()
1921 2 : if err := w.addIndexEntrySync(prevKey, InternalKey{}, bhp, w.dataBlockBuf.tmp[:]); err != nil {
1922 0 : return err
1923 0 : }
1924 : }
1925 2 : w.props.DataSize = w.meta.Size
1926 2 :
1927 2 : // Write the filter block.
1928 2 : var metaindex rawBlockWriter
1929 2 : metaindex.restartInterval = 1
1930 2 : if w.filter != nil {
1931 2 : b, err := w.filter.finish()
1932 2 : if err != nil {
1933 0 : return err
1934 0 : }
1935 2 : bh, err := w.writeBlock(b, NoCompression, &w.blockBuf)
1936 2 : if err != nil {
1937 0 : return err
1938 0 : }
1939 2 : n := encodeBlockHandle(w.blockBuf.tmp[:], bh)
1940 2 : metaindex.add(InternalKey{UserKey: []byte(w.filter.metaName())}, w.blockBuf.tmp[:n])
1941 2 : w.props.FilterPolicyName = w.filter.policyName()
1942 2 : w.props.FilterSize = bh.Length
1943 : }
1944 :
1945 2 : var indexBH BlockHandle
1946 2 : if w.twoLevelIndex {
1947 2 : w.props.IndexType = twoLevelIndex
1948 2 : // Write the two level index block.
1949 2 : indexBH, err = w.writeTwoLevelIndex()
1950 2 : if err != nil {
1951 0 : return err
1952 0 : }
1953 2 : } else {
1954 2 : w.props.IndexType = binarySearchIndex
1955 2 : // NB: RocksDB includes the block trailer length in the index size
1956 2 : // property, though it doesn't include the trailer in the filter size
1957 2 : // property.
1958 2 : w.props.IndexSize = uint64(w.indexBlock.estimatedSize()) + blockTrailerLen
1959 2 : w.props.NumDataBlocks = uint64(w.indexBlock.block.nEntries)
1960 2 :
1961 2 : // Write the single level index block.
1962 2 : indexBH, err = w.writeBlock(w.indexBlock.finish(), w.compression, &w.blockBuf)
1963 2 : if err != nil {
1964 0 : return err
1965 0 : }
1966 : }
1967 :
1968 : // Write the range-del block. The block handle must added to the meta index block
1969 : // after the properties block has been written. This is because the entries in the
1970 : // metaindex block must be sorted by key.
1971 2 : var rangeDelBH BlockHandle
1972 2 : if w.props.NumRangeDeletions > 0 {
1973 2 : if !w.rangeDelV1Format {
1974 2 : // Because the range tombstones are fragmented in the v2 format, the end
1975 2 : // key of the last added range tombstone will be the largest range
1976 2 : // tombstone key. Note that we need to make this into a range deletion
1977 2 : // sentinel because sstable boundaries are inclusive while the end key of
1978 2 : // a range deletion tombstone is exclusive. A Clone() is necessary as
1979 2 : // rangeDelBlock.curValue is the same slice that will get passed
1980 2 : // into w.writer, and some implementations of vfs.File mutate the
1981 2 : // slice passed into Write(). Also, w.meta will often outlive the
1982 2 : // blockWriter, and so cloning curValue allows the rangeDelBlock's
1983 2 : // internal buffer to get gc'd.
1984 2 : k := base.MakeRangeDeleteSentinelKey(w.rangeDelBlock.curValue).Clone()
1985 2 : w.meta.SetLargestRangeDelKey(k)
1986 2 : }
1987 2 : rangeDelBH, err = w.writeBlock(w.rangeDelBlock.finish(), NoCompression, &w.blockBuf)
1988 2 : if err != nil {
1989 0 : return err
1990 0 : }
1991 : }
1992 :
1993 : // Write the range-key block, flushing any remaining spans from the
1994 : // fragmenter first.
1995 2 : w.fragmenter.Finish()
1996 2 :
1997 2 : var rangeKeyBH BlockHandle
1998 2 : if w.props.NumRangeKeys() > 0 {
1999 2 : key := w.rangeKeyBlock.getCurKey()
2000 2 : kind := key.Kind()
2001 2 : endKey, _, ok := rangekey.DecodeEndKey(kind, w.rangeKeyBlock.curValue)
2002 2 : if !ok {
2003 0 : return errors.Newf("invalid end key: %s", w.rangeKeyBlock.curValue)
2004 0 : }
2005 2 : k := base.MakeExclusiveSentinelKey(kind, endKey).Clone()
2006 2 : w.meta.SetLargestRangeKey(k)
2007 2 : // TODO(travers): The lack of compression on the range key block matches the
2008 2 : // lack of compression on the range-del block. Revisit whether we want to
2009 2 : // enable compression on this block.
2010 2 : rangeKeyBH, err = w.writeBlock(w.rangeKeyBlock.finish(), NoCompression, &w.blockBuf)
2011 2 : if err != nil {
2012 0 : return err
2013 0 : }
2014 : }
2015 :
2016 2 : if w.valueBlockWriter != nil {
2017 2 : vbiHandle, vbStats, err := w.valueBlockWriter.finish(w, w.meta.Size)
2018 2 : if err != nil {
2019 0 : return err
2020 0 : }
2021 2 : w.props.NumValueBlocks = vbStats.numValueBlocks
2022 2 : w.props.NumValuesInValueBlocks = vbStats.numValuesInValueBlocks
2023 2 : w.props.ValueBlocksSize = vbStats.valueBlocksAndIndexSize
2024 2 : if vbStats.numValueBlocks > 0 {
2025 2 : n := encodeValueBlocksIndexHandle(w.blockBuf.tmp[:], vbiHandle)
2026 2 : metaindex.add(InternalKey{UserKey: []byte(metaValueIndexName)}, w.blockBuf.tmp[:n])
2027 2 : }
2028 : }
2029 :
2030 : // Add the range key block handle to the metaindex block. Note that we add the
2031 : // block handle to the metaindex block before the other meta blocks as the
2032 : // metaindex block entries must be sorted, and the range key block name sorts
2033 : // before the other block names.
2034 2 : if w.props.NumRangeKeys() > 0 {
2035 2 : n := encodeBlockHandle(w.blockBuf.tmp[:], rangeKeyBH)
2036 2 : metaindex.add(InternalKey{UserKey: []byte(metaRangeKeyName)}, w.blockBuf.tmp[:n])
2037 2 : }
2038 :
2039 2 : {
2040 2 : userProps := make(map[string]string)
2041 2 : for i := range w.propCollectors {
2042 1 : if err := w.propCollectors[i].Finish(userProps); err != nil {
2043 1 : return err
2044 1 : }
2045 : }
2046 2 : for i := range w.blockPropCollectors {
2047 2 : scratch := w.blockPropsEncoder.getScratchForProp()
2048 2 : // Place the shortID in the first byte.
2049 2 : scratch = append(scratch, byte(i))
2050 2 : buf, err := w.blockPropCollectors[i].FinishTable(scratch)
2051 2 : if err != nil {
2052 1 : return err
2053 1 : }
2054 2 : var prop string
2055 2 : if len(buf) > 0 {
2056 2 : prop = string(buf)
2057 2 : }
2058 : // NB: The property is populated in the map even if it is the
2059 : // empty string, since the presence in the map is what indicates
2060 : // that the block property collector was used when writing.
2061 2 : userProps[w.blockPropCollectors[i].Name()] = prop
2062 : }
2063 2 : if len(userProps) > 0 {
2064 2 : w.props.UserProperties = userProps
2065 2 : }
2066 :
2067 : // Write the properties block.
2068 2 : var raw rawBlockWriter
2069 2 : // The restart interval is set to infinity because the properties block
2070 2 : // is always read sequentially and cached in a heap located object. This
2071 2 : // reduces table size without a significant impact on performance.
2072 2 : raw.restartInterval = propertiesBlockRestartInterval
2073 2 : w.props.CompressionOptions = rocksDBCompressionOptions
2074 2 : w.props.save(w.tableFormat, &raw)
2075 2 : bh, err := w.writeBlock(raw.finish(), NoCompression, &w.blockBuf)
2076 2 : if err != nil {
2077 0 : return err
2078 0 : }
2079 2 : n := encodeBlockHandle(w.blockBuf.tmp[:], bh)
2080 2 : metaindex.add(InternalKey{UserKey: []byte(metaPropertiesName)}, w.blockBuf.tmp[:n])
2081 : }
2082 :
2083 : // Add the range deletion block handle to the metaindex block.
2084 2 : if w.props.NumRangeDeletions > 0 {
2085 2 : n := encodeBlockHandle(w.blockBuf.tmp[:], rangeDelBH)
2086 2 : // The v2 range-del block encoding is backwards compatible with the v1
2087 2 : // encoding. We add meta-index entries for both the old name and the new
2088 2 : // name so that old code can continue to find the range-del block and new
2089 2 : // code knows that the range tombstones in the block are fragmented and
2090 2 : // sorted.
2091 2 : metaindex.add(InternalKey{UserKey: []byte(metaRangeDelName)}, w.blockBuf.tmp[:n])
2092 2 : if !w.rangeDelV1Format {
2093 2 : metaindex.add(InternalKey{UserKey: []byte(metaRangeDelV2Name)}, w.blockBuf.tmp[:n])
2094 2 : }
2095 : }
2096 :
2097 : // Write the metaindex block. It might be an empty block, if the filter
2098 : // policy is nil. NoCompression is specified because a) RocksDB never
2099 : // compresses the meta-index block and b) RocksDB has some code paths which
2100 : // expect the meta-index block to not be compressed.
2101 2 : metaindexBH, err := w.writeBlock(metaindex.blockWriter.finish(), NoCompression, &w.blockBuf)
2102 2 : if err != nil {
2103 0 : return err
2104 0 : }
2105 :
2106 : // Write the table footer.
2107 2 : footer := footer{
2108 2 : format: w.tableFormat,
2109 2 : checksum: w.blockBuf.checksummer.checksumType,
2110 2 : metaindexBH: metaindexBH,
2111 2 : indexBH: indexBH,
2112 2 : }
2113 2 : encoded := footer.encode(w.blockBuf.tmp[:])
2114 2 : if err := w.writable.Write(footer.encode(w.blockBuf.tmp[:])); err != nil {
2115 0 : return err
2116 0 : }
2117 2 : w.meta.Size += uint64(len(encoded))
2118 2 : w.meta.Properties = w.props
2119 2 :
2120 2 : // Check that the features present in the table are compatible with the format
2121 2 : // configured for the table.
2122 2 : if err = w.assertFormatCompatibility(); err != nil {
2123 1 : return err
2124 1 : }
2125 :
2126 2 : if err := w.writable.Finish(); err != nil {
2127 1 : w.writable = nil
2128 1 : return err
2129 1 : }
2130 2 : w.writable = nil
2131 2 :
2132 2 : w.dataBlockBuf.clear()
2133 2 : dataBlockBufPool.Put(w.dataBlockBuf)
2134 2 : w.dataBlockBuf = nil
2135 2 : w.indexBlock.clear()
2136 2 : indexBlockBufPool.Put(w.indexBlock)
2137 2 : w.indexBlock = nil
2138 2 :
2139 2 : // Make any future calls to Set or Close return an error.
2140 2 : w.err = errWriterClosed
2141 2 : return nil
2142 : }
2143 :
2144 : // EstimatedSize returns the estimated size of the sstable being written if a
2145 : // call to Finish() was made without adding additional keys.
2146 2 : func (w *Writer) EstimatedSize() uint64 {
2147 2 : return w.coordination.sizeEstimate.size() +
2148 2 : uint64(w.dataBlockBuf.dataBlock.estimatedSize()) +
2149 2 : w.indexBlock.estimatedSize()
2150 2 : }
2151 :
2152 : // Metadata returns the metadata for the finished sstable. Only valid to call
2153 : // after the sstable has been finished.
2154 2 : func (w *Writer) Metadata() (*WriterMetadata, error) {
2155 2 : if w.writable != nil {
2156 0 : return nil, errors.New("pebble: writer is not closed")
2157 0 : }
2158 2 : return &w.meta, nil
2159 : }
2160 :
2161 : // WriterOption provide an interface to do work on Writer while it is being
2162 : // opened.
2163 : type WriterOption interface {
2164 : // writerApply is called on the writer during opening in order to set
2165 : // internal parameters.
2166 : writerApply(*Writer)
2167 : }
2168 :
2169 : // PreviousPointKeyOpt is a WriterOption that provides access to the last
2170 : // point key written to the writer while building a sstable.
2171 : type PreviousPointKeyOpt struct {
2172 : w *Writer
2173 : }
2174 :
2175 : // UnsafeKey returns the last point key written to the writer to which this
2176 : // option was passed during creation. The returned key points directly into
2177 : // a buffer belonging to the Writer. The value's lifetime ends the next time a
2178 : // point key is added to the Writer.
2179 : // Invariant: UnsafeKey isn't and shouldn't be called after the Writer is closed.
2180 2 : func (o PreviousPointKeyOpt) UnsafeKey() base.InternalKey {
2181 2 : if o.w == nil {
2182 0 : return base.InvalidInternalKey
2183 0 : }
2184 :
2185 2 : if o.w.dataBlockBuf.dataBlock.nEntries >= 1 {
2186 2 : // o.w.dataBlockBuf.dataBlock.curKey is guaranteed to point to the last point key
2187 2 : // which was added to the Writer.
2188 2 : return o.w.dataBlockBuf.dataBlock.getCurKey()
2189 2 : }
2190 0 : return base.InternalKey{}
2191 : }
2192 :
2193 2 : func (o *PreviousPointKeyOpt) writerApply(w *Writer) {
2194 2 : o.w = w
2195 2 : }
2196 :
2197 : // NewWriter returns a new table writer for the file. Closing the writer will
2198 : // close the file.
2199 2 : func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...WriterOption) *Writer {
2200 2 : o = o.ensureDefaults()
2201 2 : w := &Writer{
2202 2 : writable: writable,
2203 2 : meta: WriterMetadata{
2204 2 : SmallestSeqNum: math.MaxUint64,
2205 2 : },
2206 2 : blockSize: o.BlockSize,
2207 2 : blockSizeThreshold: (o.BlockSize*o.BlockSizeThreshold + 99) / 100,
2208 2 : indexBlockSize: o.IndexBlockSize,
2209 2 : indexBlockSizeThreshold: (o.IndexBlockSize*o.BlockSizeThreshold + 99) / 100,
2210 2 : compare: o.Comparer.Compare,
2211 2 : split: o.Comparer.Split,
2212 2 : formatKey: o.Comparer.FormatKey,
2213 2 : compression: o.Compression,
2214 2 : separator: o.Comparer.Separator,
2215 2 : successor: o.Comparer.Successor,
2216 2 : tableFormat: o.TableFormat,
2217 2 : isStrictObsolete: o.IsStrictObsolete,
2218 2 : writingToLowestLevel: o.WritingToLowestLevel,
2219 2 : cache: o.Cache,
2220 2 : restartInterval: o.BlockRestartInterval,
2221 2 : checksumType: o.Checksum,
2222 2 : indexBlock: newIndexBlockBuf(o.Parallelism),
2223 2 : rangeDelBlock: blockWriter{
2224 2 : restartInterval: 1,
2225 2 : },
2226 2 : rangeKeyBlock: blockWriter{
2227 2 : restartInterval: 1,
2228 2 : },
2229 2 : topLevelIndexBlock: blockWriter{
2230 2 : restartInterval: 1,
2231 2 : },
2232 2 : fragmenter: keyspan.Fragmenter{
2233 2 : Cmp: o.Comparer.Compare,
2234 2 : Format: o.Comparer.FormatKey,
2235 2 : },
2236 2 : }
2237 2 : if w.tableFormat >= TableFormatPebblev3 {
2238 2 : w.shortAttributeExtractor = o.ShortAttributeExtractor
2239 2 : w.requiredInPlaceValueBound = o.RequiredInPlaceValueBound
2240 2 : w.valueBlockWriter = newValueBlockWriter(
2241 2 : w.blockSize, w.blockSizeThreshold, w.compression, w.checksumType, func(compressedSize int) {
2242 2 : w.coordination.sizeEstimate.dataBlockCompressed(compressedSize, 0)
2243 2 : })
2244 : }
2245 :
2246 2 : w.dataBlockBuf = newDataBlockBuf(w.restartInterval, w.checksumType)
2247 2 :
2248 2 : w.blockBuf = blockBuf{
2249 2 : checksummer: checksummer{checksumType: o.Checksum},
2250 2 : }
2251 2 :
2252 2 : w.coordination.init(o.Parallelism, w)
2253 2 :
2254 2 : if writable == nil {
2255 0 : w.err = errors.New("pebble: nil writable")
2256 0 : return w
2257 0 : }
2258 :
2259 : // Note that WriterOptions are applied in two places; the ones with a
2260 : // preApply() method are applied here. The rest are applied down below after
2261 : // default properties are set.
2262 2 : type preApply interface{ preApply() }
2263 2 : for _, opt := range extraOpts {
2264 2 : if _, ok := opt.(preApply); ok {
2265 2 : opt.writerApply(w)
2266 2 : }
2267 : }
2268 :
2269 2 : w.props.PrefixExtractorName = "nullptr"
2270 2 : if o.FilterPolicy != nil {
2271 2 : switch o.FilterType {
2272 2 : case TableFilter:
2273 2 : w.filter = newTableFilterWriter(o.FilterPolicy)
2274 2 : if w.split != nil {
2275 2 : w.props.PrefixExtractorName = o.Comparer.Name
2276 2 : w.props.PrefixFiltering = true
2277 2 : } else {
2278 1 : w.props.WholeKeyFiltering = true
2279 1 : }
2280 0 : default:
2281 0 : panic(fmt.Sprintf("unknown filter type: %v", o.FilterType))
2282 : }
2283 : }
2284 :
2285 2 : w.props.ComparerName = o.Comparer.Name
2286 2 : w.props.CompressionName = o.Compression.String()
2287 2 : w.props.MergerName = o.MergerName
2288 2 : w.props.PropertyCollectorNames = "[]"
2289 2 : w.props.ExternalFormatVersion = rocksDBExternalFormatVersion
2290 2 :
2291 2 : if len(o.TablePropertyCollectors) > 0 || len(o.BlockPropertyCollectors) > 0 ||
2292 2 : w.tableFormat >= TableFormatPebblev4 {
2293 2 : var buf bytes.Buffer
2294 2 : buf.WriteString("[")
2295 2 : if len(o.TablePropertyCollectors) > 0 {
2296 1 : w.propCollectors = make([]TablePropertyCollector, len(o.TablePropertyCollectors))
2297 1 : for i := range o.TablePropertyCollectors {
2298 1 : w.propCollectors[i] = o.TablePropertyCollectors[i]()
2299 1 : if i > 0 {
2300 1 : buf.WriteString(",")
2301 1 : }
2302 1 : buf.WriteString(w.propCollectors[i].Name())
2303 : }
2304 : }
2305 2 : numBlockPropertyCollectors := len(o.BlockPropertyCollectors)
2306 2 : if w.tableFormat >= TableFormatPebblev4 {
2307 2 : numBlockPropertyCollectors++
2308 2 : }
2309 : // shortID is a uint8, so we cannot exceed that number of block
2310 : // property collectors.
2311 2 : if numBlockPropertyCollectors > math.MaxUint8 {
2312 0 : w.err = errors.New("pebble: too many block property collectors")
2313 0 : return w
2314 0 : }
2315 2 : if numBlockPropertyCollectors > 0 {
2316 2 : w.blockPropCollectors = make([]BlockPropertyCollector, numBlockPropertyCollectors)
2317 2 : }
2318 2 : if len(o.BlockPropertyCollectors) > 0 {
2319 2 : // The shortID assigned to a collector is the same as its index in
2320 2 : // this slice.
2321 2 : for i := range o.BlockPropertyCollectors {
2322 2 : w.blockPropCollectors[i] = o.BlockPropertyCollectors[i]()
2323 2 : if i > 0 || len(o.TablePropertyCollectors) > 0 {
2324 1 : buf.WriteString(",")
2325 1 : }
2326 2 : buf.WriteString(w.blockPropCollectors[i].Name())
2327 : }
2328 : }
2329 2 : if w.tableFormat >= TableFormatPebblev4 {
2330 2 : if numBlockPropertyCollectors > 1 || len(o.TablePropertyCollectors) > 0 {
2331 2 : buf.WriteString(",")
2332 2 : }
2333 2 : w.blockPropCollectors[numBlockPropertyCollectors-1] = &w.obsoleteCollector
2334 2 : buf.WriteString(w.obsoleteCollector.Name())
2335 : }
2336 2 : buf.WriteString("]")
2337 2 : w.props.PropertyCollectorNames = buf.String()
2338 : }
2339 :
2340 : // Apply the remaining WriterOptions that do not have a preApply() method.
2341 2 : for _, opt := range extraOpts {
2342 2 : if _, ok := opt.(preApply); ok {
2343 2 : continue
2344 : }
2345 2 : opt.writerApply(w)
2346 : }
2347 :
2348 : // Initialize the range key fragmenter and encoder.
2349 2 : w.fragmenter.Emit = w.encodeRangeKeySpan
2350 2 : w.rangeKeyEncoder.Emit = w.addRangeKey
2351 2 : return w
2352 : }
2353 :
2354 : // internalGetProperties is a private, internal-use-only function that takes a
2355 : // Writer and returns a pointer to its Properties, allowing direct mutation.
2356 : // It's used by internal Pebble flushes and compactions to set internal
2357 : // properties. It gets installed in private.
2358 2 : func internalGetProperties(w *Writer) *Properties {
2359 2 : return &w.props
2360 2 : }
2361 :
2362 2 : func init() {
2363 2 : private.SSTableWriterDisableKeyOrderChecks = func(i interface{}) {
2364 1 : w := i.(*Writer)
2365 1 : w.disableKeyOrderChecks = true
2366 1 : }
2367 2 : private.SSTableInternalProperties = internalGetProperties
2368 : }
2369 :
2370 : type obsoleteKeyBlockPropertyCollector struct {
2371 : blockIsNonObsolete bool
2372 : indexIsNonObsolete bool
2373 : tableIsNonObsolete bool
2374 : }
2375 :
2376 2 : func encodeNonObsolete(isNonObsolete bool, buf []byte) []byte {
2377 2 : if isNonObsolete {
2378 2 : return buf
2379 2 : }
2380 2 : return append(buf, 't')
2381 : }
2382 :
2383 2 : func (o *obsoleteKeyBlockPropertyCollector) Name() string {
2384 2 : return "obsolete-key"
2385 2 : }
2386 :
2387 2 : func (o *obsoleteKeyBlockPropertyCollector) Add(key InternalKey, value []byte) error {
2388 2 : // Ignore.
2389 2 : return nil
2390 2 : }
2391 :
2392 2 : func (o *obsoleteKeyBlockPropertyCollector) AddPoint(isObsolete bool) {
2393 2 : o.blockIsNonObsolete = o.blockIsNonObsolete || !isObsolete
2394 2 : }
2395 :
2396 2 : func (o *obsoleteKeyBlockPropertyCollector) FinishDataBlock(buf []byte) ([]byte, error) {
2397 2 : o.tableIsNonObsolete = o.tableIsNonObsolete || o.blockIsNonObsolete
2398 2 : return encodeNonObsolete(o.blockIsNonObsolete, buf), nil
2399 2 : }
2400 :
2401 2 : func (o *obsoleteKeyBlockPropertyCollector) AddPrevDataBlockToIndexBlock() {
2402 2 : o.indexIsNonObsolete = o.indexIsNonObsolete || o.blockIsNonObsolete
2403 2 : o.blockIsNonObsolete = false
2404 2 : }
2405 :
2406 2 : func (o *obsoleteKeyBlockPropertyCollector) FinishIndexBlock(buf []byte) ([]byte, error) {
2407 2 : indexIsNonObsolete := o.indexIsNonObsolete
2408 2 : o.indexIsNonObsolete = false
2409 2 : return encodeNonObsolete(indexIsNonObsolete, buf), nil
2410 2 : }
2411 :
2412 2 : func (o *obsoleteKeyBlockPropertyCollector) FinishTable(buf []byte) ([]byte, error) {
2413 2 : return encodeNonObsolete(o.tableIsNonObsolete, buf), nil
2414 2 : }
2415 :
2416 : func (o *obsoleteKeyBlockPropertyCollector) UpdateKeySuffixes(
2417 : oldProp []byte, oldSuffix, newSuffix []byte,
2418 1 : ) error {
2419 1 : _, err := propToIsObsolete(oldProp)
2420 1 : if err != nil {
2421 0 : return err
2422 0 : }
2423 : // Suffix rewriting currently loses the obsolete bit.
2424 1 : o.blockIsNonObsolete = true
2425 1 : return nil
2426 : }
2427 :
2428 : // NB: obsoleteKeyBlockPropertyFilter is stateless. This aspect of the filter
2429 : // is used in table_cache.go for in-place modification of a filters slice.
2430 : type obsoleteKeyBlockPropertyFilter struct{}
2431 :
2432 2 : func (o obsoleteKeyBlockPropertyFilter) Name() string {
2433 2 : return "obsolete-key"
2434 2 : }
2435 :
2436 : // Intersects returns true if the set represented by prop intersects with
2437 : // the set in the filter.
2438 2 : func (o obsoleteKeyBlockPropertyFilter) Intersects(prop []byte) (bool, error) {
2439 2 : return propToIsObsolete(prop)
2440 2 : }
2441 :
2442 2 : func propToIsObsolete(prop []byte) (bool, error) {
2443 2 : if len(prop) == 0 {
2444 2 : return true, nil
2445 2 : }
2446 2 : if len(prop) > 1 || prop[0] != 't' {
2447 0 : return false, errors.Errorf("unexpected property %x", prop)
2448 0 : }
2449 2 : return false, nil
2450 : }
|