|
| 1 | +// Copyright 2024 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 base defines fundamental types used across Pebble, including keys, |
| 6 | +// iterators, etc. |
| 7 | +// |
| 8 | +// # Iterators |
| 9 | +// |
| 10 | +// The [InternalIterator] interface defines the iterator interface implemented |
| 11 | +// by all iterators over point keys. Internal iterators are composed to form an |
| 12 | +// "iterator stack," resulting in a single internal iterator (see mergingIter in |
| 13 | +// the pebble package) that yields a merged view of the LSM. |
| 14 | +// |
| 15 | +// The SeekGE and SeekPrefixGE positioning methods take a set of flags |
| 16 | +// [SeekGEFlags] allowing the caller to provide additional context to iterator |
| 17 | +// implementations. The TrySeekUsingNext flag is set when the caller has |
| 18 | +// knowledge that no action has been performed to move this iterator beyond the |
| 19 | +// first key that would be found if this iterator were to honestly do the |
| 20 | +// intended seek. This allows a class of optimizations where an internal |
| 21 | +// iterator may avoid a full naive repositioning if the iterator is already |
| 22 | +// at a proximate position. This also means every caller (including intermediary |
| 23 | +// internal iterators within the iterator stack) must preserve this |
| 24 | +// relationship. |
| 25 | +// |
| 26 | +// For example, if a range deletion deletes the remainder of a prefix, the |
| 27 | +// merging iterator may be able to elide a SeekPrefixGE on level iterators |
| 28 | +// beneath the range deletion. However in doing so, a TrySeekUsingNext flag |
| 29 | +// passed by the merging iterator's client no longer transitively holds for |
| 30 | +// subsequent seeks of child level iterators in all cases. The merging iterator |
| 31 | +// assumes responsibility for ensuring that SeekPrefixGE is propagated to its |
| 32 | +// consitutent iterators only when valid. |
| 33 | +// |
| 34 | +// Description of TrySeekUsingNext mechanics across the iterator stack: |
| 35 | +// |
| 36 | +// As the top-level entry point of user seeks, the [pebble.Iterator] is |
| 37 | +// responsible for detecting when consecutive seeks move monotonically forward. |
| 38 | +// It saves seek keys and compares consecutive seek keys to decide whether to |
| 39 | +// propagate the TrySeekUsingNext flag to its [InternalIterator]. |
| 40 | +// |
| 41 | +// The [pebble.Iterator] also has its own SeekPrefixGE optimization: Above the |
| 42 | +// [InternalIterator] interface, the [pebble.Iterator]'s SeekGE method detects |
| 43 | +// consecutive seeks to monotonically increasing keys and examines the current |
| 44 | +// key. If the iterator is already positioned appropriately (at a key ≥ the seek |
| 45 | +// key), it elides the entire seek of the internal iterator. |
| 46 | +// |
| 47 | +// The pebble mergingIter does not perform any TrySeekUsingNext optimization |
| 48 | +// itself, but it must preserve the TrySeekUsingNext contract in its calls to |
| 49 | +// its child iterators because it passes the TrySeekUsingNext flag as-is to its |
| 50 | +// child iterators. It can do this because it always translates calls to its |
| 51 | +// SeekGE and SeekPrefixGE methods as equivalent calls to every child iterator. |
| 52 | +// However there are a few subtleties: |
| 53 | +// |
| 54 | +// - In some cases the calls made to child iterators may only be equivalent |
| 55 | +// within the context of the iterator's visible sequence number. For example, |
| 56 | +// if a range deletion tombstone is present on a level, seek keys propagated |
| 57 | +// to lower-levelled child iterators may be adjusted without violating the |
| 58 | +// transitivity of the TrySeekUsingNext flag and its invariants so long as |
| 59 | +// the mergingIter is always reading state at the same visible sequence |
| 60 | +// number. |
| 61 | +// - The mergingIter takes care to avoid ever advancing a child iterator that's |
| 62 | +// already positioned beyond the current iteration prefix. |
| 63 | +// - When propagating TrySeekUsingNext to its child iterators, the mergingIter |
| 64 | +// must propagate it to all child iterators or none. This is required because |
| 65 | +// of the mergingIter's handling of range deletions. Unequal application of |
| 66 | +// TrySeekUsingNext may cause range deletions that have already been skipped |
| 67 | +// over in a level to go unseen, despite being relevant to other levels that |
| 68 | +// do not use TrySeekUsingNext. |
| 69 | +// |
| 70 | +// The pebble levelIter makes use of the TrySeekUsingNext flag to avoid a naive |
| 71 | +// seek among a level's file metadatas. When TrySeekUsingNext is passed by the |
| 72 | +// caller, the relevant key must fall within the current file or later. |
| 73 | +// |
| 74 | +// In-memory iterators arenaskl.Iterator and batchskl.Iterator make use of the |
| 75 | +// TrySeekUsingNext flag, attempting a fixed number of Nexts before falling back |
| 76 | +// to performing a seek using skiplist structures. |
| 77 | +// |
| 78 | +// The sstable iterators use the TrySeekUsingNext flag to avoid naive seeks |
| 79 | +// through a table's index structures: |
| 80 | +// - If an iterator is already exhausted, either because there are no |
| 81 | +// subsequent point keys or because the upper bound has been reached, the |
| 82 | +// iterator uses TrySeekUsingNext to avoid any repositioning at all. |
| 83 | +// - Otherwise, a TrySeekUsingNext flag causes the sstable Iterator to Next |
| 84 | +// forward a capped number of times, stopping as soon as a key ≥ the seek key |
| 85 | +// is discovered. |
| 86 | +package base |
0 commit comments