Skip to content

Commit 0d43ddc

Browse files
committed
sstable: genericize sstable iterators by data block iterator
In preparation for columnar blocks, this commit introduces type parameters to the {single,two}LevelIterator types. Parameterizing the sstable iterator ensures we pay the cost of dynamic dispatch only when entering the sstable iterator (as we do today). In order to continue to allocate the data block iterator struct within the single-level iterator, this commit introduces two type paramters: one for the non-pointer iterator type, and one for the pointer type on which iteration methods are defined. This adds some slightly cumbersome ergonomics to data block iterator usage sites.
1 parent 57ff76d commit 0d43ddc

File tree

10 files changed

+318
-198
lines changed

10 files changed

+318
-198
lines changed

sstable/block/block.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,45 @@ func (c *Checksummer) Checksum(block []byte, blockType []byte) (checksum uint32)
8989
return checksum
9090
}
9191

92+
// DataBlockIterator is a type constraint for implementations of block iterators
93+
// over data blocks. It's currently satisifed by the *rowblk.Iter type.
94+
//
95+
// DataBlockIterator requires that the type be a pointer to its type parameter,
96+
// D, to allow sstable iterators embed the block iterator within its struct. See
97+
// this example from the Go generics proposal:
98+
// https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example
99+
type DataBlockIterator[D any] interface {
100+
base.InternalIterator
101+
102+
// Handle returns the handle to the block.
103+
Handle() BufferHandle
104+
// InitHandle initializes the block from the provided buffer handle.
105+
InitHandle(base.Compare, base.Split, BufferHandle, IterTransforms) error
106+
// Valid returns true if the iterator is currently positioned at a valid KV.
107+
Valid() bool
108+
// KV returns the key-value pair at the current iterator position. The
109+
// iterator must be Valid().
110+
KV() *base.InternalKV
111+
// ResetForReuse resets the iterator so that it may be used for iteration
112+
// over a new block. It returns the non-pointer D type to allow resetting
113+
// while initializing the containing struct, eg::
114+
// iter = sstableIter{dataBlockIter: iter.dataBlockIter.ResetForReuse()}
115+
ResetForReuse() D
116+
// FirstUserKey returns the first user key contained within the data block.
117+
FirstUserKey() []byte
118+
// Invalidate invalidates the block iterator, removing references to the block
119+
// it was initialized with.
120+
Invalidate()
121+
// IsDataInvalidated returns true when the iterator has been invalidated
122+
// using an Invalidate call.
123+
//
124+
// NB: this is different from Valid which indicates whether the current *KV*
125+
// is valid.
126+
IsDataInvalidated() bool
127+
128+
*D // non-interface type constraint element
129+
}
130+
92131
// IterTransforms allow on-the-fly transformation of data at iteration time.
93132
//
94133
// These transformations could in principle be implemented as block transforms

sstable/data_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/cockroachdb/pebble/internal/testkeys"
2323
"github.com/cockroachdb/pebble/objstorage"
2424
"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
25+
"github.com/cockroachdb/pebble/sstable/rowblk"
2526
"github.com/cockroachdb/pebble/vfs"
2627
)
2728

@@ -427,8 +428,8 @@ func runIterCmd(
427428
continue
428429
case "internal-iter-state":
429430
fmt.Fprintf(&b, "| %T:\n", origIter)
430-
si, _ := origIter.(*singleLevelIterator)
431-
if twoLevelIter, ok := origIter.(*twoLevelIterator); ok {
431+
si, _ := origIter.(*singleLevelIterator[rowblk.Iter, *rowblk.Iter])
432+
if twoLevelIter, ok := origIter.(*twoLevelIterator[rowblk.Iter, *rowblk.Iter]); ok {
432433
si = &twoLevelIter.secondLevel
433434
if twoLevelIter.topLevelIndex.Valid() {
434435
fmt.Fprintf(&b, "| topLevelIndex.Key() = %q\n", twoLevelIter.topLevelIndex.Key())

sstable/reader.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,11 @@ func (r *Reader) newPointIter(
207207
var res Iterator
208208
var err error
209209
if r.Properties.IndexType == twoLevelIndex {
210-
res, err = newTwoLevelIterator(ctx, r, vState, transforms, lower, upper, filterer, filterBlockSizeLimit,
210+
res, err = newRowBlockTwoLevelIterator(
211+
ctx, r, vState, transforms, lower, upper, filterer, filterBlockSizeLimit,
211212
stats, categoryAndQoS, statsCollector, rp, nil /* bufferPool */)
212213
} else {
213-
res, err = newSingleLevelIterator(
214+
res, err = newRowBlockSingleLevelIterator(
214215
ctx, r, vState, transforms, lower, upper, filterer, filterBlockSizeLimit,
215216
stats, categoryAndQoS, statsCollector, rp, nil /* bufferPool */)
216217
}
@@ -261,7 +262,7 @@ func (r *Reader) newCompactionIter(
261262
transforms.HideObsoletePoints = true
262263
}
263264
if r.Properties.IndexType == twoLevelIndex {
264-
i, err := newTwoLevelIterator(
265+
i, err := newRowBlockTwoLevelIterator(
265266
context.Background(),
266267
r, vState, transforms, nil /* lower */, nil /* upper */, nil,
267268
NeverUseFilterBlock, nil /* stats */, categoryAndQoS, statsCollector, rp, bufferPool,
@@ -272,7 +273,7 @@ func (r *Reader) newCompactionIter(
272273
i.SetupForCompaction()
273274
return i, nil
274275
}
275-
i, err := newSingleLevelIterator(
276+
i, err := newRowBlockSingleLevelIterator(
276277
context.Background(), r, vState, transforms, nil /* lower */, nil, /* upper */
277278
nil, NeverUseFilterBlock, nil /* stats */, categoryAndQoS, statsCollector, rp, bufferPool,
278279
)

sstable/reader_iter.go

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"github.com/cockroachdb/pebble/internal/base"
1313
"github.com/cockroachdb/pebble/internal/invariants"
14+
"github.com/cockroachdb/pebble/sstable/block"
15+
"github.com/cockroachdb/pebble/sstable/rowblk"
1416
)
1517

1618
// Iterator iterates over an entire table of data.
@@ -108,27 +110,33 @@ type Iterator interface {
108110
//
109111
// TODO(sumeer): remove the aforementioned defensive code.
110112

111-
var singleLevelIterPool = sync.Pool{
112-
New: func() interface{} {
113-
i := &singleLevelIterator{}
114-
// Note: this is a no-op if invariants are disabled or race is enabled.
115-
invariants.SetFinalizer(i, checkSingleLevelIterator)
116-
return i
117-
},
118-
}
113+
var (
114+
singleLevelIterRowBlockPool sync.Pool // *singleLevelIterator[rowblk.Iter, *rowblk.Iter]
115+
twoLevelIterRowBlockPool sync.Pool // *twoLevelIterator[rowblk.Iter, *rowblk.Iter]
116+
)
119117

120-
var twoLevelIterPool = sync.Pool{
121-
New: func() interface{} {
122-
i := &twoLevelIterator{}
123-
// Note: this is a no-op if invariants are disabled or race is enabled.
124-
invariants.SetFinalizer(i, checkTwoLevelIterator)
125-
return i
126-
},
118+
func init() {
119+
singleLevelIterRowBlockPool = sync.Pool{
120+
New: func() interface{} {
121+
i := &singleLevelIterator[rowblk.Iter, *rowblk.Iter]{pool: &singleLevelIterRowBlockPool}
122+
// Note: this is a no-op if invariants are disabled or race is enabled.
123+
invariants.SetFinalizer(i, checkSingleLevelIterator[rowblk.Iter, *rowblk.Iter])
124+
return i
125+
},
126+
}
127+
twoLevelIterRowBlockPool = sync.Pool{
128+
New: func() interface{} {
129+
i := &twoLevelIterator[rowblk.Iter, *rowblk.Iter]{pool: &twoLevelIterRowBlockPool}
130+
// Note: this is a no-op if invariants are disabled or race is enabled.
131+
invariants.SetFinalizer(i, checkTwoLevelIterator[rowblk.Iter, *rowblk.Iter])
132+
return i
133+
},
134+
}
127135
}
128136

129-
func checkSingleLevelIterator(obj interface{}) {
130-
i := obj.(*singleLevelIterator)
131-
if p := i.data.Handle().Get(); p != nil {
137+
func checkSingleLevelIterator[D any, PD block.DataBlockIterator[D]](obj interface{}) {
138+
i := obj.(*singleLevelIterator[D, PD])
139+
if p := PD(&i.data).Handle().Get(); p != nil {
132140
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %p\n", p)
133141
os.Exit(1)
134142
}
@@ -138,9 +146,9 @@ func checkSingleLevelIterator(obj interface{}) {
138146
}
139147
}
140148

141-
func checkTwoLevelIterator(obj interface{}) {
142-
i := obj.(*twoLevelIterator)
143-
if p := i.secondLevel.data.Handle().Get(); p != nil {
149+
func checkTwoLevelIterator[D any, PD block.DataBlockIterator[D]](obj interface{}) {
150+
i := obj.(*twoLevelIterator[D, PD])
151+
if p := PD(&i.secondLevel.data).Handle().Get(); p != nil {
144152
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %p\n", p)
145153
os.Exit(1)
146154
}

0 commit comments

Comments
 (0)