#At file:///home/lsoares/Workspace/bzr/work/bugfixing/46554/mysql-next-mr-wl5092/ based on revid:luis.soares@stripped
3151 Luis Soares 2010-06-14
BUG#46554: RBR: pending rows event can be flushed too soon
sometimes.
While packing a record into an rows event, the server performs
several checks to see if a new rows event is needed. Among the
conditions checked, there's one that deals with bitmaps from the
pending event ('p') and the table ('t') that the server accessed
for the current record. If they are different, then 'p' is
flushed and a new pending event, 'np', is created. If not, then
'p' is used. However, the only bitmaps compared are
't'->write_set against 'p'->get_cols(), which in some cases,
means we are comparing bitmaps related to write_sets (the ones
from 't') against bitmaps related to read_sets (the ones from
'p')! In current versions of the server this issue is masked (as
both read and write sets are always fully set).
On the other hand, for 6.0 codebase this was not the case
because if the table has a PK, only some columns will be flagged
in the read_set. In such cases the write_set and the read_set
will most likely not match causing the pending to be flushed.
Additionally, the write_set is meaningless for delete events and
the read_set is meaningless for write events! So comparison must
take into account the type of the event to choose what bitmap to
take from the table to make the comparison.
As stated, this was the case for 6.0 codebase, but also for trees
patched with WL#5092, because the user is given the option
to choose whether to log partial (not all columns will be flagged
in the read and write set) or full rows. If the user chooses to
log partial rows, the problem may surface.
Finally, This seems to be in fact a regression introduced by
patch for BUG#33055 plus a post-merge fix that is related:
- sp1r-mkindahl@stripped
Since BUG#33055 was backported to WL#5092 tree, this regression
tagged along.
To fix this problem, we deploy a member function in
Rows_log_event that is used to perform the correct comparison of
the pending and table bitmaps.
@ mysql-test/suite/binlog/t/binlog_row_binlog.test
Added test case from the bug report.
@ mysql-test/suite/perfschema/r/binlog_mix.result
Removing spurious events from the result file.
@ mysql-test/suite/perfschema/r/binlog_row.result
Removing spurious events from the result file.
@ sql/log_event.h
1. Added utility member function to compare read and write bitmaps.
2. Added get_cols_ai accessor for m_cols_ai.
@ sql/sql_class.cc
Replaced bitmap check with the proper call to the newly introduced
member function that compares bitmaps according to the event type.
modified:
mysql-test/suite/binlog/r/binlog_row_binlog.result
mysql-test/suite/binlog/t/binlog_row_binlog.test
mysql-test/suite/perfschema/r/binlog_mix.result
mysql-test/suite/perfschema/r/binlog_row.result
sql/log_event.h
sql/sql_class.cc
=== modified file 'mysql-test/suite/binlog/r/binlog_row_binlog.result'
--- a/mysql-test/suite/binlog/r/binlog_row_binlog.result 2010-04-20 09:10:43 +0000
+++ b/mysql-test/suite/binlog/r/binlog_row_binlog.result 2010-06-14 00:55:41 +0000
@@ -1,3 +1,25 @@
+SELECT @@session.binlog_row_image INTO @saved_binlog_row_image;
+SET binlog_row_image= MINIMAL;
+SHOW VARIABLES LIKE 'binlog_row_image';
+Variable_name Value
+binlog_row_image MINIMAL
+RESET MASTER;
+CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(100)) ENGINE=Innodb;
+INSERT INTO t1 VALUES (1,'a'), (2,'b'), (3, 'c');
+DELETE FROM t1;
+show binlog events from <binlog_start>;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Query # # use `test`; CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(100)) ENGINE=Innodb
+master-bin.000001 # Query # # BEGIN
+master-bin.000001 # Table_map # # table_id: # (test.t1)
+master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
+master-bin.000001 # Xid # # COMMIT /* XID */
+master-bin.000001 # Query # # BEGIN
+master-bin.000001 # Table_map # # table_id: # (test.t1)
+master-bin.000001 # Delete_rows # # table_id: # flags: STMT_END_F
+master-bin.000001 # Xid # # COMMIT /* XID */
+DROP TABLE t1;
+SET binlog_row_image= @saved_binlog_row_image;
drop table if exists t1, t2;
reset master;
create table t1 (a int) engine=innodb;
=== modified file 'mysql-test/suite/binlog/t/binlog_row_binlog.test'
--- a/mysql-test/suite/binlog/t/binlog_row_binlog.test 2007-06-27 12:28:02 +0000
+++ b/mysql-test/suite/binlog/t/binlog_row_binlog.test 2010-06-14 00:55:41 +0000
@@ -1,5 +1,22 @@
+-- source include/have_binlog_format_row.inc
+
+#
+# BUG#46554: RBR: pending rows event can be flushed too soon sometimes.
+
+SELECT @@session.binlog_row_image INTO @saved_binlog_row_image;
+SET binlog_row_image= MINIMAL;
+SHOW VARIABLES LIKE 'binlog_row_image';
+RESET MASTER;
+CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(100)) ENGINE=Innodb;
+INSERT INTO t1 VALUES (1,'a'), (2,'b'), (3, 'c');
+## before the patch there would be 3 delete row events
+DELETE FROM t1;
+-- source include/show_binlog_events.inc
+DROP TABLE t1;
+
+SET binlog_row_image= @saved_binlog_row_image;
+
# This is a wrapper for binlog.test so that the same test case can be used
# For both statement and row based bin logs 9/19/2005 [jbm]
--- source include/have_binlog_format_row.inc
-- source extra/binlog_tests/binlog.test
=== modified file 'mysql-test/suite/perfschema/r/binlog_mix.result'
--- a/mysql-test/suite/perfschema/r/binlog_mix.result 2010-05-26 09:29:59 +0000
+++ b/mysql-test/suite/perfschema/r/binlog_mix.result 2010-06-14 00:55:41 +0000
@@ -25,18 +25,6 @@ show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (performance_schema.SETUP_INSTRUMENTS)
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; drop table if exists test.t1
@@ -55,17 +43,5 @@ master-bin.000001 # Query # # use `test`
master-bin.000001 # Query # # use `test`; drop table test.t2
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (performance_schema.SETUP_INSTRUMENTS)
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT
=== modified file 'mysql-test/suite/perfschema/r/binlog_row.result'
--- a/mysql-test/suite/perfschema/r/binlog_row.result 2010-05-26 09:29:59 +0000
+++ b/mysql-test/suite/perfschema/r/binlog_row.result 2010-06-14 00:55:41 +0000
@@ -25,18 +25,6 @@ show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (performance_schema.SETUP_INSTRUMENTS)
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; drop table if exists test.t1
@@ -55,17 +43,5 @@ master-bin.000001 # Query # # use `test`
master-bin.000001 # Query # # use `test`; drop table test.t2
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (performance_schema.SETUP_INSTRUMENTS)
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
-master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT
=== modified file 'sql/log_event.h'
--- a/sql/log_event.h 2010-05-26 13:36:29 +0000
+++ b/sql/log_event.h 2010-06-14 00:55:41 +0000
@@ -3544,9 +3544,58 @@ public:
virtual int get_data_size();
MY_BITMAP const *get_cols() const { return &m_cols; }
+ MY_BITMAP const *get_cols_ai() const { return &m_cols_ai; }
size_t get_width() const { return m_width; }
ulong get_table_id() const { return m_table_id; }
+#if defined(MYSQL_SERVER)
+ /*
+ This member function compares the table's read/write_set
+ with this event's m_cols and m_cols_ai. Comparison takes
+ into account what type of rows event is this: Delete, Write or
+ Update, therefore it uses the correct m_cols[_ai] according
+ to the event type code.
+
+ Note that this member function should only be called for the
+ following events:
+ - Delete_rows_log_event
+ - Wrirte_rows_log_event
+ - Update_rows_log_event
+
+ @param[IN] table The table to compare this events bitmaps
+ against.
+
+ @return TRUE if sets match, FALSE otherwise. (following
+ bitmap_cmp return logic).
+
+ */
+ virtual bool read_write_bitmaps_cmp(TABLE *table)
+ {
+ bool res= FALSE;
+
+ switch (get_type_code())
+ {
+ case DELETE_ROWS_EVENT:
+ res= bitmap_cmp(get_cols(), table->read_set);
+ break;
+ case UPDATE_ROWS_EVENT:
+ res= (bitmap_cmp(get_cols(), table->read_set) &&
+ bitmap_cmp(get_cols_ai(), table->write_set));
+ break;
+ case WRITE_ROWS_EVENT:
+ res= bitmap_cmp(get_cols(), table->write_set);
+ break;
+ default:
+ /*
+ We should just compare bitmaps for Delete, Write
+ or Update rows events.
+ */
+ DBUG_ASSERT(0);
+ }
+ return res;
+ }
+#endif
+
#ifdef MYSQL_SERVER
virtual bool write_data_header(IO_CACHE *file);
virtual bool write_data_body(IO_CACHE *file);
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2010-05-19 21:35:03 +0000
+++ b/sql/sql_class.cc 2010-06-14 00:55:41 +0000
@@ -4034,7 +4034,7 @@ THD::binlog_prepare_pending_rows_event(T
pending->get_table_id() != table->s->table_map_id ||
pending->get_type_code() != type_code ||
pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
- !bitmap_cmp(pending->get_cols(), table->write_set))
+ pending->read_write_bitmaps_cmp(table) == FALSE)
{
/* Create a new RowsEventT... */
Rows_log_event* const
Attachment: [text/bzr-bundle] bzr/luis.soares@sun.com-20100614005541-76n8gd2gnd3v8uhy.bundle