@@ -32,6 +32,12 @@ 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
+ // topLevelIndexLoaded is set to true if the top-level index block load
37
+ // operation completed regardless of success or failure. In case of failure,
38
+ // topLevelIndexLoadError is set to avoid retrying the load operation.
39
+ topLevelIndexLoaded bool
40
+ topLevelIndexLoadError error
35
41
}
36
42
37
43
var _ Iterator = (* twoLevelIteratorRowBlocks )(nil )
@@ -45,6 +51,13 @@ func (i *twoLevelIterator[I, PI, D, PD]) loadSecondLevelIndexBlock(dir int8) loa
45
51
// the index fails.
46
52
PD (& i .secondLevel .data ).Invalidate ()
47
53
PI (& i .secondLevel .index ).Invalidate ()
54
+
55
+ // Ensure top-level index is loaded before accessing it
56
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
57
+ i .secondLevel .err = err
58
+ return loadBlockFailed
59
+ }
60
+
48
61
if ! PI (& i .topLevelIndex ).Valid () {
49
62
return loadBlockFailed
50
63
}
@@ -87,6 +100,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) loadSecondLevelIndexBlock(dir int8) loa
87
100
// appropriate bound, depending on the iteration direction, and returns either
88
101
// `blockIntersects` or `blockExcluded`.
89
102
func (i * twoLevelIterator [I , PI , D , PD ]) resolveMaybeExcluded (dir int8 ) intersectsResult {
103
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
104
+ i .secondLevel .err = err
105
+ return blockExcluded // Conservative choice when we can't load the index
106
+ }
107
+
90
108
// This iterator is configured with a bound-limited block property filter.
91
109
// The bpf determined this entire index block could be excluded from
92
110
// iteration based on the property encoded in the block handle. However, we
@@ -181,14 +199,7 @@ func newColumnBlockTwoLevelIterator(
181
199
objstorage .NoReadBefore , & i .secondLevel .vbRHPrealloc )
182
200
}
183
201
i .secondLevel .data .InitOnce (r .keySchema , r .Comparer , & i .secondLevel .internalValueConstructor )
184
- topLevelIndexH , err := r .readTopLevelIndexBlock (ctx , i .secondLevel .readEnv .Block , i .secondLevel .indexFilterRH )
185
- if err == nil {
186
- err = i .topLevelIndex .InitHandle (r .Comparer , topLevelIndexH , opts .Transforms )
187
- }
188
- if err != nil {
189
- _ = i .Close ()
190
- return nil , err
191
- }
202
+
192
203
return i , nil
193
204
}
194
205
@@ -235,14 +246,6 @@ func newRowBlockTwoLevelIterator(
235
246
i .secondLevel .data .SetHasValuePrefix (true )
236
247
}
237
248
238
- topLevelIndexH , err := r .readTopLevelIndexBlock (ctx , i .secondLevel .readEnv .Block , i .secondLevel .indexFilterRH )
239
- if err == nil {
240
- err = i .topLevelIndex .InitHandle (r .Comparer , topLevelIndexH , opts .Transforms )
241
- }
242
- if err != nil {
243
- _ = i .Close ()
244
- return nil , err
245
- }
246
249
return i , nil
247
250
}
248
251
@@ -275,6 +278,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekGE(
275
278
err := i .secondLevel .err
276
279
i .secondLevel .err = nil // clear cached iteration error
277
280
281
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
282
+ i .secondLevel .err = err
283
+ return nil
284
+ }
285
+
278
286
// The twoLevelIterator could be already exhausted. Utilize that when
279
287
// trySeekUsingNext is true. See the comment about data-exhausted, PGDE, and
280
288
// bounds-exhausted near the top of the file.
@@ -417,6 +425,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekPrefixGE(
417
425
err := i .secondLevel .err
418
426
i .secondLevel .err = nil // clear cached iteration error
419
427
428
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
429
+ i .secondLevel .err = err
430
+ return nil
431
+ }
432
+
420
433
// The twoLevelIterator could be already exhausted. Utilize that when
421
434
// trySeekUsingNext is true. See the comment about data-exhausted, PGDE, and
422
435
// bounds-exhausted near the top of the file.
@@ -584,6 +597,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) virtualLastSeekLE() *base.InternalKV {
584
597
panic ("unexpected virtualLastSeekLE with exclusive upper bounds" )
585
598
}
586
599
key := i .secondLevel .upper
600
+
601
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
602
+ i .secondLevel .err = err
603
+ return nil
604
+ }
605
+
587
606
// Need to position the topLevelIndex.
588
607
//
589
608
// The previous exhausted state of singleLevelIterator is no longer
@@ -641,6 +660,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) SeekLT(
641
660
// Seek optimization only applies until iterator is first positioned after SetBounds.
642
661
i .secondLevel .boundsCmp = 0
643
662
663
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
664
+ i .secondLevel .err = err
665
+ return nil
666
+ }
667
+
644
668
var result loadBlockResult
645
669
// NB: Unlike SeekGE, we don't have a fast-path here since we don't know
646
670
// whether the topLevelIndex is positioned after the position that would
@@ -714,6 +738,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) First() *base.InternalKV {
714
738
// Seek optimization only applies until iterator is first positioned after SetBounds.
715
739
i .secondLevel .boundsCmp = 0
716
740
741
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
742
+ i .secondLevel .err = err
743
+ return nil
744
+ }
745
+
717
746
if ! PI (& i .topLevelIndex ).First () {
718
747
return nil
719
748
}
@@ -763,6 +792,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) Last() *base.InternalKV {
763
792
// Seek optimization only applies until iterator is first positioned after SetBounds.
764
793
i .secondLevel .boundsCmp = 0
765
794
795
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
796
+ i .secondLevel .err = err
797
+ return nil
798
+ }
799
+
766
800
if ! PI (& i .topLevelIndex ).Last () {
767
801
return nil
768
802
}
@@ -830,6 +864,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) NextPrefix(succKey []byte) *base.Intern
830
864
831
865
// Did not find prefix in the existing second-level index block. This is the
832
866
// slow-path where we seek the iterator.
867
+
868
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
869
+ i .secondLevel .err = err
870
+ return nil
871
+ }
872
+
833
873
if ! PI (& i .topLevelIndex ).SeekGE (succKey ) {
834
874
PD (& i .secondLevel .data ).Invalidate ()
835
875
PI (& i .secondLevel .index ).Invalidate ()
@@ -877,6 +917,11 @@ func (i *twoLevelIterator[I, PI, D, PD]) skipForward() *base.InternalKV {
877
917
return nil
878
918
}
879
919
920
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
921
+ i .secondLevel .err = err
922
+ return nil
923
+ }
924
+
880
925
// It is possible that skipBackward went too far and the virtual table lower
881
926
// bound is after the first key in the block we are about to load, in which
882
927
// case we must use SeekGE below. The keys in the block we are about to load
@@ -954,6 +999,12 @@ func (i *twoLevelIterator[I, PI, D, PD]) skipBackward() *base.InternalKV {
954
999
if i .secondLevel .err != nil || i .secondLevel .exhaustedBounds < 0 {
955
1000
return nil
956
1001
}
1002
+
1003
+ if err := i .ensureTopLevelIndexLoaded (); err != nil {
1004
+ i .secondLevel .err = err
1005
+ return nil
1006
+ }
1007
+
957
1008
i .secondLevel .exhaustedBounds = 0
958
1009
if ! PI (& i .topLevelIndex ).Prev () {
959
1010
PD (& i .secondLevel .data ).Invalidate ()
@@ -1009,6 +1060,39 @@ func (i *twoLevelIterator[I, PI, D, PD]) SetupForCompaction() {
1009
1060
1010
1061
// Close implements internalIterator.Close, as documented in the pebble
1011
1062
// package.
1063
+ func (i * twoLevelIterator [I , PI , D , PD ]) ensureTopLevelIndexLoaded () error {
1064
+ // If already loaded, return cached error (nil means success)
1065
+ if i .topLevelIndexLoaded {
1066
+ return i .topLevelIndexLoadError
1067
+ }
1068
+
1069
+ // Perform the deferred top-level index loading calls
1070
+ topLevelIndexH , err := i .secondLevel .reader .readTopLevelIndexBlock (
1071
+ i .secondLevel .ctx ,
1072
+ i .secondLevel .readEnv .Block ,
1073
+ i .secondLevel .indexFilterRH ,
1074
+ )
1075
+ if err != nil {
1076
+ i .topLevelIndexLoadError = err
1077
+ i .topLevelIndexLoaded = true // Mark as loaded to avoid retry
1078
+ return err
1079
+ }
1080
+
1081
+ err = PI (& i .topLevelIndex ).InitHandle (
1082
+ i .secondLevel .reader .Comparer ,
1083
+ topLevelIndexH ,
1084
+ i .secondLevel .transforms ,
1085
+ )
1086
+ if err != nil {
1087
+ i .topLevelIndexLoadError = err
1088
+ i .topLevelIndexLoaded = true
1089
+ return err
1090
+ }
1091
+
1092
+ i .topLevelIndexLoaded = true
1093
+ return nil
1094
+ }
1095
+
1012
1096
func (i * twoLevelIterator [I , PI , D , PD ]) Close () error {
1013
1097
if invariants .Enabled && i .secondLevel .pool != nil {
1014
1098
panic ("twoLevelIterator's singleLevelIterator has its own non-nil pool" )
@@ -1019,6 +1103,8 @@ func (i *twoLevelIterator[I, PI, D, PD]) Close() error {
1019
1103
err = firstError (err , PI (& i .topLevelIndex ).Close ())
1020
1104
i .useFilterBlock = false
1021
1105
i .lastBloomFilterMatched = false
1106
+ i .topLevelIndexLoaded = false
1107
+ i .topLevelIndexLoadError = nil
1022
1108
if pool != nil {
1023
1109
pool .Put (i )
1024
1110
}
0 commit comments