Commit 731a5aba authored by Alexey Yurchenko's avatar Alexey Yurchenko Committed by Julius Goryavsky

Use only MySQL code for TOI error vote

For TOI events specifically we have a situation where in case of the
same error different nodes may generate different messages. This may
be for two reasons:
 - different locale setting between the current client session and
   server default (we can reasonably require server locales to be
   identical on all nodes, but user can change message locale for the
   session)
 - non-deterministic course of STATEMENT execution e.g. for ALTER TABLE

On the other hand we may reasonably expect TOI event failures since
they are executed after replication, so we must ensure that voting is
consistent. For that purpose error codes should be sufficiently unique
and deterministic for TOI event failures as DDLs normally deal with
a single object, so we can merely use MySQL error codes to vote on.

Notice that this problem does not happen with regular transactional
writesets, since the originator node will always vote success and
replica nodes are assumed to have the same global locale setting.
As such different error messages indicate different errors even if
the error code is the same (e.g. ER_DUP_KEY can happen on different
rows tables).

Use only MySQL error code (without the error message) for error voting
in case of TOI event failure.
Signed-off-by: default avatarJulius Goryavsky <julius.goryavsky@mariadb.com>
parent 7119149f
......@@ -7,8 +7,9 @@ connection node_3;
SET SESSION wsrep_on=OFF;
DROP SCHEMA test;
connection node_1;
SET SESSION lc_messages='fr_FR';
CREATE SCHEMA test;
ERROR HY000: Can't create database 'test'; database exists
ERROR HY000: Ne peut créer la base 'test'; elle existe déjà
connection node_1;
SET SESSION wsrep_sync_wait=0;
connection node_2;
......
......@@ -24,6 +24,9 @@ DROP SCHEMA test;
# This should fail on nodes 1 and 2 and succeed on node 3
--connection node_1
# Make error message on source node different by changing locale
# It should still agree with node 2
SET SESSION lc_messages='fr_FR';
--error ER_DB_CREATE_EXISTS
CREATE SCHEMA test;
......
......@@ -82,7 +82,9 @@ wsrep_get_apply_format(THD* thd)
return thd->wsrep_rgi->rli->relay_log.description_event_for_exec;
}
void wsrep_store_error(const THD* const thd, wsrep::mutable_buffer& dst)
void wsrep_store_error(const THD* const thd,
wsrep::mutable_buffer& dst,
bool const include_msg)
{
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
......@@ -100,9 +102,17 @@ void wsrep_store_error(const THD* const thd, wsrep::mutable_buffer& dst)
uint const err_code= cond->get_sql_errno();
const char* const err_str= cond->get_message_text();
slider+= my_snprintf(slider, buf_end - slider, " %s, Error_code: %d;",
if (include_msg)
{
slider+= snprintf(slider, buf_end - slider, " %s, Error_code: %d;",
err_str, err_code);
}
else
{
slider+= snprintf(slider, buf_end - slider, " Error_code: %d;",
err_code);
}
}
if (slider != dst.data())
{
......
......@@ -35,7 +35,21 @@ int wsrep_apply_events(THD* thd,
#define WSREP_ERR_FAILED 6 // Operation failed for some internal reason
#define WSREP_ERR_ABORTED 7 // Operation was aborted externally
void wsrep_store_error(const THD* thd, wsrep::mutable_buffer& buf);
/* Loops over THD diagnostic area and concatenates all error messages
* and error codes to a single continuous buffer to create a unique
* but consistent failure signature which provider can use for voting
* between the nodes in the cluster.
*
* @param thd THD context
* @param dst buffer to store the signature
* @param include_msg whether to use MySQL error message in addition to
* MySQL error code. Note that in the case of a TOI
* operation the message may be not consistent between
* the nodes e.g. due to a different client locale setting
* and should be omitted */
void wsrep_store_error(const THD* thd,
wsrep::mutable_buffer& buf,
bool include_msg);
class Format_description_log_event;
void wsrep_set_apply_format(THD*, Format_description_log_event*);
......
......@@ -123,14 +123,15 @@ static void wsrep_setup_uk_and_fk_checks(THD* thd)
static int apply_events(THD* thd,
Relay_log_info* rli,
const wsrep::const_buffer& data,
wsrep::mutable_buffer& err)
wsrep::mutable_buffer& err,
bool const include_msg)
{
int const ret= wsrep_apply_events(thd, rli, data.data(), data.size());
if (ret || wsrep_thd_has_ignored_error(thd))
{
if (ret)
{
wsrep_store_error(thd, err);
wsrep_store_error(thd, err, include_msg);
}
wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
}
......@@ -427,7 +428,7 @@ int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta,
#endif
thd->set_time();
int ret= apply_events(thd, m_rli, data, err);
int ret= apply_events(thd, m_rli, data, err, false);
wsrep_thd_set_ignored_error(thd, false);
trans_commit(thd);
......@@ -595,7 +596,7 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
#endif /* ENABLED_DEBUG_SYNC */
wsrep_setup_uk_and_fk_checks(thd);
int ret= apply_events(thd, m_rli, data, err);
int ret= apply_events(thd, m_rli, data, err, true);
thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
......@@ -764,7 +765,7 @@ int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta,
ws_meta,
thd->wsrep_sr().fragments());
}
ret= ret || apply_events(thd, m_rli, data, err);
ret= ret || apply_events(thd, m_rli, data, err, true);
thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
{
......
......@@ -2489,7 +2489,10 @@ static void wsrep_TOI_end(THD *thd) {
if (thd->is_error() && !wsrep_must_ignore_error(thd))
{
wsrep_store_error(thd, err);
/* use only error code, for the message can be inconsistent
* between the nodes due to differing lc_message settings
* in client session and server applier thread */
wsrep_store_error(thd, err, false);
}
int const ret= client_state.leave_toi_local(err);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment