Skip to content

Fix a bug in MultiScan that moves iterator backward#14106

Closed
cbi42 wants to merge 3 commits intofacebook:mainfrom
cbi42:multi-scan-always-forward
Closed

Fix a bug in MultiScan that moves iterator backward#14106
cbi42 wants to merge 3 commits intofacebook:mainfrom
cbi42:multi-scan-always-forward

Conversation

@cbi42
Copy link
Contributor

@cbi42 cbi42 commented Nov 6, 2025

Summary: MultiScanUnexpectedSeekTarget() currently uses user key comparison to decide on the next data block for multiscan. This can cause a multiscan to move backward in the following scenario:

data block 1: ..., k@7, k@6
data block 2: k@5, ...

DB iter scan through k@7, k@6 and k@5 and decides to seek to k@0 due to option max_sequential_skip_in_iterations. Multiscan was on data block 2, but moves to data block 1 after the seek.

This can cause assertion failure in debug mode and seg fault in prod since older data blocks are unpinned and freed as we advanced a multiscan. This PR fixes the issue by forcing a multiscan to never go backward.

Test plan:

  • added a new unit test that reproduces the scenario: ./db_iterator_test --gtest_filter="*ReseekAcrossBlocksSameUserKey*"

@meta-cla meta-cla bot added the CLA Signed label Nov 6, 2025
@meta-codesync
Copy link

meta-codesync bot commented Nov 6, 2025

@cbi42 has imported this pull request. If you are a Meta employee, you can view this in D86428845.

@cbi42 cbi42 marked this pull request as ready for review November 6, 2025 17:33
@cbi42 cbi42 requested review from anand1976 and xingbowang November 6, 2025 17:33
Copy link
Contributor

@xingbowang xingbowang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this. Looks good to me.

I have 2 follow up questions.

  1. What if the same key span 2 files with multiple verions?
  2. Would engine run into some kind of dead loop? if it repeatedly do re-seek? Or there is some kind of upper level logic to ensure it only reseek once?

@cbi42
Copy link
Contributor Author

cbi42 commented Nov 7, 2025

  1. What if the same key span 2 files with multiple verions?

I don't think it makes a difference. LevelIterator::Seek() will pick the right file based on the seek key and file key ranges.

  1. Would engine run into some kind of dead loop? if it repeatedly do re-seek? Or there is some kind of upper level logic to ensure it only reseek once?

It looks like we only reseek once for a user key:

rocksdb/db/db_iter.cc

Lines 564 to 567 in 37176a4

if (num_skipped > max_skip_ && !reseek_done) {
is_key_seqnum_zero_ = false;
num_skipped = 0;
reseek_done = true;

If we seek to the last internal key for a user key, I don't think it will trigger another reseek.

@meta-codesync
Copy link

meta-codesync bot commented Nov 7, 2025

@cbi42 merged this pull request in ea75cdc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants