Skip to content

Commit 4d8b346

Browse files
jonasoreknielsen
authored andcommitted
MDEV-7257: Dump Thread Enhancements
Make the binlog dump threads not need to take LOCK_log while sending binlog events to slave. Instead, a new LOCK_binlog_end_pos is used just to coordinate tracking the current end-of-log. This is a pre-requisite for MDEV-162, "Enhanced semisync replication". It should also help reduce the contention on LOCK_log on a busy master. Also does some much-needed refactoring/cleanup of the related code in the binlog dump thread.
1 parent ea01fff commit 4d8b346

File tree

5 files changed

+1025
-719
lines changed

5 files changed

+1025
-719
lines changed

mysql-test/suite/rpl/r/rpl_checksum.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ insert into t1 values (1) /* will not be applied on slave due to simulation */;
7171
set @@global.debug_dbug='d,simulate_slave_unaware_checksum';
7272
start slave;
7373
include/wait_for_slave_io_error.inc [errno=1236]
74-
Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'Slave can not handle replication events with the checksum that master is configured to log; the first event 'master-bin.000009' at 367, the last event read from 'master-bin.000010' at 248, the last byte read from 'master-bin.000010' at 248.''
74+
Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'Slave can not handle replication events with the checksum that master is configured to log; the first event 'master-bin.000009' at 367, the last event read from 'master-bin.000010' at 4, the last byte read from 'master-bin.000010' at 248.''
7575
select count(*) as zero from t1;
7676
zero
7777
0

sql/log.cc

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3133,6 +3133,7 @@ void MYSQL_BIN_LOG::cleanup()
31333133
mysql_mutex_destroy(&LOCK_index);
31343134
mysql_mutex_destroy(&LOCK_xid_list);
31353135
mysql_mutex_destroy(&LOCK_binlog_background_thread);
3136+
mysql_mutex_destroy(&LOCK_binlog_end_pos);
31363137
mysql_cond_destroy(&update_cond);
31373138
mysql_cond_destroy(&COND_queue_busy);
31383139
mysql_cond_destroy(&COND_xid_list);
@@ -3178,6 +3179,9 @@ void MYSQL_BIN_LOG::init_pthread_objects()
31783179
&COND_binlog_background_thread, 0);
31793180
mysql_cond_init(key_BINLOG_COND_binlog_background_thread_end,
31803181
&COND_binlog_background_thread_end, 0);
3182+
3183+
mysql_mutex_init(m_key_LOCK_binlog_end_pos, &LOCK_binlog_end_pos,
3184+
MY_MUTEX_INIT_SLOW);
31813185
}
31823186

31833187

@@ -3524,10 +3528,19 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
35243528
if (flush_io_cache(&log_file) ||
35253529
mysql_file_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
35263530
goto err;
3527-
mysql_mutex_lock(&LOCK_commit_ordered);
3528-
strmake_buf(last_commit_pos_file, log_file_name);
3529-
last_commit_pos_offset= my_b_tell(&log_file);
3530-
mysql_mutex_unlock(&LOCK_commit_ordered);
3531+
3532+
my_off_t offset= my_b_tell(&log_file);
3533+
3534+
if (!is_relay_log)
3535+
{
3536+
/* update binlog_end_pos so that it can be read by after sync hook */
3537+
reset_binlog_end_pos(log_file_name, offset);
3538+
3539+
mysql_mutex_lock(&LOCK_commit_ordered);
3540+
strmake_buf(last_commit_pos_file, log_file_name);
3541+
last_commit_pos_offset= offset;
3542+
mysql_mutex_unlock(&LOCK_commit_ordered);
3543+
}
35313544

35323545
if (write_file_name_to_index_file)
35333546
{
@@ -3632,6 +3645,7 @@ int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
36323645

36333646
int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
36343647
{
3648+
mysql_mutex_assert_owner(&LOCK_log);
36353649
strmake_buf(linfo->log_file_name, log_file_name);
36363650
linfo->pos = my_b_tell(&log_file);
36373651
return 0;
@@ -4797,6 +4811,20 @@ void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident)
47974811

47984812
bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
47994813
{
4814+
/**
4815+
* there should/must be mysql_mutex_assert_owner(&LOCK_log) here...
4816+
* but code violates this! (scary monsters and super creeps!)
4817+
*
4818+
* example stacktrace:
4819+
* #8 MYSQL_BIN_LOG::is_active
4820+
* #9 MYSQL_BIN_LOG::can_purge_log
4821+
* #10 MYSQL_BIN_LOG::purge_logs
4822+
* #11 MYSQL_BIN_LOG::purge_first_log
4823+
* #12 next_event
4824+
* #13 exec_relay_log_event
4825+
*
4826+
* I didn't investigate if this is ligit...(i.e if my comment is wrong)
4827+
*/
48004828
return !strcmp(log_file_name, log_file_name_arg);
48014829
}
48024830

@@ -5359,6 +5387,7 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
53595387
binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
53605388

53615389
/* Server layer calls us with LOCK_commit_ordered locked, so this is safe. */
5390+
mysql_mutex_assert_owner(&LOCK_commit_ordered);
53625391
strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file);
53635392
cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
53645393

@@ -6013,6 +6042,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
60136042
}
60146043
else
60156044
{
6045+
/* update binlog_end_pos so it can be read by dump thread
6046+
*
6047+
* note: must be _after_ the RUN_HOOK(after_flush) or else
6048+
* semi-sync-plugin might not have put the transaction into
6049+
* it's list before dump-thread tries to send it
6050+
*/
6051+
update_binlog_end_pos(offset);
6052+
60166053
signal_update();
60176054
if ((error= rotate(false, &check_purge)))
60186055
check_purge= false;
@@ -6664,6 +6701,9 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
66646701
}
66656702

66666703
offset= my_b_tell(&log_file);
6704+
6705+
update_binlog_end_pos(offset);
6706+
66676707
/*
66686708
Take mutex to protect against a reader seeing partial writes of 64-bit
66696709
offset on 32-bit CPUs.
@@ -6709,6 +6749,9 @@ MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name,
67096749
}
67106750

67116751
offset= my_b_tell(&log_file);
6752+
6753+
update_binlog_end_pos(offset);
6754+
67126755
/*
67136756
Take mutex to protect against a reader seeing partial writes of 64-bit
67146757
offset on 32-bit CPUs.
@@ -7335,7 +7378,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
73357378
{
73367379
if (!current->error &&
73377380
RUN_HOOK(binlog_storage, after_flush,
7338-
(current->thd, log_file_name,
7381+
(current->thd,
7382+
current->cache_mngr->last_commit_pos_file,
73397383
current->cache_mngr->last_commit_pos_offset, synced)))
73407384
{
73417385
current->error= ER_ERROR_ON_WRITE;
@@ -7347,6 +7391,14 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
73477391
all_error= false;
73487392
}
73497393

7394+
/* update binlog_end_pos so it can be read by dump thread
7395+
*
7396+
* note: must be _after_ the RUN_HOOK(after_flush) or else
7397+
* semi-sync-plugin might not have put the transaction into
7398+
* it's list before dump-thread tries to send it
7399+
*/
7400+
update_binlog_end_pos(commit_offset);
7401+
73507402
if (any_error)
73517403
sql_print_error("Failed to run 'after_flush' hooks");
73527404
if (!all_error)
@@ -7387,6 +7439,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
73877439

73887440
DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered");
73897441
mysql_mutex_lock(&LOCK_commit_ordered);
7442+
/**
7443+
* TODO(jonaso): Check with Kristian,
7444+
* if we rotate:d above, this offset is "wrong"
7445+
*/
73907446
last_commit_pos_offset= commit_offset;
73917447
/*
73927448
We cannot unlock LOCK_log until we have locked LOCK_commit_ordered;
@@ -7625,6 +7681,7 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
76257681
PSI_stage_info old_stage;
76267682
DBUG_ENTER("wait_for_update_relay_log");
76277683

7684+
mysql_mutex_assert_owner(&LOCK_log);
76287685
thd->ENTER_COND(&update_cond, &LOCK_log,
76297686
&stage_slave_has_read_all_relay_log,
76307687
&old_stage);
@@ -7655,6 +7712,7 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
76557712
int ret= 0;
76567713
DBUG_ENTER("wait_for_update_bin_log");
76577714

7715+
mysql_mutex_assert_owner(&LOCK_log);
76587716
if (!timeout)
76597717
mysql_cond_wait(&update_cond, &LOCK_log);
76607718
else
@@ -7663,6 +7721,21 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
76637721
DBUG_RETURN(ret);
76647722
}
76657723

7724+
int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd,
7725+
struct timespec *timeout)
7726+
{
7727+
int ret= 0;
7728+
DBUG_ENTER("wait_for_update_binlog_end_pos");
7729+
7730+
mysql_mutex_assert_owner(get_binlog_end_pos_lock());
7731+
if (!timeout)
7732+
mysql_cond_wait(&update_cond, get_binlog_end_pos_lock());
7733+
else
7734+
ret= mysql_cond_timedwait(&update_cond, get_binlog_end_pos_lock(),
7735+
timeout);
7736+
DBUG_RETURN(ret);
7737+
}
7738+
76667739

76677740
/**
76687741
Close the log file.
@@ -9703,6 +9776,14 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
97039776
}
97049777
}
97059778

9779+
void assert_LOCK_log_owner(bool owner)
9780+
{
9781+
if (owner)
9782+
mysql_mutex_assert_owner(mysql_bin_log.get_log_lock());
9783+
else
9784+
mysql_mutex_assert_not_owner(mysql_bin_log.get_log_lock());
9785+
}
9786+
97069787
struct st_mysql_storage_engine binlog_storage_engine=
97079788
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
97089789

sql/log.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ class MYSQL_LOG
341341
/** Instrumentation key to use for file io in @c log_file */
342342
PSI_file_key m_log_file_key;
343343
#endif
344+
/* for documentation of mutexes held in various places in code */
345+
friend void assert_LOCK_log_owner(bool owner);
344346
};
345347

346348
class MYSQL_QUERY_LOG: public MYSQL_LOG
@@ -425,6 +427,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
425427
PSI_file_key m_key_file_log_index;
426428

427429
PSI_file_key m_key_COND_queue_busy;
430+
431+
/** The instrumentation key to use for @ LOCK_binlog_end_pos */
432+
PSI_mutex_key m_key_LOCK_binlog_end_pos;
428433
#endif
429434

430435
struct group_commit_entry
@@ -477,6 +482,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
477482

478483
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
479484
mysql_mutex_t LOCK_index;
485+
mysql_mutex_t LOCK_binlog_end_pos;
480486
mysql_mutex_t LOCK_xid_list;
481487
mysql_cond_t COND_xid_list;
482488
mysql_cond_t update_cond;
@@ -811,6 +817,67 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
811817
int bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no);
812818
bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
813819
uint64 seq_no);
820+
821+
822+
void update_binlog_end_pos(my_off_t pos)
823+
{
824+
mysql_mutex_assert_owner(&LOCK_log);
825+
mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos);
826+
lock_binlog_end_pos();
827+
/**
828+
* note: it would make more sense to assert(pos > binlog_end_pos)
829+
* but there are two places triggered by mtr that has pos == binlog_end_pos
830+
* i didn't investigate but accepted as it should do no harm
831+
*/
832+
DBUG_ASSERT(pos >= binlog_end_pos);
833+
binlog_end_pos= pos;
834+
signal_update();
835+
unlock_binlog_end_pos();
836+
}
837+
838+
/**
839+
* used when opening new file, and binlog_end_pos moves backwards
840+
*/
841+
void reset_binlog_end_pos(const char file_name[FN_REFLEN], my_off_t pos)
842+
{
843+
mysql_mutex_assert_owner(&LOCK_log);
844+
mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos);
845+
lock_binlog_end_pos();
846+
binlog_end_pos= pos;
847+
strcpy(binlog_end_pos_file, file_name);
848+
signal_update();
849+
unlock_binlog_end_pos();
850+
}
851+
852+
/*
853+
It is called by the threads(e.g. dump thread) which want to read
854+
log without LOCK_log protection.
855+
*/
856+
my_off_t get_binlog_end_pos(char file_name_buf[FN_REFLEN]) const
857+
{
858+
mysql_mutex_assert_not_owner(&LOCK_log);
859+
mysql_mutex_assert_owner(&LOCK_binlog_end_pos);
860+
strcpy(file_name_buf, binlog_end_pos_file);
861+
return binlog_end_pos;
862+
}
863+
void lock_binlog_end_pos() { mysql_mutex_lock(&LOCK_binlog_end_pos); }
864+
void unlock_binlog_end_pos() { mysql_mutex_unlock(&LOCK_binlog_end_pos); }
865+
mysql_mutex_t* get_binlog_end_pos_lock() { return &LOCK_binlog_end_pos; }
866+
867+
int wait_for_update_binlog_end_pos(THD* thd, struct timespec * timeout);
868+
869+
/*
870+
Binlog position of end of the binlog.
871+
Access to this is protected by LOCK_binlog_end_pos
872+
873+
The difference between this and last_commit_pos_{file,offset} is that
874+
the commit position is updated later. If semi-sync wait point is set
875+
to WAIT_AFTER_SYNC, the commit pos is update after semi-sync-ack has
876+
been received and the end point is updated after the write as it's needed
877+
for the dump threads to be able to semi-sync the event.
878+
*/
879+
my_off_t binlog_end_pos;
880+
char binlog_end_pos_file[FN_REFLEN];
814881
};
815882

816883
class Log_event_handler
@@ -1088,4 +1155,6 @@ static inline TC_LOG *get_tc_log_implementation()
10881155
return &tc_log_mmap;
10891156
}
10901157

1158+
void assert_LOCK_log_owner(bool owner);
1159+
10911160
#endif /* LOG_H */

sql/mysqld.cc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5167,9 +5167,18 @@ a file name for --log-bin-index option", opt_binlog_index_name);
51675167
unireg_abort(1);
51685168
}
51695169

5170-
if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
5171-
WRITE_CACHE, max_binlog_size, 0, TRUE))
5172-
unireg_abort(1);
5170+
if (opt_bin_log)
5171+
{
5172+
/**
5173+
* mutex lock is not needed here.
5174+
* but to be able to have mysql_mutex_assert_owner() in code,
5175+
* we do it anyway */
5176+
mysql_mutex_lock(mysql_bin_log.get_log_lock());
5177+
if (mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
5178+
WRITE_CACHE, max_binlog_size, 0, TRUE))
5179+
unireg_abort(1);
5180+
mysql_mutex_unlock(mysql_bin_log.get_log_lock());
5181+
}
51735182

51745183
#ifdef HAVE_REPLICATION
51755184
if (opt_bin_log && expire_logs_days)

0 commit comments

Comments
 (0)