Skip to content

Commit 749fd74

Browse files
author
Justin Jose
committed
Bug#32442772 LOG_STATUS HAS WRONG BINARY_LOG_POSITION OF GTID_EXECUTED
Description: ------------ When the server is under heavy write operation, binary_log_position of gtid_executed shown in log_status doesn't match with the position of that GTID events shown in the binary log file. Analysis: --------- A binlog commit has the following stages: 1. Flush – Protected by the LOCK_log. 2. Sync – At this stage, LOCK_log is released, and LOCK_sync is acquired. 3. Commit – Here, LOCK_sync is released, and LOCK_commit is acquired. During this stage, global_sid_lock is also acquired, and gtid_executed is updated. When querying the log_status table, LOCK_log is acquired for the binlog at Log_resource_binlog_wrapper::lock, and global_sid_lock is acquired for gtid_executed at Log_resource_gtid_state_wrapper::lock. The issue arises when a query to the log_status table is executed after stage 1 of the binlog commit. At this point, LOCK_log has already been released, the binlog file and position are updated, and Log_resource_binlog_wrapper::lock proceeds. However, while the binlog commit is still processing stage 2, Log_resource_gtid_state_wrapper::lock is invoked and acquires global_sid_lock. At this point, stage 3 of the binlog commit is unable to update gtid_executed, leading to a mismatch between the binlog position(the new values) and gtid_executed(old value) in the log_status query. Fix: ---- Increase the scope of the lock on the log_status table to also ensure that the transactions that entered in the binlog commit pipeline are completed. This adjustment will ensure that the query to the log_status table waits until gtid_executed has been fully updated, thereby guaranteeing consistency with the binlog position. Change-Id: Iab0513821a0e1ffcc8a522b61447b0d9193aafe8
1 parent a7bcd9b commit 749fd74

File tree

7 files changed

+148
-12
lines changed

7 files changed

+148
-12
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# 1. Initial setup
3+
RESET MASTER;
4+
CREATE TABLE t1(c1 INT);
5+
INSERT INTO t1 VALUES(1);
6+
INSERT INTO t1 VALUES(2);
7+
FLUSH BINARY LOGS;
8+
#
9+
# 2. Execute an INSERT query and pause after it acquires commit_lock
10+
[START] INSERT INTO t1 VALUES (3);
11+
#
12+
# 3. Execute a SELECT query on performance_schema.log_status and wait
13+
# until it is waiting for MYSQL_BIN_LOG::prep_xids_cond
14+
SELECT REPLACE(LOCAL->"$.gtid_executed", '"', '') INTO @ps_log_status FROM performance_schema.log_status;
15+
#
16+
# 4. Resume the INSERT query
17+
[END] INSERT INTO t1 VALUES (3);
18+
#
19+
# 5. Let the SELECT query complete
20+
#
21+
# 6. Verify that the correct GTID set is received from
22+
# performance_schema.log_status
23+
include/assert.inc [Gtid_executed is updated with GTID 4]
24+
#
25+
# 7. Cleanup
26+
DROP TABLE t1;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
##############################################################################
2+
# ==== Purpose ====
3+
# The purpose of this test is to verify that, at any given time,
4+
# the gtid_executed value shown in log_status table matches the correct
5+
# executed GTIDs.
6+
#
7+
# ==== Requirement ====
8+
# At any given time, the gtid_executed value shown in log_status must match
9+
# the correct executed GTIDs.
10+
#
11+
# ==== Implementation ====
12+
# 1. Initial setup
13+
# 2. Execute an INSERT query and pause after it acquires commit_lock
14+
# 3. Execute a SELECT query on performance_schema.log_status and verify
15+
# that it is waiting on MYSQL_BIN_LOG::prep_xids_cond
16+
# 4. Resume the INSERT query
17+
# 5. Let the SELECT query complete
18+
# 6. Verify that the correct GTID set is received from
19+
# performance_schema.log_status
20+
# 7. Clean up
21+
#
22+
# ==== References ====
23+
# Bug#32442772 LOG_STATUS HAS WRONG BINARY_LOG_POSITION OF GTID_EXECUTED
24+
###############################################################################
25+
26+
# This test case is binary log format agnostic
27+
--source include/have_binlog_format_row.inc
28+
29+
--source include/have_debug.inc
30+
--source include/have_debug_sync.inc
31+
32+
--echo #
33+
--echo # 1. Initial setup
34+
--connect (con1,localhost,root,,)
35+
--connect (con2,localhost,root,,)
36+
37+
--connection default
38+
RESET MASTER;
39+
--let $server_uuid= `SELECT @@GLOBAL.SERVER_UUID`
40+
CREATE TABLE t1(c1 INT);
41+
INSERT INTO t1 VALUES(1);
42+
INSERT INTO t1 VALUES(2);
43+
FLUSH BINARY LOGS;
44+
45+
--echo #
46+
--echo # 2. Execute an INSERT query and pause after it acquires commit_lock
47+
--let $auxiliary_connection = default
48+
--let $statement_connection = con1
49+
--let $sync_point = update_gtid_state_before_global_sid_lock
50+
--let $statement = INSERT INTO t1 VALUES (3)
51+
--source include/execute_to_sync_point.inc
52+
53+
--echo #
54+
--echo # 3. Execute a SELECT query on performance_schema.log_status and wait
55+
--echo # until it is waiting for MYSQL_BIN_LOG::prep_xids_cond
56+
--connection con2
57+
--send SELECT REPLACE(LOCAL->"$.gtid_executed", '"', '') INTO @ps_log_status FROM performance_schema.log_status
58+
59+
--connection default
60+
--let $wait_condition = SELECT COUNT(*) = 1 FROM performance_schema.threads T, performance_schema.events_waits_current W WHERE T.THREAD_ID=W.THREAD_ID AND W.EVENT_NAME LIKE "%MYSQL_BIN_LOG::prep_xids_cond" AND T.PROCESSLIST_INFO LIKE "%FROM%log_status%"
61+
--source include/wait_condition.inc
62+
63+
--echo #
64+
--echo # 4. Resume the INSERT query
65+
--source include/execute_from_sync_point.inc
66+
67+
--echo #
68+
--echo # 5. Let the SELECT query complete
69+
--connection con2
70+
--reap
71+
72+
--echo #
73+
--echo # 6. Verify that the correct GTID set is received from
74+
--echo # performance_schema.log_status
75+
--let $assert_text= Gtid_executed is updated with GTID 4
76+
--let $assert_cond= "[SELECT @ps_log_status]" = "$server_uuid:1-4"
77+
--source include/assert.inc
78+
79+
--echo #
80+
--echo # 7. Cleanup
81+
--connection default
82+
--disconnect con1
83+
--disconnect con2
84+
DROP TABLE t1;

sql/binlog.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6559,6 +6559,15 @@ void MYSQL_BIN_LOG::dec_prep_xids(THD *thd) {
65596559
}
65606560
}
65616561

6562+
void MYSQL_BIN_LOG::wait_for_prep_xids() {
6563+
DBUG_TRACE;
6564+
mysql_mutex_lock(&LOCK_xids);
6565+
while (get_prep_xids() > 0) {
6566+
mysql_cond_wait(&m_prep_xids_cond, &LOCK_xids);
6567+
}
6568+
mysql_mutex_unlock(&LOCK_xids);
6569+
}
6570+
65626571
/*
65636572
Wrappers around new_file_impl to avoid using argument
65646573
to control locking. The argument 1) less readable 2) breaks
@@ -6621,7 +6630,6 @@ int MYSQL_BIN_LOG::new_file_impl(
66216630
mysql_mutex_assert_owner(&LOCK_log);
66226631
DBUG_EXECUTE_IF("semi_sync_3-way_deadlock",
66236632
DEBUG_SYNC(current_thd, "before_rotate_binlog"););
6624-
mysql_mutex_lock(&LOCK_xids);
66256633
/*
66266634
We need to ensure that the number of prepared XIDs are 0.
66276635
@@ -6630,10 +6638,7 @@ int MYSQL_BIN_LOG::new_file_impl(
66306638
- We keep the LOCK_log to block new transactions from being
66316639
written to the binary log.
66326640
*/
6633-
while (get_prep_xids() > 0) {
6634-
mysql_cond_wait(&m_prep_xids_cond, &LOCK_xids);
6635-
}
6636-
mysql_mutex_unlock(&LOCK_xids);
6641+
wait_for_prep_xids();
66376642

66386643
mysql_mutex_lock(&LOCK_index);
66396644

sql/binlog.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ class MYSQL_BIN_LOG : public TC_LOG {
269269
inline uint get_sync_period() { return *sync_period_ptr; }
270270

271271
public:
272+
/**
273+
Wait until the number of prepared XIDs are zero.
274+
*/
275+
void wait_for_prep_xids();
276+
272277
/*
273278
This is used to start writing to a new log file. The difference from
274279
new_file() is locking. new_file_without_locking() does not acquire

sql/log_resource.cc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,15 @@ bool Log_resource_binlog_wrapper::collect_info() {
8787
return error;
8888
}
8989

90-
void Log_resource_gtid_state_wrapper::lock() { global_sid_lock->wrlock(); }
90+
void Log_resource_gtid_state_wrapper::lock() {
91+
/*
92+
Ensure that the number of prepared XIDs is zero to confirm the completion
93+
of transactions in the BGC pipeline.
94+
*/
95+
binlog->wait_for_prep_xids();
96+
97+
global_sid_lock->wrlock();
98+
}
9199

92100
void Log_resource_gtid_state_wrapper::unlock() { global_sid_lock->unlock(); }
93101

@@ -137,9 +145,10 @@ Log_resource *Log_resource_factory::get_wrapper(MYSQL_BIN_LOG *binlog,
137145
}
138146

139147
Log_resource *Log_resource_factory::get_wrapper(Gtid_state *gtid_state,
140-
Json_dom *json) {
148+
Json_dom *json,
149+
MYSQL_BIN_LOG *binlog) {
141150
Log_resource_gtid_state_wrapper *wrapper;
142-
wrapper = new Log_resource_gtid_state_wrapper(gtid_state, json);
151+
wrapper = new Log_resource_gtid_state_wrapper(gtid_state, json, binlog);
143152
return wrapper;
144153
}
145154

sql/log_resource.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ class Log_resource_binlog_wrapper : public Log_resource {
176176
*/
177177
class Log_resource_gtid_state_wrapper : public Log_resource {
178178
Gtid_state *gtid_state = nullptr;
179+
MYSQL_BIN_LOG *binlog = nullptr;
179180

180181
public:
181182
/**
@@ -184,11 +185,14 @@ class Log_resource_gtid_state_wrapper : public Log_resource {
184185
@param[in] gtid_state_arg the pointer to the Gtid_state object resource.
185186
@param[in] json_arg the pointer to the JSON object to be populated with the
186187
resource log information.
188+
@param[in] binlog_arg the pointer to the MYSQL_BIN_LOG object resource.
187189
*/
188190

189191
Log_resource_gtid_state_wrapper(Gtid_state *gtid_state_arg,
190-
Json_dom *json_arg)
191-
: Log_resource(json_arg), gtid_state(gtid_state_arg) {}
192+
Json_dom *json_arg, MYSQL_BIN_LOG *binlog_arg)
193+
: Log_resource(json_arg),
194+
gtid_state(gtid_state_arg),
195+
binlog(binlog_arg) {}
192196

193197
void lock() override;
194198
void unlock() override;
@@ -256,10 +260,12 @@ class Log_resource_factory {
256260
@param[in] gtid_state the pointer to the Gtid_state object resource.
257261
@param[in] json the pointer to the JSON object to be populated with the
258262
resource log information.
263+
@param[in] binlog the pointer to the MYSQL_BIN_LOG object resource.
259264
@return the pointer to the new Log_resource.
260265
*/
261266

262-
static Log_resource *get_wrapper(Gtid_state *gtid_state, Json_dom *json);
267+
static Log_resource *get_wrapper(Gtid_state *gtid_state, Json_dom *json,
268+
MYSQL_BIN_LOG *binlog);
263269

264270
/**
265271
Creates a Log_resource wrapper based on a handlerton.

storage/perfschema/table_log_status.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ int table_log_status::make_row() {
226226
*/
227227
{
228228
Log_resource *res;
229-
res = Log_resource_factory::get_wrapper(gtid_state, &json_local);
229+
res = Log_resource_factory::get_wrapper(gtid_state, &json_local,
230+
&mysql_bin_log);
230231
error = DBUG_EVALUATE_IF("log_status_oom_gtid", 1, !res);
231232
if (error) {
232233
my_error(ER_UNABLE_TO_COLLECT_LOG_STATUS, MYF(0), "LOCAL",

0 commit comments

Comments
 (0)