-
Notifications
You must be signed in to change notification settings - Fork 30
SDSTOR-22729: index/wb_cache: Fix recovery corruption after root-split crash #900
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,9 @@ void IndexCPContext::add_to_txn_journal(uint32_t index_ordinal, const IndexBuffe | |
| auto record_size = txn_record::size_for_num_ids(created_bufs.size() + freed_bufs.size() + (left_child_buf ? 1 : 0) + | ||
| (parent_buf ? 1 : 0)); | ||
| std::unique_lock< iomgr::FiberManagerLib::mutex > lg{m_txn_journal_mtx}; | ||
| if (parent_buf && parent_buf->is_meta_buf() && !left_child_buf && !created_bufs.empty()) { | ||
| m_root_changed_ordinals.insert(index_ordinal); | ||
| } | ||
| if (m_txn_journal_buf.bytes() == nullptr) { | ||
| m_txn_journal_buf = | ||
| std::move(sisl::io_blob_safe{std::max(sizeof(txn_journal), 512ul), 512, sisl::buftag::metablk}); | ||
|
|
@@ -64,6 +67,27 @@ void IndexCPContext::add_to_txn_journal(uint32_t index_ordinal, const IndexBuffe | |
| } | ||
| } | ||
|
|
||
| IndexBufferPtrList IndexCPContext::root_change_preflush_bufs() { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this collects all the bufs , excluding meta_buf and freed buf, in the cp where a new root if found(root split). we need to flush these bufs for new root recovery if crash happens. |
||
| IndexBufferPtrList bufs; | ||
| std::set< BlkId > selected_blkids; | ||
| std::unique_lock< iomgr::FiberManagerLib::mutex > lg{m_txn_journal_mtx}; | ||
| if (m_root_changed_ordinals.empty()) { return bufs; } | ||
|
|
||
| m_dirty_buf_list.foreach_entry([this, &bufs, &selected_blkids](IndexBufferPtr const& buf) { | ||
| if (buf->is_meta_buf() || buf->m_node_freed || buf->m_created_cp_id != id() || | ||
| !m_root_changed_ordinals.contains(buf->m_index_ordinal)) { | ||
| return; | ||
| } | ||
| if (selected_blkids.insert(buf->blkid()).second) { bufs.push_back(buf); } | ||
| }); | ||
| return bufs; | ||
| } | ||
|
|
||
| BlkId IndexCPContext::recovered_root_id(uint32_t ordinal) const { | ||
| auto const it = m_recovered_root_ids.find(ordinal); | ||
| return it == m_recovered_root_ids.end() ? BlkId{} : it->second; | ||
| } | ||
|
|
||
| void IndexCPContext::add_to_dirty_list(const IndexBufferPtr& buf) { | ||
| m_dirty_buf_list.push_back(buf); | ||
| buf->set_state(index_buf_state_t::DIRTY); | ||
|
|
@@ -246,6 +270,17 @@ std::map< BlkId, IndexBufferPtr > IndexCPContext::recover(sisl::byte_view sb) { | |
| txn_record const* rec = r_cast< txn_record const* >(cur_ptr); | ||
| HS_DBG_ASSERT_GT(rec->total_ids(), 0, "Invalid txn_record, has no ids in it"); | ||
|
|
||
| // Root-change records contain no split/merge side effects. Retaining the last record per ordinal identifies | ||
| // the final intended root even if the same CP grows and then collapses the tree. | ||
| if (rec->is_parent_meta && rec->num_freed_ids == 0) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this part is used to identify what is the final root according to txn_journal |
||
| bool const is_root_split = !rec->has_inplace_child && rec->num_new_ids == 1; | ||
| bool const is_root_collapse = rec->has_inplace_child && rec->num_new_ids == 0; | ||
| if (is_root_split || is_root_collapse) { | ||
| auto const root_idx = rec->has_inplace_parent ? 1 : 0; | ||
| m_recovered_root_ids[rec->index_ordinal] = rec->blk_id(root_idx); | ||
| } | ||
| } | ||
|
|
||
| process_txn_record(rec, buf_map); | ||
| cur_ptr += rec->size(); | ||
| LOGTRACEMOD(wbcache, "Recovered txn record: {}: {}", t, rec->to_string()); | ||
|
|
@@ -264,24 +299,11 @@ std::map< BlkId, IndexBufferPtr > IndexCPContext::recover(sisl::byte_view sb) { | |
| buffer->m_up_buffer->to_string()); | ||
| } | ||
| }; | ||
| #if 0 | ||
| auto dag_print = [](const std::map< BlkId, IndexBufferPtr >& dags, std::string delimiter) { | ||
| int index = 1; | ||
| for (const auto& [blkid, bufferPtr] : dags) { | ||
| LOGTRACEMOD(wbcache, "{}{} - blkid {} buffer {} ", delimiter, index++, blkid.to_integer(), | ||
| bufferPtr->to_string()); | ||
| } | ||
| }; | ||
| LOGTRACEMOD(wbcache,"Before modify : \n "); | ||
| dag_print(buf_map, "Before: "); | ||
| #endif | ||
| for (auto& [blkid, bufferPtr] : buf_map) { | ||
| modifyBuffer(bufferPtr); | ||
| } | ||
| // LOGTRACEMOD(wbcache,"\n\n\nAFTER modify : \n "); | ||
| // dag_print(buf_map, "After: "); | ||
|
|
||
| auto sanityCheck = [](const std::map< BlkId, IndexBufferPtr >& dags) { | ||
| auto sanityCheck = [cp_id = id()](const std::map< BlkId, IndexBufferPtr >& dags) { | ||
| for (const auto& [blkid, bufferPtr] : dags) { | ||
| auto up_buffer = bufferPtr->m_up_buffer; | ||
| if (up_buffer) { | ||
|
|
@@ -290,9 +312,11 @@ std::map< BlkId, IndexBufferPtr > IndexCPContext::recover(sisl::byte_view sb) { | |
| "Sanity check failed: Buffer {} blkdid {} has an up_buffer {} blkid that is marked as freed.", | ||
| bufferPtr->to_string(), blkid.to_integer(), up_buffer->to_string(), | ||
| up_buffer->blkid().to_integer()); | ||
| HS_REL_ASSERT(up_buffer->m_created_cp_id == -1, | ||
| "Sanity check failed: Buffer {} has an up_buffer {} that just created (created_cp_id={})", | ||
| bufferPtr->to_string(), up_buffer->to_string(), up_buffer->m_created_cp_id); | ||
| if (bufferPtr->m_created_cp_id == cp_id) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have linked the old root to new root(new root is created in the cp , but it is now a valid up_buffer) for root split case, so change this check. |
||
| HS_REL_ASSERT(up_buffer->m_created_cp_id != cp_id, | ||
| "Sanity check failed: new Buffer {} has an up_buffer {} created in the same CP ({})", | ||
| bufferPtr->to_string(), up_buffer->to_string(), cp_id); | ||
| } | ||
| HS_REL_ASSERT(up_buffer->m_index_ordinal == bufferPtr->m_index_ordinal, | ||
| "Sanity check failed: Buffer {} has an up_buffer {} with different index_ordinal " | ||
| "(up_ordinal={}, buf_ordinal={})", | ||
|
|
@@ -331,7 +355,8 @@ void IndexCPContext::process_txn_record(txn_record const* rec, std::map< BlkId, | |
| auto cpg = cp_mgr().cp_guard(); | ||
|
|
||
| auto const rec_to_buf = [&buf_map, &cpg](txn_record const* rec, bool is_meta, BlkId const& bid, | ||
| IndexBufferPtr const& up_buf) -> IndexBufferPtr { | ||
| IndexBufferPtr const& up_buf, | ||
| bool mark_created_in_cp = false) -> IndexBufferPtr { | ||
| IndexBufferPtr buf; | ||
| // MetaIndexBuffer always has blkid={0,0,0,0} regardless of which BTree table it belongs to. | ||
| // When multiple tables have a root split in the same CP, all their MetaBufs share the same blkid | ||
|
|
@@ -356,9 +381,11 @@ void IndexCPContext::process_txn_record(txn_record const* rec, std::map< BlkId, | |
| buf = it->second; | ||
| } | ||
|
|
||
| if (mark_created_in_cp) { buf->m_created_cp_id = cpg->id(); } | ||
|
|
||
| if (up_buf) { | ||
| auto real_up_buf = up_buf; | ||
| if (up_buf->m_created_cp_id == cpg->id()) { | ||
| if (up_buf->m_created_cp_id == cpg->id() && buf->m_created_cp_id == cpg->id()) { | ||
| real_up_buf = up_buf->m_up_buffer; | ||
| } else if (up_buf->m_node_freed) { | ||
| real_up_buf = up_buf->m_up_buffer; | ||
|
|
@@ -367,8 +394,6 @@ void IndexCPContext::process_txn_record(txn_record const* rec, std::map< BlkId, | |
| } | ||
|
|
||
| #ifndef NDEBUG | ||
| // if (!is_sibling_link || (buf->m_up_buffer == real_up_buf)) { return buf;} | ||
| // Already linked with same buf or its not a sibling link to override | ||
| if (real_up_buf->is_in_down_buffers(buf)) { return buf; } | ||
| #endif | ||
|
|
||
|
|
@@ -391,9 +416,8 @@ void IndexCPContext::process_txn_record(txn_record const* rec, std::map< BlkId, | |
| } | ||
|
|
||
| for (uint8_t idx{0}; idx < rec->num_new_ids; ++idx) { | ||
| auto new_buf = rec_to_buf(rec, false /* is_meta */, rec->blk_id(cur_idx++), | ||
| inplace_child_buf ? inplace_child_buf : parent_buf); | ||
| new_buf->m_created_cp_id = cpg->id(); | ||
| rec_to_buf(rec, false /* is_meta */, rec->blk_id(cur_idx++), inplace_child_buf ? inplace_child_buf : parent_buf, | ||
| true /* mark_created_in_cp */); | ||
| } | ||
|
|
||
| for (uint8_t idx{0}; idx < rec->num_freed_ids; ++idx) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.