Skip to content

Commit 55d1733

Browse files
committed
db: consolidate Batch-related Iterator fields
A pebble.Iterator supports reading through an indexed Batch, observing the mutations of the batch on top of the LSM state. This commit takes the various Iterator fields necessary to support this iteration mode and bundles them into a separate struct which is moved onto the iterAlloc struct. The fields continue to be allocated together with the rest of the iterator. Consolidating the fields helps aid legibility of the pebble.Iterator struct, and in the future may help us limit the amount of memory we must zero when recycling a pebble.Iterator. Informs #4049.
1 parent bb70e15 commit 55d1733

File tree

3 files changed

+65
-49
lines changed

3 files changed

+65
-49
lines changed

db.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ type iterAlloc struct {
910910
boundsBuf [2][]byte
911911
prefixOrFullSeekKey []byte
912912
merging mergingIter
913+
batchState iteratorBatchState
913914
mlevels [3 + numLevels]mergingIterLevel
914915
levels [3 + numLevels]levelIter
915916
levelsPositioned [3 + numLevels]bool
@@ -1017,7 +1018,6 @@ func (d *DB) newIter(
10171018
keyBuf: buf.keyBuf,
10181019
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
10191020
boundsBuf: buf.boundsBuf,
1020-
batch: batch,
10211021
fc: d.fileCache,
10221022
newIters: newIters,
10231023
newIterRangeKey: newIterRangeKey,
@@ -1033,7 +1033,9 @@ func (d *DB) newIter(
10331033
dbi.opts.disableLazyCombinedIteration = true
10341034
}
10351035
if batch != nil {
1036-
dbi.batchSeqNum = dbi.batch.nextSeqNum()
1036+
dbi.batch = &buf.batchState
1037+
dbi.batch.batch = batch
1038+
dbi.batch.batchSeqNum = batch.nextSeqNum()
10371039
}
10381040
return finishInitializingIter(ctx, buf)
10391041
}
@@ -1086,7 +1088,7 @@ func finishInitializingIter(ctx context.Context, buf *iterAlloc) *Iterator {
10861088
// contains any range keys.
10871089
useLazyCombinedIteration := dbi.rangeKey == nil &&
10881090
dbi.opts.KeyTypes == IterKeyTypePointsAndRanges &&
1089-
(dbi.batch == nil || dbi.batch.countRangeKeys == 0) &&
1091+
(dbi.batch == nil || dbi.batch.batch.countRangeKeys == 0) &&
10901092
!dbi.opts.disableLazyCombinedIteration
10911093
if useLazyCombinedIteration {
10921094
// The user requested combined iteration, and there's no indexed
@@ -1371,23 +1373,23 @@ func (i *Iterator) constructPointIter(
13711373

13721374
// Top-level is the batch, if any.
13731375
if i.batch != nil {
1374-
if i.batch.index == nil {
1376+
if i.batch.batch.index == nil {
13751377
// This isn't an indexed batch. We shouldn't have gotten this far.
13761378
panic(errors.AssertionFailedf("creating an iterator over an unindexed batch"))
13771379
} else {
1378-
i.batch.initInternalIter(&i.opts, &i.batchPointIter)
1379-
i.batch.initRangeDelIter(&i.opts, &i.batchRangeDelIter, i.batchSeqNum)
1380+
i.batch.batch.initInternalIter(&i.opts, &i.batch.pointIter)
1381+
i.batch.batch.initRangeDelIter(&i.opts, &i.batch.rangeDelIter, i.batch.batchSeqNum)
13801382
// Only include the batch's rangedel iterator if it's non-empty.
13811383
// This requires some subtle logic in the case a rangedel is later
13821384
// written to the batch and the view of the batch is refreshed
13831385
// during a call to SetOptions—in this case, we need to reconstruct
13841386
// the point iterator to add the batch rangedel iterator.
13851387
var rangeDelIter keyspan.FragmentIterator
1386-
if i.batchRangeDelIter.Count() > 0 {
1387-
rangeDelIter = &i.batchRangeDelIter
1388+
if i.batch.rangeDelIter.Count() > 0 {
1389+
rangeDelIter = &i.batch.rangeDelIter
13881390
}
13891391
mlevels = append(mlevels, mergingIterLevel{
1390-
iter: &i.batchPointIter,
1392+
iter: &i.batch.pointIter,
13911393
rangeDelIter: rangeDelIter,
13921394
})
13931395
}
@@ -1441,7 +1443,9 @@ func (i *Iterator) constructPointIter(
14411443
buf.merging.levelsPositioned = buf.levelsPositioned[:len(mlevels)]
14421444
}
14431445
buf.merging.snapshot = i.seqNum
1444-
buf.merging.batchSnapshot = i.batchSeqNum
1446+
if i.batch != nil {
1447+
buf.merging.batchSnapshot = i.batch.batchSeqNum
1448+
}
14451449
buf.merging.combinedIterState = &i.lazyCombinedIter.combinedIterState
14461450
i.pointIter = invalidating.MaybeWrapIfInvariants(&buf.merging).(topLevelIterator)
14471451
i.merging = &buf.merging

iterator.go

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -247,27 +247,16 @@ type Iterator struct {
247247
externalIter *externalIterState
248248
// Following fields used when constructing an iterator stack, eg, in Clone
249249
// and SetOptions or when re-fragmenting a batch's range keys/range dels.
250-
// Non-nil if this Iterator includes a Batch.
251-
batch *Batch
252250
fc *fileCacheHandle
253251
newIters tableNewIters
254252
newIterRangeKey keyspanimpl.TableNewSpanIter
255253
lazyCombinedIter lazyCombinedIter
256254
seqNum base.SeqNum
257-
// batchSeqNum is used by Iterators over indexed batches to detect when the
258-
// underlying batch has been mutated. The batch beneath an indexed batch may
259-
// be mutated while the Iterator is open, but new keys are not surfaced
260-
// until the next call to SetOptions.
261-
batchSeqNum base.SeqNum
262-
// batch{PointIter,RangeDelIter,RangeKeyIter} are used when the Iterator is
263-
// configured to read through an indexed batch. If a batch is set, these
264-
// iterators will be included within the iterator stack regardless of
265-
// whether the batch currently contains any keys of their kind. These
266-
// pointers are used during a call to SetOptions to refresh the Iterator's
267-
// view of its indexed batch.
268-
batchPointIter batchIter
269-
batchRangeDelIter keyspan.Iter
270-
batchRangeKeyIter keyspan.Iter
255+
// batch is non-nil if this Iterator includes an indexed batch. Batch
256+
// contains all the state pertaining to iterating over the indexed batch.
257+
// The iteratorBatchState struct is bundled within the iterAlloc struct to
258+
// reduce allocations.
259+
batch *iteratorBatchState
271260
// merging is a pointer to this iterator's point merging iterator. It
272261
// appears here because key visibility is handled by the merging iterator.
273262
// During SetOptions on an iterator over an indexed batch, this field is
@@ -336,6 +325,28 @@ func (i *Iterator) equal(a, b []byte) bool {
336325
return i.comparer.Equal(a, b)
337326
}
338327

328+
// iteratorBatchState holds state pertaining to iterating over an indexed batch.
329+
// When an iterator is configured to read through an indexed batch, the iterator
330+
// maintains a pointer to this struct. This struct is embedded within the
331+
// iterAlloc struct to reduce allocations.
332+
type iteratorBatchState struct {
333+
batch *Batch
334+
// batchSeqNum is used by Iterators over indexed batches to detect when the
335+
// underlying batch has been mutated. The batch beneath an indexed batch may
336+
// be mutated while the Iterator is open, but new keys are not surfaced
337+
// until the next call to SetOptions.
338+
batchSeqNum base.SeqNum
339+
// batch{PointIter,RangeDelIter,RangeKeyIter} are used when the Iterator is
340+
// configured to read through an indexed batch. If a batch is set, these
341+
// iterators will be included within the iterator stack regardless of
342+
// whether the batch currently contains any keys of their kind. These
343+
// pointers are used during a call to SetOptions to refresh the Iterator's
344+
// view of its indexed batch.
345+
pointIter batchIter
346+
rangeDelIter keyspan.Iter
347+
rangeKeyIter keyspan.Iter
348+
}
349+
339350
// iteratorRangeKeyState holds an iterator's range key iteration state.
340351
type iteratorRangeKeyState struct {
341352
// rangeKeyIter holds the range key iterator stack that iterates over the
@@ -2621,18 +2632,18 @@ func (i *Iterator) SetOptions(o *IterOptions) {
26212632
// iterator or range-key iterator but we require one, it'll be created in
26222633
// the slow path that reconstructs the iterator in finishInitializingIter.
26232634
if i.batch != nil {
2624-
nextBatchSeqNum := (base.SeqNum(len(i.batch.data)) | base.SeqNumBatchBit)
2625-
if nextBatchSeqNum != i.batchSeqNum {
2626-
i.batchSeqNum = nextBatchSeqNum
2635+
nextBatchSeqNum := (base.SeqNum(len(i.batch.batch.data)) | base.SeqNumBatchBit)
2636+
if nextBatchSeqNum != i.batch.batchSeqNum {
2637+
i.batch.batchSeqNum = nextBatchSeqNum
26272638
if i.merging != nil {
26282639
i.merging.batchSnapshot = nextBatchSeqNum
26292640
}
26302641
// Prevent a no-op seek optimization on the next seek. We won't be
26312642
// able to reuse the top-level Iterator state, because it may be
26322643
// incorrect after the inclusion of new batch mutations.
26332644
i.batchJustRefreshed = true
2634-
if i.pointIter != nil && i.batch.countRangeDels > 0 {
2635-
if i.batchRangeDelIter.Count() == 0 {
2645+
if i.pointIter != nil && i.batch.batch.countRangeDels > 0 {
2646+
if i.batch.rangeDelIter.Count() == 0 {
26362647
// When we constructed this iterator, there were no
26372648
// rangedels in the batch. Iterator construction will
26382649
// have excluded the batch rangedel iterator from the
@@ -2651,11 +2662,11 @@ func (i *Iterator) SetOptions(o *IterOptions) {
26512662
// which is the count of fragmented range deletions, NOT
26522663
// the number of range deletions written to the batch
26532664
// [i.batch.countRangeDels].
2654-
i.batch.initRangeDelIter(&i.opts, &i.batchRangeDelIter, nextBatchSeqNum)
2665+
i.batch.batch.initRangeDelIter(&i.opts, &i.batch.rangeDelIter, nextBatchSeqNum)
26552666
}
26562667
}
2657-
if i.rangeKey != nil && i.batch.countRangeKeys > 0 {
2658-
if i.batchRangeKeyIter.Count() == 0 {
2668+
if i.rangeKey != nil && i.batch.batch.countRangeKeys > 0 {
2669+
if i.batch.rangeKeyIter.Count() == 0 {
26592670
// When we constructed this iterator, there were no range
26602671
// keys in the batch. Iterator construction will have
26612672
// excluded the batch rangekey iterator from the range key
@@ -2673,7 +2684,7 @@ func (i *Iterator) SetOptions(o *IterOptions) {
26732684
// tell based on i.batchRangeKeyIter.Count(), which is the
26742685
// count of fragmented range keys, NOT the number of
26752686
// range keys written to the batch [i.batch.countRangeKeys].
2676-
i.batch.initRangeKeyIter(&i.opts, &i.batchRangeKeyIter, nextBatchSeqNum)
2687+
i.batch.batch.initRangeKeyIter(&i.opts, &i.batch.rangeKeyIter, nextBatchSeqNum)
26772688
i.invalidate()
26782689
}
26792690
}
@@ -2705,7 +2716,7 @@ func (i *Iterator) SetOptions(o *IterOptions) {
27052716
// used by the iterator now contains range keys. Lazy combined iteration
27062717
// is not compatible with batch range keys because we always need to
27072718
// merge the batch's range keys into iteration.
2708-
if i.rangeKey != nil || !i.opts.rangeKeys() || i.batch == nil || i.batch.countRangeKeys == 0 {
2719+
if i.rangeKey != nil || !i.opts.rangeKeys() || i.batch == nil || i.batch.batch.countRangeKeys == 0 {
27092720
// Fast path. This preserves the Seek-using-Next optimizations as
27102721
// long as the iterator wasn't already invalidated up above.
27112722
return
@@ -2869,21 +2880,22 @@ func (i *Iterator) CloneWithContext(ctx context.Context, opts CloneOptions) (*It
28692880
keyBuf: buf.keyBuf,
28702881
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
28712882
boundsBuf: buf.boundsBuf,
2872-
batch: i.batch,
2873-
batchSeqNum: i.batchSeqNum,
28742883
fc: i.fc,
28752884
newIters: i.newIters,
28762885
newIterRangeKey: i.newIterRangeKey,
28772886
seqNum: i.seqNum,
28782887
}
2879-
dbi.processBounds(dbi.opts.LowerBound, dbi.opts.UpperBound)
2880-
2881-
// If the caller requested the clone have a current view of the indexed
2882-
// batch, set the clone's batch sequence number appropriately.
2883-
if i.batch != nil && opts.RefreshBatchView {
2884-
dbi.batchSeqNum = (base.SeqNum(len(i.batch.data)) | base.SeqNumBatchBit)
2888+
if i.batch != nil {
2889+
dbi.batch = &buf.batchState
2890+
dbi.batch.batch = i.batch.batch
2891+
dbi.batch.batchSeqNum = i.batch.batchSeqNum
2892+
// If the caller requested the clone have a current view of the indexed
2893+
// batch, set the clone's batch sequence number appropriately.
2894+
if opts.RefreshBatchView {
2895+
dbi.batch.batchSeqNum = (base.SeqNum(len(i.batch.batch.data)) | base.SeqNumBatchBit)
2896+
}
28852897
}
2886-
2898+
dbi.processBounds(dbi.opts.LowerBound, dbi.opts.UpperBound)
28872899
return finishInitializingIter(ctx, buf), nil
28882900
}
28892901

range_keys.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ func (i *Iterator) constructRangeKeyIter() {
3131

3232
// If there's an indexed batch with range keys, include it.
3333
if i.batch != nil {
34-
if i.batch.index == nil {
34+
if i.batch.batch.index == nil {
3535
// This isn't an indexed batch. We shouldn't have gotten this far.
3636
panic(errors.AssertionFailedf("creating an iterator over an unindexed batch"))
3737
} else {
3838
// Only include the batch's range key iterator if it has any keys.
3939
// NB: This can force reconstruction of the rangekey iterator stack
4040
// in SetOptions if subsequently range keys are added. See
4141
// SetOptions.
42-
if i.batch.countRangeKeys > 0 {
43-
i.batch.initRangeKeyIter(&i.opts, &i.batchRangeKeyIter, i.batchSeqNum)
44-
i.rangeKey.iterConfig.AddLevel(&i.batchRangeKeyIter)
42+
if i.batch.batch.countRangeKeys > 0 {
43+
i.batch.batch.initRangeKeyIter(&i.opts, &i.batch.rangeKeyIter, i.batch.batchSeqNum)
44+
i.rangeKey.iterConfig.AddLevel(&i.batch.rangeKeyIter)
4545
}
4646
}
4747
}

0 commit comments

Comments
 (0)