diff options
author | unknown <knielsen@knielsen-hq.org> | 2012-06-21 21:17:34 +0200 |
---|---|---|
committer | unknown <knielsen@knielsen-hq.org> | 2012-06-21 21:17:34 +0200 |
commit | a5731b27b7eb9e0c30659219a00d92bcb8e08246 (patch) | |
tree | 1e09180d2e5b1fc95cd8f098899c658c117cf4b0 /sql/log.cc | |
parent | e7362d457a41a8c8a1925c2d0be59df450511320 (diff) | |
download | mariadb-git-a5731b27b7eb9e0c30659219a00d92bcb8e08246.tar.gz |
MDEV-342: Do not mark old binlog file as cleanly closed during rotate until
the new file is fully synced to disk and binlog index. This fixes a window
where a crash would leave next server restart unable to detect that a crash
occured, causing recovery to fail.
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 60 |
1 files changed, 51 insertions, 9 deletions
diff --git a/sql/log.cc b/sql/log.cc index e80a2aaec51..801d945f3f0 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2464,9 +2464,10 @@ void MYSQL_LOG::init_pthread_objects() SYNOPSIS close() - exiting Bitmask. For the slow and general logs the only used bit is - LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call - open at once after close. + exiting Bitmask. LOG_CLOSE_TO_BE_OPENED is used if we intend to call + open at once after close. LOG_CLOSE_DELAYED_CLOSE is used for + binlog rotation, to delay actual close of the old file until + we have successfully created the new file. NOTES One can do an open on the object at once after doing a close. @@ -2487,7 +2488,8 @@ void MYSQL_LOG::close(uint exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } - if (mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) + if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) && + mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) { write_error= 1; sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); @@ -3189,6 +3191,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name, if (write_file_name_to_index_file) { #ifdef HAVE_REPLICATION +#ifdef ENABLED_DEBUG_SYNC + if (current_thd) + DEBUG_SYNC(current_thd, "binlog_open_before_update_index"); +#endif DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_SUICIDE();); #endif @@ -4297,6 +4303,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) { int error= 0, close_on_error= FALSE; char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open; + uint close_flag; + bool delay_close= false; + File old_file; + LINT_INIT(old_file); DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) @@ -4380,7 +4390,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) } old_name=name; name=0; // Don't free name - close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX); + close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX; + if (!is_relay_log) + { + /* + We need to keep the old binlog file open (and marked as in-use) until + the new one is fully created and synced to disk and index. Otherwise we + leave a window where if we crash, there is no binlog file marked as + crashed for server restart to detect the need for recovery. + */ + old_file= log_file.file; + close_flag|= LOG_CLOSE_DELAYED_CLOSE; + delay_close= true; + } + close(close_flag); if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF) { DBUG_ASSERT(!is_relay_log); @@ -4423,6 +4446,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) end: + if (delay_close) + { + clear_inuse_flag_when_closing(old_file); + mysql_file_close(old_file, MYF(MY_WME)); + } + if (error && close_on_error /* rotate or reopen failed */) { /* @@ -6275,6 +6304,8 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, - LOG_CLOSE_TO_BE_OPENED : if we intend to call open at once after close. - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log + - LOG_CLOSE_DELAYED_CLOSE : do not yet close the file and clear the + LOG_EVENT_BINLOG_IN_USE_F flag @note One can do an open on the object at once after doing a close. @@ -6304,12 +6335,11 @@ void MYSQL_BIN_LOG::close(uint exiting) #endif /* HAVE_REPLICATION */ /* don't pwrite in a file opened with O_APPEND - it doesn't work */ - if (log_file.type == WRITE_CACHE && log_type == LOG_BIN) + if (log_file.type == WRITE_CACHE && log_type == LOG_BIN + && !(exiting & LOG_CLOSE_DELAYED_CLOSE)) { - my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; my_off_t org_position= mysql_file_tell(log_file.file, MYF(0)); - uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F - mysql_file_pwrite(log_file.file, &flags, 1, offset, MYF(0)); + clear_inuse_flag_when_closing(log_file.file); /* Restore position so that anything we have in the IO_cache is written to the correct position. @@ -6344,6 +6374,18 @@ void MYSQL_BIN_LOG::close(uint exiting) } +/* + Clear the LOG_EVENT_BINLOG_IN_USE_F; this marks the binlog file as cleanly + closed and not needing crash recovery. +*/ +void MYSQL_BIN_LOG::clear_inuse_flag_when_closing(File file) +{ + my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; + uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F + mysql_file_pwrite(file, &flags, 1, offset, MYF(0)); +} + + void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg) { /* |