summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormats@romeo.(none) <>2007-01-17 15:06:37 +0100
committermats@romeo.(none) <>2007-01-17 15:06:37 +0100
commit3920f529eaed9f358bfe722082aa17b424442995 (patch)
tree9eae6a73e0dce9a2735898f9cba02326b8eab6a9
parent1fb9105ebd647be9d4b4c582d1e8354f0ea38414 (diff)
downloadmariadb-git-3920f529eaed9f358bfe722082aa17b424442995.tar.gz
BUG#23171 (Illegal slave restart position):
Third patch of the bug fix where the code for skipping events and for executing events is factored out into three functions: - shall_skip() to decide if the event shall be skipped and the reason for it; - do_apply_event(), where the event is applied to the database; and - do_update_pos(), which updates the actual relay log position and group positions.
-rw-r--r--mysql-test/r/rpl_row_tabledefs_2myisam.result2
-rw-r--r--mysql-test/r/rpl_row_tabledefs_3innodb.result2
-rw-r--r--sql/log_event.cc214
-rw-r--r--sql/rpl_rli.cc20
-rw-r--r--sql/rpl_rli.h9
-rw-r--r--sql/slave.cc75
6 files changed, 226 insertions, 96 deletions
diff --git a/mysql-test/r/rpl_row_tabledefs_2myisam.result b/mysql-test/r/rpl_row_tabledefs_2myisam.result
index ae792a5dc2a..b3a55ec2447 100644
--- a/mysql-test/r/rpl_row_tabledefs_2myisam.result
+++ b/mysql-test/r/rpl_row_tabledefs_2myisam.result
@@ -121,7 +121,7 @@ Replicate_Do_Table
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
-Last_Errno 1364
+Last_Errno 1105
Last_Error Error in Write_rows event: error during transaction execution on table test.t1_nodef
Skip_Counter 0
Exec_Master_Log_Pos #
diff --git a/mysql-test/r/rpl_row_tabledefs_3innodb.result b/mysql-test/r/rpl_row_tabledefs_3innodb.result
index b7f0b7b15e2..a7f3cd3db9a 100644
--- a/mysql-test/r/rpl_row_tabledefs_3innodb.result
+++ b/mysql-test/r/rpl_row_tabledefs_3innodb.result
@@ -121,7 +121,7 @@ Replicate_Do_Table
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
-Last_Errno 1364
+Last_Errno 1105
Last_Error Error in Write_rows event: error during transaction execution on table test.t1_nodef
Skip_Counter 0
Exec_Master_Log_Pos #
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 3bdc6412dc6..ec7136b5d58 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -89,9 +89,10 @@ public:
operator&()
DESCRIPTION
- Function to return a pointer to the internal, so that the object
- can be treated as a IO_CACHE and used with the my_b_* IO_CACHE
- functions
+
+ Function to return a pointer to the internal cache, so that the
+ object can be treated as a IO_CACHE and used with the my_b_*
+ IO_CACHE functions
RETURN VALUE
A pointer to the internal IO_CACHE.
@@ -593,6 +594,19 @@ int Log_event::do_update_pos(RELAY_LOG_INFO *rli)
return 0; // Cannot fail currently
}
+
+Log_event::enum_skip_reason
+Log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+ if (this->server_id == ::server_id && !replicate_same_server_id)
+ return EVENT_SKIP_SAME_SID;
+ else if (rli->slave_skip_counter > 0)
+ return EVENT_SKIP_COUNT;
+ else
+ return EVENT_NOT_SKIPPED;
+}
+
+
/*
Log_event::pack_info()
*/
@@ -736,7 +750,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
ulong data_len;
int result=0;
char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
- DBUG_ENTER("read_log_event");
+ DBUG_ENTER("Log_event::read_log_event");
if (log_lock)
pthread_mutex_lock(log_lock);
@@ -811,7 +825,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file,
const Format_description_log_event *description_event)
#endif
{
- DBUG_ENTER("Log_event::read_log_event(IO_CACHE *, Format_description_log_event *");
+ DBUG_ENTER("Log_event::read_log_event");
DBUG_ASSERT(description_event != 0);
char head[LOG_EVENT_MINIMAL_HEADER_LEN];
/*
@@ -2472,16 +2486,6 @@ bool Format_description_log_event::write(IO_CACHE* file)
}
#endif
-/*
- SYNOPSIS
- Format_description_log_event::do_apply_event()
-
- IMPLEMENTATION
- Save the information which describes the binlog's format, to be
- able to read all coming events. Call
- Start_log_event_v3::do_apply_event().
-*/
-
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
{
@@ -2561,6 +2565,12 @@ int Format_description_log_event::do_update_pos(RELAY_LOG_INFO *rli)
}
}
+Log_event::enum_skip_reason
+Format_description_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+ return Log_event::EVENT_NOT_SKIPPED;
+}
+
#endif
/**************************************************************************
@@ -3425,6 +3435,16 @@ bool Rotate_log_event::write(IO_CACHE* file)
}
#endif
+/**
+ Helper function to detect if the event is inside a group.
+ */
+static bool is_in_group(THD *const thd, RELAY_LOG_INFO *const rli)
+{
+ return (thd->options & OPTION_BEGIN) != 0 ||
+ (rli->last_event_start_time > 0);
+}
+
+
/*
Rotate_log_event::do_apply_event()
@@ -3446,30 +3466,40 @@ bool Rotate_log_event::write(IO_CACHE* file)
int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
DBUG_ENTER("Rotate_log_event::do_update_pos");
+#ifndef DBUG_OFF
+ char buf[32];
+#endif
+
+ DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu", this->server_id, ::server_id));
+ DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
+ DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
+
pthread_mutex_lock(&rli->data_lock);
rli->event_relay_log_pos= my_b_tell(rli->cur_log);
/*
- If we are in a transaction: the only normal case is when the I/O thread was
- copying a big transaction, then it was stopped and restarted: we have this
- in the relay log:
+ If we are in a transaction or in a group: the only normal case is
+ when the I/O thread was copying a big transaction, then it was
+ stopped and restarted: we have this in the relay log:
+
BEGIN
...
ROTATE (a fake one)
...
COMMIT or ROLLBACK
- In that case, we don't want to touch the coordinates which correspond to
- the beginning of the transaction.
- Starting from 5.0.0, there also are some rotates from the slave itself, in
- the relay log.
+
+ In that case, we don't want to touch the coordinates which
+ correspond to the beginning of the transaction. Starting from
+ 5.0.0, there also are some rotates from the slave itself, in the
+ relay log.
*/
- if (!(thd->options & OPTION_BEGIN))
+ if (!is_in_group(thd, rli))
{
memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update();
rli->group_master_log_pos= pos;
rli->group_relay_log_pos= rli->event_relay_log_pos;
- DBUG_PRINT("info", ("group_master_log_name: '%s' "
- "group_master_log_pos: %lu",
+ DBUG_PRINT("info", ("new group_master_log_name: '%s' "
+ "new group_master_log_pos: %lu",
rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
/*
@@ -3492,6 +3522,24 @@ int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli)
DBUG_RETURN(0);
}
+
+Log_event::enum_skip_reason
+Rotate_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+
+ enum_skip_reason reason= Log_event::shall_skip(rli);
+
+ switch (reason) {
+ case Log_event::EVENT_NOT_SKIPPED:
+ case Log_event::EVENT_SKIP_COUNT:
+ return Log_event::EVENT_NOT_SKIPPED;
+
+ case Log_event::EVENT_SKIP_SAME_SID:
+ return Log_event::EVENT_SKIP_SAME_SID;
+ }
+ DBUG_ASSERT(0);
+}
+
#endif
@@ -3620,6 +3668,26 @@ int Intvar_log_event::do_update_pos(RELAY_LOG_INFO *rli)
rli->inc_event_relay_log_pos();
return 0;
}
+
+
+Log_event::enum_skip_reason
+Intvar_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+ /*
+ It is a common error to set the slave skip counter to 1 instead
+ of 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1,
+ we just say that this event should be skipped because of the
+ slave skip count, but we do not change the value of the slave
+ skip counter since it will be decreased by the following insert
+ event.
+ */
+ if (rli->slave_skip_counter == 1)
+ return Log_event::EVENT_SKIP_COUNT;
+ else
+ return Log_event::shall_skip(rli);
+}
+
#endif
@@ -3694,6 +3762,25 @@ int Rand_log_event::do_update_pos(RELAY_LOG_INFO *rli)
return 0;
}
+
+Log_event::enum_skip_reason
+Rand_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+ /*
+ It is a common error to set the slave skip counter to 1 instead
+ of 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1,
+ we just say that this event should be skipped because of the
+ slave skip count, but we do not change the value of the slave
+ skip counter since it will be decreased by the following insert
+ event.
+ */
+ if (rli->slave_skip_counter == 1)
+ return Log_event::EVENT_SKIP_COUNT;
+ else
+ return Log_event::shall_skip(rli);
+}
+
#endif /* !MYSQL_CLIENT */
@@ -4116,6 +4203,23 @@ int User_var_log_event::do_update_pos(RELAY_LOG_INFO *rli)
return 0;
}
+Log_event::enum_skip_reason
+User_var_log_event::shall_skip(RELAY_LOG_INFO *rli)
+ {
+ /*
+ It is a common error to set the slave skip counter to 1 instead
+ of 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1,
+ we just say that this event should be skipped because of the
+ slave skip count, but we do not change the value of the slave
+ skip counter since it will be decreased by the following insert
+ event.
+ */
+ if (rli->slave_skip_counter == 1)
+ return Log_event::EVENT_SKIP_COUNT;
+ else
+ return Log_event::shall_skip(rli);
+ }
#endif /* !MYSQL_CLIENT */
@@ -5814,9 +5918,9 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
break;
default:
- slave_print_msg(ERROR_LEVEL, rli, error,
+ slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
"Error in %s event: row application failed",
- get_type_str());
+ get_type_str(), error);
thd->query_error= 1;
break;
}
@@ -5835,11 +5939,12 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
if (error)
{ /* error has occured during the transaction */
- slave_print_msg(ERROR_LEVEL, rli, error,
+ slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
"Error in %s event: error during transaction execution "
"on table %s.%s",
get_type_str(), table->s->db.str,
table->s->table_name.str);
+
/*
If one day we honour --skip-slave-errors in row-based replication, and
the error should be skipped, then we would clear mappings, rollback,
@@ -5851,7 +5956,7 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
thread is certainly going to stop.
*/
thd->reset_current_stmt_binlog_row_based();
- const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 1);
+ const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, error);
thd->query_error= 1;
DBUG_RETURN(error);
}
@@ -5934,9 +6039,9 @@ int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
wait (reached end of last relay log and nothing gets appended
there), we timeout after one minute, and notify DBA about the
problem. When WL#2975 is implemented, just remove the member
- st_relay_log_info::unsafe_to_stop_at and all its occurences.
+ st_relay_log_info::last_event_start_time and all its occurences.
*/
- const_cast<RELAY_LOG_INFO*>(rli)->unsafe_to_stop_at= time(0);
+ const_cast<RELAY_LOG_INFO*>(rli)->last_event_start_time= time(0);
}
DBUG_ASSERT(error == 0);
@@ -6599,6 +6704,32 @@ copy_extra_record_fields(TABLE *table,
return 0; // All OK
}
+/**
+ Check if an error is a duplicate key error.
+
+ This function is used to check if an error code is one of the
+ duplicate key error, i.e., and error code for which it is sensible
+ to do a <code>get_dup_key()</code> to retrieve the duplicate key.
+
+ @param errcode The error code to check.
+
+ @return <code>true</code> if the error code is such that
+ <code>get_dup_key()</code> will return true, <code>false</code>
+ otherwise.
+ */
+bool
+is_duplicate_key_error(int errcode)
+{
+ switch (errcode)
+ {
+ case HA_ERR_FOUND_DUPP_KEY:
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ return true;
+ }
+ return false;
+}
+
+
/*
Replace the provided record in the database.
@@ -6633,10 +6764,15 @@ replace_record(THD *thd, TABLE *table,
while ((error= table->file->ha_write_row(table->record[0])))
{
+ if (!is_duplicate_key_error(error))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
if ((keynum= table->file->get_dup_key(error)) < 0)
{
/* We failed to retrieve the duplicate key */
- DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
+ DBUG_RETURN(error);
}
/*
@@ -6653,7 +6789,10 @@ replace_record(THD *thd, TABLE *table,
{
error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
if (error)
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
}
else
{
@@ -6670,12 +6809,15 @@ replace_record(THD *thd, TABLE *table,
}
key_copy((byte*)key.get(), table->record[0], table->key_info + keynum, 0);
- error= table->file->index_read_idx(table->record[1], keynum,
+ error= table->file->index_read_idx(table->record[1], keynum,
(const byte*)key.get(),
table->key_info[keynum].key_length,
HA_READ_KEY_EXACT);
if (error)
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
}
/*
@@ -6708,15 +6850,21 @@ replace_record(THD *thd, TABLE *table,
{
error=table->file->ha_update_row(table->record[1],
table->record[0]);
+ if (error)
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
else
{
if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
/* Will retry ha_write_row() with the offending row removed. */
}
}
+
DBUG_RETURN(error);
}
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 75e4bd168ee..36334351da2 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -36,7 +36,7 @@ st_relay_log_info::st_relay_log_info()
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0),
tables_to_lock(0), tables_to_lock_count(0),
- unsafe_to_stop_at(0)
+ last_event_start_time(0)
{
DBUG_ENTER("st_relay_log_info::st_relay_log_info");
@@ -1001,6 +1001,22 @@ bool st_relay_log_info::is_until_satisfied()
log_pos= group_relay_log_pos;
}
+#ifndef DBUG_OFF
+ {
+ char buf[32];
+ DBUG_PRINT("info", ("group_master_log_name='%s', group_master_log_pos=%s",
+ group_master_log_name, llstr(group_master_log_pos, buf)));
+ DBUG_PRINT("info", ("group_relay_log_name='%s', group_relay_log_pos=%s",
+ group_relay_log_name, llstr(group_relay_log_pos, buf)));
+ DBUG_PRINT("info", ("(%s) log_name='%s', log_pos=%s",
+ until_condition == UNTIL_MASTER_POS ? "master" : "relay",
+ log_name, llstr(log_pos, buf)));
+ DBUG_PRINT("info", ("(%s) until_log_name='%s', until_log_pos=%s",
+ until_condition == UNTIL_MASTER_POS ? "master" : "relay",
+ until_log_name, llstr(until_log_pos, buf)));
+ }
+#endif
+
if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
{
/*
@@ -1095,7 +1111,7 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error)
m_table_map.clear_tables();
close_thread_tables(thd);
clear_tables_to_lock();
- unsafe_to_stop_at= 0;
+ last_event_start_time= 0;
DBUG_VOID_RETURN;
}
#endif
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index cecf351fd88..ed9ef3a9115 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -305,7 +305,14 @@ typedef struct st_relay_log_info
DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
}
- time_t unsafe_to_stop_at;
+ /*
+ Used by row-based replication to detect that it should not stop at
+ this event, but give it a chance to send more events. The time
+ where the last event inside a group started is stored here. If the
+ variable is zero, we are not in a group (but may be in a
+ transaction).
+ */
+ time_t last_event_start_time;
} RELAY_LOG_INFO;
diff --git a/sql/slave.cc b/sql/slave.cc
index ed5f2aa5c44..b58069a82ca 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -517,11 +517,11 @@ static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli)
really one minute of idleness, we don't timeout if the slave SQL thread
is actively working.
*/
- if (!rli->unsafe_to_stop_at)
+ if (rli->last_event_start_time == 0)
DBUG_RETURN(1);
DBUG_PRINT("info", ("Slave SQL thread is in an unsafe situation, giving "
"it some grace period"));
- if (difftime(time(0), rli->unsafe_to_stop_at) > 60)
+ if (difftime(time(0), rli->last_event_start_time) > 60)
{
slave_print_msg(ERROR_LEVEL, rli, 0,
"SQL thread had to stop in an unsafe situation, in "
@@ -1737,61 +1737,14 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
now the relay log starts with its Format_desc, has a Rotate etc).
*/
- DBUG_PRINT("info",("type_code=%d, server_id=%d",type_code,ev->server_id));
+ DBUG_PRINT("info",("type_code=%d (%s), server_id=%d",
+ type_code, ev->get_type_str(), ev->server_id));
- if ((ev->server_id == (uint32) ::server_id &&
- !replicate_same_server_id &&
- type_code != FORMAT_DESCRIPTION_EVENT) ||
- (rli->slave_skip_counter &&
- type_code != ROTATE_EVENT && type_code != STOP_EVENT &&
- type_code != START_EVENT_V3 && type_code!= FORMAT_DESCRIPTION_EVENT))
- {
- DBUG_PRINT("info", ("event skipped"));
- /*
- We only skip the event here and do not increase the group log
- position. In the event that we have to restart, this means
- that we might have to skip the event again, but that is a
- minor issue.
-
- If we were to increase the group log position when skipping an
- event, it might be that we are restarting at the wrong
- position and have events before that we should have executed,
- so not increasing the group log position is a sure bet in this
- case.
-
- In this way, we just step the group log position when we
- *know* that we are at the end of a group.
- */
- rli->inc_event_relay_log_pos();
- /*
- Protect against common user error of setting the counter to 1
- instead of 2 while recovering from an insert which used auto_increment,
- rand or user var.
- */
- if (rli->slave_skip_counter &&
- !((type_code == INTVAR_EVENT ||
- type_code == RAND_EVENT ||
- type_code == USER_VAR_EVENT) &&
- rli->slave_skip_counter == 1) &&
- /*
- The events from ourselves which have something to do with the relay
- log itself must be skipped, true, but they mustn't decrement
- rli->slave_skip_counter, because the user is supposed to not see
- these events (they are not in the master's binlog) and if we
- decremented, START SLAVE would for example decrement when it sees
- the Rotate, so the event which the user probably wanted to skip
- would not be skipped.
- */
- !(ev->server_id == (uint32) ::server_id &&
- (type_code == ROTATE_EVENT || type_code == STOP_EVENT ||
- type_code == START_EVENT_V3 || type_code == FORMAT_DESCRIPTION_EVENT)))
- --rli->slave_skip_counter;
- pthread_mutex_unlock(&rli->data_lock);
- delete ev;
- DBUG_RETURN(0); // avoid infinite update loops
- }
- pthread_mutex_unlock(&rli->data_lock);
+ /*
+ Execute the event, but first we set some data that is needed for
+ the thread.
+ */
thd->server_id = ev->server_id; // use the original server id for logging
thd->set_time(); // time the query
@@ -1799,7 +1752,8 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
if (!ev->when)
ev->when = time(NULL);
ev->thd = thd; // because up to this point, ev->thd == 0
- exec_res = ev->exec_event(rli);
+
+ exec_res= ev->exec_event(rli);
DBUG_PRINT("info", ("exec_event result = %d", exec_res));
DBUG_ASSERT(rli->sql_thd==thd);
/*
@@ -2354,13 +2308,17 @@ Slave SQL thread aborted. Can't execute init_slave query");
THD_CHECK_SENTRY(thd);
if (exec_relay_log_event(thd,rli))
{
+ DBUG_PRINT("info", ("exec_relay_log_event() failed"));
// do not scare the user if SQL thread was simply killed or stopped
if (!sql_slave_killed(thd,rli))
{
/*
- retrieve as much info as possible from the thd and, error codes and warnings
- and print this to the error log as to allow the user to locate the error
+ retrieve as much info as possible from the thd and, error
+ codes and warnings and print this to the error log as to
+ allow the user to locate the error
*/
+ DBUG_PRINT("info", ("thd->net.last_errno=%d; rli->last_slave_errno=%d",
+ thd->net.last_errno, rli->last_slave_errno));
if (thd->net.last_errno != 0)
{
if (rli->last_slave_errno == 0)
@@ -2682,6 +2640,7 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN(1);
}
+
pthread_mutex_lock(&mi->data_lock);
ev->log_pos= mi->master_log_pos; /* 3.23 events don't contain log_pos */
switch (ev->get_type_code()) {