#At file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-43929/mysql-5.1-bugteam/ based on revid:bjorn.munch@stripped
2912 Alfranio Correia 2009-05-29
BUG#43929 binlog corruption when max_binlog_cache_size is exceeded
Large transactions and statements may corrupt the binary log if the size of the
cache, which is set by the max_binlog_cache_size, is not enough to store the
the changes.
In a nutshell, to fix the bug, we save the position of the next character in the
cache before starting processing a statement. If there is a problem, we simply
restore the position thus removing any effect of the statement from the cache.
Unfortunately, to avoid corrupting the binary log, we may end up loosing changes
on non-transactional tables if they do not fit in the cache. In such cases, we
store an Incident_log_event in order to stop the slave and alert users that some
changes were not logged.
Precisely, for every non-transactional changes that cannot fit into the cache, we
do the following:
a) its respective statement gives an error
b) the statement is *not* logged
c) an incident event is logged
For transactional changes that cannot fit into the cache, we do the following:
a) its respective statement gives an error
b) the statement is *not* logged
To work properly, this patch requires that callers to MYSQL_BIN_LOG::write and
THD::binlog_query handle any error returned and take the appropriate actions
such as undoing the effects of a statement. We already changed some calls from
the sql_insert.cc, sql_update.cc and sql_insert.cc modules but the remaining
calls spread all over the code should be handled in BUG#37148.
added:
mysql-test/suite/rpl/include/bug43929_binlog.sql
mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result
mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt
mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test
modified:
mysql-test/include/commit.inc
mysql-test/r/commit_1innodb.result
sql/log.cc
sql/log.h
sql/sql_delete.cc
sql/sql_insert.cc
sql/sql_update.cc
=== modified file 'mysql-test/include/commit.inc'
--- a/mysql-test/include/commit.inc 2009-01-23 12:22:05 +0000
+++ b/mysql-test/include/commit.inc 2009-05-29 10:20:18 +0000
@@ -669,13 +669,9 @@ call p_verify_status_increment(1, 0, 1,
insert t1 set a=3;
call p_verify_status_increment(2, 2, 2, 2);
savepoint a;
-call p_verify_status_increment(0, 0, 0, 0);
+call p_verify_status_increment(1, 0, 0, 0);
insert t1 set a=4;
---echo # Binlog does not register itself this time for other than the 1st
---echo # statement of the transaction with MIXED/STATEMENT binlog_format.
---echo # It needs registering with the ROW format. Therefore 1,0,2,2 are
---echo # the correct arguments to this test after bug#40221 fixed.
-call p_verify_status_increment(1, 0, 2, 2);
+call p_verify_status_increment(2, 2, 2, 2);
release savepoint a;
rollback;
call p_verify_status_increment(0, 0, 0, 0);
=== modified file 'mysql-test/r/commit_1innodb.result'
--- a/mysql-test/r/commit_1innodb.result 2009-02-10 14:44:58 +0000
+++ b/mysql-test/r/commit_1innodb.result 2009-05-29 10:20:18 +0000
@@ -766,15 +766,11 @@ call p_verify_status_increment(2, 2, 2,
SUCCESS
savepoint a;
-call p_verify_status_increment(0, 0, 0, 0);
+call p_verify_status_increment(1, 0, 0, 0);
SUCCESS
insert t1 set a=4;
-# Binlog does not register itself this time for other than the 1st
-# statement of the transaction with MIXED/STATEMENT binlog_format.
-# It needs registering with the ROW format. Therefore 1,0,2,2 are
-# the correct arguments to this test after bug#40221 fixed.
-call p_verify_status_increment(1, 0, 2, 2);
+call p_verify_status_increment(2, 2, 2, 2);
SUCCESS
release savepoint a;
=== added file 'mysql-test/suite/rpl/include/bug43929_binlog.sql'
--- a/mysql-test/suite/rpl/include/bug43929_binlog.sql 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/include/bug43929_binlog.sql 2009-05-29 10:20:18 +0000
@@ -0,0 +1,36 @@
+let $data='vb
+alxxMCC5o X3dAkvHXY,WhFV72Z6;oNHpL,WfTAOYKMdb8OQ tUfYXjlNcois8V7hcnk5;.ixojWOcBpMGOVQl9,p3 v313eQ3j6mbGH7nh3sQeJn,
+G aEbB8 :YorJ1 wPzDerR8kLazeliXwxedpxhNUel0XpBO.KLdjNW MGlkaNSbtQVpfcmR wZPeyLWK0avXphFRd:5c4dCEMC XHsZvEZy Q1CrNIGTaJuKIkH5UEaDuacHzICkRugIn nW1Z MHYV7w d ZYA:U;V4Kku99ioob4TQHlLlanD RH vUq64phzC77pPIhf3PWxlB dLhimAG ;l0UMCqxV2alAHV iW,uIfp2,UH2 7Kf
+dJHeh4VmzxmAP6lG6ih2uIwfl;KwcLN qGrF70W , :QxfiB X7 dhEMEP: r3RllCaS.CTCCBaMQa;87vvEZnO6NmLsoEeoQx:zG6UTKvHRSyWcYC7bcWDF,jngS7QNZQwyoY H7ZC0ocebLf NpTy42NJ cupL LYQ7Xg2X16h2T
+csX,sqT B hRDsocDBBuu9j Iz9k EL1VuamulUc3zsFHI.dKWQRyCPTE0NEiy4eMNhBT4 L1Klqey YmWYuXZkrFUV89c:AONXIg
+ DsvgkPgNwZOTi; 9DGec F2XHLWH1yY R1Cjh 5PmE60jAl3B4SOz.coG:Oy.G boG 4;kZRTpIBda3eG3S6
+s2; Y.a vEV:MZKvOf6
+U3jkTX49wafxd;i:gJ
+sxTpvLa2B2jj 4cBG Bazi5 XpKqfTcBW0iUhKDlD6KamzWUk
+R9i
+n,STLTyG6DP:WH1fmw5:N wmQU3kpSJsGeXwxJ .BnmaMdcfqqinSpryqgTBvQ ,B:feTao.xkF.:l tY pW3u;D CbU
+8ahKp4rteHuaX
+, zXZ4kT 9DX;8Rwwi6JNaDWkSO6JHtjczuZat.NxW,UM0w2s5gCOgbx:jlfEeq4XDe1xsFHORNa 0RTT ftMPj7eqP OkqUteCtOyfweMZ;LdvYwGFjM7xOfa9jSQv6Ypmhd;MIZ.rBb,p7Ce3aPDra8eoKy6YxLsqxPIzAO pZPVTrmmj7Jyc.QVmF57gdRMv3X8kXcvTz TArOZ wZr4acAu8eJjr
+7Z;.Sy3p9nGivSprQjVR:X0,Af4iYk,WAoXRyT tu09b6c:v.Ci 7EMjstiR7,f L653d hfswxvY;IB.gWXEVFn3EhhxoYCcL0fftXcf Ec3ZW9 A2hYMa;F9GWeCvshNZc2:Bwa5VzwaarfVpf
+HSA4iREIbgg a,;61.
+
+5 rnGNC CsaoRevNeDjhN80 wB.bS7oL ,ioctAQWi9gQS 6pV .pgLtnbPA:gEz:Mq:DkAtpulNoziTWitVAoMu;zwrKibXoQEHU0ji4CJ4GSUuzZ,yrmPe1Tx 2n8mJ15Aa7f
+abtu
+rgaf
+yWY2m8pM2Ps gmiMdJXiYQkU0Girlk;OhB;mdClF.dWhvl:NhkIUoWEO0b6b13pxB3XXa3V Kr3l.edsfoX950p:cmPPlNrOiAl.Oat934PC
+dM hBgUUk:RCgi.uGTu.tG0Umia7 9.2u1L.yE6ToYn7sB;aF9mups.v3XJYJh3gkaOojNU7:;Q2OpiIrnee1VRn 6q DY9nFwe9zjZ8ciJ oUQLaEooVFhuner;ZFRO2
+lHu:w0Lu
+dajd2lZ3FBw
+j
+X
+aaUARW xmaccUvRc3b6V5nrVHnOTQFpWYCoqvat1dtNl8aY
+KEa6YzWL8,Le53mjfK
+FQp8r4RFOj7:xVSWr1GQbKKXiKd4C7tblGaSy8VU9w7Wkr6 SVKaJn6Nd1hWymraIH0K13bb3 K,0ersTE.hX2
+Z xS1lC98j,g9V:4kU1E ZR;Fhpa2Jfr3MY:Vsz
+T1c2aV8q5b4;NCEnu5;M5S.i9 tI1EIK;Lban Z
+06nyfms94A7ukJ2h8utxZSa6PX52k81L1VR 6Z TXlVfLa;18bSy;LhHpOY,B3Z0 e5N0WXgVqh,1AV25CHLJ;yFtz;Pr011 Mqhya.phYDvzzJYrPY6;Znji
+cYC2kIv;FqS otKpKQ:hOThDkwU2AnGZs848aF6kH1rgyRJWGcihB
+kXhjBfb0zM2bL,Jmpb3K3GEIMNzc35V THnO495KjefyL.Lvfmt nk saDs qo;hHpOW,;12
+ZTXRWxxt
+qKIfVus3D:mvPd8wq4Vw0cItYLeta2E6MP78kdJlBPxGbtSRyPtG6MD4GNYIroTNohSWwiy5v0iXeFzeaDkdb:qfK9eICd LD GStfa
+mOe,Uhs ul;Ziev3 mleNCPku8aDKovqPd,OX:JN 3,Sb 9WwS:gprHC n6E1';
=== added file 'mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result'
--- a/mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result 2009-05-29 10:20:18 +0000
@@ -0,0 +1,137 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
+CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=MyIsam;
+CREATE TABLE t3(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
+########################################################################################
+# 1 - SINGLE STATEMENT
+########################################################################################
+*** Single statment on transactional table ***
+Got one of the listed errors
+*** Single statment on non-transactional table ***
+*** After WL#2687 the difference between STATEMENT/MIXED and ROW will not exist. ***
+Got one of the listed errors
+*** Single statment on both transactional and non-transactional tables. ***
+*** After WL#2687 we will be able to change the order of the tables. ***
+Got one of the listed errors
+RESET MASTER;
+STOP SLAVE;
+RESET SLAVE;
+START SLAVE;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+source include/diff_master_slave.inc;
+########################################################################################
+# 3 - BEGIN - COMMIT
+########################################################################################
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+COMMIT;
+source include/diff_master_slave.inc;
+########################################################################################
+# 4 - BEGIN - ROLLBACK
+########################################################################################
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+ROLLBACK;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+source include/diff_master_slave.inc;
+########################################################################################
+# 5 - PROCEDURE
+########################################################################################
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+CREATE PROCEDURE p1(pd VARCHAR(30000))
+BEGIN
+INSERT INTO t1 (a, data) VALUES (1, pd);
+INSERT INTO t1 (a, data) VALUES (2, pd);
+INSERT INTO t1 (a, data) VALUES (3, pd);
+INSERT INTO t1 (a, data) VALUES (4, pd);
+INSERT INTO t1 (a, data) VALUES (5, 's');
+END//
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+BEGIN;
+Got one of the listed errors
+COMMIT;
+TRUNCATE TABLE t1;
+BEGIN;
+Got one of the listed errors
+ROLLBACK;
+source include/diff_master_slave.inc;
+########################################################################################
+# 6 - XID
+########################################################################################
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+ROLLBACK TO sv;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+COMMIT;
+source include/diff_master_slave.inc;
+########################################################################################
+# 7 - NON-TRANS TABLE
+########################################################################################
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+BEGIN;
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+Got one of the listed errors
+COMMIT;
+BEGIN;
+Got one of the listed errors
+COMMIT;
+########################################################################################
+# CLEAN
+########################################################################################
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE IF EXISTS t4;
+DROP TABLE IF EXISTS t5;
+DROP TABLE IF EXISTS t6;
+DROP PROCEDURE p1;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE IF EXISTS t4;
+DROP TABLE IF EXISTS t5;
+DROP TABLE IF EXISTS t6;
+DROP PROCEDURE p1;
=== added file 'mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt'
--- a/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt 2009-05-29 10:20:18 +0000
@@ -0,0 +1 @@
+--binlog_cache_size=4096 --max_binlog_cache_size=8192
=== added file 'mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test'
--- a/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test 2009-05-29 10:20:18 +0000
@@ -0,0 +1,404 @@
+########################################################################################
+# This test verifies if the binlog is not corrupted when the cache buffer is not
+# big enough to accommodate the updates.
+#
+# 1 - Single Statements:
+# 1.1 - Single statement on transactional table.
+# 1.2 - Single statement on non-transactional table.
+# 1.3 - Single statement on both transactional and non-transactional tables.
+# In both 1.2 and 1.3, an incident event is logged to notify the user that the
+# master and slave are diverging.
+#
+# 2 - Transactions ended by an implicit commit.
+#
+# 3 - Transactions ended by a COMMIT.
+#
+# 4 - Transactions ended by a ROLLBACK.
+#
+# 5 - Transactions with a failing statement that updates a non-transactional
+# table. In this case, a failure means that the statement does not get into
+# the cache and an incident event is logged to notify the user that the master
+# and slave are diverging.
+#
+########################################################################################
+
+########################################################################################
+# Configuring the environment
+########################################################################################
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/not_embedded.inc
+--source include/not_windows.inc
+
+CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
+CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=MyIsam;
+CREATE TABLE t3(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
+
+--source suite/rpl/include/bug43929_binlog.sql
+
+--echo ########################################################################################
+--echo # 1 - SINGLE STATEMENT
+--echo ########################################################################################
+
+connection master;
+
+--echo *** Single statment on transactional table ***
+--disable_query_log
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+eval INSERT INTO t1 (a, data) VALUES (1,
+ CONCAT($data, $data, $data, $data, $data));
+--enable_query_log
+
+--echo *** Single statment on non-transactional table ***
+--echo *** After WL#2687 the difference between STATEMENT/MIXED and ROW will not exist. ***
+--disable_query_log
+--disable_warnings
+if (`SELECT @@binlog_format = 'STATEMENT' || @@binlog_format = 'MIXED'`)
+{
+ eval INSERT INTO t2 (a, data) VALUES (2,
+ CONCAT($data, $data, $data, $data, $data, $data));
+ --echo Got one of the listed errors
+}
+if (`SELECT @@binlog_format = 'ROW'`)
+{
+ --error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+ eval INSERT INTO t2 (a, data) VALUES (2,
+ CONCAT($data, $data, $data, $data, $data, $data));
+
+ RESET MASTER;
+ connection slave;
+ --source include/wait_for_slave_sql_to_stop.inc
+ STOP SLAVE;
+ --source include/wait_for_slave_to_stop.inc
+ RESET SLAVE;
+ START SLAVE;
+ --source include/wait_for_slave_to_start.inc
+}
+--enable_warnings
+--enable_query_log
+
+connection master;
+
+--disable_query_log
+eval INSERT INTO t1 (a, data) VALUES (3, $data);
+eval INSERT INTO t1 (a, data) VALUES (4, $data);
+eval INSERT INTO t1 (a, data) VALUES (5, $data);
+eval INSERT INTO t2 (a, data) VALUES (3, $data);
+eval INSERT INTO t2 (a, data) VALUES (4, $data);
+eval INSERT INTO t2 (a, data) VALUES (5, $data);
+--enable_query_log
+
+--echo *** Single statment on both transactional and non-transactional tables. ***
+--echo *** After WL#2687 we will be able to change the order of the tables. ***
+--disable_query_log
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+eval UPDATE t2, t1 SET t2.data = CONCAT($data, $data, $data, $data),
+ t1.data = CONCAT($data, $data, $data, $data);
+--enable_query_log
+
+RESET MASTER;
+connection slave;
+--source include/wait_for_slave_sql_to_stop.inc
+STOP SLAVE;
+--source include/wait_for_slave_to_stop.inc
+RESET SLAVE;
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+
+#--echo ########################################################################################
+#--echo # 2 - BEGIN - IMPLICIT COMMIT by DDL
+#--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (1, $data);
+--eval INSERT INTO t1 (a, data) VALUES (2, $data);
+--eval INSERT INTO t1 (a, data) VALUES (3, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (4, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (5, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (6, $data);
+--eval INSERT INTO t1 (a, data) VALUES (7, 's');
+--eval INSERT INTO t2 (a, data) VALUES (8, 's');
+--eval INSERT INTO t1 (a, data) VALUES (9, 's');
+--enable_query_log
+
+--disable_query_log
+ALTER TABLE t3 ADD COLUMN d int;
+--enable_query_log
+
+--disable_query_log
+--eval INSERT INTO t2 (a, data) VALUES (10, $data);
+--eval INSERT INTO t2 (a, data) VALUES (11, $data);
+--eval INSERT INTO t2 (a, data) VALUES (12, $data);
+--eval INSERT INTO t2 (a, data) VALUES (13, $data);
+--enable_query_log
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (14, $data);
+--eval INSERT INTO t1 (a, data) VALUES (15, $data);
+--eval INSERT INTO t1 (a, data) VALUES (16, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (17, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (18, $data);
+--eval INSERT INTO t1 (a, data) VALUES (19, 's');
+--eval INSERT INTO t2 (a, data) VALUES (20, 's');
+--eval INSERT INTO t1 (a, data) VALUES (21, 's');
+--enable_query_log
+
+if (`SELECT @@binlog_format = 'STATEMENT' || @@binlog_format = 'MIXED'`)
+{
+ --disable_query_log
+ CREATE TABLE t4 SELECT * FROM t1;
+ --enable_query_log
+ --echo Got one of the listed errors
+}
+if (`SELECT @@binlog_format = 'ROW'`)
+{
+ --disable_query_log
+ --error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+ CREATE TABLE t4 SELECT * FROM t1;
+ --enable_query_log
+}
+
+--disable_query_log
+--eval INSERT INTO t2 (a, data) VALUES (15, $data);
+--enable_query_log
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (22, $data);
+--eval INSERT INTO t1 (a, data) VALUES (23, $data);
+--eval INSERT INTO t1 (a, data) VALUES (24, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (25, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (26, $data);
+--eval INSERT INTO t1 (a, data) VALUES (27, 's');
+--eval INSERT INTO t2 (a, data) VALUES (28, 's');
+--eval INSERT INTO t1 (a, data) VALUES (29, 's');
+--enable_query_log
+
+--disable_query_log
+CREATE TABLE t5 (a int);
+--enable_query_log
+
+let $diff_statement= SELECT * FROM t1;
+--source include/diff_master_slave.inc
+
+--echo ########################################################################################
+--echo # 3 - BEGIN - COMMIT
+--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (1, $data);
+--eval INSERT INTO t1 (a, data) VALUES (2, $data);
+--eval INSERT INTO t1 (a, data) VALUES (3, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (4, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (5, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (6, $data);
+--eval INSERT INTO t1 (a, data) VALUES (7, 's');
+--eval INSERT INTO t2 (a, data) VALUES (8, 's');
+--eval INSERT INTO t1 (a, data) VALUES (9, 's');
+--enable_query_log
+COMMIT;
+
+let $diff_statement= SELECT * FROM t1;
+--source include/diff_master_slave.inc
+
+--echo ########################################################################################
+--echo # 4 - BEGIN - ROLLBACK
+--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (1, $data);
+--eval INSERT INTO t1 (a, data) VALUES (2, $data);
+--eval INSERT INTO t1 (a, data) VALUES (3, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (4, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (5, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (6, $data);
+--eval INSERT INTO t1 (a, data) VALUES (7, 's');
+--eval INSERT INTO t2 (a, data) VALUES (8, 's');
+--eval INSERT INTO t1 (a, data) VALUES (9, 's');
+--enable_query_log
+ROLLBACK;
+
+let $diff_statement= SELECT * FROM t1;
+--source include/diff_master_slave.inc
+
+--echo ########################################################################################
+--echo # 5 - PROCEDURE
+--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+DELIMITER //;
+
+CREATE PROCEDURE p1(pd VARCHAR(30000))
+BEGIN
+ INSERT INTO t1 (a, data) VALUES (1, pd);
+ INSERT INTO t1 (a, data) VALUES (2, pd);
+ INSERT INTO t1 (a, data) VALUES (3, pd);
+ INSERT INTO t1 (a, data) VALUES (4, pd);
+ INSERT INTO t1 (a, data) VALUES (5, 's');
+END//
+
+DELIMITER ;//
+
+TRUNCATE TABLE t1;
+
+--disable_query_log
+eval CALL p1($data);
+--enable_query_log
+
+TRUNCATE TABLE t1;
+
+BEGIN;
+--disable_query_log
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+eval CALL p1($data);
+--enable_query_log
+COMMIT;
+
+TRUNCATE TABLE t1;
+
+BEGIN;
+--disable_query_log
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+eval CALL p1($data);
+--enable_query_log
+ROLLBACK;
+
+let $diff_statement= SELECT * FROM t1;
+--source include/diff_master_slave.inc
+
+--echo ########################################################################################
+--echo # 6 - XID
+--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (1, $data);
+--eval INSERT INTO t1 (a, data) VALUES (2, $data);
+--eval INSERT INTO t1 (a, data) VALUES (3, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (4, $data);
+SAVEPOINT sv;
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (5, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (6, $data);
+--eval INSERT INTO t1 (a, data) VALUES (7, 's');
+--eval INSERT INTO t2 (a, data) VALUES (8, 's');
+--eval INSERT INTO t1 (a, data) VALUES (9, 's');
+--enable_query_log
+ROLLBACK TO sv;
+COMMIT;
+
+let $diff_statement= SELECT * FROM t1;
+--source include/diff_master_slave.inc
+
+--echo ########################################################################################
+--echo # 7 - NON-TRANS TABLE
+--echo ########################################################################################
+
+connection master;
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+TRUNCATE TABLE t3;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (1, $data);
+--eval INSERT INTO t1 (a, data) VALUES (2, $data);
+--eval INSERT INTO t2 (a, data) VALUES (3, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (4, $data);
+--eval INSERT INTO t2 (a, data) VALUES (5, $data);
+--eval INSERT INTO t2 (a, data) VALUES (6, $data);
+--eval INSERT INTO t2 (a, data) VALUES (7, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (8, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (9, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (10, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval UPDATE t2 SET data= CONCAT($data, $data);
+--eval INSERT INTO t1 (a, data) VALUES (11, 's');
+--eval INSERT INTO t1 (a, data) VALUES (12, 's');
+--eval INSERT INTO t2 (a, data) VALUES (13, 's');
+--eval INSERT INTO t1 (a, data) VALUES (14, 's');
+--enable_query_log
+COMMIT;
+
+BEGIN;
+--disable_query_log
+--eval INSERT INTO t1 (a, data) VALUES (15, $data);
+--eval INSERT INTO t1 (a, data) VALUES (16, $data);
+--eval INSERT INTO t2 (a, data) VALUES (17, $data);
+--error ER_TRANS_CACHE_FULL, ER_BINLOG_LOGGING_IMPOSSIBLE
+--eval INSERT INTO t1 (a, data) VALUES (18, $data);
+--enable_query_log
+COMMIT;
+
+connection slave;
+--source include/wait_for_slave_sql_to_stop.inc
+
+--echo ########################################################################################
+--echo # CLEAN
+--echo ########################################################################################
+
+--disable_warnings
+connection master;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE IF EXISTS t4;
+DROP TABLE IF EXISTS t5;
+DROP TABLE IF EXISTS t6;
+DROP PROCEDURE p1;
+connection slave;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE IF EXISTS t4;
+DROP TABLE IF EXISTS t5;
+DROP TABLE IF EXISTS t6;
+DROP PROCEDURE p1;
+--enable_warnings
=== modified file 'sql/log.cc'
--- a/sql/log.cc 2009-04-09 15:25:25 +0000
+++ b/sql/log.cc 2009-05-29 10:20:18 +0000
@@ -153,9 +153,10 @@ private:
class binlog_trx_data {
public:
binlog_trx_data()
- : at_least_one_stmt(0), m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF)
+ : at_least_one_stmt(0), incident(FALSE), m_pending(0),
+ before_stmt_pos(MY_OFF_T_UNDEF)
{
- trans_log.end_of_file= max_binlog_cache_size;
+ calculate_cache_size();
}
~binlog_trx_data()
@@ -184,6 +185,7 @@ public:
delete pending();
set_pending(0);
reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0);
+ calculate_cache_size();
if (pos < before_stmt_pos)
before_stmt_pos= MY_OFF_T_UNDEF;
@@ -206,7 +208,8 @@ public:
if (!empty())
truncate(0);
before_stmt_pos= MY_OFF_T_UNDEF;
- trans_log.end_of_file= max_binlog_cache_size;
+ incident= FALSE;
+ calculate_cache_size();
DBUG_ASSERT(empty());
}
@@ -222,11 +225,29 @@ public:
IO_CACHE trans_log; // The transaction cache
+ void set_incident(void)
+ {
+ incident= TRUE;
+ }
+
+ bool has_incident(void)
+ {
+ return(incident);
+ }
+
+ my_off_t calculate_cache_size(void)
+ {
+ my_off_t diff= (incident ? 0 : reserved_cache_size);
+ trans_log.end_of_file= (((max_binlog_cache_size - diff) < IO_SIZE) ?
+ IO_SIZE : (max_binlog_cache_size - diff));
+ return trans_log.end_of_file;
+ }
/**
Boolean that is true if there is at least one statement in the
transaction cache.
*/
bool at_least_one_stmt;
+ bool incident;
private:
/*
@@ -240,6 +261,7 @@ public:
Binlog position before the start of the current statement.
*/
my_off_t before_stmt_pos;
+ static const my_off_t reserved_cache_size= 512;
};
handlerton *binlog_hton;
@@ -1391,7 +1413,8 @@ binlog_end_trans(THD *thd, binlog_trx_da
*/
if (end_ev != NULL)
{
- thd->binlog_flush_pending_rows_event(TRUE);
+ if (thd->binlog_flush_pending_rows_event(TRUE))
+ DBUG_RETURN(1);
/*
Doing a commit or a rollback including non-transactional tables,
i.e., ending a transaction where we might write the transaction
@@ -1545,9 +1568,11 @@ static int binlog_rollback(handlerton *h
YESNO(all),
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
- if (all && thd->transaction.all.modified_non_trans_table ||
- !all && thd->transaction.stmt.modified_non_trans_table ||
- (thd->options & OPTION_KEEP_LOG))
+ if ((all && thd->transaction.all.modified_non_trans_table) ||
+ (!all && thd->transaction.stmt.modified_non_trans_table &&
+ !mysql_bin_log.check_write_error(thd)) ||
+ ((thd->options & OPTION_KEEP_LOG) &&
+ !mysql_bin_log.check_write_error(thd)))
{
/*
We write the transaction cache with a rollback last if we have
@@ -1561,21 +1586,82 @@ static int binlog_rollback(handlerton *h
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
error= binlog_end_trans(thd, trx_data, &qev, all);
}
- else if (all && !thd->transaction.all.modified_non_trans_table ||
- !all && !thd->transaction.stmt.modified_non_trans_table)
+ else
{
/*
- If we have modified only transactional tables, we can truncate
- the transaction cache without writing anything to the binary
- log.
+ We reach this point if either only transactional tables were modified or
+ the effect of a statement that did not get into the binlog needs to be
+ rolled back. In the later case, if the statement changed non-transactional
+ tables or had the OPTION_KEEP_LOG associated, we write an incident event
+ to the binlog in order to stop the slave and notify users that some
+ changes on the master did not get into the binlog and the slave will be
+ inconsistent. On the other hand, if the statement is transactional is
+ safe to roll it back.
*/
error= binlog_end_trans(thd, trx_data, 0, all);
+ if ((thd->transaction.all.modified_non_trans_table ||
+ thd->transaction.stmt.modified_non_trans_table ||
+ (thd->options & OPTION_KEEP_LOG)) &&
+ !trx_data->has_incident() && mysql_bin_log.check_write_error(thd))
+ {
+ /*
+ We need to allocate extra space to write the incident event.
+ */
+ trx_data->set_incident();
+ trx_data->calculate_cache_size();
+ LEX_STRING const write_error_msg=
+ { C_STRING_WITH_LEN("error writing to the binary log") };
+ Incident incident= INCIDENT_LOST_EVENTS;
+ Incident_log_event ev(thd, incident, write_error_msg);
+ mysql_bin_log.write(&ev);
+ /*
+ We need to reset the cache if not in a transaction as there will be no
+ call to commit or rollback a transaction in order to unset the incident
+ flag and the extra space allocated.
+ */
+ if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ trx_data->reset();
+ }
}
if (!all)
trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
DBUG_RETURN(error);
}
+void MYSQL_BIN_LOG::set_write_error(THD *thd)
+{
+ DBUG_ENTER("set_write_error");
+
+ if (check_write_error(thd))
+ DBUG_VOID_RETURN;
+
+ my_error(ER_BINLOG_LOGGING_IMPOSSIBLE, MYF(MY_WME),
+ "Error writing events");
+
+ DBUG_VOID_RETURN;
+}
+
+bool MYSQL_BIN_LOG::check_write_error(THD *thd)
+{
+ DBUG_ENTER("check_write_error");
+
+ bool checked= FALSE;
+
+ if (!thd->is_error())
+ DBUG_RETURN(checked);
+
+ switch (thd->main_da.sql_errno())
+ {
+ case ER_TRANS_CACHE_FULL:
+ case ER_ERROR_ON_WRITE:
+ case ER_BINLOG_LOGGING_IMPOSSIBLE:
+ checked= TRUE;
+ break;
+ }
+
+ DBUG_RETURN(checked);
+}
+
/**
@note
How do we handle this (unlikely but legal) case:
@@ -3851,6 +3937,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_row
if (pending->write(file))
{
pthread_mutex_unlock(&LOCK_log);
+ set_write_error(thd);
DBUG_RETURN(1);
}
@@ -3925,7 +4012,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
*/
bool const end_stmt=
thd->prelocked_mode && thd->lex->requires_prelocking();
- thd->binlog_flush_pending_rows_event(end_stmt);
+ if (thd->binlog_flush_pending_rows_event(end_stmt))
+ DBUG_RETURN(error);
pthread_mutex_lock(&LOCK_log);
@@ -3976,8 +4064,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
DBUG_PRINT("info", ("Using trans_log: cache: %d, trans_log_pos: %lu",
event_info->get_cache_stmt(),
(ulong) trans_log_pos));
- if (trans_log_pos == 0)
- thd->binlog_start_trans_and_stmt();
+ thd->binlog_start_trans_and_stmt();
file= trans_log;
}
/*
@@ -4055,7 +4142,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
Write the SQL command
*/
- if (event_info->write(file))
+ if (event_info->write(file) ||
+ DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
goto err;
if (file == &log_file) // we are writing to the real log (disk)
@@ -4071,10 +4159,11 @@ err:
if (error)
{
if (my_errno == EFBIG)
- my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(0));
+ my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
else
- my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno);
- write_error=1;
+ my_error(ER_BINLOG_LOGGING_IMPOSSIBLE, MYF(MY_WME),
+ "Error writing events");
+ write_error= 1;
}
}
=== modified file 'sql/log.h'
--- a/sql/log.h 2009-01-23 12:22:05 +0000
+++ b/sql/log.h 2009-05-29 10:20:18 +0000
@@ -359,6 +359,8 @@ public:
bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
+ void set_write_error(THD *thd);
+ bool check_write_error(THD *thd);
void start_union_events(THD *thd, query_id_t query_id_param);
void stop_union_events(THD *thd);
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc 2009-05-15 13:25:29 +0000
+++ b/sql/sql_delete.cc 2009-05-29 10:20:18 +0000
@@ -404,7 +404,7 @@ cleanup:
thd->query, thd->query_length,
is_trans, FALSE, killed_status);
- if (log_result && transactional_table)
+ if (log_result)
{
error=1;
}
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2009-05-15 12:57:51 +0000
+++ b/sql/sql_insert.cc 2009-05-29 10:20:18 +0000
@@ -893,8 +893,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query, thd->query_length,
transactional_table, FALSE,
- (error>0) ? thd->killed : THD::NOT_KILLED) &&
- transactional_table)
+ (error>0) ? thd->killed : THD::NOT_KILLED))
{
error=1;
}
=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc 2009-05-15 13:03:22 +0000
+++ b/sql/sql_update.cc 2009-05-29 10:20:18 +0000
@@ -799,8 +799,7 @@ int mysql_update(THD *thd,
thd->clear_error();
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query, thd->query_length,
- transactional_table, FALSE, killed_status) &&
- transactional_table)
+ transactional_table, FALSE, killed_status))
{
error=1; // Rollback update
}
@@ -2079,8 +2078,7 @@ bool multi_update::send_eof()
thd->clear_error();
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query, thd->query_length,
- transactional_tables, FALSE, killed_status) &&
- trans_safe)
+ transactional_tables, FALSE, killed_status))
{
local_error= 1; // Rollback update
}
Attachment: [text/bzr-bundle]
| Thread |
|---|
| • bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929 | Alfranio Correia | 29 May |