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; ...@@ -7,8 +7,9 @@ connection node_3;
SET SESSION wsrep_on=OFF; SET SESSION wsrep_on=OFF;
DROP SCHEMA test; DROP SCHEMA test;
connection node_1; connection node_1;
SET SESSION lc_messages='fr_FR';
CREATE SCHEMA test; 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; connection node_1;
SET SESSION wsrep_sync_wait=0; SET SESSION wsrep_sync_wait=0;
connection node_2; connection node_2;
......
...@@ -24,6 +24,9 @@ DROP SCHEMA test; ...@@ -24,6 +24,9 @@ DROP SCHEMA test;
# This should fail on nodes 1 and 2 and succeed on node 3 # This should fail on nodes 1 and 2 and succeed on node 3
--connection node_1 --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 --error ER_DB_CREATE_EXISTS
CREATE SCHEMA test; CREATE SCHEMA test;
......
...@@ -82,7 +82,9 @@ wsrep_get_apply_format(THD* thd) ...@@ -82,7 +82,9 @@ wsrep_get_apply_format(THD* thd)
return thd->wsrep_rgi->rli->relay_log.description_event_for_exec; 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= Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions(); thd->get_stmt_da()->sql_conditions();
...@@ -100,8 +102,16 @@ void wsrep_store_error(const THD* const thd, wsrep::mutable_buffer& dst) ...@@ -100,8 +102,16 @@ void wsrep_store_error(const THD* const thd, wsrep::mutable_buffer& dst)
uint const err_code= cond->get_sql_errno(); uint const err_code= cond->get_sql_errno();
const char* const err_str= cond->get_message_text(); const char* const err_str= cond->get_message_text();
slider+= my_snprintf(slider, buf_end - slider, " %s, Error_code: %d;", if (include_msg)
err_str, err_code); {
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()) if (slider != dst.data())
......
...@@ -35,7 +35,21 @@ int wsrep_apply_events(THD* thd, ...@@ -35,7 +35,21 @@ int wsrep_apply_events(THD* thd,
#define WSREP_ERR_FAILED 6 // Operation failed for some internal reason #define WSREP_ERR_FAILED 6 // Operation failed for some internal reason
#define WSREP_ERR_ABORTED 7 // Operation was aborted externally #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; class Format_description_log_event;
void wsrep_set_apply_format(THD*, 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) ...@@ -123,14 +123,15 @@ static void wsrep_setup_uk_and_fk_checks(THD* thd)
static int apply_events(THD* thd, static int apply_events(THD* thd,
Relay_log_info* rli, Relay_log_info* rli,
const wsrep::const_buffer& data, 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()); int const ret= wsrep_apply_events(thd, rli, data.data(), data.size());
if (ret || wsrep_thd_has_ignored_error(thd)) if (ret || wsrep_thd_has_ignored_error(thd))
{ {
if (ret) 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()); 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, ...@@ -427,7 +428,7 @@ int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta,
#endif #endif
thd->set_time(); 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); wsrep_thd_set_ignored_error(thd, false);
trans_commit(thd); trans_commit(thd);
...@@ -595,7 +596,7 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta, ...@@ -595,7 +596,7 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
#endif /* ENABLED_DEBUG_SYNC */ #endif /* ENABLED_DEBUG_SYNC */
wsrep_setup_uk_and_fk_checks(thd); 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(); thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit)) 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, ...@@ -764,7 +765,7 @@ int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta,
ws_meta, ws_meta,
thd->wsrep_sr().fragments()); 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(); thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit)) if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
{ {
......
...@@ -2489,7 +2489,10 @@ static void wsrep_TOI_end(THD *thd) { ...@@ -2489,7 +2489,10 @@ static void wsrep_TOI_end(THD *thd) {
if (thd->is_error() && !wsrep_must_ignore_error(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); 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