Skip to content

MDEV-26057 Assertion `!vcol->v_indexes.empty() in trx_undo_log_v_idx - #5631

Merged
Thirunarayanan merged 1 commit into
11.4from
MDEV-26057
Sep 8, 2026
Merged

MDEV-26057 Assertion `!vcol->v_indexes.empty() in trx_undo_log_v_idx#5631
Thirunarayanan merged 1 commit into
11.4from
MDEV-26057

Conversation

@Thirunarayanan

Copy link
Copy Markdown
Member

Problem:

  • Rollback of an INPLACE ALTER TABLE is executed while holding only a shared metadata lock on the table, so DML can run concurrently. rollback_inplace_alter_table() resets dict_col_t::ord_part in a critical section of its own, after row_merge_drop_indexes() already removed the aborted indexes from the dictionary cache and emptied dict_v_col_t::v_indexes. During this time, DML statement can see a virtual column with ord_part set and an empty v_indexes, which makes assert failure in trx_undo_report_insert_virtual().

Solution:

row_merge_reset_ord_part(): Added a function to reset dict_col_t::ord_part for the columns that are no longer a field of any index remaining in the dictionary cache.
For virtual columns the decision is based on dict_v_col_t::v_indexes being empty, and no element is ever removed from that list.

row_merge_drop_indexes(): Added a call to row_merge_reset_ord_part() in the branch that removes the indexes from the cache, in the same dict_sys.latch critical section. That branch is taken only when MDL_EXCLUSIVE is held or when this is the only handle to the table, so no concurrent DML can observe the intermediate state. In the lazy drop branch the indexes and their v_indexes entries stay in the cache and nothing is reset; that is done later, when the indexes are dropped while holding MDL_EXCLUSIVE.

check_col_exists_in_indexes(): Removed the only_committed parameter, which no longer has any caller.

@CLAassistant

CLAassistant commented Sep 3, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@iMineLink iMineLink left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I failed in having the MTR test reproduce the assertion on a build with the storage/ hunks reverted: I get a timeout instead. Code change looks sensible but I hope the test could reproduce the issue un an unpatched build.

Comment thread mysql-test/suite/innodb/t/innodb-virtual-columns-debug.test

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The race is addressed within the dictionary-latch critical section and covered by a targeted regression test, with only a minor annotation typo remaining.

Pull request overview

Fixes MDEV-26057 by atomically resetting stale index metadata during failed INPLACE ALTER rollback.

Changes:

  • Resets ord_part while holding dict_sys.latch.
  • Removes obsolete committed-index filtering.
  • Adds a synchronized concurrency regression test.
File summaries
File Description
storage/innobase/row/row0merge.cc Performs atomic metadata cleanup.
storage/innobase/handler/handler0alter.cc Removes delayed cleanup and obsolete parameter.
storage/innobase/trx/trx0rec.cc Adds test synchronization.
mysql-test/suite/innodb/t/innodb-virtual-columns-debug.test Reproduces concurrent rollback and DML.
mysql-test/suite/innodb/r/innodb-virtual-columns-debug.result Records expected regression-test output.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread mysql-test/suite/innodb/t/innodb-virtual-columns-debug.test

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The index scan can preserve stale ord_part for FTS columns, and the PR contains substantial undocumented unrelated work.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 26/26 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread storage/innobase/row/row0merge.cc Outdated
Comment thread strings/json_normalize.c Outdated

@iMineLink iMineLink left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have only a change request on the test file, to make if fail with assertion if the fix is not included, and think Copilot's suggestion to use index->n_uniq in row_merge_col_is_indexed() is sound. Thanks.

Comment thread mysql-test/suite/innodb/t/innodb-virtual-columns-debug.test
Comment thread storage/innobase/row/row0merge.cc Outdated
Comment thread storage/innobase/row/row0quiesce.cc Outdated
Comment thread storage/innobase/row/row0quiesce.cc Outdated
Comment thread storage/innobase/trx/trx0rec.cc Outdated
Problem:
========
-  Rollback of an INPLACE ALTER TABLE is executed while holding only a
shared metadata lock on the table, so DML can run concurrently.
rollback_inplace_alter_table() resets dict_col_t::ord_part in a
critical section of its own, after row_merge_drop_indexes() already
removed the aborted indexes from the dictionary cache and emptied
dict_v_col_t::v_indexes. During this time, DML statement can see a
virtual column with ord_part set and an empty v_indexes, which
makes assert failure in trx_undo_report_insert_virtual().

Solution:
========
row_merge_reset_ord_part(): Added a function to reset
dict_col_t::ord_part for the columns that are no longer a field of
any index remaining in the dictionary cache.
For virtual columns the decision is based on dict_v_col_t::v_indexes
being empty, and no element is ever removed from that list.

row_merge_drop_indexes(): Added a call to row_merge_reset_ord_part()
in the branch that removes the indexes from the cache, in the same
dict_sys.latch critical section. That branch is taken only when
MDL_EXCLUSIVE is held or when this is the only handle to the table,
so no concurrent DML can observe the intermediate state.
In the lazy drop branch the indexes and their v_indexes entries
stay in the cache and nothing is reset; that is done later,
when the indexes are dropped while holding MDL_EXCLUSIVE.

check_col_exists_in_indexes(): Removed the only_committed parameter,
which no longer has any caller.

row_quiesce_col_ord_part(): Added a function to get
dict_col_t::ord_part and dict_col_t::max_prefix of a column
from the committed indexes that are
present in the dictionary cache.

row_quiesce_write_table(): Write the row_quiesce_col_ord_part() return
values to the .cfg file instead of the cached dict_col_t fields,
because a rolled back ADD INDEX leaves ord_part set until the
aborted index is removed by a later DDL, and
max_prefix is never reset when an index is dropped, which makes
IMPORT TABLESPACE reject the tablespace with a bogus schema mismatch.

@iMineLink iMineLink left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thank you for addressing the review points.

@Thirunarayanan
Thirunarayanan merged commit 42038e1 into 11.4 Sep 8, 2026
17 of 18 checks passed
@Thirunarayanan
Thirunarayanan deleted the MDEV-26057 branch September 8, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants