Skip to content

Commit 1cedb60

Browse files
committed
sstable: don't skipBackward past empty blocks with hideObsolete
Currently, if we skipBackward in singleLevelIterator and come across a seemingly empty data block, we stop right there. This is not safe if hide obsolete points is true, as the block could have just been obsolete in its entirety. This change makes the iterator continue iterating backward until some other termination condition is exhausted. This matches the behaviour for two-level iterators, as well as for skipForward in single-level iterators. Fixes #3761.
1 parent 4cf4f85 commit 1cedb60

File tree

3 files changed

+53
-1
lines changed

3 files changed

+53
-1
lines changed

sstable/reader_iter_single_lvl.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,15 @@ func (i *singleLevelIterator) skipBackward() *base.InternalKV {
14471447
}
14481448
kv := i.data.Last()
14491449
if kv == nil {
1450-
return nil
1450+
// The block iter could have hid some obsolete points, so it isn't
1451+
// safe to assume that there are no keys if we keep skipping backwards.
1452+
// Check the previous block, but check the lower bound before doing
1453+
// that.
1454+
if i.lower != nil && i.cmp(indexKey.K.UserKey, i.lower) < 0 {
1455+
i.exhaustedBounds = -1
1456+
return nil
1457+
}
1458+
continue
14511459
}
14521460
if i.blockLower != nil && i.cmp(kv.K.UserKey, i.blockLower) < 0 {
14531461
i.exhaustedBounds = -1

sstable/reader_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,17 @@ func TestReaderHideObsolete(t *testing.T) {
545545
t, opts, "testdata/reader_hide_obsolete",
546546
nil /* Reader */, true)
547547
})
548+
opts = WriterOptions{
549+
TableFormat: TableFormatPebblev4,
550+
BlockSize: blockSize,
551+
IndexBlockSize: 1 << 30, // Force a single-level index block.
552+
Comparer: testkeys.Comparer,
553+
}
554+
t.Run(fmt.Sprintf("singleLevel/blockSize=%s", dName), func(t *testing.T) {
555+
runTestReader(
556+
t, opts, "testdata/reader_hide_obsolete",
557+
nil /* Reader */, true)
558+
})
548559
}
549560
}
550561

@@ -797,6 +808,13 @@ func runTestReader(t *testing.T, o WriterOptions, dir string, r *Reader, printVa
797808
require.True(t, retHideObsoletePoints)
798809
}
799810
}
811+
if d.HasArg("hide-obsolete-points-without-filter") {
812+
var hideObsoletePoints bool
813+
d.ScanArgs(t, "hide-obsolete-points-without-filter", &hideObsoletePoints)
814+
if hideObsoletePoints {
815+
transforms.HideObsoletePoints = true
816+
}
817+
}
800818
var filterer *BlockPropertiesFilterer
801819
if len(bpfs) > 0 {
802820
filterer = newBlockPropertiesFilterer(bpfs, nil, nil)

sstable/testdata/reader_hide_obsolete/iter

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,29 @@ d.SINGLEDEL.40:
316316
d.MERGE.30:D30
317317
----
318318
MERGE not supported in a strict-obsolete sstable
319+
320+
# Regression test for #3761. If an entire block contains obsolete points,
321+
# skipBackward should still skip to blocks earlier in the sstable.
322+
323+
build writing-to-lowest-level
324+
a.SET.10:A10
325+
force-obsolete: b.DEL.5:
326+
force-obsolete: b.SETWITHDEL.2:foo
327+
force-obsolete: c.DEL.0:
328+
force-obsolete: cc.DEL.5:
329+
----
330+
331+
iter hide-obsolete-points-without-filter=true
332+
last
333+
----
334+
<a:10>:A10
335+
336+
iter hide-obsolete-points-without-filter=true
337+
seek-lt d
338+
----
339+
<a:10>:A10
340+
341+
iter hide-obsolete-points-without-filter=true
342+
seek-lt c
343+
----
344+
<a:10>:A10

0 commit comments

Comments
 (0)