Commit 8504580c authored by Alfranio Correia's avatar Alfranio Correia

BUG#56343 binlog_cache_use status is bigger than expected

The binlog_cache_use is incremented twice when changes to a transactional table
are committed, i.e. TC_LOG_BINLOG::log_xid calls is called. The problem happens
because log_xid calls both binlog_flush_stmt_cache and binlog_flush_trx_cache
without checking if such caches are empty thus unintentionally increasing the
binlog_cache_use value twice.

To fix the problem we avoided incrementing the binlog_cache_use if the cache is
empty. We also decided to increment binlog_cache_use when the cache is truncated
as the cache is used although its content is discarded and is not written to the
binary log.

Note that binlog_cache_use is incremented for both types of cache, transactional
and non-transactional and that the behavior presented in this patch also applies
to the binlog_cache_disk_use.

Finally, we re-organized the code around the functions binlog_flush_trx_cache and
binlog_flush_stmt_cache.

mysql-test/extra/binlog_tests/binlog_cache_stat.test:
  Updated the test case with comments and additional tests to check both
  transactional and non-transactional changes and what happens when a 
  is transaction either committed or aborted.
mysql-test/suite/binlog/r/binlog_innodb.result:
  Updated the result file.
mysql-test/suite/binlog/r/binlog_mixed_cache_stat.result:
  Updated the result file.
mysql-test/suite/binlog/r/binlog_row_cache_stat.result:
  Updated the result file.
mysql-test/suite/binlog/r/binlog_stm_cache_stat.result:
  Updated the result file.
mysql-test/suite/binlog/t/binlog_mixed_cache_stat.test:
  Updated the test case with a new source file.
mysql-test/suite/binlog/t/binlog_row_cache_stat.test:
  Updated the test case with a new source file.
mysql-test/suite/binlog/t/binlog_stm_cache_stat.test:
  Updated the test case with a new source file.
sql/log_event.cc:
  Introduced debug information to check if Query_log_event("BEGIN"),
  Query_log_event("COMMIT") and Query_log_event("ROLLBACK").
sql/log_event.h:
  Guaranteed Xid_log_event is always classified as a transactional event.
parent 3f975a7f
# Embedded server doesn't support binlog
-- source include/not_embedded.inc
-- source include/have_innodb.inc
# Creating tables
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
create table t1 (a int) engine=innodb;
create table t2 (a int) engine=myisam;
#
# Let us test binlog_cache_use and binlog_cache_disk_use status vars.
# Actually this test has nothing to do with innodb per se, it just
# requires transactional table.
#
# This test checks binlog_cache_use and binlog_cache_disk_use when
# transactions are committed and after when they are aborted.
#
#
# Checking commit.
#
--echo **** Preparing the enviroment to check commit and its effect on
--echo **** the binlog_cache_use and binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Now we are going to create transactional changes which are long enough so
--echo **** they will be flushed to disk...
--echo **** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
let $1=2000;
disable_query_log;
begin;
while ($1)
{
eval insert into t1 values( $1 );
dec $1;
}
commit;
enable_query_log;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Transactional changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
commit;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Non-Transactional changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Mixed changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
#
# Checking abort.
#
--echo **** Preparing the enviroment to check abort and its effect on
--echo **** the binlog_cache_use and binlog_cache_disk_use
--echo **** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Now we are going to create transactional changes which are long enough so
--echo **** they will be flushed to disk...
--echo **** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
let $1=2000;
disable_query_log;
begin;
while ($1)
{
eval insert into t1 values( $1 );
dec $1;
}
rollback;
enable_query_log;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Transactional changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
rollback;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Non-Transactional changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
rollback;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--echo **** Mixed changes which should not be flushed to disk and so should not
--echo **** increase binlog_cache_disk_use.
--echo **** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
rollback;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
drop table t1, t2;
# Embedded server doesn't support binlog
-- source include/not_embedded.inc
-- source include/have_innodb.inc
#
# Let us test binlog_cache_use and binlog_cache_disk_use status vars.
# Actually this test has nothing to do with innodb per se, it just requires
# transactional table.
#
flush status;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int) engine=innodb;
# Now we are going to create transaction which is long enough so its
# transaction binlog will be flushed to disk...
let $1=2000;
disable_query_log;
begin;
while ($1)
{
eval insert into t1 values( $1 );
dec $1;
}
commit;
enable_query_log;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
# Transaction which should not be flushed to disk and so should not
# increase binlog_cache_disk_use.
begin;
delete from t1;
commit;
show status like "binlog_cache_use";
show status like "binlog_cache_disk_use";
drop table t1;
...@@ -123,7 +123,7 @@ Binlog_cache_disk_use 0 ...@@ -123,7 +123,7 @@ Binlog_cache_disk_use 0
create table t1 (a int) engine=innodb; create table t1 (a int) engine=innodb;
show status like "binlog_cache_use"; show status like "binlog_cache_use";
Variable_name Value Variable_name Value
Binlog_cache_use 2 Binlog_cache_use 1
show status like "binlog_cache_disk_use"; show status like "binlog_cache_disk_use";
Variable_name Value Variable_name Value
Binlog_cache_disk_use 1 Binlog_cache_disk_use 1
...@@ -132,7 +132,7 @@ delete from t1; ...@@ -132,7 +132,7 @@ delete from t1;
commit; commit;
show status like "binlog_cache_use"; show status like "binlog_cache_use";
Variable_name Value Variable_name Value
Binlog_cache_use 4 Binlog_cache_use 2
show status like "binlog_cache_disk_use"; show status like "binlog_cache_disk_use";
Variable_name Value Variable_name Value
Binlog_cache_disk_use 1 Binlog_cache_disk_use 1
......
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
drop table if exists t1;
create table t1 (a int) engine=innodb;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
begin;
delete from t1;
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 4
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1;
drop table if exists t1, t2;
create table t1 (a int) engine=innodb;
create table t2 (a int) engine=myisam;
**** Preparing the enviroment to check commit and its effect on
**** the binlog_cache_use and binlog_cache_disk_use.
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Preparing the enviroment to check abort and its effect on
**** the binlog_cache_use and binlog_cache_disk_use
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
rollback;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1, t2;
drop table if exists t1, t2;
create table t1 (a int) engine=innodb;
create table t2 (a int) engine=myisam;
**** Preparing the enviroment to check commit and its effect on
**** the binlog_cache_use and binlog_cache_disk_use.
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Preparing the enviroment to check abort and its effect on
**** the binlog_cache_use and binlog_cache_disk_use
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
rollback;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1, t2;
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
drop table if exists t1;
create table t1 (a int) engine=innodb;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
begin;
delete from t1;
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 4
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1;
drop table if exists t1, t2;
create table t1 (a int) engine=innodb;
create table t2 (a int) engine=myisam;
**** Preparing the enviroment to check commit and its effect on
**** the binlog_cache_use and binlog_cache_disk_use.
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Preparing the enviroment to check abort and its effect on
**** the binlog_cache_use and binlog_cache_disk_use
**** Expected: binlog_cache_use = 0, binlog_cache_disk_use = 0.
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
**** Now we are going to create transactional changes which are long enough so
**** they will be flushed to disk...
**** Expected: binlog_cache_use = 1, binlog_cache_disk_use = 1.
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 1
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 2, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
rollback;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Non-Transactional changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 3, binlog_cache_disk_use = 1.
begin;
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 3
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
**** Mixed changes which should not be flushed to disk and so should not
**** increase binlog_cache_disk_use.
**** Expected: binlog_cache_use = 5, binlog_cache_disk_use = 1.
begin;
insert into t1 values( 1 );
insert into t2 values( 1 );
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 5
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1, t2;
flush status;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 0
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 0
drop table if exists t1;
create table t1 (a int) engine=innodb;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 2
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
begin;
delete from t1;
commit;
show status like "binlog_cache_use";
Variable_name Value
Binlog_cache_use 4
show status like "binlog_cache_disk_use";
Variable_name Value
Binlog_cache_disk_use 1
drop table t1;
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
# For both statement and row based bin logs 9/19/2005 [jbm] # For both statement and row based bin logs 9/19/2005 [jbm]
-- source include/have_binlog_format_mixed.inc -- source include/have_binlog_format_mixed.inc
-- source extra/binlog_tests/innodb_stat.test -- source extra/binlog_tests/binlog_cache_stat.test
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
# For both statement and row based bin logs 9/19/2005 [jbm] # For both statement and row based bin logs 9/19/2005 [jbm]
-- source include/have_binlog_format_row.inc -- source include/have_binlog_format_row.inc
-- source extra/binlog_tests/innodb_stat.test -- source extra/binlog_tests/binlog_cache_stat.test
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
# For both statement and row based bin logs 9/19/2005 [jbm] # For both statement and row based bin logs 9/19/2005 [jbm]
-- source include/have_binlog_format_statement.inc -- source include/have_binlog_format_statement.inc
-- source extra/binlog_tests/innodb_stat.test -- source extra/binlog_tests/binlog_cache_stat.test
...@@ -210,6 +210,9 @@ class binlog_cache_data ...@@ -210,6 +210,9 @@ class binlog_cache_data
public: public:
binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF), binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE) incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE)
#ifndef DBUG_OFF
, has_trans(FALSE)
#endif
{ {
cache_log.end_of_file= max_binlog_cache_size; cache_log.end_of_file= max_binlog_cache_size;
} }
...@@ -262,6 +265,14 @@ public: ...@@ -262,6 +265,14 @@ public:
incident= FALSE; incident= FALSE;
before_stmt_pos= MY_OFF_T_UNDEF; before_stmt_pos= MY_OFF_T_UNDEF;
cache_log.end_of_file= max_binlog_cache_size; cache_log.end_of_file= max_binlog_cache_size;
/*
When a truncate is called there may be write activity and by consequence
"disk_writes" is increased. This breaks the "disk_writes"' use by the
binary log which aims to compute the ratio between in-memory cache usage
and disk cache usage. To avoid this undesirable behavior, we reset the
variable after truncating the cache.
*/
cache_log.disk_writes= 0;
DBUG_ASSERT(empty()); DBUG_ASSERT(empty());
} }
...@@ -292,6 +303,18 @@ public: ...@@ -292,6 +303,18 @@ public:
before_stmt_pos= MY_OFF_T_UNDEF; before_stmt_pos= MY_OFF_T_UNDEF;
} }
#ifndef DBUG_OFF
bool is_transactional()
{
return(has_trans);
}
void set_transactional(bool is_transactional)
{
has_trans= is_transactional;
}
#endif
/* /*
Cache to store data before copying it to the binary log. Cache to store data before copying it to the binary log.
*/ */
...@@ -321,6 +344,13 @@ private: ...@@ -321,6 +344,13 @@ private:
*/ */
bool changes_to_non_trans_temp_table_flag; bool changes_to_non_trans_temp_table_flag;
#ifndef DBUG_OFF
/*
Defines the type of the cache either: transactional or non-transactional.
*/
bool has_trans;
#endif
/* /*
It truncates the cache to a certain position. This includes deleting the It truncates the cache to a certain position. This includes deleting the
pending event. pending event.
...@@ -1506,56 +1536,146 @@ static int binlog_close_connection(handlerton *hton, THD *thd) ...@@ -1506,56 +1536,146 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
} }
/** /**
This function flushes a transactional cache upon commit/rollback. This function computes binlog cache and disk usage.
@param thd The thread whose transaction should be flushed @param cache_data Pointer to the cache where data is
@param cache_mngr Pointer to the cache data to be flushed stored.
@param end_ev The end event either commit/rollback.
@return
nonzero if an error pops up when flushing the transactional cache.
*/ */
static int static inline void
binlog_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, binlog_compute_statistics(binlog_cache_data* cache_data)
Log_event *end_ev)
{ {
DBUG_ENTER("binlog_flush_trx_cache"); if (!cache_data->empty())
int error=0; {
IO_CACHE *cache_log= &cache_mngr->trx_cache.cache_log; statistic_increment(binlog_cache_use, &LOCK_status);
if (cache_data->cache_log.disk_writes != 0)
statistic_increment(binlog_cache_disk_use, &LOCK_status);
}
}
/* /**
This function handles transactional changes and as such This function flushes a cache upon commit/rollback.
this flag equals to true.
*/
bool const is_transactional= TRUE;
if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional)) @param thd The thread whose transaction should be flushed
DBUG_RETURN(1); @param cache_data Pointer to the cache
/* @param end_ev The end event either commit/rollback
Doing a commit or a rollback including non-transactional tables, @param is_transactional The type of the cache: transactional or
i.e., ending a transaction where we might write the transaction non-transactional
cache to the binary log.
@return
We can always end the statement when ending a transaction since nonzero if an error pops up when flushing the cache.
transactions are not allowed inside stored functions. If they */
were, we would have to ensure that we're not ending a statement static inline int
inside a stored function. binlog_flush_cache(THD *thd, binlog_cache_data* cache_data, Log_event *end_evt,
*/ bool is_transactional)
error= mysql_bin_log.write(thd, &cache_mngr->trx_cache.cache_log, end_ev, {
cache_mngr->trx_cache.has_incident()); DBUG_ENTER("binlog_flush_cache");
cache_mngr->reset_cache(&cache_mngr->trx_cache); int error= 0;
statistic_increment(binlog_cache_use, &LOCK_status); if (!cache_data->empty())
if (cache_log->disk_writes != 0)
{ {
statistic_increment(binlog_cache_disk_use, &LOCK_status); #ifndef DBUG_OFF
cache_log->disk_writes= 0; DBUG_PRINT("info", ("is_transactional(%d), event(%d), cache_data(%d)",
is_transactional, end_evt->use_trans_cache(),
cache_data->is_transactional()));
DBUG_ASSERT(is_transactional == end_evt->use_trans_cache() &&
is_transactional == cache_data->is_transactional());
#endif
if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
DBUG_RETURN(1);
/*
Doing a commit or a rollback including non-transactional tables,
i.e., ending a transaction where we might write the transaction
cache to the binary log.
We can always end the statement when ending a transaction since
transactions are not allowed inside stored functions. If they
were, we would have to ensure that we're not ending a statement
inside a stored function.
*/
error= mysql_bin_log.write(thd, &cache_data->cache_log, end_evt,
cache_data->has_incident());
} }
binlog_compute_statistics(cache_data);
cache_data->reset();
DBUG_ASSERT(cache_mngr->trx_cache.empty()); DBUG_ASSERT(cache_data->empty());
DBUG_RETURN(error); DBUG_RETURN(error);
} }
/**
This function flushes the stmt-cache upon commit.
@param thd The thread whose transaction should be flushed
@param cache_mngr Pointer to the cache manager
@return
nonzero if an error pops up when flushing the cache.
*/
static inline int
binlog_commit_flush_stmt_cache(THD *thd,
binlog_cache_mngr *cache_mngr)
{
Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
FALSE, FALSE, TRUE, 0);
return (binlog_flush_cache(thd, &cache_mngr->stmt_cache, &end_evt,
FALSE));
}
/**
This function flushes the trx-cache upon commit.
@param thd The thread whose transaction should be flushed
@param cache_mngr Pointer to the cache manager
@return
nonzero if an error pops up when flushing the cache.
*/
static inline int
binlog_commit_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr)
{
Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
TRUE, FALSE, TRUE, 0);
return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
TRUE));
}
/**
This function flushes the trx-cache upon rollback.
@param thd The thread whose transaction should be flushed
@param cache_mngr Pointer to the cache manager
@return
nonzero if an error pops up when flushing the cache.
*/
static inline int
binlog_rollback_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr)
{
Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
TRUE, FALSE, TRUE, 0);
return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
TRUE));
}
/**
This function flushes the trx-cache upon commit.
@param thd The thread whose transaction should be flushed
@param cache_mngr Pointer to the cache manager
@param xid Transaction Id
@return
nonzero if an error pops up when flushing the cache.
*/
static inline int
binlog_commit_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr,
my_xid xid)
{
Xid_log_event end_evt(thd, xid);
return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
TRUE));
}
/** /**
This function truncates the transactional cache upon committing or rolling This function truncates the transactional cache upon committing or rolling
back either a transaction or a statement. back either a transaction or a statement.
...@@ -1579,23 +1699,26 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all) ...@@ -1579,23 +1699,26 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
*/ */
bool const is_transactional= TRUE; bool const is_transactional= TRUE;
DBUG_PRINT("info", ("thd->options={ %s%s}, transaction: %s", DBUG_PRINT("info", ("thd->options={ %s %s}, transaction: %s",
FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT), FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
FLAGSTR(thd->variables.option_bits, OPTION_BEGIN), FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
all ? "all" : "stmt")); all ? "all" : "stmt"));
thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
/* /*
If rolling back an entire transaction or a single statement not If rolling back an entire transaction or a single statement not
inside a transaction, we reset the transaction cache. inside a transaction, we reset the transaction cache.
*/ */
thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
if (ending_trans(thd, all)) if (ending_trans(thd, all))
{ {
if (cache_mngr->trx_cache.has_incident()) if (cache_mngr->trx_cache.has_incident())
error= mysql_bin_log.write_incident(thd, TRUE); error= mysql_bin_log.write_incident(thd, TRUE);
cache_mngr->reset_cache(&cache_mngr->trx_cache);
thd->clear_binlog_table_maps(); thd->clear_binlog_table_maps();
binlog_compute_statistics(&cache_mngr->trx_cache);
cache_mngr->reset_cache(&cache_mngr->trx_cache);
} }
/* /*
If rolling back a statement in a transaction, we truncate the If rolling back a statement in a transaction, we truncate the
...@@ -1619,51 +1742,6 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) ...@@ -1619,51 +1742,6 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
return 0; return 0;
} }
/**
This function flushes the non-transactional to the binary log upon
committing or rolling back a statement.
@param thd The thread whose transaction should be flushed
@param cache_mngr Pointer to the cache data to be flushed
@return
nonzero if an error pops up when flushing the non-transactional cache.
*/
static int
binlog_flush_stmt_cache(THD *thd, binlog_cache_mngr *cache_mngr)
{
int error= 0;
DBUG_ENTER("binlog_flush_stmt_cache");
/*
If we are flushing the statement cache, it means that the changes get
through otherwise the cache is empty and this routine should not be called.
*/
DBUG_ASSERT(cache_mngr->stmt_cache.has_incident() == FALSE);
/*
This function handles non-transactional changes and as such this flag equals
to false.
*/
bool const is_transactional= FALSE;
IO_CACHE *cache_log= &cache_mngr->stmt_cache.cache_log;
if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
DBUG_RETURN(1);
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
if ((error= mysql_bin_log.write(thd, cache_log, &qev,
cache_mngr->stmt_cache.has_incident())))
DBUG_RETURN(error);
cache_mngr->reset_cache(&cache_mngr->stmt_cache);
statistic_increment(binlog_cache_use, &LOCK_status);
if (cache_log->disk_writes != 0)
{
statistic_increment(binlog_cache_disk_use, &LOCK_status);
cache_log->disk_writes= 0;
}
DBUG_RETURN(error);
}
/** /**
This function is called once after each statement. This function is called once after each statement.
...@@ -1690,19 +1768,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) ...@@ -1690,19 +1768,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
YESNO(thd->transaction.all.modified_non_trans_table), YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table))); YESNO(thd->transaction.stmt.modified_non_trans_table)));
if (!cache_mngr->stmt_cache.empty()) binlog_commit_flush_stmt_cache(thd, cache_mngr);
{
binlog_flush_stmt_cache(thd, cache_mngr);
}
if (cache_mngr->trx_cache.empty())
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset_cache(&cache_mngr->trx_cache);
DBUG_RETURN(0);
}
/* /*
We commit the transaction if: We commit the transaction if:
...@@ -1711,16 +1777,14 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) ...@@ -1711,16 +1777,14 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
Otherwise, we accumulate the changes. Otherwise, we accumulate the changes.
*/ */
if (ending_trans(thd, all)) if (ending_trans(thd, all))
{ error= binlog_commit_flush_trx_cache(thd, cache_mngr);
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
}
/* /*
This is part of the stmt rollback. This is part of the stmt rollback.
*/ */
if (!all) if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF); cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -1755,19 +1819,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1755,19 +1819,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
error= mysql_bin_log.write_incident(thd, TRUE); error= mysql_bin_log.write_incident(thd, TRUE);
cache_mngr->reset_cache(&cache_mngr->stmt_cache); cache_mngr->reset_cache(&cache_mngr->stmt_cache);
} }
else if (!cache_mngr->stmt_cache.empty()) else
{ binlog_commit_flush_stmt_cache(thd, cache_mngr);
binlog_flush_stmt_cache(thd, cache_mngr);
}
if (cache_mngr->trx_cache.empty())
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset_cache(&cache_mngr->trx_cache);
DBUG_RETURN(0);
}
if (mysql_bin_log.check_write_error(thd)) if (mysql_bin_log.check_write_error(thd))
{ {
...@@ -1796,7 +1849,6 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1796,7 +1849,6 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
. the format is MIXED, non-trans table was updated and . the format is MIXED, non-trans table was updated and
aborting a single statement transaction; aborting a single statement transaction;
*/ */
if (ending_trans(thd, all) && if (ending_trans(thd, all) &&
((thd->variables.option_bits & OPTION_KEEP_LOG) || ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
(trans_has_updated_non_trans_table(thd) && (trans_has_updated_non_trans_table(thd) &&
...@@ -1806,10 +1858,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1806,10 +1858,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
(trans_has_updated_non_trans_table(thd) && (trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) && ending_single_stmt_trans(thd,all) &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED))) thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
{ error= binlog_rollback_flush_trx_cache(thd, cache_mngr);
Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE, TRUE, 0);
error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
}
/* /*
Truncate the cache if: Truncate the cache if:
. aborting a single or multi-statement transaction or; . aborting a single or multi-statement transaction or;
...@@ -1832,7 +1881,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1832,7 +1881,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
This is part of the stmt rollback. This is part of the stmt rollback.
*/ */
if (!all) if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF); cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -4364,6 +4414,11 @@ int THD::binlog_setup_trx_data() ...@@ -4364,6 +4414,11 @@ int THD::binlog_setup_trx_data()
cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr; cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
#ifndef DBUG_OFF
cache_mngr->trx_cache.set_transactional(TRUE);
cache_mngr->stmt_cache.set_transactional(FALSE);
#endif
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -6291,15 +6346,14 @@ void TC_LOG_BINLOG::close() ...@@ -6291,15 +6346,14 @@ void TC_LOG_BINLOG::close()
int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid) int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid)
{ {
DBUG_ENTER("TC_LOG_BINLOG::log"); DBUG_ENTER("TC_LOG_BINLOG::log");
Xid_log_event xle(thd, xid);
binlog_cache_mngr *cache_mngr= binlog_cache_mngr *cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
/* /*
We always commit the entire transaction when writing an XID. Also We always commit the entire transaction when writing an XID. Also
note that the return value is inverted. note that the return value is inverted.
*/ */
DBUG_RETURN(!binlog_flush_stmt_cache(thd, cache_mngr) && DBUG_RETURN(!binlog_commit_flush_stmt_cache(thd, cache_mngr) &&
!binlog_flush_trx_cache(thd, cache_mngr, &xle)); !binlog_commit_flush_trx_cache(thd, cache_mngr, xid));
} }
void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
......
...@@ -2537,6 +2537,22 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, ...@@ -2537,6 +2537,22 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
bool trx_cache= FALSE; bool trx_cache= FALSE;
cache_type= Log_event::EVENT_INVALID_CACHE; cache_type= Log_event::EVENT_INVALID_CACHE;
#ifndef DBUG_OFF
/*
If debug is enabled, we make sure that begin, commit and rollback
have the cache_type exclusively defined by the param using_trans.
as this is important while checking if the correct cache is used.
Note this is the only way to accurately set the type of the cache
when a begin, commit or rollback event is created because such
events may be artificially produced to compose the binary log.
*/
if (strncmp("BEGIN", query_arg, query_length) &&
strncmp("COMMIT", query_arg, query_length) &&
strncmp("ROLLBACK", query_arg, query_length))
{
#endif
switch (lex->sql_command) switch (lex->sql_command)
{ {
case SQLCOM_DROP_TABLE: case SQLCOM_DROP_TABLE:
...@@ -2574,6 +2590,15 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, ...@@ -2574,6 +2590,15 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE; cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
else else
cache_type= Log_event::EVENT_STMT_CACHE; cache_type= Log_event::EVENT_STMT_CACHE;
#ifndef DBUG_OFF
}
else if (using_trans)
cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
else
cache_type= Log_event::EVENT_STMT_CACHE;
#endif
DBUG_ASSERT(cache_type != Log_event::EVENT_INVALID_CACHE); DBUG_ASSERT(cache_type != Log_event::EVENT_INVALID_CACHE);
DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %lu", DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %lu",
(ulong) flags2, sql_mode)); (ulong) flags2, sql_mode));
......
...@@ -2504,7 +2504,7 @@ class Xid_log_event: public Log_event ...@@ -2504,7 +2504,7 @@ class Xid_log_event: public Log_event
my_xid xid; my_xid xid;
#ifdef MYSQL_SERVER #ifdef MYSQL_SERVER
Xid_log_event(THD* thd_arg, my_xid x): Log_event(thd_arg,0,0), xid(x) {} Xid_log_event(THD* thd_arg, my_xid x): Log_event(thd_arg, 0, TRUE), xid(x) {}
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol); void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */ #endif /* HAVE_REPLICATION */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment