List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:June 24 2009 7:25am
Subject:Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:2950)
Bug#42829
View as plain text  
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().

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