Skip to content

Commit 970e96b

Browse files
author
Sujatha Sivakumar
committed
Bug#25076007: SERVER CRASHES IF BINARY LOG IS ROTATED AND
DISK-FULL CONDITION OCCURS Description: ============ Master Server crashes if binary log is rotated and disk-full condition occurs on binlog stored partition. Analysis: ========= In Master Slave setup when slave requests for data from the master, a dump thread is created and the dump tread initially checks for the existence of binary log. If the binary log is enabled it continuously reads data from the binary log and sends it to slave. If due to some critical error, as mentioned in bug scenario, rotation of binary log fails due to disk-full scenario then the action specified in binlog_error_action is taken. If users choose to ignore the error, then the binary log will be disabled.Both the binary log file/index file are closed. Their corresponding IO_CACHEs are cleared. The server will continue to do its work without writing transactions into the binary log. In such case dump thread will get EOF on current binary log and it will try to move to next binary log. At this point dump thread assumes that binary log is still active and tries to read the next binary log file name from index file IO_CACHE. Since the IO_CACHE is in cleared state it causes server to exit abnormally. Fix: === Added a check for the existence of binary log when dump thread is switching to next binary log. If binary log is in disabled state, all binary logs up to the current active log are transmitted to slave and an error is returned to the receiver thread. During testing valgrind issues were found in open_binlog code. At the time of disabling binary log only the IO_CACHE of binary log index file is cleared without closing the actual binary log index file. This results in memory leak. Replaced individual cleanup steps with appropriate cleanup function call.
1 parent 9e6c628 commit 970e96b

File tree

5 files changed

+152
-35
lines changed

5 files changed

+152
-35
lines changed

sql/binlog.cc

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -2481,7 +2481,8 @@ void MYSQL_BIN_LOG::cleanup()
24812481
if (inited)
24822482
{
24832483
inited= 0;
2484-
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
2484+
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT, true /*need_lock_log=true*/,
2485+
true /*need_lock_index=true*/);
24852486
mysql_mutex_destroy(&LOCK_log);
24862487
mysql_mutex_destroy(&LOCK_index);
24872488
mysql_mutex_destroy(&LOCK_commit);
@@ -2519,15 +2520,24 @@ void MYSQL_BIN_LOG::init_pthread_objects()
25192520
bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
25202521
const char *log_name, bool need_lock_index)
25212522
{
2523+
bool error= false;
25222524
File index_file_nr= -1;
2523-
DBUG_ASSERT(!my_b_inited(&index_file));
2525+
2526+
if (need_lock_index)
2527+
mysql_mutex_lock(&LOCK_index);
2528+
else
2529+
mysql_mutex_assert_owner(&LOCK_index);
25242530

25252531
/*
25262532
First open of this class instance
25272533
Create an index file that will hold all file names uses for logging.
25282534
Add new entries to the end of it.
25292535
*/
25302536
myf opt= MY_UNPACK_FILENAME;
2537+
2538+
if (my_b_inited(&index_file))
2539+
goto end;
2540+
25312541
if (!index_file_name_arg)
25322542
{
25332543
index_file_name_arg= log_name; // Use same basename for index file
@@ -2539,7 +2549,8 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
25392549
if (set_crash_safe_index_file_name(index_file_name_arg))
25402550
{
25412551
sql_print_error("MYSQL_BIN_LOG::set_crash_safe_index_file_name failed.");
2542-
return TRUE;
2552+
error= true;
2553+
goto end;
25432554
}
25442555

25452556
/*
@@ -2553,7 +2564,8 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
25532564
{
25542565
sql_print_error("MYSQL_BIN_LOG::open_index_file failed to "
25552566
"move crash_safe_index_file to index file.");
2556-
return TRUE;
2567+
error= true;
2568+
goto end;
25572569
}
25582570

25592571
if ((index_file_nr= mysql_file_open(m_key_file_log_index,
@@ -2575,7 +2587,8 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
25752587
*/
25762588
if (index_file_nr >= 0)
25772589
mysql_file_close(index_file_nr, MYF(0));
2578-
return TRUE;
2590+
error= true;
2591+
goto end;
25792592
}
25802593

25812594
#ifdef HAVE_REPLICATION
@@ -2589,17 +2602,20 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
25892602

25902603
if (set_purge_index_file_name(index_file_name_arg) ||
25912604
open_purge_index_file(FALSE) ||
2592-
purge_index_entry(NULL, NULL, need_lock_index) ||
2605+
purge_index_entry(NULL, NULL, false) ||
25932606
close_purge_index_file() ||
25942607
DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
25952608
{
25962609
sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
25972610
"file.");
2598-
return TRUE;
2611+
error= TRUE;
2612+
goto end;
25992613
}
26002614
#endif
2601-
2602-
return FALSE;
2615+
end:
2616+
if (need_lock_index)
2617+
mysql_mutex_unlock(&LOCK_index);
2618+
return error;
26032619
}
26042620

26052621

@@ -3161,11 +3177,11 @@ bool MYSQL_BIN_LOG::open_binlog(const char *log_name,
31613177
enum cache_type io_cache_type_arg,
31623178
ulong max_size_arg,
31633179
bool null_created_arg,
3180+
bool need_lock_log,
31643181
bool need_lock_index,
31653182
bool need_sid_lock,
31663183
Format_description_log_event *extra_description_event)
31673184
{
3168-
File file= -1;
31693185

31703186
// lock_index must be acquired *before* sid_lock.
31713187
DBUG_ASSERT(need_sid_lock || !need_lock_index);
@@ -3377,24 +3393,22 @@ bool MYSQL_BIN_LOG::open_binlog(const char *log_name,
33773393
purge_index_entry(NULL, NULL, need_lock_index);
33783394
close_purge_index_file();
33793395
#endif
3380-
if (file >= 0)
3381-
mysql_file_close(file, MYF(0));
3382-
end_io_cache(&log_file);
3383-
end_io_cache(&index_file);
3384-
my_free(name);
3385-
name= NULL;
3386-
log_state= LOG_CLOSED;
3396+
33873397
if (binlog_error_action == ABORT_SERVER)
33883398
{
33893399
exec_binlog_error_action_abort("Either disk is full or file system is read "
33903400
"only while opening the binlog. Aborting the"
33913401
" server.");
33923402
}
33933403
else
3404+
{
33943405
sql_print_error("Could not use %s for logging (error %d). "
33953406
"Turning logging off for the whole duration of the MySQL "
33963407
"server process. To turn it on again: fix the cause, "
3397-
"shutdown the MySQL server and restart it.", name, errno);
3408+
"shutdown the MySQL server and restart it.",
3409+
(new_name) ? new_name : name, errno);
3410+
close(LOG_CLOSE_INDEX, need_lock_log, need_lock_index);
3411+
}
33983412
DBUG_RETURN(1);
33993413
}
34003414

@@ -3678,6 +3692,12 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
36783692
else
36793693
mysql_mutex_assert_owner(&LOCK_index);
36803694

3695+
if (!my_b_inited(&index_file))
3696+
{
3697+
error= LOG_INFO_IO;
3698+
goto end;
3699+
}
3700+
36813701
// extend relative paths for log_name to be searched
36823702
if (log_name)
36833703
{
@@ -3770,6 +3790,11 @@ int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock_index)
37703790
else
37713791
mysql_mutex_assert_owner(&LOCK_index);
37723792

3793+
if (!my_b_inited(&index_file))
3794+
{
3795+
error= LOG_INFO_IO;
3796+
goto err;
3797+
}
37733798
/* As the file is flushed, we can't get an error here */
37743799
my_b_seek(&index_file, linfo->index_file_offset);
37753800

@@ -3854,7 +3879,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
38543879
/* Save variables so that we can reopen the log */
38553880
save_name=name;
38563881
name=0; // Protect against free
3857-
close(LOG_CLOSE_TO_BE_OPENED);
3882+
close(LOG_CLOSE_TO_BE_OPENED, false/*need_lock_log=false*/,
3883+
false/*need_lock_index=false*/);
38583884

38593885
/*
38603886
First delete all old log files and then update the index file.
@@ -3907,7 +3933,9 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
39073933
}
39083934

39093935
/* Start logging with a new file */
3910-
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
3936+
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED,
3937+
false/*need_lock_log=false*/,
3938+
false/*need_lock_index=false*/);
39113939
if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update)
39123940
{
39133941
if (my_errno == ENOENT)
@@ -3953,6 +3981,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
39533981
if (!open_index_file(index_file_name, 0, false/*need_lock_index=false*/))
39543982
if ((error= open_binlog(save_name, 0, io_cache_type,
39553983
max_size, false,
3984+
false/*need_lock_log=false*/,
39563985
false/*need_lock_index=false*/,
39573986
false/*need_sid_lock=false*/,
39583987
NULL)))
@@ -5010,7 +5039,9 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock_log, Format_description_log_even
50105039

50115040
old_name=name;
50125041
name=0; // Don't free name
5013-
close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX);
5042+
close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX,
5043+
false/*need_lock_log=false*/,
5044+
false/*need_lock_index=false*/);
50145045

50155046
if (checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF)
50165047
{
@@ -5041,6 +5072,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock_log, Format_description_log_even
50415072
file_to_open= new_name_ptr;
50425073
error= open_binlog(old_name, new_name_ptr, io_cache_type,
50435074
max_size, true/*null_created_arg=true*/,
5075+
false/*need_lock_log=false*/,
50445076
false/*need_lock_index=false*/,
50455077
true/*need_sid_lock=true*/,
50465078
extra_description_event);
@@ -5073,7 +5105,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock_log, Format_description_log_even
50735105
- switch server to protected/readonly mode
50745106
- ...
50755107
*/
5076-
close(LOG_CLOSE_INDEX);
50775108
if (binlog_error_action == ABORT_SERVER)
50785109
{
50795110
exec_binlog_error_action_abort("Either disk is full or file system is"
@@ -5087,12 +5118,15 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock_log, Format_description_log_even
50875118
"again: fix the cause, shutdown the MySQL "
50885119
"server and restart it.",
50895120
new_name_ptr, errno);
5121+
close(LOG_CLOSE_INDEX, false /*need_lock_log=false*/,
5122+
false/*need_lock_index=false*/);
50905123
}
50915124

50925125
mysql_mutex_unlock(&LOCK_index);
50935126
if (need_lock_log)
50945127
mysql_mutex_unlock(&LOCK_log);
50955128

5129+
DEBUG_SYNC(current_thd, "after_disable_binlog");
50965130
DBUG_RETURN(error);
50975131
}
50985132

@@ -6113,10 +6147,17 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
61136147
The internal structures are not freed until cleanup() is called
61146148
*/
61156149

6116-
void MYSQL_BIN_LOG::close(uint exiting)
6150+
void MYSQL_BIN_LOG::close(uint exiting, bool need_lock_log,
6151+
bool need_lock_index)
61176152
{ // One can't set log_type here!
61186153
DBUG_ENTER("MYSQL_BIN_LOG::close");
61196154
DBUG_PRINT("enter",("exiting: %d", (int) exiting));
6155+
6156+
if (need_lock_log)
6157+
mysql_mutex_lock(&LOCK_log);
6158+
else
6159+
mysql_mutex_assert_owner(&LOCK_log);
6160+
61206161
if (log_state == LOG_OPENED)
61216162
{
61226163
#ifdef HAVE_REPLICATION
@@ -6159,6 +6200,11 @@ void MYSQL_BIN_LOG::close(uint exiting)
61596200
called a not complete close earlier and the index file is still open.
61606201
*/
61616202

6203+
if (need_lock_index)
6204+
mysql_mutex_lock(&LOCK_index);
6205+
else
6206+
mysql_mutex_assert_owner(&LOCK_index);
6207+
61626208
if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
61636209
{
61646210
end_io_cache(&index_file);
@@ -6170,9 +6216,17 @@ void MYSQL_BIN_LOG::close(uint exiting)
61706216
errno, my_strerror(errbuf, sizeof(errbuf), errno));
61716217
}
61726218
}
6219+
6220+
if (need_lock_index)
6221+
mysql_mutex_unlock(&LOCK_index);
6222+
61736223
log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
61746224
my_free(name);
61756225
name= NULL;
6226+
6227+
if (need_lock_log)
6228+
mysql_mutex_unlock(&LOCK_log);
6229+
61766230
DBUG_VOID_RETURN;
61776231
}
61786232

@@ -6237,6 +6291,7 @@ int MYSQL_BIN_LOG::open_binlog(const char *opt_name)
62376291
{
62386292
/* generate a new binlog to mask a corrupted one */
62396293
open_binlog(opt_name, 0, WRITE_CACHE, max_binlog_size, false,
6294+
true/*need_lock_log=true*/,
62406295
true/*need_lock_index=true*/,
62416296
true/*need_sid_lock=true*/,
62426297
NULL);
@@ -7036,7 +7091,8 @@ void MYSQL_BIN_LOG::handle_binlog_flush_or_sync_error(THD *thd,
70367091
"the cause, shutdown the MySQL server and restart it.",
70377092
errmsg);
70387093
}
7039-
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
7094+
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT, false/*need_lock_log=false*/,
7095+
true/*need_lock_index=true*/);
70407096
if (need_lock_log)
70417097
mysql_mutex_unlock(&LOCK_log);
70427098
DEBUG_SYNC(thd, "after_binlog_closed_due_to_error");

sql/binlog.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#ifndef BINLOG_H_INCLUDED
2-
/* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
2+
/* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -585,6 +585,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
585585
enum cache_type io_cache_type_arg,
586586
ulong max_size,
587587
bool null_created,
588+
bool need_lock_log,
588589
bool need_lock_index, bool need_sid_lock,
589590
Format_description_log_event *extra_description_event);
590591
bool open_index_file(const char *index_file_name_arg,
@@ -658,7 +659,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
658659
int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
659660
bool need_lock_index);
660661
bool reset_logs(THD* thd);
661-
void close(uint exiting);
662+
void close(uint exiting, bool need_lock_log, bool need_lock_index);
662663

663664
// iterating through the log index file
664665
int find_log_pos(LOG_INFO* linfo, const char* log_name,

sql/mysqld.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5059,6 +5059,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
50595059
const_cast<Gtid_set*>(gtid_state->get_logged_gtids()));
50605060
if (mysql_bin_log.open_binlog(opt_bin_logname, 0,
50615061
WRITE_CACHE, max_binlog_size, false,
5062+
true/*need_lock_log=true*/,
50625063
true/*need_lock_index=true*/,
50635064
true/*need_sid_lock=true*/,
50645065
NULL))

0 commit comments

Comments
 (0)