List:Commits« Previous MessageNext Message »
From:Luís Soares Date:December 2 2009 4:26pm
Subject:Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:3215)
Bug#45292
View as plain text  
Hi Alfranio,

  Nice Work. We're almost there.

STATUS
------

  Not approved.

REQUIRED CHANGES
----------------

  RC1. I am still missing your OK regarding the test running on
       windows how it is today (using the cat_file on index
       files). Please make sure that it does not fail there
       because of filesystem paths not being masked out.

  RC2. Mind the spurious new lines ;) . 

  RC3. I still think that we should check the return value of
       find_log_pos  in MYSQL_BIN_LOG::purge_index_entry .

REQUESTS
--------

  R1. Why not call the new function "define_new_file":
      "MYSQL_LOG::init_and_set_log_file_name" instead?

      Given that it merges the two procedures...

  R2. About the guards HAVE_REPLICATION, I see that Zhenxing has
      already mentioned that these are being misused in some other
      places in the code. So I am ok with filing a new bug to
      address this.

SUGGESTIONS
-----------
n/a

DETAILS 
-------
n/a

On Mon, 2009-11-30 at 17:58 +0000, Alfranio Correia wrote:
> #At
> file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-45292/mysql-5.1-bugteam/
> based on revid:li-bing.song@stripped
> 
>  3215 Alfranio Correia	2009-11-30
>       BUG#45292 orphan binary log created when starting server twice
>       
>       This patch fixes three bugs as follows. First, aborting the server while
> purging
>       binary logs might generate orphan files due to how the purge operation was
>       implemented:
>       
>                 (purge routine - sql/log.cc - MYSQL_BIN_LOG::purge_logs)
>       
>             1 - register the files to be removed in a temporary buffer.
>             2 - update the log-bin.index.
>             3 - flush the log-bin.index.
>             4 - erase the files whose names where register in the temporary buffer
>             in step 1.
>       
>       Thus a failure while  executing step 4 would generate an orphan file. Second,
>       a similar issue might happen while creating a new binary as follows:
>       
>                 (create routine - sql/log.cc - MYSQL_BIN_LOG::open)
>       
>             1 - open the new log-bin.
>             2 - update the log-bin.index.
>       
>       Thus a failure while executing step 1 would generate an orphan file.
>       
>       To fix these issues, we record the files to be purged or created before really
>       removing or adding them. So if a failure happens such records can be used to
>       automatically remove dangling files. The new steps might be outlined as
> follows:
>       
>                 (purge routine - sql/log.cc - MYSQL_BIN_LOG::purge_logs)
>       
>             1 - register the files to be removed in the log-bin.~rec~ placed in
>             the data directory.
>             2 - update the log-bin.index.
>             3 - flush the log-bin.index.
>             4 - delete the log-bin.~rec~.
>       
>                 (create routine - sql/log.cc - MYSQL_BIN_LOG::open)
>       
>             1 - register the file to be created in the log-bin.~rec~
>             placed in the data directory.
>             2 - open the new log-bin.
>             3 - update the log-bin.index.
>             4 - delete the log-bin.~rec~.
>       
>                 (recovery routine - sql/log.cc - MYSQL_BIN_LOG::open_index_file)
>       
>             1 - open the log-bin.index.
>             2 - open the log-bin.~rec~.
>             3 - for each file in log-bin.~rec~.
>               3.1 Check if the file is in the log-bin.index and if so ignore it.
>               3.2 Otherwise, delete it.
>       
>       The third issue can be described as follows. The purge operation was allowing
>       to remove a file in use thus leading to the loss of data and possible
>       inconsistencies between the master and slave. Roughly, the routine was only
>       taking into account the dump threads and so if a slave was not connect the
>       file might be delete even though it was in use.
> 
>     modified:
>       mysql-test/suite/binlog/r/binlog_index.result
>       mysql-test/suite/binlog/t/binlog_index.test
>       sql/log.cc
>       sql/log.h
>       sql/mysqld.cc
>       sql/rpl_rli.cc
> === modified file 'mysql-test/suite/binlog/r/binlog_index.result'
> --- a/mysql-test/suite/binlog/r/binlog_index.result	2009-01-23 12:22:05 +0000
> +++ b/mysql-test/suite/binlog/r/binlog_index.result	2009-11-30 17:58:07 +0000
> @@ -1,3 +1,8 @@
> +call mtr.add_suppression('Attempting backtrace');
> +call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to process registered
> files that would be purged.');
> +call mtr.add_suppression('MSYQL_BIN_LOG::open failed to sync the index file');
> +call mtr.add_suppression('Turning logging off for the whole duration of the MySQL
> server process.');
> +call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to clean registers before
> purging logs.');
>  flush logs;
>  flush logs;
>  flush logs;
> @@ -21,7 +26,6 @@ flush logs;
>  *** must be a warning master-bin.000001 was not found ***
>  Warnings:
>  Warning	1612	Being purged log master-bin.000001 was not found
> -Warning	1612	Being purged log master-bin.000001 was not found
>  *** must show one record, of the active binlog, left in the index file after PURGE
> ***
>  show binary logs;
>  Log_name	File_size
> @@ -37,4 +41,79 @@ Level	Code	Message
>  Error	1377	a problem with deleting master-bin.000001; consider examining
> correspondence of your binlog index file to the actual binlog files
>  Error	1377	Fatal error during log purge
>  reset master;
> +# crash_purge_before_update_index
> +flush logs;
> +SET SESSION debug="+d,crash_purge_before_update_index";
> +purge binary logs TO 'master-bin.000002';
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000001
> +./master-bin.000002
> +./master-bin.000003
> +# crash_purge_non_critical_after_update_index
> +flush logs;
> +SET SESSION debug="+d,crash_purge_non_critical_after_update_index";
> +purge binary logs TO 'master-bin.000004';
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000004
> +./master-bin.000005
> +# crash_purge_critical_after_update_index
> +flush logs;
> +SET SESSION debug="+d,crash_purge_critical_after_update_index";
> +purge binary logs TO 'master-bin.000006';
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000006
> +./master-bin.000007
> +# crash_create_non_critical_before_update_index
> +SET SESSION debug="+d,crash_create_non_critical_before_update_index";
> +flush logs;
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000006
> +./master-bin.000007
> +./master-bin.000008
> +# crash_create_critical_before_update_index
> +SET SESSION debug="+d,crash_create_critical_before_update_index";
> +flush logs;
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000006
> +./master-bin.000007
> +./master-bin.000008
> +./master-bin.000009
> +# crash_create_after_update_index
> +SET SESSION debug="+d,crash_create_after_update_index";
> +flush logs;
> +ERROR HY000: Lost connection to MySQL server during query
> +./master-bin.000006
> +./master-bin.000007
> +./master-bin.000008
> +./master-bin.000009
> +./master-bin.000010
> +./master-bin.000011
> +#
> +# This should put the server in unsafe state and stop
> +# accepting any command. If we inject a fault at this
> +# point and continue the execution the server crashes.
> +# Besides the flush command does not report an error.
> +#
> +# fault_injection_registering_index
> +SET SESSION debug="+d,fault_injection_registering_index";
> +flush logs;
> +./master-bin.000006
> +./master-bin.000007
> +./master-bin.000008
> +./master-bin.000009
> +./master-bin.000010
> +./master-bin.000011
> +./master-bin.000012
> +# fault_injection_updating_index
> +SET SESSION debug="+d,fault_injection_updating_index";
> +flush logs;
> +./master-bin.000006
> +./master-bin.000007
> +./master-bin.000008
> +./master-bin.000009
> +./master-bin.000010
> +./master-bin.000011
> +./master-bin.000012
> +./master-bin.000013
> +SET SESSION debug="";
>  End of tests
> 
> === modified file 'mysql-test/suite/binlog/t/binlog_index.test'
> --- a/mysql-test/suite/binlog/t/binlog_index.test	2008-04-05 11:09:53 +0000
> +++ b/mysql-test/suite/binlog/t/binlog_index.test	2009-11-30 17:58:07 +0000
> @@ -3,6 +3,12 @@
>  #
>  source include/have_log_bin.inc;
>  source include/not_embedded.inc;
> +call mtr.add_suppression('Attempting backtrace');
> +call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to process registered
> files that would be purged.');
> +call mtr.add_suppression('MSYQL_BIN_LOG::open failed to sync the index file');
> +call mtr.add_suppression('Turning logging off for the whole duration of the MySQL
> server process.');
> +call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to clean registers before
> purging logs.');
> +let $old=`select @@debug`;
>  
>  #
>  # testing purge binary logs TO
> @@ -66,4 +72,127 @@ rmdir $MYSQLD_DATADIR/master-bin.000001;
>  --disable_warnings
>  reset master;
>  --enable_warnings
> +
> +--echo # crash_purge_before_update_index
> +flush logs;
> +
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_purge_before_update_index";
> +--error 2013
> +purge binary logs TO 'master-bin.000002';
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +file_exists $MYSQLD_DATADIR/master-bin.000001;
> +file_exists $MYSQLD_DATADIR/master-bin.000002;
> +file_exists $MYSQLD_DATADIR/master-bin.000003;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # crash_purge_non_critical_after_update_index
> +flush logs;
> +
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_purge_non_critical_after_update_index";
> +--error 2013
> +purge binary logs TO 'master-bin.000004';
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000001;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000002;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000003;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # crash_purge_critical_after_update_index
> +flush logs;
> +
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_purge_critical_after_update_index";
> +--error 2013
> +purge binary logs TO 'master-bin.000006';
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000004;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000005;
> +file_exists $MYSQLD_DATADIR/master-bin.000006;
> +file_exists $MYSQLD_DATADIR/master-bin.000007;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000008;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # crash_create_non_critical_before_update_index
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_create_non_critical_before_update_index";
> +--error 2013
> +flush logs;
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +file_exists $MYSQLD_DATADIR/master-bin.000008;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000009;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # crash_create_critical_before_update_index
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_create_critical_before_update_index";
> +--error 2013
> +flush logs;
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +file_exists $MYSQLD_DATADIR/master-bin.000009;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000010;
> +--error 1
> +file_exists $MYSQLD_DATADIR/master-bin.000011;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # crash_create_after_update_index
> +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
> +SET SESSION debug="+d,crash_create_after_update_index";
> +--error 2013
> +flush logs;
> +
> +--enable_reconnect
> +--source include/wait_until_connected_again.inc
> +
> +file_exists $MYSQLD_DATADIR/master-bin.000010;
> +file_exists $MYSQLD_DATADIR/master-bin.000011;
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo #
> +--echo # This should put the server in unsafe state and stop
> +--echo # accepting any command. If we inject a fault at this
> +--echo # point and continue the execution the server crashes.
> +--echo # Besides the flush command does not report an error.
> +--echo #
> +
> +--echo # fault_injection_registering_index
> +SET SESSION debug="+d,fault_injection_registering_index";
> +flush logs;
> +--source include/restart_mysqld.inc
> +
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +--echo # fault_injection_updating_index
> +SET SESSION debug="+d,fault_injection_updating_index";
> +flush logs;
> +--source include/restart_mysqld.inc
> +
> +cat_file $MYSQLD_DATADIR/master-bin.index;
> +
> +eval SET SESSION debug="$old";
> +
>  --echo End of tests
> 
> === modified file 'sql/log.cc'
> --- a/sql/log.cc	2009-11-20 20:56:43 +0000
> +++ b/sql/log.cc	2009-11-30 17:58:07 +0000
> @@ -1897,6 +1897,22 @@ void MYSQL_LOG::init(enum_log_type log_t
>  }
>  
> 
> +bool MYSQL_LOG::define_new_file(const char *log_name,
> +                                const char *new_name,
> +                                enum_log_type log_type_arg,
> +                                enum cache_type io_cache_type_arg)
> +{
> +  init(log_type_arg, io_cache_type_arg);
> +
> +  if (new_name && !strmov(log_file_name, new_name))
> +    return TRUE;
> +  else if (!new_name && generate_new_name(log_file_name, log_name))
> +    return TRUE;
> +
> +  return FALSE;
> +}
> +
> +
>  /*
>    Open a (new) log file.
>  
> @@ -1929,17 +1945,14 @@ bool MYSQL_LOG::open(const char *log_nam
>  
>    write_error= 0;
>  
> -  init(log_type_arg, io_cache_type_arg);
> -
>    if (!(name= my_strdup(log_name, MYF(MY_WME))))
>    {
>      name= (char *)log_name; // for the error message
>      goto err;
>    }
>  
> -  if (new_name)
> -    strmov(log_file_name, new_name);
> -  else if (generate_new_name(log_file_name, name))
> +  if (define_new_file(name, new_name,
> +                      log_type_arg, io_cache_type_arg))
>      goto err;
>  
>    if (io_cache_type == SEQ_READ_APPEND)
> @@ -2429,7 +2442,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG()
>    */
>    index_file_name[0] = 0;
>    bzero((char*) &index_file, sizeof(index_file));
> -  bzero((char*) &purge_temp, sizeof(purge_temp));
> +  bzero((char*) &purge_index_file, sizeof(purge_index_file));
>  }
>  
>  /* this is called only once */
> @@ -2473,7 +2486,7 @@ void MYSQL_BIN_LOG::init_pthread_objects
>  
> 
>  bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
> -                                const char *log_name)
> +                                    const char *log_name, bool need_mutex)
>  {
>    File index_file_nr= -1;
>    DBUG_ASSERT(!my_b_inited(&index_file));
> @@ -2498,7 +2511,8 @@ bool MYSQL_BIN_LOG::open_index_file(cons
>         init_io_cache(&index_file, index_file_nr,
>                       IO_SIZE, WRITE_CACHE,
>                       my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
> -			0, MYF(MY_WME | MY_WAIT_IF_FULL)))
> +			0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
> +      DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
>    {
>      /*
>        TODO: all operations creating/deleting the index file or a log, should
> @@ -2509,6 +2523,28 @@ bool MYSQL_BIN_LOG::open_index_file(cons
>        my_close(index_file_nr,MYF(0));
>      return TRUE;
>    }
> +
> +  #ifdef HAVE_REPLICATION
> +  /*
> +    Sync the index by purging any binary log file that is not registered.
> +    In other words, either purge binary log files that were removed from
> +    the index but not purged from the file system due to a crash or purge
> +    any binary log file that was created but not register in the index
> +    due to a crash.
> +  */
> +
> +  if (set_purge_index_file_name(index_file_name_arg) ||
> +      open_purge_index_file(FALSE) ||
> +      purge_index_entry(NULL, NULL, need_mutex) ||
> +      close_purge_index_file() ||
> +      DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
> +  {
> +    sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
> +                    "file.");
> +    return TRUE;
> +  }
> +  #endif
> +
>    return FALSE;
>  }
>  
> @@ -2533,17 +2569,44 @@ bool MYSQL_BIN_LOG::open(const char *log
>                           enum cache_type io_cache_type_arg,
>                           bool no_auto_events_arg,
>                           ulong max_size_arg,
> -                         bool null_created_arg)
> +                         bool null_created_arg,
> +                         bool need_mutex)
>  {
>    File file= -1;
> +
>    DBUG_ENTER("MYSQL_BIN_LOG::open");
>    DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
>  
> -  write_error=0;
> +  if (MYSQL_LOG::define_new_file(log_name, new_name, log_type_arg,
> +                                 io_cache_type_arg))
> +  {
> +    sql_print_error("MSYQL_BIN_LOG::open failed to generate new file name.");
> +    DBUG_RETURN(1);
> +  }
> +
> +  #ifdef HAVE_REPLICATION
> +  if (open_purge_index_file(TRUE) ||
> +      register_create_index_entry(log_file_name) ||
> +      sync_purge_index_file() ||
> +      DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0))
> +  {
> +    sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file.");
> +    DBUG_RETURN(1);
> +  }
> +  DBUG_EXECUTE_IF("crash_create_non_critical_before_update_index", abort(););
> +  #endif
> +
> +  write_error= 0;
>  
>    /* open the main log file */
> -  if (MYSQL_LOG::open(log_name, log_type_arg, new_name, io_cache_type_arg))
> +  if (MYSQL_LOG::open(log_name, log_type_arg, new_name,
> +                      io_cache_type_arg))
> +  {
> +    #ifdef HAVE_REPLICATION
> +    close_purge_index_file();
> +    #endif
>      DBUG_RETURN(1);                            /* all warnings issued */
> +  }
>  
>    init(no_auto_events_arg, max_size_arg);
>  
> @@ -2569,9 +2632,6 @@ bool MYSQL_BIN_LOG::open(const char *log
>        write_file_name_to_index_file= 1;
>      }
>  
> -    DBUG_ASSERT(my_b_inited(&index_file) != 0);
> -    reinit_io_cache(&index_file, WRITE_CACHE,
> -                    my_b_filelength(&index_file), 0, 0);
>      if (need_start_event && !no_auto_events)
>      {
>        /*
> @@ -2629,23 +2689,43 @@ bool MYSQL_BIN_LOG::open(const char *log
>  
>      if (write_file_name_to_index_file)
>      {
> +      #ifdef HAVE_REPLICATION
> +      DBUG_EXECUTE_IF("crash_create_critical_before_update_index", abort(););
> +      #endif
> +
> +      DBUG_ASSERT(my_b_inited(&index_file) != 0);
> +      reinit_io_cache(&index_file, WRITE_CACHE,
> +                      my_b_filelength(&index_file), 0, 0);
>        /*
>          As this is a new log file, we write the file name to the index
>          file. As every time we write to the index file, we sync it.
>        */
> -      if (my_b_write(&index_file, (uchar*) log_file_name,
> -		     strlen(log_file_name)) ||
> -	  my_b_write(&index_file, (uchar*) "\n", 1) ||
> -	  flush_io_cache(&index_file) ||
> +      if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
> +          my_b_write(&index_file, (uchar*) log_file_name,
> +                     strlen(log_file_name)) ||
> +          my_b_write(&index_file, (uchar*) "\n", 1) ||
> +          flush_io_cache(&index_file) ||
>            my_sync(index_file.file, MYF(MY_WME)))
> -	goto err;
> +        goto err;
> +
> +      #ifdef HAVE_REPLICATION
> +      DBUG_EXECUTE_IF("crash_create_after_update_index", abort(););
> +      #endif
>      }
>    }
>    log_state= LOG_OPENED;
>  
> +  #ifdef HAVE_REPLICATION
> +  close_purge_index_file();
> +  #endif
> +
>    DBUG_RETURN(0);
>  
>  err:
> +  #ifdef HAVE_REPLICATION
> +  purge_index_entry(NULL, NULL, need_mutex);
> +  close_purge_index_file();
> +  #endif
>    sql_print_error("Could not use %s for logging (error %d). \
>  Turning logging off for the whole duration of the MySQL server process. \
>  To turn it on again: fix the cause, \
> @@ -2902,8 +2982,15 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
>    name=0;					// Protect against free
>    close(LOG_CLOSE_TO_BE_OPENED);
>  
> -  /* First delete all old log files */
> +  /*
> +    First delete all old log files and then update the index file.
> +    As we first delete the log files and do not use sort of logging,
> +    a crash may lead to an inconsistent state where the index has
> +    references to non-existent files.
>  
> +    We need to invert the steps and use the purge_index_file methods
> +    in order to make the operation safe.
> +  */
>    if (find_log_pos(&linfo, NullS, 0))
>    {
>      error=1;
> @@ -2970,8 +3057,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
>    }
>    if (!thd->slave_thread)
>      need_start_event=1;
> -  if (!open_index_file(index_file_name, 0))
> -    open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0);
> +  if (!open_index_file(index_file_name, 0, FALSE))
> +    open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0,
> FALSE);
>    my_free((uchar*) save_name, MYF(0));
>  
>  err:
> @@ -3158,7 +3245,7 @@ int MYSQL_BIN_LOG::purge_logs(const char
>                            bool need_update_threads, 
>                            ulonglong *decrease_log_space)
>  {
> -  int error;
> +  int error= 0;
>    bool exit_loop= 0;
>    LOG_INFO log_info;
>    THD *thd= current_thd;
> @@ -3169,33 +3256,15 @@ int MYSQL_BIN_LOG::purge_logs(const char
>      pthread_mutex_lock(&LOCK_index);
>    if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) 
>    {
> -    sql_print_error("MYSQL_LOG::purge_logs was called with file %s not "
> +    sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
>                      "listed in the index.", to_log);
>      goto err;
>    }
>  
> -  /*
> -    For crash recovery reasons the index needs to be updated before
> -    any files are deleted. Move files to be deleted into a temp file
> -    to be processed after the index is updated.
> -  */
> -  if (!my_b_inited(&purge_temp))
> +  if ((error= open_purge_index_file(TRUE)))
>    {
> -    if ((error=open_cached_file(&purge_temp, mysql_tmpdir, TEMP_PREFIX,
> -                                DISK_BUFFER_SIZE, MYF(MY_WME))))
> -    {
> -      sql_print_error("MYSQL_LOG::purge_logs failed to open purge_temp");
> -      goto err;
> -    }
> -  }
> -  else
> -  {
> -    if ((error=reinit_io_cache(&purge_temp, WRITE_CACHE, 0, 0, 1)))
> -    {
> -      sql_print_error("MYSQL_LOG::purge_logs failed to reinit purge_temp "
> -                      "for write");
> -      goto err;
> -    }
> +    sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
> +    goto err;
>    }
>  
>    /*
> @@ -3205,51 +3274,170 @@ int MYSQL_BIN_LOG::purge_logs(const char
>    if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
>      goto err;
>    while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
> +         !is_active(log_info.log_file_name) &&
>           !log_in_use(log_info.log_file_name))
>    {
> -    if ((error=my_b_write(&purge_temp, (const uchar*)log_info.log_file_name,
> -                          strlen(log_info.log_file_name))) ||
> -        (error=my_b_write(&purge_temp, (const uchar*)"\n", 1)))
> +    if ((error= register_purge_index_entry(log_info.log_file_name)))
>      {
> -      sql_print_error("MYSQL_LOG::purge_logs failed to copy %s to purge_temp",
> +      sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register
> file.",
>                        log_info.log_file_name);
>        goto err;
>      }
>  
>      if (find_next_log(&log_info, 0) || exit_loop)
>        break;
> - }
> +  }
> +
> +  DBUG_EXECUTE_IF("crash_purge_before_update_index", abort(););
> +
> +  if ((error= sync_purge_index_file()))
> +  {
> +    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to flush register file.");
> +    goto err;
> +  }
>  
>    /* We know how many files to delete. Update index file. */
>    if ((error=update_log_index(&log_info, need_update_threads)))
>    {
> -    sql_print_error("MSYQL_LOG::purge_logs failed to update the index file");
> +    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
>      goto err;
>    }
>  
> -  DBUG_EXECUTE_IF("crash_after_update_index", abort(););
> +  DBUG_EXECUTE_IF("crash_purge_critical_after_update_index", abort(););
> +
> +err:
> +  /* Read each entry from purge_index_file and delete the file. */
> +  if ((error= purge_index_entry(thd, decrease_log_space, FALSE)))
> +    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files"
> +                    " that would be purged.");
> +  close_purge_index_file();
> +
> +  DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", abort(););
>  
> -  /* Switch purge_temp for read. */
> -  if ((error=reinit_io_cache(&purge_temp, READ_CACHE, 0, 0, 0)))
> +  if (need_mutex)
> +    pthread_mutex_unlock(&LOCK_index);
> +  DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::set_purge_index_file_name(const char *base_file_name)
> +{
> +  int error= 0;
> +  DBUG_ENTER("MYSQL_BIN_LOG::set_purge_index_file_name");
> +  if (fn_format(purge_index_file_name, base_file_name, mysql_data_home,
> +                ".~rec~", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
> +                              MY_REPLACE_EXT)) == NULL)
>    {
> -    sql_print_error("MSYQL_LOG::purge_logs failed to reinit purge_temp "
> +      error= 1;
> +      sql_print_error("MYSQL_BIN_LOG::set_purge_index_file_name failed to set "
> +                      "file name.");
> +
> +  }
> +  DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
> +{
> +  int error= 0;
> +  File file= -1;
> +
> +  DBUG_ENTER("MYSQL_BIN_LOG::purge_index_file");
> +
> +  if (destroy)
> +    close_purge_index_file();
> +
> +  if (!my_b_inited(&purge_index_file))
> +  {
> +    if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
> +                       MYF(MY_WME | ME_WAITTANG))) < 0  ||
> +        init_io_cache(&purge_index_file, file, IO_SIZE,
> +                      (destroy ? WRITE_CACHE : READ_CACHE),
> +                      0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
> +    {
> +      error= 1;
> +      sql_print_error("MYSQL_BIN_LOG::open_purge_index_file failed to open register
> "
> +                      " file.");
> +    }
> +  }
> +  DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::close_purge_index_file()
> +{
> +  int error= 0;
> +
> +  DBUG_ENTER("MYSQL_BIN_LOG::close_purge_index_file");
> +
> +  if (my_b_inited(&purge_index_file))
> +  {
> +    end_io_cache(&purge_index_file);
> +    error= my_close(purge_index_file.file, MYF(0));
> +  }
> +  my_delete(purge_index_file_name, MYF(0));
> +  bzero((char*) &purge_index_file, sizeof(purge_index_file));
> +
> +  DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::sync_purge_index_file()
> +{
> +  int error= 0;
> +  DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
> +
> +  if ((error= flush_io_cache(&purge_index_file)) ||
> +      (error= my_sync(purge_index_file.file, MYF(MY_WME))))
> +    DBUG_RETURN(error);
> +
> +  DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
> +{
> +  int error= 0;
> +  DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
> +
> +  if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry)))
> ||
> +      (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
> +    DBUG_RETURN (error);
> +
> +  DBUG_RETURN(error);
> +}
> +
> +
> +int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
> +{
> +  DBUG_ENTER("MYSQL_BIN_LOG::register_create_index_entry");
> +  DBUG_RETURN(register_purge_index_entry(entry));
> +}
> +
> +int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> +                                     bool need_mutex)
> +{
> +  MY_STAT s;
> +  int error= 0;
> +  LOG_INFO log_info;
> +  LOG_INFO check_log_info;
> +
> +  DBUG_ENTER("MYSQL_BIN_LOG:_purge_index_entry");
> +
> +  if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
> +  {
> +    sql_print_error("MSYQL_BIN_LOG::purge_index_entry failed to reinit register file
> "
>                      "for read");
>      goto err;
>    }
>  
> -  /* Read each entry from purge_temp and delete the file. */
>    for (;;)
>    {
>      uint length;
>  
> -    if ((length=my_b_gets(&purge_temp, log_info.log_file_name,
> +    if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
>                            FN_REFLEN)) <= 1)
>      {
> -      if (purge_temp.error)
> +      if (purge_index_file.error)
>        {
> -        error= purge_temp.error;
> -        sql_print_error("MSYQL_LOG::purge_logs error %d reading from "
> -                        "purge_temp", error);
> +        error= purge_index_file.error;
> +        sql_print_error("MSYQL_BIN_LOG::purge_index_entry error %d reading from "
> +                        "register file.", error);
>          goto err;
>        }
>  
> @@ -3260,9 +3448,6 @@ int MYSQL_BIN_LOG::purge_logs(const char
>      /* Get rid of the trailing '\n' */
>      log_info.log_file_name[length-1]= 0;
>  
> -    ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
> -
> -    MY_STAT s;
>      if (!my_stat(log_info.log_file_name, &s, MYF(0)))
>      {
>        if (my_errno == ENOENT) 
> @@ -3310,64 +3495,71 @@ int MYSQL_BIN_LOG::purge_logs(const char
>      }
>      else
>      {
> -      DBUG_PRINT("info",("purging %s",log_info.log_file_name));
> -      if (!my_delete(log_info.log_file_name, MYF(0)))
> +      if (find_log_pos(&check_log_info, log_info.log_file_name, need_mutex))
>        {
> -        if (decrease_log_space)
> -          *decrease_log_space-= s.st_size;
> -      }
> -      else
> -      {
> -        if (my_errno == ENOENT) 
> +        if (!need_mutex)
>          {
> -          if (thd)
> -          {
> -            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> -                                ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> -                                log_info.log_file_name);
> -          }
> -          sql_print_information("Failed to delete file '%s'",
> -                                log_info.log_file_name);
> -          my_errno= 0;
> +          /*
> +            This is to avoid triggering an error in NDB.
> +          */
> +          ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
> +        }
> +
> +        DBUG_PRINT("info",("purging %s",log_info.log_file_name));
> +        if (!my_delete(log_info.log_file_name, MYF(0)))
> +        {
> +          if (decrease_log_space)
> +            *decrease_log_space-= s.st_size;
>          }
>          else
>          {
> -          if (thd)
> +          if (my_errno == ENOENT)
>            {
> -            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
> -                                ER_BINLOG_PURGE_FATAL_ERR,
> -                                "a problem with deleting %s; "
> -                                "consider examining correspondence "
> -                                "of your binlog index file "
> -                                "to the actual binlog files",
> +            if (thd)
> +            {
> +              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> +                                  ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> +                                  log_info.log_file_name);
> +            }
> +            sql_print_information("Failed to delete file '%s'",
>                                  log_info.log_file_name);
> +            my_errno= 0;
>            }
>            else
>            {
> -            sql_print_information("Failed to delete file '%s'; "
> +            if (thd)
> +            {
> +              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
> +                                  ER_BINLOG_PURGE_FATAL_ERR,
> +                                  "a problem with deleting %s; "
>                                    "consider examining correspondence "
>                                    "of your binlog index file "
>                                    "to the actual binlog files",
>                                    log_info.log_file_name);
> -          }
> -          if (my_errno == EMFILE)
> -          {
> -            DBUG_PRINT("info",
> -                       ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
> -            error= LOG_INFO_EMFILE;
> +            }
> +            else
> +            {
> +              sql_print_information("Failed to delete file '%s'; "
> +                                    "consider examining correspondence "
> +                                    "of your binlog index file "
> +                                    "to the actual binlog files",
> +                                    log_info.log_file_name);
> +            }
> +            if (my_errno == EMFILE)
> +            {
> +              DBUG_PRINT("info",
> +                         ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
> +              error= LOG_INFO_EMFILE;
> +              goto err;
> +            }
> +            error= LOG_INFO_FATAL;
>              goto err;
>            }
> -          error= LOG_INFO_FATAL;
> -          goto err;
>          }
>        }
>      }
>    }
> -
>  err:
> -  close_cached_file(&purge_temp);
> -  if (need_mutex)
> -    pthread_mutex_unlock(&LOCK_index);
>    DBUG_RETURN(error);
>  }
>  
> @@ -3407,7 +3599,8 @@ int MYSQL_BIN_LOG::purge_logs_before_dat
>      goto err;
>  
>    while (strcmp(log_file_name, log_info.log_file_name) &&
> -	 !log_in_use(log_info.log_file_name))
> +	 !is_active(log_info.log_file_name) &&
> +         !log_in_use(log_info.log_file_name))
>    {
>      if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)))
>      {
> @@ -3416,14 +3609,6 @@ int MYSQL_BIN_LOG::purge_logs_before_dat
>          /*
>            It's not fatal if we can't stat a log file that does not exist.
>          */
> -        if (thd)
> -        {
> -          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> -                              ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> -                              log_info.log_file_name);
> -        }
> -        sql_print_information("Failed to execute my_stat on file '%s'",
> -                              log_info.log_file_name);
>          my_errno= 0;
>        }
>        else
> @@ -3604,6 +3789,7 @@ void MYSQL_BIN_LOG::new_file_impl(bool n
>    name=0;				// Don't free name
>    close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX);
>  
> +
>    /*
>       Note that at this point, log_state != LOG_CLOSED (important for is_open()).
>    */
> @@ -3618,9 +3804,9 @@ void MYSQL_BIN_LOG::new_file_impl(bool n
>    */
>  
>    /* reopen index binlog file, BUG#34582 */
> -  if (!open_index_file(index_file_name, 0))
> -    open(old_name, log_type, new_name_ptr, 
> -         io_cache_type, no_auto_events, max_size, 1);
> +  if (!open_index_file(index_file_name, 0, FALSE))
> +    open(old_name, log_type, new_name_ptr,
> +         io_cache_type, no_auto_events, max_size, 1, FALSE);
>    my_free(old_name,MYF(0));
>  
>  end:
> @@ -5522,7 +5708,7 @@ int TC_LOG_BINLOG::open(const char *opt_
>    if (using_heuristic_recover())
>    {
>      /* generate a new binlog to mask a corrupted one */
> -    open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0);
> +    open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
>      cleanup();
>      return 1;
>    }
> 
> === modified file 'sql/log.h'
> --- a/sql/log.h	2009-06-18 13:52:46 +0000
> +++ b/sql/log.h	2009-11-30 17:58:07 +0000
> @@ -172,6 +172,10 @@ public:
>              enum_log_type log_type,
>              const char *new_name,
>              enum cache_type io_cache_type_arg);
> +  bool define_new_file(const char *log_name,
> +                       const char *new_name,
> +                       enum_log_type log_type_arg,
> +                       enum cache_type io_cache_type_arg);
>    void init(enum_log_type log_type_arg,
>              enum cache_type io_cache_type_arg);
>    void close(uint exiting);
> @@ -233,14 +237,15 @@ class MYSQL_BIN_LOG: public TC_LOG, priv
>    pthread_cond_t update_cond;
>    ulonglong bytes_written;
>    IO_CACHE index_file;
> +  char index_file_name[FN_REFLEN];
>    /*
> -    purge_temp is a temp file used in purge_logs so that the index file
> +    purge_file is a temp file used in purge_logs so that the index file
>      can be updated before deleting files from disk, yielding better crash
>      recovery. It is created on demand the first time purge_logs is called
>      and then reused for subsequent calls. It is cleaned up in cleanup().
>    */
> -  IO_CACHE purge_temp;
> -  char index_file_name[FN_REFLEN];
> +  IO_CACHE purge_index_file;
> +  char purge_index_file_name[FN_REFLEN];
>    /*
>       The max size before rotation (usable only if log_type == LOG_BIN: binary
>       logs and relay logs).
> @@ -349,9 +354,10 @@ public:
>              const char *new_name,
>  	    enum cache_type io_cache_type_arg,
>  	    bool no_auto_events_arg, ulong max_size,
> -            bool null_created);
> +            bool null_created,
> +            bool need_mutex);
>    bool open_index_file(const char *index_file_name_arg,
> -                       const char *log_name);
> +                       const char *log_name, bool need_mutex);
>    /* Use this to start writing a new log file */
>    void new_file();
>  
> @@ -384,6 +390,15 @@ public:
>                   ulonglong *decrease_log_space);
>    int purge_logs_before_date(time_t purge_time);
>    int purge_first_log(Relay_log_info* rli, bool included);
> +  int set_purge_index_file_name(const char *base_file_name);
> +  int open_purge_index_file(bool destroy);
> +  int close_purge_index_file();
> +  int clean_purge_index_file();
> +  int sync_purge_index_file();
> +  int register_purge_index_entry(const char* entry);
> +  int register_create_index_entry(const char* entry);
> +  int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> +                        bool need_mutex);
>    bool reset_logs(THD* thd);
>    void close(uint exiting);
>  
> 
> === modified file 'sql/mysqld.cc'
> --- a/sql/mysqld.cc	2009-11-20 12:09:50 +0000
> +++ b/sql/mysqld.cc	2009-11-30 17:58:07 +0000
> @@ -3932,7 +3932,7 @@ a file name for --log-bin-index option",
>        my_free(opt_bin_logname, MYF(MY_ALLOW_ZERO_PTR));
>        opt_bin_logname=my_strdup(buf, MYF(0));
>      }
> -    if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln))
> +    if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE))
>      {
>        unireg_abort(1);
>      }
> @@ -4096,7 +4096,7 @@ a file name for --log-bin-index option",
>    }
>  
>    if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
> -                                        WRITE_CACHE, 0, max_binlog_size, 0))
> +                                        WRITE_CACHE, 0, max_binlog_size, 0, TRUE))
>      unireg_abort(1);
>  
>  #ifdef HAVE_REPLICATION
> 
> === modified file 'sql/rpl_rli.cc'
> --- a/sql/rpl_rli.cc	2009-11-05 06:07:31 +0000
> +++ b/sql/rpl_rli.cc	2009-11-30 17:58:07 +0000
> @@ -181,10 +181,10 @@ a file name for --relay-log-index option
>        note, that if open() fails, we'll still have index file open
>        but a destructor will take care of that
>      */
> -    if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln) ||
> +    if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln, TRUE) ||
>          rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
>                              (max_relay_log_size ? max_relay_log_size :
> -                            max_binlog_size), 1))
> +                            max_binlog_size), 1, TRUE))
>      {
>        pthread_mutex_unlock(&rli->data_lock);
>        sql_print_error("Failed in open_log() called from init_relay_log_info()");
> 
-- 
Luís

Thread
bzr commit into mysql-5.1-bugteam branch (alfranio.correia:3215)Bug#45292Alfranio Correia30 Nov
  • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:3215)Bug#45292Luís Soares2 Dec
  • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:3215)Bug#45292He Zhenxing3 Dec
    • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:3215)Bug#45292Alfranio Correia3 Dec