List:Commits« Previous MessageNext Message »
From:Guilhem Bichot Date:November 22 2007 6:06pm
Subject:Re: bk commit into 5.2 tree (guilhem:1.2611) WL#866
View as plain text  
Hello,

thanks for your new reviews.
I signal here two bugs which I found:

On Tue, Nov 06, 2007 at 08:56:12PM +0100, Guilhem Bichot wrote:
> ChangeSet@stripped, 2007-11-06 20:56:05+01:00, guilhem@stripped +59 -0
>   WL#866 - Online backup driver for MyISAM.
>   When this driver (file myisam_backup_log.cc) is to do a backup,
>   it starts physical logging of changes for target tables (file
>   mi_log.c), dirtily copies tables, then the physical log.
>   When this driver does a restore, it copies back tables and the log,
>   applies the log (file mi_examine_log.c).
>   The data file is always backed up; for the index file two methods are
>   available: either only its 64KB header is copied and then restore will
>   repair indexes, or the whole index file is copied.
>   Backup starts and runs without disturbing any running or new update
>   except at the very end: when creating the validity point it locks all
>   backed-up MyISAM tables with a read lock (so, stalls new update
>   statements and waits for running update statements to finish).
>   I recommend to reviewers to read in order myisam_backup_driver.cc,
>   mi_log.c then other files.
>   GUILHEM_TODO tags identify the most urgent todos.


> @@ -436,6 +445,17 @@ int mi_reset(MI_INFO *info)
>    */
>    if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
>    {
> +    /*
> +      Logically there should not be a WRITE_CACHE at this stage. It is
> +      important: mi_log_stop_physical() may be flushing the write cache at
> +      this same moment, if we do it here concurrently it is not
> +      safe. Basically, a client thread can flush the WRITE_CACHE if and only
> +      if it is not concurrent with mi_log_stop_physical() i.e. holds a
> +      write-thr_lock or intern_lock. mi_reset() for example does not satisfy
> +      this, because the SQL layer unlocks tables before calling
> +      ha_myisam::reset().
> +    */
> +    DBUG_ASSERT(!(info->opt_flag & WRITE_CACHE_USED));
>      info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
>      error= end_io_cache(&info->rec_cache);
>    }

There is a case where this assertion wrongly fires.
Replication slave is executing a row-based binlog event
(Insert_rows_log_event) generated by a REPLACE:
- the slave calls ha_start_bulk_insert()
in Write_rows_log_event::do_before_row_operations(), that sets up
WRITE_CACHE_USED.
- Rows_log_event::write_row() tries a ha_myisam::write_row()
- which fails because of duplicate key (ok, this was REPLACE)
- so the duplicate record is retrieved (for an update soon to come, as
Rows_log_event::write_row() emulates REPLACE) and that calls
ha_rnd_init() which calls mi_reset(), while WRITE_CACHE_USED is still
true, asserts.
It is a case where the assertion is over-paranoid: the assertion is to
make sure that the end_io_cache() of mi_reset() never happens at the
same time as the end_io_cache() of mi_log_stop_physical(); but here
the table is locked (by some replication function) so
mi_log_stop_physical() cannot be running.
I will try to change it to
DBUG_ASSERT(!((info->opt_flag & WRITE_CACHE_USED) &&
             (info->lock.type == TL_UNLOCK)));
which should trap only the dangerous case, that WRITE_CACHE_USED be
end-ed when the table is not locked.


> diff -Nrup a/storage/myisam/mi_log.c b/storage/myisam/mi_log.c
> --- a/storage/myisam/mi_log.c	2007-08-13 15:11:16 +02:00

> +void _myisam_log_command(IO_CACHE *log, enum myisam_log_commands command,
> +                         void *info_or_share,
> +                         const uchar *buffert, uint length, int result)
>  {
> -  uchar buff[9];
> -  int error,old_errno;
> +  uchar buff[14];
> +  int error, old_errno, bufflen;
>    ulong pid=(ulong) GETPID();
> -
> +  my_bool logical= (log == &myisam_logical_log);
> +  MYISAM_SHARE *share;
> +  File file= logical ? ((MI_INFO *)info_or_share)->dfile :
> +    (share= (MYISAM_SHARE *)info_or_share)->kfile;
> +  LINT_INIT(share);

This LINT_INIT(share), in builds where it expands to share=0, will
cancel the effect of the assignement to "share" in the previous line.

-- 
   __  ___     ___ ____  __
  /  |/  /_ __/ __/ __ \/ /    Mr. Guilhem Bichot <guilhem@stripped>
 / /|_/ / // /\ \/ /_/ / /__   MySQL AB, Lead Software Engineer
/_/  /_/\_, /___/\___\_\___/   Bordeaux, France
       <___/   www.mysql.com   
Thread
bk commit into 5.2 tree (guilhem:1.2611)Guilhem Bichot6 Nov
  • Review II of MyISAM backup [Re: bk commit into 5.2 tree (guilhem:1.2611)]Ingo Strüwing21 Nov
    • Re: Review II of MyISAM backup [Re: bk commit into 5.2 tree(guilhem:1.2611)]Guilhem Bichot27 Nov
      • Re: Review II of MyISAM backup [Re: bk commit into 5.2 tree (guilhem:1.2611)]Ingo Strüwing27 Nov
  • Re: bk commit into 5.2 tree (guilhem:1.2611) WL#866Guilhem Bichot22 Nov