Skip to content

Commit c60ac30

Browse files
Bill QuBill Qu
Bill Qu
authored and
Bill Qu
committed
Bug#19424075 WRITE/SYNC REDO LOG BEFORE FLUSH THREAD CACHE TO BINLOG
According to the crash recovery logic of mysql server, we only need to guarantee that write/sync prepared transaction to InnoDB redo log before writing to binary log. And prepared records of transactions were not committed into InnoDB redo log in a group. To improve performance, write/sync prepared records of transactions to InnoDB redo log in a group right before writing to binary log during binlog group commit flush stage.
1 parent 23c547f commit c60ac30

20 files changed

+226
-154
lines changed

mysql-test/r/mysqld--help-notwin.result

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,6 @@ The following options may be given as the first argument:
8585
--binlog-ignore-db=name
8686
Tells the master that updates to the given database
8787
should not be logged to the binary log.
88-
--binlog-max-flush-queue-time=#
89-
The maximum time that the binary log group commit will
90-
keep reading transactions before it flush the
91-
transactions to the binary log (and optionally sync,
92-
depending on the value of sync_binlog).
9388
--binlog-order-commits
9489
Issue internal commit calls in the same order as
9590
transactions are written to the binary log. Default is to
@@ -1119,7 +1114,6 @@ binlog-error-action IGNORE_ERROR
11191114
binlog-format STATEMENT
11201115
binlog-group-commit-sync-delay 0
11211116
binlog-group-commit-sync-no-delay-count 0
1122-
binlog-max-flush-queue-time 0
11231117
binlog-order-commits TRUE
11241118
binlog-row-event-max-size 8192
11251119
binlog-row-image FULL

mysql-test/r/mysqld--help-win.result

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,6 @@ The following options may be given as the first argument:
8585
--binlog-ignore-db=name
8686
Tells the master that updates to the given database
8787
should not be logged to the binary log.
88-
--binlog-max-flush-queue-time=#
89-
The maximum time that the binary log group commit will
90-
keep reading transactions before it flush the
91-
transactions to the binary log (and optionally sync,
92-
depending on the value of sync_binlog).
9388
--binlog-order-commits
9489
Issue internal commit calls in the same order as
9590
transactions are written to the binary log. Default is to
@@ -1119,7 +1114,6 @@ binlog-error-action IGNORE_ERROR
11191114
binlog-format STATEMENT
11201115
binlog-group-commit-sync-delay 0
11211116
binlog-group-commit-sync-no-delay-count 0
1122-
binlog-max-flush-queue-time 0
11231117
binlog-order-commits TRUE
11241118
binlog-row-event-max-size 8192
11251119
binlog-row-image FULL
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
CREATE TABLE t1(c1 INT) ENGINE= InnoDB;
2+
# Crash right after flushing InnoDB redo log
3+
SET SESSION DEBUG="+d,crash_after_flush_engine_log";
4+
BEGIN;
5+
INSERT INTO t1 VALUES(1);
6+
COMMIT;
7+
ERROR HY000: Lost connection to MySQL server during query
8+
# Restart the master server
9+
#
10+
# Verify that a transaction can not be recovered during server
11+
# recovery from a crash, which happened after flushing it to
12+
# InnoDB redo log and before flushing it to binary log.
13+
#
14+
include/assert.inc [Table t1 must not contain 1]
15+
# Crash right after flushing binary log
16+
SET SESSION DEBUG="+d,crash_after_flush_binlog";
17+
BEGIN;
18+
INSERT INTO t1 VALUES(2);
19+
COMMIT;
20+
ERROR HY000: Lost connection to MySQL server during query
21+
# Restart the master server
22+
#
23+
# Verify that a transaction can be recovered during server
24+
# recovery from a crash, which happened after flushing it
25+
# to binary log.
26+
#
27+
include/assert.inc [Table t1 must contain 2]
28+
DROP TABLE t1;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#
2+
# Bug#19424075 WRITE/SYNC REDO LOG BEFORE FLUSH THREAD CACHE TO BINLOG
3+
#
4+
# Verify that a transaction can not be recovered during server
5+
# recovery from a crash, which happened after flushing it to
6+
# InnoDB redo log and before flushing it to binary log. And
7+
# a transaction can be recovered during server recovery from
8+
# a crash, which happened after flushing it to binary log.
9+
#
10+
--source include/not_embedded.inc
11+
--source include/not_valgrind.inc
12+
--source include/have_log_bin.inc
13+
14+
CREATE TABLE t1(c1 INT) ENGINE= InnoDB;
15+
16+
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
17+
--echo # Crash right after flushing InnoDB redo log
18+
SET SESSION DEBUG="+d,crash_after_flush_engine_log";
19+
BEGIN;
20+
INSERT INTO t1 VALUES(1);
21+
# 2013 - CR_SERVER_LOST
22+
--error 2013
23+
COMMIT;
24+
--source include/wait_until_disconnected.inc
25+
26+
--enable_reconnect
27+
--echo # Restart the master server
28+
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
29+
--source include/wait_until_connected_again.inc
30+
--disable_reconnect
31+
32+
--echo #
33+
--echo # Verify that a transaction can not be recovered during server
34+
--echo # recovery from a crash, which happened after flushing it to
35+
--echo # InnoDB redo log and before flushing it to binary log.
36+
--echo #
37+
--let $assert_text= Table t1 must not contain 1
38+
--let $assert_cond= [SELECT count(*) FROM t1 WHERE c1=1] = 0
39+
--source include/assert.inc
40+
41+
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
42+
--echo # Crash right after flushing binary log
43+
SET SESSION DEBUG="+d,crash_after_flush_binlog";
44+
BEGIN;
45+
INSERT INTO t1 VALUES(2);
46+
# 2013 - CR_SERVER_LOST
47+
--error 2013
48+
COMMIT;
49+
--source include/wait_until_disconnected.inc
50+
51+
--enable_reconnect
52+
--echo # Restart the master server
53+
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
54+
--source include/wait_until_connected_again.inc
55+
--disable_reconnect
56+
57+
--echo #
58+
--echo # Verify that a transaction can be recovered during server
59+
--echo # recovery from a crash, which happened after flushing it
60+
--echo # to binary log.
61+
--echo #
62+
--let $assert_text= Table t1 must contain 2
63+
--let $assert_cond= [SELECT count(*) FROM t1 WHERE c1=2] = 1
64+
--source include/assert.inc
65+
66+
# Cleanup
67+
DROP TABLE t1;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
--binlog-max-flush-queue-time=1 --skip-binlog-order-commits
1+
--skip-binlog-order-commits
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--sync-binlog=1 --innodb-flush-log-at-trx-commit=1

mysql-test/suite/sys_vars/r/binlog_max_flush_queue_time_basic.result

Lines changed: 0 additions & 26 deletions
This file was deleted.

mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ time_zone,autocommit,character_set_client,character_set_results,character_set_co
1515
SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%';
1616
VARIABLE_NAME VARIABLE_VALUE
1717
SESSION_TRACK_SCHEMA ON
18-
SESSION_TRACK_SYSTEM_VARIABLES time_zone,autocommit,character_set_client,character_set_results,character_set_connection
1918
SESSION_TRACK_STATE_CHANGE OFF
19+
SESSION_TRACK_SYSTEM_VARIABLES time_zone,autocommit,character_set_client,character_set_results,character_set_connection
2020
# via INFORMATION_SCHEMA.SESSION_VARIABLES
2121
SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%';
2222
VARIABLE_NAME VARIABLE_VALUE
2323
SESSION_TRACK_SCHEMA ON
24-
SESSION_TRACK_SYSTEM_VARIABLES time_zone,autocommit,character_set_client,character_set_results,character_set_connection
2524
SESSION_TRACK_STATE_CHANGE OFF
25+
SESSION_TRACK_SYSTEM_VARIABLES time_zone,autocommit,character_set_client,character_set_results,character_set_connection
2626
SET @global_saved_tmp = @@global.session_track_system_variables;
2727

2828
# Altering global variable's value

mysql-test/suite/sys_vars/t/binlog_max_flush_queue_time_basic.test

Lines changed: 0 additions & 26 deletions
This file was deleted.

sql/binlog.cc

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6953,6 +6953,25 @@ int MYSQL_BIN_LOG::prepare(THD *thd, bool all)
69536953
{
69546954
DBUG_ENTER("MYSQL_BIN_LOG::prepare");
69556955

6956+
DBUG_ASSERT(opt_bin_log);
6957+
/*
6958+
The applier thread explicitly overrides the value of sql_log_bin
6959+
with the value of log_slave_updates.
6960+
*/
6961+
DBUG_ASSERT(thd->slave_thread ?
6962+
opt_log_slave_updates : thd->variables.sql_log_bin);
6963+
6964+
/*
6965+
Set HA_IGNORE_DURABILITY to not flush the prepared record of the
6966+
transaction to the log of storage engine (for example, InnoDB
6967+
redo log) during the prepare phase. So that we can flush prepared
6968+
records of transactions to the log of storage engine in a group
6969+
right before flushing them to binary log during binlog group
6970+
commit flush stage. Reset to HA_REGULAR_DURABILITY at the
6971+
beginning of parsing next command.
6972+
*/
6973+
thd->durability_property= HA_IGNORE_DURABILITY;
6974+
69566975
int error= ha_prepare_low(thd, all);
69576976

69586977
DBUG_RETURN(error);
@@ -7221,55 +7240,33 @@ MYSQL_BIN_LOG::process_flush_stage_queue(my_off_t *total_bytes_var,
72217240
int flush_error= 1;
72227241
mysql_mutex_assert_owner(&LOCK_log);
72237242

7224-
const ulonglong max_udelay= my_atomic_load32(&opt_binlog_max_flush_queue_time);
7225-
const ulonglong start_utime= max_udelay > 0 ? my_micro_time() : 0;
7226-
72277243
/*
7228-
First we read the queue until it either is empty or the difference
7229-
between the time we started and the current time is too large.
7230-
7231-
We remember the first thread we unqueued, because this will be the
7232-
beginning of the out queue.
7233-
*/
7234-
bool has_more= true;
7235-
THD *first_seen= NULL;
7236-
while ((max_udelay == 0 || my_micro_time() < start_utime + max_udelay) && has_more)
7244+
Fetch the entire flush queue and empty it, so that the next batch
7245+
has a leader. We must do this before invoking ha_flush_logs(...)
7246+
for guaranteeing to flush prepared records of transactions before
7247+
flushing them to binary log, which is required by crash recovery.
7248+
*/
7249+
THD *first_seen= stage_manager.fetch_queue_for(Stage_manager::FLUSH_STAGE);
7250+
DBUG_ASSERT(first_seen != NULL);
7251+
/*
7252+
We flush prepared records of transactions to the log of storage
7253+
engine (for example, InnoDB redo log) in a group right before
7254+
flushing them to binary log.
7255+
*/
7256+
ha_flush_logs(NULL, true);
7257+
DBUG_EXECUTE_IF("crash_after_flush_engine_log", DBUG_SUICIDE(););
7258+
/* Flush thread caches to binary log. */
7259+
for (THD *head= first_seen ; head ; head = head->next_to_commit)
72377260
{
7238-
std::pair<bool,THD*> current= stage_manager.pop_front(Stage_manager::FLUSH_STAGE);
7239-
std::pair<int,my_off_t> result= flush_thread_caches(current.second);
7240-
has_more= current.first;
7261+
std::pair<int,my_off_t> result= flush_thread_caches(head);
72417262
total_bytes+= result.second;
72427263
if (flush_error == 1)
72437264
flush_error= result.first;
7244-
if (first_seen == NULL)
7245-
first_seen= current.second;
72467265
#ifndef DBUG_OFF
72477266
no_flushes++;
72487267
#endif
72497268
}
72507269

7251-
/*
7252-
Either the queue is empty, or we ran out of time. If we ran out of
7253-
time, we have to fetch the entire queue (and flush it) since
7254-
otherwise the next batch will not have a leader.
7255-
*/
7256-
if (has_more)
7257-
{
7258-
THD *queue= stage_manager.fetch_queue_for(Stage_manager::FLUSH_STAGE);
7259-
for (THD *head= queue ; head ; head = head->next_to_commit)
7260-
{
7261-
std::pair<int,my_off_t> result= flush_thread_caches(head);
7262-
total_bytes+= result.second;
7263-
if (flush_error == 1)
7264-
flush_error= result.first;
7265-
#ifndef DBUG_OFF
7266-
no_flushes++;
7267-
#endif
7268-
}
7269-
if (first_seen == NULL)
7270-
first_seen= queue;
7271-
}
7272-
72737270
*out_queue_var= first_seen;
72747271
*total_bytes_var= total_bytes;
72757272
if (total_bytes > 0 && my_b_tell(&log_file) >= (my_off_t) max_size)
@@ -7744,6 +7741,7 @@ int MYSQL_BIN_LOG::ordered_commit(THD *thd, bool all, bool skip_commit)
77447741
my_off_t flush_end_pos= 0;
77457742
if (flush_error == 0 && total_bytes > 0)
77467743
flush_error= flush_cache_to_file(&flush_end_pos);
7744+
DBUG_EXECUTE_IF("crash_after_flush_binlog", DBUG_SUICIDE(););
77477745

77487746
bool update_binlog_end_pos_after_sync= (get_sync_period() == 1);
77497747

sql/ha_ndbcluster_binlog.cc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -852,11 +852,18 @@ static void ndbcluster_reset_slave(THD *thd)
852852
/**
853853
Upon the sql command flush logs, we need to ensure that all outstanding
854854
ndb data to be logged has made it to the binary log to get a deterministic
855-
behavior on the rotation of the log.
855+
behavior on the rotation of the log. Do nothing if the ndbcluster_flush_logs
856+
is caused by binlog group commit during flush stage.
857+
858+
@param hton NDB handlerton.
859+
@param binlog_group_flush true if we got invoked by binlog group
860+
commit during flush stage, false in other cases.
861+
@return false Success always.
856862
*/
857-
static bool ndbcluster_flush_logs(handlerton *hton)
863+
static bool ndbcluster_flush_logs(handlerton *hton, bool binlog_group_flush)
858864
{
859-
ndbcluster_binlog_wait(current_thd);
865+
if (!binlog_group_flush)
866+
ndbcluster_binlog_wait(current_thd);
860867
return FALSE;
861868
}
862869

sql/handler.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,25 +2165,27 @@ static my_bool flush_handlerton(THD *thd, plugin_ref plugin,
21652165
void *arg)
21662166
{
21672167
handlerton *hton= plugin_data(plugin, handlerton *);
2168-
if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
2169-
hton->flush_logs(hton))
2168+
if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
2169+
hton->flush_logs(hton, *(static_cast<bool *>(arg))))
21702170
return TRUE;
21712171
return FALSE;
21722172
}
21732173

21742174

2175-
bool ha_flush_logs(handlerton *db_type)
2175+
bool ha_flush_logs(handlerton *db_type, bool binlog_group_flush)
21762176
{
21772177
if (db_type == NULL)
21782178
{
21792179
if (plugin_foreach(NULL, flush_handlerton,
2180-
MYSQL_STORAGE_ENGINE_PLUGIN, 0))
2180+
MYSQL_STORAGE_ENGINE_PLUGIN,
2181+
static_cast<void *>(&binlog_group_flush)))
21812182
return TRUE;
21822183
}
21832184
else
21842185
{
21852186
if (db_type->state != SHOW_OPTION_YES ||
2186-
(db_type->flush_logs && db_type->flush_logs(db_type)))
2187+
(db_type->flush_logs &&
2188+
db_type->flush_logs(db_type, binlog_group_flush)))
21872189
return TRUE;
21882190
}
21892191
return FALSE;

0 commit comments

Comments
 (0)