@@ -32,6 +32,9 @@ type twoLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockIterat
32
32
// false - any filtering happens at the top level.
33
33
useFilterBlock bool
34
34
lastBloomFilterMatched bool
35
+
36
+ // Lazy loading flag for top-level index block
37
+ topLevelIndexLoaded bool
35
38
}
36
39
37
40
var _ Iterator = (* twoLevelIteratorRowBlocks )(nil )
@@ -45,6 +48,14 @@ func (i *twoLevelIterator[I, PI, D, PD]) loadSecondLevelIndexBlock(dir int8) loa
45
48
// the index fails.
46
49
PD (& i .secondLevel .data ).Invalidate ()
47
50
PI (& i .secondLevel .index ).Invalidate ()
51
+
52
+ if ! i .topLevelIndexLoaded {
53
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
54
+ i .secondLevel .err = err
55
+ return loadBlockFailed
56
+ }
57
+ }
58
+
48
59
if ! PI (& i .topLevelIndex ).Valid () {
49
60
return loadBlockFailed
50
61
}
@@ -182,14 +193,9 @@ func newColumnBlockTwoLevelIterator(
182
193
objstorage .NoReadBefore , & i .secondLevel .vbRHPrealloc )
183
194
}
184
195
i .secondLevel .data .InitOnce (r .keySchema , r .Comparer , & i .secondLevel .internalValueConstructor )
185
- topLevelIndexH , err := r .readTopLevelIndexBlock (ctx , i .secondLevel .readEnv .Block , i .secondLevel .indexFilterRH )
186
- if err == nil {
187
- err = i .topLevelIndex .InitHandle (r .Comparer , topLevelIndexH , opts .Transforms )
188
- }
189
- if err != nil {
190
- _ = i .Close ()
191
- return nil , err
192
- }
196
+
197
+ // Use lazy loading by default - top-level index will be loaded on first access
198
+ i .topLevelIndexLoaded = false
193
199
return i , nil
194
200
}
195
201
@@ -236,14 +242,8 @@ func newRowBlockTwoLevelIterator(
236
242
i .secondLevel .data .SetHasValuePrefix (true )
237
243
}
238
244
239
- topLevelIndexH , err := r .readTopLevelIndexBlock (ctx , i .secondLevel .readEnv .Block , i .secondLevel .indexFilterRH )
240
- if err == nil {
241
- err = i .topLevelIndex .InitHandle (r .Comparer , topLevelIndexH , opts .Transforms )
242
- }
243
- if err != nil {
244
- _ = i .Close ()
245
- return nil , err
246
- }
245
+ // Use lazy loading by default - top-level index will be loaded on first access
246
+ i .topLevelIndexLoaded = false
247
247
return i , nil
248
248
}
249
249
@@ -286,6 +286,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekGE(
286
286
return nil
287
287
}
288
288
289
+ if ! i .topLevelIndexLoaded {
290
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
291
+ i .secondLevel .err = err
292
+ return nil
293
+ }
294
+ }
295
+
289
296
// SeekGE performs various step-instead-of-seeking optimizations: eg enabled
290
297
// by trySeekUsingNext, or by monotonically increasing bounds (i.boundsCmp).
291
298
@@ -464,6 +471,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekPrefixGE(
464
471
// than the index block containing the sought key, resulting in a wasteful
465
472
// block load.
466
473
474
+ if ! i .topLevelIndexLoaded {
475
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
476
+ i .secondLevel .err = err
477
+ return nil
478
+ }
479
+ }
480
+
467
481
var dontSeekWithinSingleLevelIter bool
468
482
if PI (& i .topLevelIndex ).IsDataInvalidated () || ! PI (& i .topLevelIndex ).Valid () || PI (& i .secondLevel .index ).IsDataInvalidated () || err != nil ||
469
483
(i .secondLevel .boundsCmp <= 0 && ! flags .TrySeekUsingNext ()) || PI (& i .topLevelIndex ).SeparatorLT (key ) {
@@ -623,6 +637,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) virtualLastSeekLE() *base.InternalKV {
623
637
func (i * twoLevelIterator [I , PI , D , PD ]) SeekLT (
624
638
key []byte , flags base.SeekLTFlags ,
625
639
) * base.InternalKV {
640
+ if ! i .topLevelIndexLoaded {
641
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
642
+ i .secondLevel .err = err
643
+ return nil
644
+ }
645
+ }
646
+
626
647
if i .secondLevel .readEnv .Virtual != nil {
627
648
// Might have to fix upper bound since virtual sstable bounds are not
628
649
// known to callers of SeekLT.
@@ -704,6 +725,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekLT(
704
725
// to ensure that key is greater than or equal to the lower bound (e.g. via a
705
726
// call to SeekGE(lower)).
706
727
func (i * twoLevelIterator [I , PI , D , PD ]) First () * base.InternalKV {
728
+ if ! i .topLevelIndexLoaded {
729
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
730
+ i .secondLevel .err = err
731
+ return nil
732
+ }
733
+ }
707
734
// If we have a lower bound, use SeekGE. Note that in general this is not
708
735
// supported usage, except when the lower bound is there because the table is
709
736
// virtual.
@@ -749,6 +776,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) First() *base.InternalKV {
749
776
// to ensure that key is less than the upper bound (e.g. via a call to
750
777
// SeekLT(upper))
751
778
func (i * twoLevelIterator [I , PI , D , PD ]) Last () * base.InternalKV {
779
+ if ! i .topLevelIndexLoaded {
780
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
781
+ i .secondLevel .err = err
782
+ return nil
783
+ }
784
+ }
785
+
752
786
if i .secondLevel .readEnv .Virtual != nil {
753
787
if i .secondLevel .endKeyInclusive {
754
788
return i .virtualLast ()
@@ -796,6 +830,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) Last() *base.InternalKV {
796
830
// Note: twoLevelCompactionIterator.Next mirrors the implementation of
797
831
// twoLevelIterator.Next due to performance. Keep the two in sync.
798
832
func (i * twoLevelIterator [I , PI , D , PD ]) Next () * base.InternalKV {
833
+ if ! i .topLevelIndexLoaded {
834
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
835
+ i .secondLevel .err = err
836
+ return nil
837
+ }
838
+ }
799
839
// Seek optimization only applies until iterator is first positioned after SetBounds.
800
840
i .secondLevel .boundsCmp = 0
801
841
if i .secondLevel .err != nil {
@@ -814,6 +854,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) NextPrefix(succKey []byte) *base.Intern
814
854
if i .secondLevel .exhaustedBounds == + 1 {
815
855
panic ("Next called even though exhausted upper bound" )
816
856
}
857
+
858
+ if ! i .topLevelIndexLoaded {
859
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
860
+ i .secondLevel .err = err
861
+ return nil
862
+ }
863
+ }
817
864
// Seek optimization only applies until iterator is first positioned after SetBounds.
818
865
i .secondLevel .boundsCmp = 0
819
866
if i .secondLevel .err != nil {
@@ -861,6 +908,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) NextPrefix(succKey []byte) *base.Intern
861
908
// Prev implements internalIterator.Prev, as documented in the pebble
862
909
// package.
863
910
func (i * twoLevelIterator [I , PI , D , PD ]) Prev () * base.InternalKV {
911
+ if ! i .topLevelIndexLoaded {
912
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
913
+ i .secondLevel .err = err
914
+ return nil
915
+ }
916
+ }
864
917
// Seek optimization only applies until iterator is first positioned after SetBounds.
865
918
i .secondLevel .boundsCmp = 0
866
919
if i .secondLevel .err != nil {
@@ -1020,8 +1073,27 @@ func (i *twoLevelIterator[I, PI, D, PD]) Close() error {
1020
1073
err = firstError (err , PI (& i .topLevelIndex ).Close ())
1021
1074
i .useFilterBlock = false
1022
1075
i .lastBloomFilterMatched = false
1076
+ i .topLevelIndexLoaded = false
1023
1077
if pool != nil {
1024
1078
pool .Put (i )
1025
1079
}
1026
1080
return err
1027
1081
}
1082
+
1083
+ func (i * twoLevelIterator [I , PI , D , PD ]) ensureTopLevelIndexLoaded () error {
1084
+ if i .topLevelIndexLoaded {
1085
+ return nil
1086
+ }
1087
+
1088
+ topLevelIndexH , err := i .secondLevel .reader .readTopLevelIndexBlock (i .secondLevel .ctx ,
1089
+ i .secondLevel .readEnv .Block , i .secondLevel .indexFilterRH )
1090
+ if err == nil {
1091
+ err = PI (& i .topLevelIndex ).InitHandle (i .secondLevel .reader .Comparer ,
1092
+ topLevelIndexH , i .secondLevel .transforms )
1093
+ }
1094
+ if err != nil {
1095
+ return err
1096
+ }
1097
+ i .topLevelIndexLoaded = true
1098
+ return nil
1099
+ }
0 commit comments