List:Commits« Previous MessageNext Message »
From:Alfranio Correia Date:June 24 2009 9:01am
Subject:Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)
Bug#42829
View as plain text  
Andrei Elkin wrote:
> Luis, hello.
>
> To follow our conversation, I run some tests to confirm that
> the unsafe my_error() due to the transaction isolation can be safely removed
> from ha_innobase::external_lock().
>   
The call to my_error() could be removed if this what you meant to say.
However, the decision must be delegated to the engine.

I am forwarding an email that I've exchanged with Sven, Luis and Jasonh
about this.

Cheers.
> Instead of
>
>    ERROR 1598 (HY000): Binary logging not possible. Message: Transaction level
> 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'
>
> the error message would be:
>
>    ERROR 1598 (HY000): Binary logging not possible. Message: Statement-based format
> required for this statement, but not allowed by this combination of engines
>
> Certainly the former current is more readable. However, in principle
> engine could provide a message along with capabilities.
>
> Mentioning these facts and ideas, I don't mean the removal and extending
> capabilities report should be your bug's agenda. Personally I'm fine if we schedule
> that work separately, still urgent enough not to let other bug
> fixes to contribute more to ha_innodb.cc.
>
>
> Further please find some particularities.
>
>   
>> #At file:///home/lsoares/Workspace/mysql-server/bugfix/42829/5.1-bt/ based on
> revid:joro@stripped
>>
>>  2950 Luis Soares	2009-06-20
>>       BUG#42829: binlogging enabled for all schemas regardless of
>>       	   binlog-db-db / binlog-ignore-db
>>             
>>       InnoDB will return an error if statement based replication is used
>>       along with transaction isolation level READ-COMMITTED (or stricter),
>>     
>
> it's `or weaker' not `or stricter'.
> E.g
>
> http://www.oracle.com/technology/sample_code/tech/java/codesnippet/j2ee/Trasactions/Transactions.html?_template=/ocom/print
> (Transaction Isolation).
>
>   
>>       even if the statement in question is filtered out according to the
>>       binlog-do-db rules set. In this case, an error should not be printed.
>>             
>>       This patch addresses this issue by:
>>       
>>        1. adding a innodb compatibility hook that checks if statement is
>>           filtered (to be used when logging in STATEMENT mode).
>>     
>
> s/hook/hack/
>
> reads much more fair to me.
>
>   
>>       
>>        2. extending the existing check in external_lock to verify binlog
>>           filter hook before deciding to print an error.
>>       
>>        3. changing decide_logging_format, so that if statement is ignored,
>>           then no error is printed. This change also makes
>>           decide_logging_format to calculate capabilities for both, ROW and
>>           STMT formats always. 
>>       
>>           Collecting STMT mode capabilities is done considering all engines
>>           involved (this was the behavior before this patch).
>>       
>>     
>
>   
>>           Collecting ROW mode capabilities considers binlog filtering
>>           rules. So, for each engine involved in the statement, its
>>           capabilities are conly considered if the table does not belong to
>>           a filtered database.
>>     
>
> I can't find description of 
> logics of
>
>
> +    if ((log_as_row && !binlog_filter->db_ok(tables))
>        goto end;
>
> which in human words `if any table's db is found filterable the whole
> statement is filterable' in the above paragraph.
>
>   
>>       
>>           Decision on which capabilities to take (for checking if logging is
>>           possible), depends on the logging format chosen.
>>       
>>        4. extending rpl_filter with a new method that checks for a list of
>>           tables if at least one of them belongs to a non filtered
>>           database. This is useful to find out if a statement logged in ROW
>>           mode has its all changes (possibly involving different databases)
>>           filtered. Consider the following:
>>       
>>             UPDATE db1.t1, db2.t2 SET db1.t1.a=1, db2.t2.a=2;
>>       
>>           if db1 and db2 are filtered and ROW mode is used, then statement
>>           is completely filtered from binlog. On the other hand, if only db1
>>           was to be filtered, then changes to db2 will make it into the
>>           binlog. This new method is most useful in decide_logging_format.
>>       
>>          
>>      @ mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_cleanup.test
>>         Clean up shared part of the test.
>>      @ mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_mix_row.test
>>         Part of the test for mixed and row mode.
>>      @ mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_prepare.test
>>         Shared part of the test. (Prepare section)
>>      @ mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_stm.test
>>         Part of the test for statement mode.
>>      @ mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_mix_row-master.opt
>>         Option file stating which database log.
>>      @ mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_mix_row.test
>>         Mixed and Row mode logging test.
>>      @ mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_stm-master.opt
>>         Option file stating which database log.
>>      @ mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_stm.test
>>         Statement mode logging test.
>>      @ sql/rpl_filter.cc
>>         Added Rpl_filter::db_ok(TABLE_LIST* tables) which checks if at least 
>>         one table in the list provided belongs to a database that is not
>>         filtered out.
>>      @ sql/rpl_filter.h
>>         Added 'bool db_ok(TABLE_LIST *tables)' method declaration.
>>      @ sql/sql_base.cc
>>         Reworked decide_logging_format to take into account filtering rules.
>>         
>>         After this patch, it decides to skip error reporting if statement is
>>         filtered out from binlog. Also, capabilities are calculated considering
>>         filtering rules.
>>      @ sql/sql_class.cc
>>         Added thd_binlog_filter_ok to INNODB_COMPATIBILITY_HOOKS set.
>>      @ storage/innobase/handler/ha_innodb.cc
>>         Extended check in external_lock to take into consideration the
>>         filtering when deciding to throw an error.
>>      @ storage/innobase/handler/ha_innodb.h
>>         Added declaration of new hook.
>>
>>     added:
>>       mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_cleanup.test
>>       mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_mix_row.test
>>       mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_prepare.test
>>       mysql-test/extra/rpl_tests/rpl_innodb_rc_do_db_stm.test
>>       mysql-test/suite/rpl/r/rpl_innodb_rc_do_db_mix_row.result
>>       mysql-test/suite/rpl/r/rpl_innodb_rc_do_db_stm.result
>>       mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_mix_row-master.opt
>>       mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_mix_row.test
>>       mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_stm-master.opt
>>       mysql-test/suite/rpl/t/rpl_innodb_rc_do_db_stm.test
>>     modified:
>>       sql/rpl_filter.cc
>>       sql/rpl_filter.h
>>       sql/sql_base.cc
>>       sql/sql_class.cc
>>       storage/innobase/handler/ha_innodb.cc
>>       storage/innobase/handler/ha_innodb.h
>> === modified file 'sql/sql_base.cc'
>> --- a/sql/sql_base.cc	2009-05-30 13:32:28 +0000
>> +++ b/sql/sql_base.cc	2009-06-20 13:57:39 +0000
>> @@ -5050,7 +5050,6 @@ static void mark_real_tables_as_free_for
>>        table->table->query_id= 0;
>>  }
>>  
>>     
>
> ...
>
>   
>> -
>>  /**
>>     Decide on logging format to use for the statement.
>>  
>> @@ -5097,12 +5096,17 @@ int decide_logging_format(THD *thd, TABL
>>        set with all the capabilities bits set and one with no
>>        capabilities bits set.
>>       */
>> +    handler::Table_flags flags_some_set_row= 0;
>>      handler::Table_flags flags_some_set= 0;
>>      handler::Table_flags flags_all_set=
>>        HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
>> +    handler::Table_flags flags_all_set_row=
>> +      HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
>>  
>> +    my_bool log_as_row= FALSE;
>>      my_bool multi_engine= FALSE;
>>      void* prev_ht= NULL;
>> +    int error= 0;
>>      for (TABLE_LIST *table= tables; table; table= table->next_global)
>>      {
>>        if (table->placeholder())
>> @@ -5119,8 +5123,22 @@ int decide_logging_format(THD *thd, TABL
>>          if (prev_ht && prev_ht != table->table->file->ht)
>>            multi_engine= TRUE;
>>          prev_ht= table->table->file->ht;
>> +        /*
>> +          Collect capabilities for engines involved in the
>> +          statement. These are the capabilities to check when logging
>> +          in statement mode.
>> +        */
>>          flags_all_set &= flags;
>>          flags_some_set |= flags;
>> +        /*
>> +          Collect capabilities for tables in databases that would not
>> +          be filtered when logging in row mode.
>> +        */
>> +        if (binlog_filter->db_ok(table->db))
>> +        {
>> +          flags_all_set_row &= flags;
>> +          flags_some_set_row |= flags;
>> +        }
>>        }
>>      }
>>
>>     
>
> Ok, falling into a binlog_filter do rule is equivalent to gaining
> the both capabilities.
> That almost says how to merge the flags_* with the new flags_*_row right away in the
> loop.
>
> Second point, the new binlog_filter->db_ok(tables) does not look necessary
> because
> the return value can be gained in the loop.
> TL_WRITE_ALLOW_WRITE narrows the scope to remove tables for SELECTing which are
> irrelevant for filtering.
>
>   
>   
>> @@ -5135,23 +5153,98 @@ int decide_logging_format(THD *thd, TABL
>>      DBUG_PRINT("info", ("multi_engine: %s",
>>                          multi_engine ? "TRUE" : "FALSE"));
>>  
>> -    int error= 0;
>> +    /**
>> +      Flag stating whether this statement should be logged as row or
>> +      not.
>> +    */
>> +    log_as_row= (thd->variables.binlog_format==BINLOG_FORMAT_ROW ||
>> +		 (thd->variables.binlog_format==BINLOG_FORMAT_MIXED && 
>> +		  (thd->lex->is_stmt_unsafe() || 
>> +		   (flags_all_set & HA_BINLOG_STMT_CAPABLE) ==0)));
>> +
>> +    /**
>> +      Check whether statement is filtered from binlog or not.
>> +    */
>> +    if ((log_as_row && !binlog_filter->db_ok(tables)) || 
>>     
>
> I would enjoy gathering db_ok result in the above loop instead of the new method.
>
>   
>> +	(!log_as_row && !binlog_filter->db_ok(thd->db)))
>> +    {
>> +
>> +      /*	
>> +	We are filtering this statement from log. As such, we skip
>> +	error checking.
>> +
>> +	Still, we mark statement to be logged as ROW if in MIXED and
>> +	switch should happen. This way, the statement is subject to
>> +	the same filtering rules when the time comes to log it, that
>> +	were considered here.
>> +      */
>> +      goto end;
>> +    }
>> +    else
>> +    {
>> +      /*        
>> +	Choose which capabilities to take based on the filtering
>> +	analysis and the binlog format that one is to choose.
>> +        
>> +	So, if using RBR or MIXED+switch_to_row, the capabilities
>> +	considered are the ones collected for events on *not* filtered
>> +	tables (databases).
>> +
>> +	For STMT or MIXED+not_switch_to_row, the capabilities to
>> +	consider are the ones collected from all the engines involved.
>> +
>> +	Q. What is this trying to solve?
>> +        A. Some cases involving filtered and not filtered out
>> +           databases, in the same statement and when using row mode
>> +           logging. Consider scenario description and questions below.
>> +
>> +        Scenario:
>> +          1. MIXED mode
>> +          2. filtered: db2; !filtered: db1
>> +          3. capabilities: db1 => ROW, db2 => STMT
>> +          4. UPDATE db1.t1 A, db2.t1 B SET A.x=1, B.x=2;
>> +
>> +	Q. Which is the value of flags_all_set_row?
>> +	A. HA_BINLOG_ROW_CAPABLE, because, db2 is filtered (MIXED
>> +           should switch to row because flags_all_set would not have STMT
>> +	   capability).
>> +
>> +	Q. Which is the value of flags_all_set?
>> +	A. 0
>> +
>> +	Q. This statement should log the update on db1.t1 when using
>> +           ROW and MIXED mode. As such, errors should not be thrown
>> +           for this statement. In this case, which capabilities flags
>> +           should one use to conduct error checking?
>> +	A. flags_all_set_row
>> +
>> +	Basically, when one finds that is indeed logging in ROW mode,
>> +	then the vector of capabilities should just hold the
>> +	capabilities for the engines which are referenced by tables in
>> +	databases not filtered out from binlog.
>> +      */
>> +      if (log_as_row)
>> +      {
>> +        flags_all_set= flags_all_set_row;
>> +	flags_some_set= flags_some_set_row;
>> +      }
>> +    }
>> +
>>      if (flags_all_set == 0)
>>      {
>>        my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
>>                 "Statement cannot be logged to the binary log in"
>>                 " row-based nor statement-based format");
>>      }
>> -    else if (thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
>> +    else if (!log_as_row &&
>>               (flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
>>      {
>>        my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
>>                  "Statement-based format required for this statement,"
>>                  " but not allowed by this combination of engines");
>>      }
>> -    else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW ||
>> -              thd->lex->is_stmt_unsafe()) &&
>> -             (flags_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
>> +    else if (log_as_row && 
>> +	     (flags_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
>>      {
>>        my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
>>                  "Row-based format required for this statement,"
>> @@ -5179,6 +5272,7 @@ int decide_logging_format(THD *thd, TABL
>>      if (error)
>>        return -1;
>>  
>> +end:
>>      /*
>>        We switch to row-based format if we are in mixed mode and one of
>>        the following are true:
>> @@ -5191,8 +5285,7 @@ int decide_logging_format(THD *thd, TABL
>>        this code in reset_current_stmt_binlog_row_based(), it has to be
>>        here.
>>      */
>> -    if (thd->lex->is_stmt_unsafe() ||
>> -        (flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
>> +    if (log_as_row)
>>      {
>>        thd->set_current_stmt_binlog_row_based_if_mixed();
>>      }
>>
>> === modified file 'sql/sql_class.cc'
>> --- a/sql/sql_class.cc	2009-06-15 15:53:45 +0000
>> +++ b/sql/sql_class.cc	2009-06-20 13:57:39 +0000
>> @@ -41,6 +41,7 @@
>>  
>>  #include "sp_rcontext.h"
>>  #include "sp_cache.h"
>> +#include "rpl_filter.h"
>>  
>>  /*
>>    The following is used to initialise Table_ident with a internal
>> @@ -2912,6 +2913,11 @@ extern "C" void thd_mark_transaction_to_
>>  {
>>    mark_transaction_to_rollback(thd, all);
>>  }
>> +
>> +extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
>> +{
>> +  return binlog_filter->db_ok(thd->db);
>> +}
>>  #endif // INNODB_COMPATIBILITY_HOOKS */
>>  
>>  /****************************************************************************
>>
>>     
>
>   
>> === modified file 'storage/innobase/handler/ha_innodb.cc'
>> --- a/storage/innobase/handler/ha_innodb.cc	2009-05-19 08:20:28 +0000
>> +++ b/storage/innobase/handler/ha_innodb.cc	2009-06-20 13:57:39 +0000
>> @@ -6843,8 +6843,10 @@ ha_innobase::external_lock(
>>  	{
>>  		ulong const binlog_format= thd_binlog_format(thd);
>>  		ulong const tx_isolation = thd_tx_isolation(current_thd);
>> +                bool const is_binlog_filter_ok= thd_binlog_filter_ok(thd);
>>  		if (tx_isolation <= ISO_READ_COMMITTED &&
>> -		    binlog_format == BINLOG_FORMAT_STMT)
>> +		    binlog_format == BINLOG_FORMAT_STMT &&
>> +                    is_binlog_filter_ok)
>>  		{
>>  			char buf[256];
>>  			my_snprintf(buf, sizeof(buf),
>>
>>     
>
> As we agreed, you are going to make this snippet as a candidate for 
> refactoring. I have suggested to qualify it as urgent todo.
>
>   
>> === modified file 'storage/innobase/handler/ha_innodb.h'
>> --- a/storage/innobase/handler/ha_innodb.h	2009-04-24 11:28:46 +0000
>> +++ b/storage/innobase/handler/ha_innodb.h	2009-06-20 13:57:39 +0000
>> @@ -252,4 +252,11 @@ int thd_binlog_format(const MYSQL_THD th
>>    @param  all   TRUE <=> rollback main transaction.
>>  */
>>  void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all);
>> +
>> +/**
>> +  Check if binary logging is filtered for thread's current db.
>> +  @param  thd   Thread handle
>> +  @retval 1 the query is not filtered, 0 otherwise.
>> +*/
>> +bool thd_binlog_filter_ok(const MYSQL_THD thd);
>>  }
>>
>>     
>
>
> cheers,
>
> Andrei
>   

Thread
bzr commit into mysql-5.1-bugteam branch (luis.soares:2950) Bug#42829Luis Soares20 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)Bug#42829Andrei Elkin24 Jun
    • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)Bug#42829Alfranio Correia24 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)Bug#42829He Zhenxing24 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)Bug#42829Alfranio Correia2 Jul