Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

storage: IngestExternalFiles returns "external files have corrupted keys" on Mac #39696

Closed
jeffrey-xiao opened this issue Aug 15, 2019 · 13 comments
Assignees
Labels
C-enhancement Solution expected to add code/behavior + preserve backward-compat (pg compat issues are exception)

Comments

@jeffrey-xiao
Copy link
Contributor

Ingestion of SSTs with range deletion tombstones with this patch returns "external files have corrupted keys" on Macs, but not on Linux. For example, TestImportIntoCSV consistently fails with #39689. Removing the line where we set read_options fixes this error.

cc @nvanbenschoten @ajkr @tbg

@nvanbenschoten
Copy link
Member

IngestRangeDeletionTombstoneWithGlobalSeqno, the test added in that patch, passes on Mac.

@nvanbenschoten
Copy link
Member

I think this is a bug with pinning keys in the FragmentedRangeTombstoneIterator. This diff appears to fix the issue:

diff --git a/db/range_tombstone_fragmenter.h b/db/range_tombstone_fragmenter.h
index a0b77b677..dda1f303d 100644
--- a/db/range_tombstone_fragmenter.h
+++ b/db/range_tombstone_fragmenter.h
@@ -220,8 +220,7 @@ class FragmentedRangeTombstoneIterator : public InternalIterator {
   };

   void MaybePinKey() const {
-    if (pos_ != tombstones_->end() && seq_pos_ != tombstones_->seq_end() &&
-        (pinned_pos_ != pos_ || pinned_seq_pos_ != seq_pos_)) {
+    if (true) {
       current_start_key_.Set(pos_->start_key, *seq_pos_, kTypeRangeDeletion);
       pinned_pos_ = pos_;
       pinned_seq_pos_ = seq_pos_;

@ajkr
Copy link
Contributor

ajkr commented Aug 15, 2019

I wonder if the seq_pos_ != tombstones_->seq_end() is failing only in ingestion preparation because the tombstones all have seqnum zero.

edit: seems doubtful. I wonder which of the conditions are returning an unexpected result.

@nvanbenschoten
Copy link
Member

Good question. It looks like pinned_pos_ != pos_ || pinned_seq_pos_ != seq_pos_ is evaluting to false.

@ajkr
Copy link
Contributor

ajkr commented Aug 16, 2019

Huh, could it be because pinned_pos_ and pinned_seq_pos_ are uninitialized? I really cannot think of any reason why that condition would evaluate to false when our sequence of calls is SeekToFirst(), key(), Next(), key(), Next(), key(), ... seq_pos_ at least should be strictly increasing between consecutive calls to key() -> MaybePinKey(), so it should always differ from pinned_seq_pos_.

If we initialize those in the FragmentedRangeTombstoneIterator constructor to the end() of their respective containers, any chance it changes the outcome?

craig bot pushed a commit that referenced this issue Aug 16, 2019
39695: Revert "storage: reintroduce building SSTs from KV_BATCH snapshot" r=jeffrey-xiao a=jeffrey-xiao

This reverts #39689 due to a Mac only issue with ingestion. #39696 is the tracking issue for the bug.

Release note: None

Co-authored-by: Jeffrey Xiao <jeffrey.xiao1998@gmail.com>
@nvanbenschoten
Copy link
Member

Heh yep, this does the trick:

diff --git a/db/range_tombstone_fragmenter.cc b/db/range_tombstone_fragmenter.cc
index e3eb18908..39126737b 100644
--- a/db/range_tombstone_fragmenter.cc
+++ b/db/range_tombstone_fragmenter.cc
@@ -224,7 +224,9 @@ FragmentedRangeTombstoneIterator::FragmentedRangeTombstoneIterator(
       ucmp_(icmp.user_comparator()),
       tombstones_(tombstones),
       upper_bound_(_upper_bound),
-      lower_bound_(_lower_bound) {
+      lower_bound_(_lower_bound),
+      pinned_pos_(tombstones->end()),
+      pinned_seq_pos_(tombstones->seq_end()) {
   assert(tombstones_ != nullptr);
   Invalidate();
 }
@@ -240,7 +242,9 @@ FragmentedRangeTombstoneIterator::FragmentedRangeTombstoneIterator(
       tombstones_ref_(tombstones),
       tombstones_(tombstones_ref_.get()),
       upper_bound_(_upper_bound),
-      lower_bound_(_lower_bound) {
+      lower_bound_(_lower_bound),
+      pinned_pos_(tombstones->end()),
+      pinned_seq_pos_(tombstones->seq_end()) {
   assert(tombstones_ != nullptr);
   Invalidate();
 }

Did Clang change its treatment of uninitialized member variables across the version we use on Mac and Linux?

Anyway, I'm out tomorrow, so free to turn that into a PR and use it to revert the revert of the reverted revert of Jeffrey's original PR. Let's get that in for good!

@petermattis
Copy link
Collaborator

Did Clang change its treatment of uninitialized member variables across the version we use on Mac and Linux?

Probably not as that stuff has been in the C++ language spec for a while. This could be due to differences in memory allocator patterns causing the members to be accidentally initialized to zero on Linux for this test, but full of garbage on Mac OS X. If my memory serves me, it is invalid to expect an STL iterator to be initialized to any value: you have to initialize it explicitly.

@petermattis
Copy link
Collaborator

petermattis commented Aug 16, 2019

If I'm reading this correctly, vector<T>::const_iterator is a const T*. That means there is no default constructor and thus these members were definitely uninitialized.

Note that a vector<T>::iterator is invalidated by any mutation to the vector such as push_back or erase. I think there is another bug lurking here that we need to invalidate {pinned_pos_,pinned_seq_pos_ whenever tombstones_ is changed.

@petermattis
Copy link
Collaborator

Note that a vector::iterator is invalidated by any mutation to the vector such as push_back or erase. I think there is another bug lurking here that we need to invalidate {pinned_pos_,pinned_seq_pos_ whenever tombstones_ is changed.

Oh, I don't think tombstones_ ever changes. So this comment can be ignored.

@jeffrey-xiao
Copy link
Contributor Author

Closed by cockroachdb/rocksdb#44.

@jeffrey-xiao jeffrey-xiao self-assigned this Aug 16, 2019
@ajkr
Copy link
Contributor

ajkr commented Aug 16, 2019

Probably not as that stuff has been in the C++ language spec for a while. This could be due to differences in memory allocator patterns causing the members to be accidentally initialized to zero on Linux for this test, but full of garbage on Mac OS X.

@danhhz was telling me this week about how uninitialized variables can be treated differently by the compiler from an initialized variable in that the memory contents of an uninitialized variable might be ignored when reading it. Actually that conversation is what led me to this guess - if garbage data or zero data were read for these uninitialized iterators, it'd be highly unlikely to make pinned_pos_ != pos_ || pinned_seq_pos_ != seq_pos_ to be false.

@ajkr
Copy link
Contributor

ajkr commented Aug 16, 2019

BTW, how about reusing this issue to track upstreaming the fix?

@ajkr ajkr reopened this Aug 16, 2019
craig bot pushed a commit that referenced this issue Aug 16, 2019
39709: storage: re-reintroduce building SSTs from KV_BATCH snapshot r=jeffrey-xiao a=jeffrey-xiao

Reverts #39695.

Although CI was passing for the previous introduction, tests were failing locally on Mac. These failures were diagnosed in #39696 and a fix for them is cockroachdb/rocksdb#44.

Additionally, the last commit of this PR fixes an issue with the order with which we put keys in the unreplicated SST.

Co-authored-by: Jeffrey Xiao <jeffrey.xiao1998@gmail.com>
@jeffrey-xiao
Copy link
Contributor Author

Upstream fix is facebook/rocksdb#5720.

@awoods187 awoods187 added the C-enhancement Solution expected to add code/behavior + preserve backward-compat (pg compat issues are exception) label Aug 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Solution expected to add code/behavior + preserve backward-compat (pg compat issues are exception)
Projects
None yet
Development

No branches or pull requests

5 participants