1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-35570 parallel slave ALTER-SEQUENCE attempted to binlog out-of-order

Since MDEV-31503 fixes ALTER-SEQUENCE might be able to  complete its
work including binlogging before a preceding in binlog-order
transaction. There may not be data dependency between the two
transactions, but there would be
   "an attempt was made to binlog GTID D-S-XYZ which would create an
   out-of-order sequence number"
error in the gtid_strict_mode = ON.

After the preceding transaction started committing, and does it rather
slow, ALTER-SEQUNCE was able to find a time window to complete because
it temporarily releases its link with the waitee parent transaction.
And while having it released it also erroneously executes the binlogging part.

Fixed with restoring the commit dependency on the parent before
ALTER-SEQUNCE takes on binlogging.
This commit is contained in:
Andrei
2025-05-13 17:10:30 +03:00
committed by Andrei Elkin
parent e79aa9ca38
commit 8c817e2d8a
4 changed files with 175 additions and 94 deletions

View File

@@ -960,119 +960,121 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
SEQUENCE *seq;
No_such_table_error_handler no_such_table_handler;
DBUG_ENTER("Sql_cmd_alter_sequence::execute");
{
#if defined(HAVE_REPLICATION)
/* No wakeup():s of subsequent commits is allowed in this function. */
wait_for_commit_raii suspend_wfc(thd);
/* No wakeup():s of subsequent commits is allowed in this function. */
wait_for_commit_raii suspend_wfc(thd);
#endif
if (check_access(thd, ALTER_ACL, first_table->db.str,
&first_table->grant.privilege,
&first_table->grant.m_internal,
0, 0))
DBUG_RETURN(TRUE); /* purecov: inspected */
if (check_access(thd, ALTER_ACL, first_table->db.str,
&first_table->grant.privilege,
&first_table->grant.m_internal,
0, 0))
DBUG_RETURN(TRUE); /* purecov: inspected */
if (check_grant(thd, ALTER_ACL, first_table, FALSE, 1, FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
if (check_grant(thd, ALTER_ACL, first_table, FALSE, 1, FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
#ifdef WITH_WSREP
if (WSREP(thd) && wsrep_thd_is_local(thd))
{
const bool used_engine= lex->create_info.used_fields & HA_CREATE_USED_ENGINE;
if (wsrep_check_sequence(thd, new_seq, used_engine))
DBUG_RETURN(TRUE);
if (wsrep_to_isolation_begin(thd, first_table->db.str,
first_table->table_name.str,
first_table))
if (WSREP(thd) && wsrep_thd_is_local(thd))
{
DBUG_RETURN(TRUE);
const bool used_engine= lex->create_info.used_fields & HA_CREATE_USED_ENGINE;
if (wsrep_check_sequence(thd, new_seq, used_engine))
DBUG_RETURN(TRUE);
if (wsrep_to_isolation_begin(thd, first_table->db.str,
first_table->table_name.str,
first_table))
{
DBUG_RETURN(TRUE);
}
}
}
#endif /* WITH_WSREP */
if (if_exists())
thd->push_internal_handler(&no_such_table_handler);
error= open_and_lock_tables(thd, first_table, FALSE, 0);
if (if_exists())
{
trapped_errors= no_such_table_handler.safely_trapped_errors();
thd->pop_internal_handler();
}
if (unlikely(error))
{
if (trapped_errors)
if (if_exists())
thd->push_internal_handler(&no_such_table_handler);
error= open_and_lock_tables(thd, first_table, FALSE, 0);
if (if_exists())
{
StringBuffer<FN_REFLEN> tbl_name;
tbl_name.append(&first_table->db);
tbl_name.append('.');
tbl_name.append(&first_table->table_name);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_SEQUENCES,
ER_THD(thd, ER_UNKNOWN_SEQUENCES),
tbl_name.c_ptr_safe());
my_ok(thd);
DBUG_RETURN(FALSE);
trapped_errors= no_such_table_handler.safely_trapped_errors();
thd->pop_internal_handler();
}
if (unlikely(error))
{
if (trapped_errors)
{
StringBuffer<FN_REFLEN> tbl_name;
tbl_name.append(&first_table->db);
tbl_name.append('.');
tbl_name.append(&first_table->table_name);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_SEQUENCES,
ER_THD(thd, ER_UNKNOWN_SEQUENCES),
tbl_name.c_ptr_safe());
my_ok(thd);
DBUG_RETURN(FALSE);
}
DBUG_RETURN(TRUE);
}
DBUG_RETURN(TRUE);
}
table= first_table->table;
seq= table->s->sequence;
table= first_table->table;
seq= table->s->sequence;
seq->write_lock(table);
new_seq->reserved_until= seq->reserved_until;
seq->write_lock(table);
new_seq->reserved_until= seq->reserved_until;
/* Copy from old sequence those fields that the user didn't specified */
if (!(new_seq->used_fields & seq_field_used_increment))
new_seq->increment= seq->increment;
if (!(new_seq->used_fields & seq_field_used_min_value))
new_seq->min_value= seq->min_value;
if (!(new_seq->used_fields & seq_field_used_max_value))
new_seq->max_value= seq->max_value;
if (!(new_seq->used_fields & seq_field_used_start))
new_seq->start= seq->start;
if (!(new_seq->used_fields & seq_field_used_cache))
new_seq->cache= seq->cache;
if (!(new_seq->used_fields & seq_field_used_cycle))
new_seq->cycle= seq->cycle;
/* Copy from old sequence those fields that the user didn't specified */
if (!(new_seq->used_fields & seq_field_used_increment))
new_seq->increment= seq->increment;
if (!(new_seq->used_fields & seq_field_used_min_value))
new_seq->min_value= seq->min_value;
if (!(new_seq->used_fields & seq_field_used_max_value))
new_seq->max_value= seq->max_value;
if (!(new_seq->used_fields & seq_field_used_start))
new_seq->start= seq->start;
if (!(new_seq->used_fields & seq_field_used_cache))
new_seq->cache= seq->cache;
if (!(new_seq->used_fields & seq_field_used_cycle))
new_seq->cycle= seq->cycle;
/* If we should restart from a new value */
if (new_seq->used_fields & seq_field_used_restart)
{
if (!(new_seq->used_fields & seq_field_used_restart_value))
new_seq->restart= new_seq->start;
new_seq->reserved_until= new_seq->restart;
}
/* If we should restart from a new value */
if (new_seq->used_fields & seq_field_used_restart)
{
if (!(new_seq->used_fields & seq_field_used_restart_value))
new_seq->restart= new_seq->start;
new_seq->reserved_until= new_seq->restart;
}
/* Let check_and_adjust think all fields are used */
new_seq->used_fields= ~0;
if (new_seq->check_and_adjust(0))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
first_table->db.str,
first_table->table_name.str);
error= 1;
/* Let check_and_adjust think all fields are used */
new_seq->used_fields= ~0;
if (new_seq->check_and_adjust(0))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
first_table->db.str,
first_table->table_name.str);
error= 1;
seq->write_unlock(table);
goto end;
}
if (likely(!(error= new_seq->write(table, 1))))
{
/* Store the sequence values in table share */
seq->copy(new_seq);
}
else
table->file->print_error(error, MYF(0));
seq->write_unlock(table);
goto end;
if (trans_commit_stmt(thd))
error= 1;
if (trans_commit_implicit(thd))
error= 1;
DBUG_EXECUTE_IF("hold_worker_on_schedule",
{
/* delay binlogging of a parent trx in rpl_parallel_seq */
my_sleep(100000);
});
}
if (likely(!(error= new_seq->write(table, 1))))
{
/* Store the sequence values in table share */
seq->copy(new_seq);
}
else
table->file->print_error(error, MYF(0));
seq->write_unlock(table);
if (trans_commit_stmt(thd))
error= 1;
if (trans_commit_implicit(thd))
error= 1;
DBUG_EXECUTE_IF("hold_worker_on_schedule",
{
/* delay binlogging of a parent trx in rpl_parallel_seq */
my_sleep(100000);
});
if (likely(!error))
error= write_bin_log(thd, 1, thd->query(), thd->query_length());
if (likely(!error))