• Marko Mäkelä's avatar
    MDEV-23514 Race conditions between ROLLBACK and ALTER TABLE · 22c4a751
    Marko Mäkelä authored
    Since commit 15093639 (MDEV-23484)
    the rollback of InnoDB transactions is no longer protected by
    dict_operation_lock. Removing that protection revealed a race
    condition between transaction rollback and the rollback of an
    online table-rebuilding operation (OPTIMIZE TABLE, or any online
    ALTER TABLE that is rebuilding the table).
    
    row_undo_mod_clust(): Re-check dict_index_is_online_ddl() after
    acquiring index->lock, similar to how row_undo_ins_remove_clust_rec()
    is doing it. Because innobase_online_rebuild_log_free() is holding
    exclusive index->lock while invoking row_log_free(), this re-check
    will ensure that row_log_table_low() will not be invoked when
    index->online_log=NULL.
    
    A different race condition is possible between the rollback of a
    recovered transaction and the start of online secondary index creation.
    Because prepare_inplace_alter_table_dict() is not acquiring an InnoDB
    table lock in this case, and because recovered transactions are not
    covered by metadata locks (MDL), the dict_table_t::indexes could be
    modified by prepare_inplace_alter_table_dict() while the rollback of
    a recovered transaction is being executed. Normal transactions would
    be covered by MDL, and during prepare_inplace_alter_table_dict() we
    do hold MDL_EXCLUSIVE, that is, an online ALTER TABLE operation may
    not execute concurrently with other transactions that have accessed
    the table.
    
    row_undo(): To prevent a race condition with
    prepare_inplace_alter_table_dict(), acquire dict_operation_lock
    for all recovered transactions. Before MDEV-23484 we used to acquire
    it for all transactions, not only recovered ones.
    
    Note: row_merge_drop_indexes() would not invoke
    dict_index_remove_from_cache() while transactional locks
    exist on the table, or while any thread is holding an open table handle.
    OK, it does that for FULLTEXT INDEX, but ADD FULLTEXT INDEX is not
    supported as an online operation, and therefore
    prepare_inplace_alter_table_dict() would acquire a table S lock,
    which cannot succeed as long as recovered transactions on the table
    exist, because they would hold a conflicting IX lock on the table.
    22c4a751
row0undo.cc 11.8 KB