Details
-
Bug
-
Status: Closed (View Workflow)
-
Critical
-
Resolution: Fixed
-
10.2.31
Description
Looks as if some cases of InnoDB index inconsistencies – see e.g. MDEV-22373 – are missed by CHECK TABLE.
InnoDB currently ignores any other CHECK TABLE attributes than QUICK (which will suppress the time-consuming checks of the upper levels of the index trees).
Most importantly, InnoDB is ignoring the attribute EXTENDED, and is essentially only performing a MVCC COUNT(*) in each index. A verified case exists where the record counts in the indexes would match, yet the records do not match between the indexes. A true EXTENDED check should report any mismatch between the clustered index (primary key) and the secondary index records.
Note: By the design of InnoDB MVCC (see MDEV-17598 for some discussion), secondary indexes may contain multiple copies of a record, while in the clustered index the multiple versions are formed via a singly-linked list that the undo log pages are part of.
An extremely extended test would have to check that in any possible read view, the secondary indexes match the clustered index, instead of only checking the current read view.
It may turn out that only a special ‘locking’ variant of CHECK TABLE would be able to detect truly any type of mismatch.
Attachments
Issue Links
- blocks
-
MDEV-28349 Provide "crash safe" options for CHECK TABLE and ALTER TABLE ... CHECK PARTITION ...
-
- Open
-
- causes
-
MDEV-29886 Assertion `!index->table->is_temporary()' failed in trx_undo_prev_version_build upon CHECK
-
- Closed
-
-
MDEV-29978 Corruption errors upon CHECK on temporary InnoDB table
-
- Closed
-
-
MDEV-35227 Executing CHECK TABLE...EXTENDED right after server startup may attempt to access too old history
-
- Confirmed
-
- relates to
-
MDEV-25459 MVCC read from index on CHAR or VARCHAR wrongly omits rows
-
- Closed
-
-
MDEV-29593 Purge misses a chance to free not-yet-reused undo pages
-
- Closed
-
-
MDEV-29666 InnoDB fails to purge secondary index records when indexed virtual columns exist
-
- Closed
-
-
MDEV-29823 Secondary index records may never be purged after rollback
-
- Confirmed
-
-
MDEV-9663 InnoDB assertion failure: *cursor->index->name == TEMP_INDEX_PREFIX, or !cursor->index->is_committed()
-
- Closed
-
-
MDEV-11802 innodb.innodb_bug14676111 fails in buildbot due to InnoDB purge failing to start when there is work to do
-
- Closed
-
-
MDEV-22373 Unable to find a record to delete-mark ends up crashing mysqld process after upgrading from 10.1.43 to 10.4
-
- Closed
-
-
MDEV-29954 Unique hash key on column prefix is computed incorrectly
-
- Closed
-
-
MDEV-30129 MySQL 5.7 --> MariaDB 10.8 switch: mysqlcheck --check --extended hangs on table
-
- Closed
-
I got an RQG grammar that would occasionally report orphan delete-marked secondary index records, similar to
MDEV-29666. I analyzed an rr replay trace of such a failure. The cause is a race condition between rollback and purge. Rollback would not remove a secondary index record that was inserted on top of a committed delete-marked record, because purge had not advanced that far yet. After rollback, trx_t::commit() of the ‘empty’ transaction would invoke trx_purge_add_undo_to_history(), which unnecessarily adds the empty undo log to the purge queue. I tried disabling trx_undo_try_truncate() so that the already rolled-back undo log records would be added to the purge queue, but even with that work-around (not a proper fix), the grammar would fail. With that tweak present, several tests that involve BLOBs would fail, presumably because some BLOBs would be prematurely freed.The error is not easily fixed. We might relax the check and ‘wave through’ cases where a matching clustered index record cannot be found at all. Other cases (where a record with a primary key is found for a newer version of the record, but not for one that matches the secondary index record) would still be flagged:
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index e1e7ed0e12a..4baa1e66376 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -6399,16 +6399,12 @@ dberr_t row_check_index(row_prebuilt_t *prebuilt, ulint *n_rows)
if (!rec_deleted)
{
not_found:
- ib::error() << "Clustered index record not found for index "
+ ib::fatal() << "Clustered index record not found for index "
<< index->name << " of table " << index->table->name
<< ": " << rec_offsets_print(rec, offsets);
if (prebuilt->autoinc_error == DB_SUCCESS)
prebuilt->autoinc_error= DB_CORRUPTION;
}
- else if (&view == &check_table_extended_view)
- extended_not_found:
- if (view.changes_visible(page_trx_id))
- goto not_found;
did_not_find:
mtr.rollback_to_savepoint(savepoint);
goto next_rec;
@@ -6587,8 +6583,9 @@ dberr_t row_check_index(row_prebuilt_t *prebuilt, ulint *n_rows)
clust_rec version or when trx_undo_prev_version_build()
encounters a BLOB that may have been freed according to
purge_sys.view (not purge_sys.end_view). */
- if (&view == &check_table_extended_view && !got_extended_match)
- goto extended_not_found;
+ if (&view == &check_table_extended_view && !got_extended_match &&
+ view.changes_visible(page_trx_id))
+ goto not_found;
goto did_not_find;
}
Alternatively, we could issue warnings for this but not flag the secondary index as corrupted. After all, this type of corruption should only hurt performance, not cause any correctness issues.