mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-17262: mysql crashed on galera while node rejoined cluster (#895)
This patch contains a fix for the MDEV-17262/17243 issues and new mtr test. These issues (MDEV-17262/17243) have two reasons: 1) After an intermediate commit, a transaction loses its status of "transaction that registered in the MySQL for 2pc coordinator" (in the InnoDB) due to the fact that since version 10.2 the write_row() function (which located in the ha_innodb.cc) does not call trx_register_for_2pc(m_prebuilt->trx) during the processing of split transactions. It is necessary to restore this call inside the write_row() when an intermediate commit was made (for a split transaction). Similarly, we need to set the flag of the started transaction (m_prebuilt->sql_stat_start) after intermediate commit. The table->file->extra(HA_EXTRA_FAKE_START_STMT) called from the wsrep_load_data_split() function (which located in sql_load.cc) will also do this, but it will be too late. As a result, the call to the wsrep_append_keys() function from the InnoDB engine may be lost or function may be called with invalid transaction identifier. 2) If a transaction with the LOAD DATA statement is divided into logical mini-transactions (of the 10K rows) and binlog is rotated, then in rare cases due to the wsrep handler re-registration at the boundary of the split, the last portion of data may be lost. Since splitting of the LOAD DATA into mini-transactions is technical, I believe that we should not allow these mini-transactions to fall into separate binlogs. Therefore, it is necessary to prohibit the rotation of binlog in the middle of processing LOAD DATA statement. https://jira.mariadb.org/browse/MDEV-17262 and https://jira.mariadb.org/browse/MDEV-17243
This commit is contained in:
@ -41,6 +41,7 @@
|
||||
#include "sql_trigger.h"
|
||||
#include "sql_derived.h"
|
||||
#include "sql_show.h"
|
||||
#include "debug_sync.h"
|
||||
|
||||
extern "C" int _my_b_net_read(IO_CACHE *info, uchar *Buffer, size_t Count);
|
||||
|
||||
@ -119,21 +120,43 @@ static bool wsrep_load_data_split(THD *thd, const TABLE *table,
|
||||
if (hton->db_type != DB_TYPE_INNODB)
|
||||
DBUG_RETURN(false);
|
||||
WSREP_DEBUG("intermediate transaction commit in LOAD DATA");
|
||||
wsrep_set_load_multi_commit(thd, true);
|
||||
if (wsrep_run_wsrep_commit(thd, true) != WSREP_TRX_OK) DBUG_RETURN(true);
|
||||
if (binlog_hton->commit(binlog_hton, thd, true)) DBUG_RETURN(true);
|
||||
wsrep_post_commit(thd, true);
|
||||
hton->commit(hton, thd, true);
|
||||
wsrep_set_load_multi_commit(thd, false);
|
||||
DEBUG_SYNC(thd, "intermediate_transaction_commit");
|
||||
table->file->extra(HA_EXTRA_FAKE_START_STMT);
|
||||
}
|
||||
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
# define WSREP_LOAD_DATA_SPLIT(thd,table,info) \
|
||||
if (wsrep_load_data_split(thd,table,info)) DBUG_RETURN(1)
|
||||
/*
|
||||
If the commit fails, then an early return from
|
||||
the function occurs there and therefore we need
|
||||
to reset the table->auto_increment_field_not_null
|
||||
flag, which is usually reset after calling
|
||||
the write_record():
|
||||
*/
|
||||
#define WSREP_LOAD_DATA_SPLIT(thd,table,info) \
|
||||
if (wsrep_load_data_split(thd,table,info)) \
|
||||
{ \
|
||||
table->auto_increment_field_not_null= FALSE; \
|
||||
DBUG_RETURN(1); \
|
||||
}
|
||||
#else /* WITH_WSREP */
|
||||
#define WSREP_LOAD_DATA_SPLIT(thd,table,info) /* empty */
|
||||
#endif /* WITH_WSREP */
|
||||
|
||||
#define WRITE_RECORD(thd,table,info) \
|
||||
do { \
|
||||
int err_= write_record(thd, table, &info); \
|
||||
table->auto_increment_field_not_null= FALSE; \
|
||||
if (err_) \
|
||||
DBUG_RETURN(1); \
|
||||
} while (0)
|
||||
|
||||
class READ_INFO: public Load_data_param
|
||||
{
|
||||
File file;
|
||||
@ -911,7 +934,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
List_iterator_fast<Item> it(fields_vars);
|
||||
Item *item;
|
||||
TABLE *table= table_list->table;
|
||||
bool err, progress_reports;
|
||||
bool progress_reports;
|
||||
ulonglong counter, time_to_report_progress;
|
||||
DBUG_ENTER("read_fixed_length");
|
||||
|
||||
@ -1003,11 +1026,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
}
|
||||
|
||||
WSREP_LOAD_DATA_SPLIT(thd, table, info);
|
||||
err= write_record(thd, table, &info);
|
||||
table->auto_increment_field_not_null= FALSE;
|
||||
if (err)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
WRITE_RECORD(thd, table, info);
|
||||
|
||||
/*
|
||||
We don't need to reset auto-increment field since we are restoring
|
||||
its default value at the beginning of each loop iteration.
|
||||
@ -1040,7 +1060,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
Item *item;
|
||||
TABLE *table= table_list->table;
|
||||
uint enclosed_length;
|
||||
bool err, progress_reports;
|
||||
bool progress_reports;
|
||||
ulonglong counter, time_to_report_progress;
|
||||
DBUG_ENTER("read_sep_field");
|
||||
|
||||
@ -1124,7 +1144,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
{
|
||||
Load_data_outvar *dst= item->get_load_data_outvar_or_error();
|
||||
DBUG_ASSERT(dst);
|
||||
if (dst->load_data_set_no_data(thd, &read_info))
|
||||
if (unlikely(dst->load_data_set_no_data(thd, &read_info)))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
@ -1146,10 +1166,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
}
|
||||
|
||||
WSREP_LOAD_DATA_SPLIT(thd, table, info);
|
||||
err= write_record(thd, table, &info);
|
||||
table->auto_increment_field_not_null= FALSE;
|
||||
if (err)
|
||||
DBUG_RETURN(1);
|
||||
WRITE_RECORD(thd, table, info);
|
||||
|
||||
/*
|
||||
We don't need to reset auto-increment field since we are restoring
|
||||
its default value at the beginning of each loop iteration.
|
||||
@ -1267,13 +1285,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
case VIEW_CHECK_ERROR:
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
WSREP_LOAD_DATA_SPLIT(thd, table, info);
|
||||
err= write_record(thd, table, &info);
|
||||
table->auto_increment_field_not_null= false;
|
||||
if (err)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
WRITE_RECORD(thd, table, info);
|
||||
|
||||
/*
|
||||
We don't need to reset auto-increment field since we are restoring
|
||||
its default value at the beginning of each loop iteration.
|
||||
|
Reference in New Issue
Block a user