@@ -165,6 +165,10 @@ type singleLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockIte
165
165
useFilterBlock bool
166
166
lastBloomFilterMatched bool
167
167
168
+ // indexLoaded is set to true if the index block load operation completed
169
+ // successfully.
170
+ indexLoaded bool
171
+
168
172
transforms IterTransforms
169
173
170
174
// All fields above this field are cleared when resetting the iterator for reuse.
@@ -191,9 +195,8 @@ type singleLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockIte
191
195
// singleLevelIterator implements the base.InternalIterator interface.
192
196
var _ base.InternalIterator = (* singleLevelIteratorRowBlocks )(nil )
193
197
194
- // newColumnBlockSingleLevelIterator reads the index block and creates and
195
- // initializes a singleLevelIterator over an sstable with column-oriented data
196
- // blocks.
198
+ // newColumnBlockSingleLevelIterator creates a singleLevelIterator over an
199
+ // sstable with column-oriented data blocks that loads the index block lazily.
197
200
//
198
201
// Note that lower, upper are iterator bounds and are separate from virtual
199
202
// sstable bounds. If the virtualState passed in is not nil, then virtual
@@ -215,20 +218,12 @@ func newColumnBlockSingleLevelIterator(
215
218
i .vbRH = r .blockReader .UsePreallocatedReadHandle (objstorage .NoReadBefore , & i .vbRHPrealloc )
216
219
}
217
220
i .data .InitOnce (r .keySchema , r .Comparer , & i .internalValueConstructor )
218
- indexH , err := r .readTopLevelIndexBlock (ctx , i .readEnv .Block , i .indexFilterRH )
219
- if err == nil {
220
- err = i .index .InitHandle (r .Comparer , indexH , opts .Transforms )
221
- }
222
- if err != nil {
223
- _ = i .Close ()
224
- return nil , err
225
- }
221
+
226
222
return i , nil
227
223
}
228
224
229
- // newRowBlockSingleLevelIterator reads the index block and creates and
230
- // initializes a singleLevelIterator over an sstable with row-oriented data
231
- // blocks.
225
+ // newRowBlockSingleLevelIterator creates a singleLevelIterator over an
226
+ // sstable with row-oriented data blocks that loads the index block lazily.
232
227
//
233
228
// Note that lower, upper are iterator bounds and are separate from virtual
234
229
// sstable bounds. If the virtualState passed in is not nil, then virtual
@@ -256,20 +251,13 @@ func newRowBlockSingleLevelIterator(
256
251
i .data .SetHasValuePrefix (true )
257
252
}
258
253
259
- indexH , err := r .readTopLevelIndexBlock (ctx , i .readEnv .Block , i .indexFilterRH )
260
- if err == nil {
261
- err = i .index .InitHandle (r .Comparer , indexH , opts .Transforms )
262
- }
263
- if err != nil {
264
- _ = i .Close ()
265
- return nil , err
266
- }
267
254
return i , nil
268
255
}
269
256
270
257
// init initializes the singleLevelIterator struct. It does not read the index.
271
258
func (i * singleLevelIterator [I , PI , D , PD ]) init (ctx context.Context , r * Reader , opts IterOptions ) {
272
259
i .inPool = false
260
+ i .indexLoaded = false
273
261
i .ctx = ctx
274
262
i .lower = opts .Lower
275
263
i .upper = opts .Upper
@@ -316,7 +304,7 @@ func (i *singleLevelIterator[I, PI, D, PD]) SetupForCompaction() {
316
304
317
305
const clearLen = unsafe .Offsetof (singleLevelIteratorRowBlocks {}.clearForResetBoundary )
318
306
319
- // Assert that clearLen is consistent betwen the row and columnar implementations.
307
+ // Assert that clearLen is consistent between the row and columnar implementations.
320
308
const clearLenColBlocks = unsafe .Offsetof (singleLevelIteratorColumnBlocks {}.clearForResetBoundary )
321
309
const _ uintptr = clearLen - clearLenColBlocks
322
310
const _ uintptr = clearLenColBlocks - clearLen
@@ -451,6 +439,11 @@ func (i *singleLevelIterator[I, PI, P, PD]) SetContext(ctx context.Context) {
451
439
// unpositioned. If unsuccessful, it sets i.err to any error encountered, which
452
440
// may be nil if we have simply exhausted the entire table.
453
441
func (i * singleLevelIterator [I , PI , P , PD ]) loadDataBlock (dir int8 ) loadBlockResult {
442
+ if ! i .ensureIndexLoaded () {
443
+ // Ensure the data block iterator is invalidated
444
+ PD (& i .data ).Invalidate ()
445
+ return loadBlockFailed
446
+ }
454
447
if ! PI (& i .index ).Valid () {
455
448
// Ensure the data block iterator is invalidated even if loading of the block
456
449
// fails.
@@ -681,6 +674,9 @@ func (i *singleLevelIterator[I, PI, D, PD]) SeekGE(
681
674
func (i * singleLevelIterator [I , PI , D , PD ]) seekGEHelper (
682
675
key []byte , boundsCmp int , flags base.SeekGEFlags ,
683
676
) * base.InternalKV {
677
+ if ! i .ensureIndexLoaded () {
678
+ return nil
679
+ }
684
680
// Invariant: trySeekUsingNext => !i.data.isDataInvalidated() && i.exhaustedBounds != +1
685
681
686
682
// SeekGE performs various step-instead-of-seeking optimizations: eg enabled
@@ -904,6 +900,10 @@ func (i *singleLevelIterator[I, PI, D, PD]) virtualLast() *base.InternalKV {
904
900
// uses of this method in the future. Does a SeekLE on the upper bound of the
905
901
// file/iterator.
906
902
func (i * singleLevelIterator [I , PI , D , PD ]) virtualLastSeekLE () * base.InternalKV {
903
+ if ! i .ensureIndexLoaded () {
904
+ return nil
905
+ }
906
+
907
907
// Callers of SeekLE don't know about virtual sstable bounds, so we may
908
908
// have to internally restrict the bounds.
909
909
//
@@ -1013,6 +1013,10 @@ func (i *singleLevelIterator[I, PI, D, PD]) SeekLT(
1013
1013
// Seek optimization only applies until iterator is first positioned after SetBounds.
1014
1014
i .boundsCmp = 0
1015
1015
1016
+ if ! i .ensureIndexLoaded () {
1017
+ return nil
1018
+ }
1019
+
1016
1020
// Seeking operations perform various step-instead-of-seeking optimizations:
1017
1021
// eg by considering monotonically increasing bounds (i.boundsCmp).
1018
1022
@@ -1120,6 +1124,10 @@ func (i *singleLevelIterator[I, PI, D, PD]) firstInternal() *base.InternalKV {
1120
1124
// Seek optimization only applies until iterator is first positioned after SetBounds.
1121
1125
i .boundsCmp = 0
1122
1126
1127
+ if ! i .ensureIndexLoaded () {
1128
+ return nil
1129
+ }
1130
+
1123
1131
if ! PI (& i .index ).First () {
1124
1132
PD (& i .data ).Invalidate ()
1125
1133
return nil
@@ -1184,6 +1192,10 @@ func (i *singleLevelIterator[I, PI, D, PD]) lastInternal() *base.InternalKV {
1184
1192
// Seek optimization only applies until iterator is first positioned after SetBounds.
1185
1193
i .boundsCmp = 0
1186
1194
1195
+ if ! i .ensureIndexLoaded () {
1196
+ return nil
1197
+ }
1198
+
1187
1199
if ! PI (& i .index ).Last () {
1188
1200
PD (& i .data ).Invalidate ()
1189
1201
return nil
@@ -1272,6 +1284,9 @@ func (i *singleLevelIterator[I, PI, D, PD]) NextPrefix(succKey []byte) *base.Int
1272
1284
// Did not find prefix in the existing data block. This is the slow-path
1273
1285
// where we effectively seek the iterator.
1274
1286
// The key is likely to be in the next data block, so try one step.
1287
+ if ! i .ensureIndexLoaded () {
1288
+ return nil
1289
+ }
1275
1290
if ! PI (& i .index ).Next () {
1276
1291
// The target key is greater than any key in the index block.
1277
1292
// Invalidate the block iterator so that a subsequent call to Prev()
@@ -1343,6 +1358,9 @@ func (i *singleLevelIterator[I, PI, D, PD]) Prev() *base.InternalKV {
1343
1358
1344
1359
func (i * singleLevelIterator [I , PI , D , PD ]) skipForward () * base.InternalKV {
1345
1360
for {
1361
+ if ! i .ensureIndexLoaded () {
1362
+ return nil
1363
+ }
1346
1364
if ! PI (& i .index ).Next () {
1347
1365
PD (& i .data ).Invalidate ()
1348
1366
break
@@ -1421,6 +1439,9 @@ func (i *singleLevelIterator[I, PI, D, PD]) skipForward() *base.InternalKV {
1421
1439
1422
1440
func (i * singleLevelIterator [I , PI , D , PD ]) skipBackward () * base.InternalKV {
1423
1441
for {
1442
+ if ! i .ensureIndexLoaded () {
1443
+ return nil
1444
+ }
1424
1445
if ! PI (& i .index ).Prev () {
1425
1446
PD (& i .data ).Invalidate ()
1426
1447
break
@@ -1545,3 +1566,25 @@ func (i *singleLevelIterator[I, PI, D, PD]) String() string {
1545
1566
func (i * singleLevelIterator [I , PI , D , PD ]) DebugTree (tp treeprinter.Node ) {
1546
1567
tp .Childf ("%T(%p) fileNum=%s" , i , i , i .String ())
1547
1568
}
1569
+
1570
+ func (i * singleLevelIterator [I , PI , D , PD ]) ensureIndexLoaded () bool {
1571
+ if i .indexLoaded {
1572
+ return true
1573
+ }
1574
+
1575
+ // Perform the deferred index loading calls
1576
+ indexH , err := i .reader .readTopLevelIndexBlock (i .ctx , i .readEnv .Block , i .indexFilterRH )
1577
+ if err != nil {
1578
+ i .err = err
1579
+ return false
1580
+ }
1581
+
1582
+ err = PI (& i .index ).InitHandle (i .reader .Comparer , indexH , i .transforms )
1583
+ if err != nil {
1584
+ i .err = err
1585
+ return false
1586
+ }
1587
+
1588
+ i .indexLoaded = true
1589
+ return true
1590
+ }
0 commit comments