List:Commits« Previous MessageNext Message »
From:daogang.qu Date:January 10 2011 5:18am
Subject:bzr commit into mysql-trunk branch (daogang.qu:3208) WL#5493
View as plain text  
#At file:///home/daogang/bzrwork/wl5493/mysql-trunk/ based on revid:alexander.nozdrin@stripped

 3208 daogang.qu@stripped	2011-01-10
      WL#5493 Binlog crash-safe when master crashed
      
      Trim the crashed binlog file to last valid transaction or event
      (non-transaction) base on binlog size(valid_pos) when MYSQL server
      crashed in the middle of binlog. And add a temp file to guarantee
      the binlog index file to be crash safe.
      
      WL#5440 Test binlog and replication when master crashed
      
      Test whether the transaction is binlogged partly and the replication
      works fine or not when the master crashed.
     @ mysql-test/r/crash_commit_before.result
        Removed the result file. Because its test file is removed.
     @ mysql-test/suite/binlog/r/binlog_index.result
        Test result for WL#5493
     @ mysql-test/suite/binlog/r/rpl_crash_safe_master_checksum.result
        Test result for WL#5493.
     @ mysql-test/suite/binlog/t/binlog_delete_and_flush_index.test
        Updated for WL#5493. The index_file will be renamed
        from binlog_crash_safe_index_file after every appending
        and purging to the index_file. So we need change its
        mode for LOAD_FILE every time.
     @ mysql-test/suite/binlog/t/binlog_index.test
        Added Test cases to verify that the index file will be crash safe.
     @ mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum.test
        Added test to verify if the crashed binlog file is trimmed to
        last valid transaction or event(non-transaction) correctly.
     @ mysql-test/suite/rpl/r/rpl_crash_safe_master.result
        Test result of WL#5493 and WL#5440.
     @ mysql-test/suite/rpl/t/rpl_crash_safe_master.test
        Added test to verify if the crashed binlog file is trimmed to
        last valid transaction or event(non-transaction) correctly.
     @ mysql-test/t/crash_commit_before-master.opt
        Removed the opt file. Because its test file is removed.
     @ mysql-test/t/crash_commit_before.test
        Removed the test file. Because the test case is tested
        in rpl_crashed_master.test file.
     @ mysys/my_rename.c
        Cleared obsolete code. Because all non-windows platforms will support
        the "rename()" POSIX interface.
     @ sql/binlog.cc
        Added code to trim the crashed binlog file to last valid transaction
        or event(non-transaction) base on valid_pos, make binlog index to be
        crash safe.
     @ sql/binlog.h
        Added code to define these functions for binlog index crash safe.
     @ sql/log_event.cc
        Updated code to remove sql_print_error() line, because its
        direct caller will invoke sql_print_error() to print error.

    removed:
      mysql-test/r/crash_commit_before.result
      mysql-test/t/crash_commit_before-master.opt
      mysql-test/t/crash_commit_before.test
    added:
      mysql-test/suite/binlog/r/rpl_crash_safe_master_checksum.result
      mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum-master.opt
      mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum.test
      mysql-test/suite/rpl/r/rpl_crash_safe_master.result
      mysql-test/suite/rpl/t/rpl_crash_safe_master.test
    modified:
      mysql-test/suite/binlog/r/binlog_index.result
      mysql-test/suite/binlog/t/binlog_delete_and_flush_index.test
      mysql-test/suite/binlog/t/binlog_index.test
      mysys/my_rename.c
      sql/binlog.cc
      sql/binlog.h
      sql/log_event.cc
=== removed file 'mysql-test/r/crash_commit_before.result'
--- a/mysql-test/r/crash_commit_before.result	2007-04-03 09:36:33 +0000
+++ b/mysql-test/r/crash_commit_before.result	1970-01-01 00:00:00 +0000
@@ -1,14 +0,0 @@
-CREATE TABLE t1(a int) engine=innodb;
-START TRANSACTION;
-insert into t1 values(9);
-SET SESSION debug="d,crash_commit_before";
-COMMIT;
-ERROR HY000: Lost connection to MySQL server during query
-SHOW CREATE TABLE t1;
-Table	Create Table
-t1	CREATE TABLE `t1` (
-  `a` int(11) DEFAULT NULL
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-SELECT * FROM t1;
-a
-DROP TABLE t1;

=== modified file 'mysql-test/suite/binlog/r/binlog_index.result'
--- a/mysql-test/suite/binlog/r/binlog_index.result	2009-12-17 17:10:18 +0000
+++ b/mysql-test/suite/binlog/r/binlog_index.result	2011-01-10 05:18:21 +0000
@@ -147,5 +147,147 @@ master-bin.000011
 master-bin.000012
 master-bin.000013
 
+# Test case6: Set DEBUG POINT before rename index file when
+#             appending a binlog file name to index file.
+show binary logs;
+Log_name	File_size
+master-bin.000006	#
+master-bin.000007	#
+master-bin.000008	#
+master-bin.000009	#
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+SET SESSION debug="+d,crash_create_before_rename_index_file";
+flush logs;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test if the index file has the correct data,
+# i.e. binlog file name is added.
+show binary logs;
+Log_name	File_size
+master-bin.000006	#
+master-bin.000007	#
+master-bin.000008	#
+master-bin.000009	#
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+# Test case7: Set DEBUG POINT after rename index file when
+#             appending a binlog file name to index file.
+SET SESSION debug="+d,crash_create_after_rename_index_file";
+flush logs;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test if the index file has the correct data,
+# i.e. binlog file name is added.
+show binary logs;
+Log_name	File_size
+master-bin.000006	#
+master-bin.000007	#
+master-bin.000008	#
+master-bin.000009	#
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+# Test case8: Set DEBUG POINT after rename index file when
+#             purging the index file.
+show binary logs;
+Log_name	File_size
+master-bin.000006	#
+master-bin.000007	#
+master-bin.000008	#
+master-bin.000009	#
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+SET SESSION debug="+d,crash_create_after_rename_index_file";
+purge binary logs TO 'master-bin.000010';
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test if the index file has the correct data,
+# i.e. requested binlog file names are removed.
+show binary logs;
+Log_name	File_size
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+master-bin.000018	#
+# Test case9: Set DEBUG POINT befor rename index file when
+#             purging the index file.
+show binary logs;
+Log_name	File_size
+master-bin.000010	#
+master-bin.000011	#
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+master-bin.000018	#
+SET SESSION debug="+d,crash_create_before_rename_index_file";
+purge binary logs TO 'master-bin.000012';
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test if the index file has the correct data,
+# i.e. requested binlog file names are removed.
+show binary logs;
+Log_name	File_size
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+master-bin.000018	#
+master-bin.000019	#
+# Test case10: Inject a fault to copy part content to the temp file
+#              when purging the index file.
+show binary logs;
+Log_name	File_size
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+master-bin.000018	#
+master-bin.000019	#
+SET SESSION debug="+d,fault_injection_copy_part_file";
+purge binary logs TO 'master-bin.000014';
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the index file is complete, although is not purged successfully.
+show binary logs;
+Log_name	File_size
+master-bin.000012	#
+master-bin.000013	#
+master-bin.000014	#
+master-bin.000015	#
+master-bin.000016	#
+master-bin.000017	#
+master-bin.000018	#
+master-bin.000019	#
+master-bin.000020	#
 SET SESSION debug="";
 End of tests

=== added file 'mysql-test/suite/binlog/r/rpl_crash_safe_master_checksum.result'
--- a/mysql-test/suite/binlog/r/rpl_crash_safe_master_checksum.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/binlog/r/rpl_crash_safe_master_checksum.result	2011-01-10 05:18:21 +0000
@@ -0,0 +1,30 @@
+call mtr.add_suppression("Attempting backtrace");
+call mtr.add_suppression("allocated tablespace *., old maximum was 0");
+call mtr.add_suppression("Error in Log_event::read_log_event()");
+call mtr.add_suppression("Buffered warning: Performance schema disabled");
+RESET MASTER;
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+CREATE TABLE t2(a LONGBLOB) ENGINE=MYISAM;
+# Test case5: Inject wrong value of crc for a log event, and
+#             then set DBUG POINT to casue the master crash.
+INSERT INTO t2 (a) VALUES (REPEAT('a',1));
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',1));
+SET SESSION debug="d,fault_injection_crc_value";
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+COMMIT;
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',3));
+SET SESSION debug="d,crash_commit_after_prepare";
+COMMIT;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the transaction with a log event injected a wrong crc value
+# will be trimmed from the crashed binlog file
+show binlog events in 'master-bin.000001' 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: # (test.t2)
+master-bin.000001	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000001	#	Query	#	#	COMMIT
+DROP TABLE t1, t2;

=== modified file 'mysql-test/suite/binlog/t/binlog_delete_and_flush_index.test'
--- a/mysql-test/suite/binlog/t/binlog_delete_and_flush_index.test	2009-10-20 08:39:40 +0000
+++ b/mysql-test/suite/binlog/t/binlog_delete_and_flush_index.test	2011-01-10 05:18:21 +0000
@@ -77,6 +77,7 @@ FLUSH LOGS;
 -- remove_file $datadir/master-bin.000001
 
 -- echo ### assertion: index file contains renamed binlog and the new one
+-- chmod 0644 $index
 -- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
 -- eval SET @index=LOAD_FILE('$index')
 -- replace_regex /\.[\\\/]master/master/
@@ -106,6 +107,7 @@ DROP TABLE t1;
 -- file_exists $datadir/$current_binlog
 
 -- echo ### assertion: show index file contents and these should match show binary logs issued above
+-- chmod 0644 $index
 -- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
 -- eval SET @index=LOAD_FILE('$index')
 -- replace_regex /\.[\\\/]master/master/

=== modified file 'mysql-test/suite/binlog/t/binlog_index.test'
--- a/mysql-test/suite/binlog/t/binlog_index.test	2009-12-08 16:03:19 +0000
+++ b/mysql-test/suite/binlog/t/binlog_index.test	2011-01-10 05:18:21 +0000
@@ -230,6 +230,163 @@ flush logs;
 -- replace_regex /\.[\\\/]master/master/
 SELECT @index;
 
+#
+# WL#5493
+# Test case6 verifies if the index file has the correct data,
+# i.e. if binlog file name is added after the master restarts
+# when setting DEBUG POINT before renaming index file. 
+#
+# Test case7 verifies if the index file has the correct data,
+# i.e. if binlog file name is added after the master restarts
+# when setting DEBUG POINT after renaming index file.
+#
+# Test case8 verifies if the index file has the correct data,
+# i.e. if requested binlog file names are removed after the
+# master restarts when setting DEBUG POINT after purging index
+# file.
+#
+# Test case9 verifies if the index file has the correct data,
+# i.e. if requested binlog file names are removed after the
+# master restarts when setting DEBUG POINT before purging index
+# file.
+#
+# Test case10 verifies if the index file has the correct data,
+# i.e. although requested binlog file names are not removed
+# after the master restarts when injecting a fault to the
+# temp file in the process of purging the index file.
+#
+
+-- echo # Test case6: Set DEBUG POINT before rename index file when
+-- echo #             appending a binlog file name to index file.
+-- source include/show_binary_logs.inc
+file_exists $MYSQLD_DATADIR/master-bin.000013;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000014;
+
+# Write file to make mysql-test-run.pl expect crash and restart
+SET SESSION debug="+d,crash_create_before_rename_index_file";
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+--error 2013
+flush logs;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test if the index file has the correct data,
+-- echo # i.e. binlog file name is added.
+-- source include/show_binary_logs.inc
+file_exists $MYSQLD_DATADIR/master-bin.000014;
+file_exists $MYSQLD_DATADIR/master-bin.000015;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000016;
+
+-- echo # Test case7: Set DEBUG POINT after rename index file when
+-- echo #             appending a binlog file name to index file.
+# Write file to make mysql-test-run.pl expect crash and restart
+SET SESSION debug="+d,crash_create_after_rename_index_file";
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+-- error 2013
+flush logs;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test if the index file has the correct data,
+-- echo # i.e. binlog file name is added.
+-- source include/show_binary_logs.inc
+file_exists $MYSQLD_DATADIR/master-bin.000016;
+file_exists $MYSQLD_DATADIR/master-bin.000017;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000018;
+
+-- echo # Test case8: Set DEBUG POINT after rename index file when
+-- echo #             purging the index file.
+
+-- source include/show_binary_logs.inc
+# Write file to make mysql-test-run.pl expect crash and restart
+SET SESSION debug="+d,crash_create_after_rename_index_file";
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+-- error 2013
+purge binary logs TO 'master-bin.000010';
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test if the index file has the correct data,
+-- echo # i.e. requested binlog file names are removed.
+-- source include/show_binary_logs.inc
+-- error 1
+file_exists $MYSQLD_DATADIR/master-bin.000008;
+-- error 1
+file_exists $MYSQLD_DATADIR/master-bin.000009;
+file_exists $MYSQLD_DATADIR/master-bin.000010;
+
+-- echo # Test case9: Set DEBUG POINT befor rename index file when
+-- echo #             purging the index file.
+
+-- source include/show_binary_logs.inc
+# Write file to make mysql-test-run.pl expect crash and restart
+SET SESSION debug="+d,crash_create_before_rename_index_file";
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+-- error 2013
+purge binary logs TO 'master-bin.000012';
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test if the index file has the correct data,
+-- echo # i.e. requested binlog file names are removed.
+-- source include/show_binary_logs.inc
+-- error 1
+file_exists $MYSQLD_DATADIR/master-bin.000010;
+-- error 1
+file_exists $MYSQLD_DATADIR/master-bin.000011;
+file_exists $MYSQLD_DATADIR/master-bin.000012;
+
+-- echo # Test case10: Inject a fault to copy part content to the temp file
+-- echo #              when purging the index file.
+
+-- source include/show_binary_logs.inc
+# Write file to make mysql-test-run.pl expect crash and restart
+SET SESSION debug="+d,fault_injection_copy_part_file";
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+-- error 2013
+purge binary logs TO 'master-bin.000014';
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test the index file is complete, although is not purged successfully.
+-- source include/show_binary_logs.inc
+file_exists $MYSQLD_DATADIR/master-bin.000012;
+file_exists $MYSQLD_DATADIR/master-bin.000013;
+file_exists $MYSQLD_DATADIR/master-bin.000014;
+
 eval SET SESSION debug="$old";
 
 --echo End of tests

=== added file 'mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum-master.opt'
--- a/mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum-master.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum-master.opt	2011-01-10 05:18:21 +0000
@@ -0,0 +1 @@
+--binlog-checksum=CRC32 --master-verify-checksum=1

=== added file 'mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum.test'
--- a/mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/binlog/t/rpl_crash_safe_master_checksum.test	2011-01-10 05:18:21 +0000
@@ -0,0 +1,61 @@
+#
+# WL#5493 & WL#5440
+# Test case5 verifies if a transaction with a wrong crc value
+# will be trimmed from the crashed binlog file after
+# master restarts when injecting a wrong crc value
+# for a statment of the transaction and then setting
+# DEBUG POINT to cause master crash.
+#
+
+# Don't test this under valgrind, memory leaks will occur
+-- source include/not_valgrind.inc
+-- source include/not_embedded.inc
+-- source include/have_debug.inc
+-- source include/have_innodb.inc
+-- source include/have_binlog_format_row.inc
+
+call mtr.add_suppression("Attempting backtrace");
+call mtr.add_suppression("allocated tablespace *., old maximum was 0");
+call mtr.add_suppression("Error in Log_event::read_log_event()");
+call mtr.add_suppression("Buffered warning: Performance schema disabled");
+
+# Reset master
+RESET MASTER;
+
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+CREATE TABLE t2(a LONGBLOB) ENGINE=MYISAM;
+
+-- echo # Test case5: Inject wrong value of crc for a log event, and
+-- echo #             then set DBUG POINT to casue the master crash.
+# Write file to make mysql-test-run.pl expect crash and restart
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+-- let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+INSERT INTO t2 (a) VALUES (REPEAT('a',1));
+
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',1));
+SET SESSION debug="d,fault_injection_crc_value";
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+COMMIT;
+
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',3));
+SET SESSION debug="d,crash_commit_after_prepare";
+# Run the crashing query
+-- error 2013
+COMMIT;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test the transaction with a log event injected a wrong crc value
+-- echo # will be trimmed from the crashed binlog file
+-- source include/show_binlog_events.inc
+
+DROP TABLE t1, t2;
+

=== added file 'mysql-test/suite/rpl/r/rpl_crash_safe_master.result'
--- a/mysql-test/suite/rpl/r/rpl_crash_safe_master.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_crash_safe_master.result	2011-01-10 05:18:21 +0000
@@ -0,0 +1,128 @@
+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;
+include/stop_slave.inc
+RESET MASTER;
+include/start_slave.inc
+call mtr.add_suppression("Attempting backtrace");
+call mtr.add_suppression("allocated tablespace *., old maximum was 0");
+call mtr.add_suppression("Error in Log_event::read_log_event()");
+call mtr.add_suppression("Buffered warning: Performance schema disabled");
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+# Test case1: Set DEBUG POINT before binlog to make
+#             the master crash for transaction
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+SET SESSION debug="d,crash_commit_after_prepare";
+COMMIT;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the transaction statements will not be binlogged
+show binlog events in 'master-bin.000001' from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	#	#	use `test`; CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB
+# On master, test the data will be rolled back after restart.
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+# On slave, test replication will work fine, and the data 
+#           is not replicated
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+# Test case2: Set DEBUG POINT after binlog, and before the date
+#             is committed to make crash for transaction
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+SET SESSION debug="d,crash_commit_after_log";
+COMMIT;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the transaction statements will be binlogged
+show binlog events in 'master-bin.000002' from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000002	#	Query	#	#	BEGIN
+master-bin.000002	#	Table_map	#	#	table_id: # (test.t1)
+master-bin.000002	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000002	#	Table_map	#	#	table_id: # (test.t1)
+master-bin.000002	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000002	#	Table_map	#	#	table_id: # (test.t1)
+master-bin.000002	#	Write_rows	#	#	table_id: # flags: STMT_END_F
+master-bin.000002	#	Xid	#	#	COMMIT /* XID */
+# On master, test the data will be recovered after the master restart
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+3
+# On slave, test replication will work fine, and the data is replicated
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+3
+DROP TABLE t1;
+include/stop_slave.inc
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+# Test case3: Set DEBUG POINT in the middle of binlog to
+#             make the master crash for transaction.
+SET SESSION debug="d,half_binlogged_transaction";
+BEGIN;
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+COMMIT;
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the halfly binlogged transaction will be trimmed
+# from the crashed binlog file
+show binlog events in 'master-bin.000003' from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000003	#	Query	#	#	use `test`; CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB
+# Test the data will not be recovered successfully
+# after the master restart.
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+0
+# Test case4: Set DEBUG POINT in the middle of binlog to
+#             make the master crash for non-transaction.
+SET SESSION debug="d,half_binlogged_transaction";
+CREATE TABLE t2(a LONGBLOB) ENGINE=MYISAM;
+INSERT INTO t2 (a) VALUES (REPEAT('a',16384));
+ERROR HY000: Lost connection to MySQL server during query
+# Restart the master server
+# Test the halfly binlogged non-transaction statement will be trimmed
+# from the crashed binlog file
+show binlog events in 'master-bin.000004' from <binlog_start>;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000004	#	Query	#	#	use `test`; CREATE TABLE t2(a LONGBLOB) ENGINE=MYISAM
+# Test the data will not be recovered successfully
+# after the master restart.
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+0
+DROP TABLE t1, t2;

=== added file 'mysql-test/suite/rpl/t/rpl_crash_safe_master.test'
--- a/mysql-test/suite/rpl/t/rpl_crash_safe_master.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_crash_safe_master.test	2011-01-10 05:18:21 +0000
@@ -0,0 +1,199 @@
+#
+# WL#5493 & WL#5440
+# Test case1 verifies if the transaction statements will not be
+# binlogged and replication will work fine, but the data will
+# be rolled back on master after the master restarts when setting
+# DEBUG POINT before binlog to make the master crash.
+# 
+# Test case2 verifies if the transaction statements will be
+# binlogged and replication will work fine, and the data will
+# be recovered after the master restarts when setting DEBUG
+# POINT after binlog, and before the date is committed to make
+# the master crash.
+#
+# Test case3 verifies if the halfly binlogged transaction 
+# statements will be trimmed from the crashed binlog file
+# and the data will not be recovered successfully after
+# the master restarts when setting DEBUG POINT in the
+# middle of binlog to make the master crash
+#
+# Test case4 verifies if the halfly binlogged non-transaction
+# statement will be trimmed from the crashed binlog file
+# and the data will not be recovered successfully after
+# the master restarts when setting DEBUG POINT in the
+# middle of binlog to make the master crash.
+#
+
+# Don't test this under valgrind, memory leaks will occur
+-- source include/not_valgrind.inc
+-- source include/not_embedded.inc
+-- source include/master-slave.inc
+-- source include/have_debug.inc
+-- source include/have_innodb.inc
+-- source include/have_binlog_format_row.inc
+
+# Reset master
+connection slave;
+--source include/stop_slave.inc 
+--source include/wait_for_slave_to_stop.inc
+
+connection master;
+RESET MASTER;
+
+connection slave;
+--source include/start_slave.inc
+--source include/wait_for_slave_to_start.inc
+
+connection master;
+call mtr.add_suppression("Attempting backtrace");
+call mtr.add_suppression("allocated tablespace *., old maximum was 0");
+call mtr.add_suppression("Error in Log_event::read_log_event()");
+call mtr.add_suppression("Buffered warning: Performance schema disabled");
+
+-- let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+-- let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+
+-- echo # Test case1: Set DEBUG POINT before binlog to make
+-- echo #             the master crash for transaction
+#SET SESSION debug="d,crash_trans_commit_before_binlog";
+BEGIN;
+let $rows= 3;
+WHILE($rows)
+{
+  INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+  dec $rows;
+}
+# Write file to make mysql-test-run.pl expect crash and restart
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="d,crash_commit_after_prepare";
+# Run the crashing query
+-- error 2013
+COMMIT;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc 
+-- disable_reconnect
+
+-- echo # Test the transaction statements will not be binlogged
+-- source include/show_binlog_events.inc
+
+-- echo # On master, test the data will be rolled back after restart.
+SELECT COUNT(*) FROM t1;
+
+sync_slave_with_master;
+-- echo # On slave, test replication will work fine, and the data 
+-- echo #           is not replicated
+SELECT COUNT(*) FROM t1;
+
+connection master;
+-- let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+-- let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+-- echo # Test case2: Set DEBUG POINT after binlog, and before the date
+-- echo #             is committed to make crash for transaction
+#SET SESSION debug="d,crash_trans_commit_after_binlog";
+BEGIN;
+let $rows= 3;
+WHILE($rows)
+{
+  INSERT INTO t1 (a) VALUES (REPEAT('a',2));
+  dec $rows;
+}
+# Write file to make mysql-test-run.pl expect crash and restart
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="d,crash_commit_after_log";
+# Run the crashing query
+-- error 2013
+COMMIT;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test the transaction statements will be binlogged
+-- source include/show_binlog_events.inc
+
+-- echo # On master, test the data will be recovered after the master restart
+SELECT COUNT(*) FROM t1;
+
+sync_slave_with_master;
+-- echo # On slave, test replication will work fine, and the data is replicated
+SELECT COUNT(*) FROM t1;
+
+connection master;
+DROP TABLE t1;
+sync_slave_with_master;
+
+-- source include/stop_slave.inc
+
+connection master;
+-- let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+-- let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+# Test transaction with large data inserted
+CREATE TABLE t1(a LONGBLOB) ENGINE=INNODB;
+
+-- echo # Test case3: Set DEBUG POINT in the middle of binlog to
+-- echo #             make the master crash for transaction.
+SET SESSION debug="d,half_binlogged_transaction";
+BEGIN;
+let $rows= 24;
+WHILE($rows)
+{
+  INSERT INTO t1 (a) VALUES (REPEAT('a',6144));
+  dec $rows;
+}
+# Write file to make mysql-test-run.pl expect crash and restart
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+# Run the crashing query
+-- error 2013
+COMMIT;
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test the halfly binlogged transaction will be trimmed
+-- echo # from the crashed binlog file
+-- source include/show_binlog_events.inc
+
+-- echo # Test the data will not be recovered successfully
+-- echo # after the master restart.
+SELECT COUNT(*) FROM t1;
+
+-- echo # Test case4: Set DEBUG POINT in the middle of binlog to
+-- echo #             make the master crash for non-transaction.
+SET SESSION debug="d,half_binlogged_transaction";
+# Write file to make mysql-test-run.pl expect crash and restart
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+-- let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+CREATE TABLE t2(a LONGBLOB) ENGINE=MYISAM;
+-- error 2013
+INSERT INTO t2 (a) VALUES (REPEAT('a',16384));
+
+-- source include/wait_until_disconnected.inc
+-- enable_reconnect
+-- echo # Restart the master server
+-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+-- source include/wait_until_connected_again.inc
+-- disable_reconnect
+
+-- echo # Test the halfly binlogged non-transaction statement will be trimmed
+-- echo # from the crashed binlog file
+-- source include/show_binlog_events.inc
+
+-- echo # Test the data will not be recovered successfully
+-- echo # after the master restart.
+SELECT COUNT(*) FROM t2;
+
+DROP TABLE t1, t2;
+

=== removed file 'mysql-test/t/crash_commit_before-master.opt'
--- a/mysql-test/t/crash_commit_before-master.opt	2010-06-21 07:58:54 +0000
+++ b/mysql-test/t/crash_commit_before-master.opt	1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
---skip-stack-trace --skip-core-file
---default-storage-engine=MyISAM
---innodb-file-per-table=0

=== removed file 'mysql-test/t/crash_commit_before.test'
--- a/mysql-test/t/crash_commit_before.test	2007-12-12 17:19:24 +0000
+++ b/mysql-test/t/crash_commit_before.test	1970-01-01 00:00:00 +0000
@@ -1,35 +0,0 @@
--- source include/not_embedded.inc
-# Don't test this under valgrind, memory leaks will occur
---source include/not_valgrind.inc
-
-# Binary must be compiled with debug for crash to occur
---source include/have_debug.inc
-
---source include/have_innodb.inc
-
-CREATE TABLE t1(a int) engine=innodb;
-START TRANSACTION;
-insert into t1 values(9);
-
-# Setup the mysqld to crash at certain point
-SET SESSION debug="d,crash_commit_before";
-
-# Write file to make mysql-test-run.pl expect crash and restart
---exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
-
-# Run the crashing query
---error 2013
-COMMIT;
-
-# Turn on reconnect
---enable_reconnect
-
-# Call script that will poll the server waiting for it to be back online again
---source include/wait_until_connected_again.inc
-
-SHOW CREATE TABLE t1;
-
-SELECT * FROM t1;
-
-
-DROP TABLE t1;

=== modified file 'mysys/my_rename.c'
--- a/mysys/my_rename.c	2010-07-15 11:41:37 +0000
+++ b/mysys/my_rename.c	2011-01-10 05:18:21 +0000
@@ -48,16 +48,11 @@ int my_rename(const char *from, const ch
                            MOVEFILE_REPLACE_EXISTING))
   {
     my_osmaperr(GetLastError());
-    my_errno= errno;
 #else
-#if defined(HAVE_RENAME)
   if (rename(from,to))
-#else
-  if (link(from, to) || unlink(from))
-#endif
   {
-    my_errno=errno;
 #endif
+    my_errno=errno;
     error = -1;
     if (MyFlags & (MY_FAE+MY_WME))
       my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno);

=== modified file 'sql/binlog.cc'
--- a/sql/binlog.cc	2010-11-16 12:14:06 +0000
+++ b/sql/binlog.cc	2011-01-10 05:18:21 +0000
@@ -1138,58 +1138,51 @@ int query_error_code(THD *thd, bool not_
 
 
 /**
-  Move all data up in a file in an filename index file.
+  Copy content of 'from' file from offset to 'to' file.
 
-    We do the copy outside of the IO_CACHE as the cache buffers would just
-    make things slower and more complicated.
-    In most cases the copy loop should only do one read.
+  - We do the copy outside of the IO_CACHE as the cache
+  buffers would just make things slower and more complicated.
+  In most cases the copy loop should only do one read.
 
-  @param index_file			File to move
-  @param offset			Move everything from here to beginning
+  @param from          File to copy.
+  @param to            File to copy to.
+  @param offset        Offset in 'from' file.
 
-  @note
-    File will be truncated to be 'offset' shorter or filled up with newlines
 
   @retval
-    0	ok
+    0    ok
+  @retval
+    -1    error
 */
-
-#ifdef HAVE_REPLICATION
-
-static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
+static bool copy_file(IO_CACHE *from, IO_CACHE *to, my_off_t offset)
 {
   int bytes_read;
-  my_off_t init_offset= offset;
-  File file= index_file->file;
   uchar io_buf[IO_SIZE*2];
-  DBUG_ENTER("copy_up_file_and_fill");
+  DBUG_ENTER("copy_file");
 
-  for (;; offset+= bytes_read)
+  mysql_file_seek(from->file, offset, MY_SEEK_SET, MYF(0));
+  while(TRUE)
   {
-    mysql_file_seek(file, offset, MY_SEEK_SET, MYF(0));
-    if ((bytes_read= (int) mysql_file_read(file, io_buf, sizeof(io_buf),
+    if ((bytes_read= (int) mysql_file_read(from->file, io_buf, sizeof(io_buf),
                                            MYF(MY_WME)))
-	< 0)
+        < 0)
       goto err;
+    if (DBUG_EVALUATE_IF("fault_injection_copy_part_file", 1, 0))
+      bytes_read= bytes_read/2;
     if (!bytes_read)
-      break;					// end of file
-    mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
-    if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
+      break;                                    // end of file
+    if (mysql_file_write(to->file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
       goto err;
   }
-  /* The following will either truncate the file or fill the end with \n' */
-  if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
-      mysql_file_sync(file, MYF(MY_WME)))
-    goto err;
 
-  /* Reset data in old index cache */
-  reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
   DBUG_RETURN(0);
 
 err:
   DBUG_RETURN(1);
 }
 
+
+#ifdef HAVE_REPLICATION
 /**
    Load data's io cache specific hook to be executed
    before a chunk of data is being read into the cache's buffer
@@ -1443,6 +1436,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
   index_file_name[0] = 0;
   bzero((char*) &index_file, sizeof(index_file));
   bzero((char*) &purge_index_file, sizeof(purge_index_file));
+  bzero((char*) &crash_safe_index_file, sizeof(crash_safe_index_file));
 }
 
 /* this is called only once */
@@ -1504,6 +1498,27 @@ bool MYSQL_BIN_LOG::open_index_file(cons
   }
   fn_format(index_file_name, index_file_name_arg, mysql_data_home,
             ".index", opt);
+
+  if (set_crash_safe_index_file_name(index_file_name_arg))
+  {
+    sql_print_error("MYSQL_BIN_LOG::set_crash_safe_index_file_name failed.");
+    return TRUE;
+  }
+
+  /*
+    We need move crash_safe_index_file to index_file if the index_file
+    does not exist and crash_safe_index_file exists when mysqld server
+    restarts.
+  */
+  if (my_access(index_file_name, F_OK) &&
+      !my_access(crash_safe_index_file_name, F_OK) &&
+      my_rename(crash_safe_index_file_name, index_file_name, MYF(MY_WME)))
+  {
+    sql_print_error("MYSQL_BIN_LOG::open_index_file failed to "
+                    "move crash_safe_index_file to index file.");
+    return TRUE;
+  }
+
   if ((index_file_nr= mysql_file_open(key_file_binlog_index,
                                       index_file_name,
                                       O_RDWR | O_CREAT | O_BINARY,
@@ -1711,18 +1726,15 @@ bool MYSQL_BIN_LOG::open(const char *log
 #endif
 
       DBUG_ASSERT(my_b_inited(&index_file) != 0);
-      reinit_io_cache(&index_file, WRITE_CACHE,
-                      my_b_filelength(&index_file), 0, 0);
+
       /*
-        As this is a new log file, we write the file name to the index
-        file. As every time we write to the index file, we sync it.
+        The new log file name is appended into crash safe index file after
+        all the content of index file is copyed into the crash safe index
+        file. Then move the crash safe index file to index file.
       */
       if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
-          my_b_write(&index_file, (uchar*) log_file_name,
-                     strlen(log_file_name)) ||
-          my_b_write(&index_file, (uchar*) "\n", 1) ||
-          flush_io_cache(&index_file) ||
-          mysql_file_sync(index_file.file, MYF(MY_WME)))
+          add_log_to_index((uchar*) log_file_name, strlen(log_file_name),
+                           need_mutex))
         goto err;
 
 #ifdef HAVE_REPLICATION
@@ -1759,6 +1771,135 @@ shutdown the MySQL server and restart it
 }
 
 
+/**
+  Move crash safe index file to index file.
+
+  @param need_mutex    Set it to FALSE if its caller already has a
+                       lock on LOCK_index
+
+  @retval
+    0    ok
+  @retval
+    -1    error
+*/
+int MYSQL_BIN_LOG::move_crash_safe_index_file_to_index_file(bool need_mutex)
+{
+  int error= 0;
+  File fd= -1;
+  DBUG_ENTER("MYSQL_BIN_LOG::move_crash_safe_index_file_to_index_file");
+
+  if (need_mutex)
+    mysql_mutex_lock(&LOCK_index);
+  mysql_mutex_assert_owner(&LOCK_index);
+
+  if (my_b_inited(&index_file))
+  {
+    end_io_cache(&index_file);
+    if (mysql_file_close(index_file.file, MYF(0)) < 0)
+    {
+      error= -1;
+      sql_print_error("MYSQL_BIN_LOG::move_crash_safe_index_file_to_index_file "
+                      "failed to close the index file.");
+      goto err;
+    }
+    mysql_file_delete(key_file_binlog_index, index_file_name, MYF(MY_WME));
+  }
+
+  DBUG_EXECUTE_IF("crash_create_before_rename_index_file", DBUG_SUICIDE(););
+  if (my_rename(crash_safe_index_file_name, index_file_name, MYF(MY_WME)))
+  {
+    error= -1;
+    sql_print_error("MYSQL_BIN_LOG::move_crash_safe_index_file_to_index_file "
+                    "failed to move crash_safe_index_file to index file.");
+    goto err;
+  }
+  DBUG_EXECUTE_IF("crash_create_after_rename_index_file", DBUG_SUICIDE(););
+
+  if ((fd= mysql_file_open(key_file_binlog_index,
+                           index_file_name,
+                           O_RDWR | O_CREAT | O_BINARY,
+                           MYF(MY_WME))) < 0 ||
+           mysql_file_sync(fd, MYF(MY_WME)) ||
+           init_io_cache(&index_file, fd, IO_SIZE, READ_CACHE,
+                         mysql_file_seek(fd, 0L, MY_SEEK_END, MYF(0)),
+                                         0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+  {
+    error= -1;
+    sql_print_error("MYSQL_BIN_LOG::move_crash_safe_index_file_to_index_file "
+                      "failed to open the index file.");
+    goto err;
+  }
+
+err:
+  if (need_mutex)
+    mysql_mutex_unlock(&LOCK_index);
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Append log file name to index file.
+
+  - To make crash safe, we copy all the content of index file
+  to crash safe index file firstly and then append the log
+  file name to the crash safe index file. Finally move the
+  crash safe index file to index file.
+
+  @retval
+    0   ok
+  @retval
+    -1   error
+*/
+int MYSQL_BIN_LOG::add_log_to_index(uchar* log_file_name,
+                                    int name_len, bool need_mutex)
+{
+  DBUG_ENTER("MYSQL_BIN_LOG::add_log_to_index");
+
+  if (open_crash_safe_index_file())
+  {
+    sql_print_error("MYSQL_BIN_LOG::add_log_to_index failed to "
+                    "open the crash safe index file.");
+    goto err;
+  }
+
+  if (copy_file(&index_file, &crash_safe_index_file, 0))
+  {
+    sql_print_error("MYSQL_BIN_LOG::add_log_to_index failed to "
+                    "copy index file to crash safe index file.");
+    goto err;
+  }
+
+  if (my_b_write(&crash_safe_index_file, log_file_name, name_len) ||
+      my_b_write(&crash_safe_index_file, (uchar*) "\n", 1) ||
+      flush_io_cache(&crash_safe_index_file) ||
+      mysql_file_sync(crash_safe_index_file.file, MYF(MY_WME)))
+  {
+    sql_print_error("MYSQL_BIN_LOG::add_log_to_index failed to "
+                    "append log file name: %s, to crash "
+                    "safe index file.", log_file_name);
+    goto err;
+  }
+
+  if (close_crash_safe_index_file())
+  {
+    sql_print_error("MYSQL_BIN_LOG::add_log_to_index failed to "
+                    "close the crash safe index file.");
+    goto err;
+  }
+
+  if (move_crash_safe_index_file_to_index_file(need_mutex))
+  {
+    sql_print_error("MYSQL_BIN_LOG::add_log_to_index failed to "
+                    "move crash safe index file to index file.");
+    goto err;
+  }
+
+  DBUG_RETURN(0);
+
+err:
+  DBUG_RETURN(-1);
+}
+
 int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
 {
   mysql_mutex_lock(&LOCK_log);
@@ -2082,6 +2223,92 @@ err:
 
 
 /**
+  Set the name of crash safe index file.
+
+  @retval
+    0   ok
+  @retval
+    1   error
+*/
+int MYSQL_BIN_LOG::set_crash_safe_index_file_name(const char *base_file_name)
+{
+  int error= 0;
+  DBUG_ENTER("MYSQL_BIN_LOG::set_crash_safe_index_file_name");
+  if (fn_format(crash_safe_index_file_name, base_file_name, mysql_data_home,
+                ".index_crash_safe", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
+                                         MY_REPLACE_EXT)) == NULL)
+  {
+    error= 1;
+    sql_print_error("MYSQL_BIN_LOG::set_crash_safe_index_file_name failed "
+                    "to set file name.");
+  }
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Open a (new) crash safe index file.
+
+  @note
+    The crash safe index file is a special file
+    used for guaranteeing index file crash safe.
+  @retval
+    0   ok
+  @retval
+    1   error
+*/
+int MYSQL_BIN_LOG::open_crash_safe_index_file()
+{
+  int error= 0;
+  File file= -1;
+
+  DBUG_ENTER("MYSQL_BIN_LOG::open_crash_safe_index_file");
+
+  if (!my_b_inited(&crash_safe_index_file))
+  {
+    if ((file= my_open(crash_safe_index_file_name, O_RDWR | O_CREAT | O_BINARY,
+                       MYF(MY_WME | ME_WAITTANG))) < 0  ||
+        init_io_cache(&crash_safe_index_file, file, IO_SIZE, WRITE_CACHE,
+                      0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
+    {
+      error= 1;
+      sql_print_error("MYSQL_BIN_LOG::open_crash_safe_index_file failed "
+                      "to open temporary index file.");
+    }
+  }
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Close the crash safe index file.
+
+  @note
+    The crash safe file is just closed, is not deleted.
+    Because it is moved to index file later on.
+  @retval
+    0   ok
+  @retval
+    1   error
+*/
+int MYSQL_BIN_LOG::close_crash_safe_index_file()
+{
+  int error= 0;
+
+  DBUG_ENTER("MYSQL_BIN_LOG::close_crash_safe_index_file");
+
+  if (my_b_inited(&crash_safe_index_file))
+  {
+    end_io_cache(&crash_safe_index_file);
+    error= my_close(crash_safe_index_file.file, MYF(0));
+  }
+  bzero((char*) &crash_safe_index_file, sizeof(crash_safe_index_file));
+
+  DBUG_RETURN(error);
+}
+
+
+/**
   Delete relay log files prior to rli->group_relay_log_name
   (i.e. all logs which are not involved in a non-finished group
   (transaction)), remove them from the index file and start on next
@@ -2216,19 +2443,67 @@ err:
   DBUG_RETURN(error);
 }
 
+
 /**
-  Update log index_file.
-*/
+  Remove logs from index file.
 
-int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
+  - To make crash safe, we copy the content of index file
+  from index_file_start_offset recored in log_info to
+  crash safe index file firstly and then move the crash
+  safe index file to index file.
+
+  @param linfo                  Store here the found log file name and
+                                position to the NEXT log file name in
+                                the index file.
+
+  @param need_update_threads    If we want to update the log coordinates
+                                of all threads. False for relay logs,
+                                true otherwise.
+
+  @retval
+    0    ok
+  @retval
+    LOG_INFO_IO    Got IO error while reading/writing file
+*/
+int MYSQL_BIN_LOG::remove_logs_from_index(LOG_INFO* log_info, bool need_update_threads)
 {
-  if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset))
-    return LOG_INFO_IO;
+  if (open_crash_safe_index_file())
+  {
+    sql_print_error("MYSQL_BIN_LOG::remove_logs_from_index failed to "
+                    "open the crash safe index file.");
+    goto err;
+  }
+
+  if (copy_file(&index_file, &crash_safe_index_file,
+                log_info->index_file_start_offset))
+  {
+    sql_print_error("MYSQL_BIN_LOG::remove_logs_from_index failed to "
+                    "copy index file to crash safe index file.");
+    goto err;
+  }
+
+  if (close_crash_safe_index_file())
+  {
+    sql_print_error("MYSQL_BIN_LOG::remove_logs_from_index failed to "
+                    "close the crash safe index file.");
+    goto err;
+  }
+  DBUG_EXECUTE_IF("fault_injection_copy_part_file", DBUG_SUICIDE(););
+
+  if (move_crash_safe_index_file_to_index_file(FALSE))
+  {
+    sql_print_error("MYSQL_BIN_LOG::remove_logs_from_index failed to "
+                    "move crash safe index file to index file.");
+    goto err;
+  }
 
   // now update offsets in index file for running threads
   if (need_update_threads)
     adjust_linfo_offsets(log_info->index_file_start_offset);
   return 0;
+
+err:
+  return LOG_INFO_IO;
 }
 
 /**
@@ -2313,7 +2588,7 @@ int MYSQL_BIN_LOG::purge_logs(const char
   }
 
   /* We know how many files to delete. Update index file. */
-  if ((error=update_log_index(&log_info, need_update_threads)))
+  if ((error=remove_logs_from_index(&log_info, need_update_threads)))
   {
     sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
     goto err;
@@ -3386,6 +3661,9 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE
   if (do_checksum)
     crc= crc_0= my_checksum(0L, NULL, 0);
 
+  if (DBUG_EVALUATE_IF("fault_injection_crc_value", 1, 0))
+    crc= crc - 1;
+
   do
   {
     /*
@@ -3655,9 +3933,9 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_C
         goto err;
 
       bool synced= 0;
+      DBUG_EXECUTE_IF("half_binlogged_transaction", DBUG_SUICIDE(););
       if (flush_and_sync(&synced))
         goto err;
-      DBUG_EXECUTE_IF("half_binlogged_transaction", DBUG_SUICIDE(););
       if (cache->error)				// Error on read
       {
         sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno);
@@ -3920,6 +4198,9 @@ int MYSQL_BIN_LOG::open(const char *opt_
     Log_event  *ev=0;
     Format_description_log_event fdle(BINLOG_VERSION);
     char        log_name[FN_REFLEN];
+    my_off_t    valid_pos= 0;
+    my_off_t    binlog_size;
+    MY_STAT     s;
 
     if (! fdle.is_valid())
       goto err;
@@ -3941,13 +4222,17 @@ int MYSQL_BIN_LOG::open(const char *opt_
       goto err;
     }
 
+    my_stat(log_name, &s, MYF(0));
+    binlog_size= s.st_size;
+
     if ((ev= Log_event::read_log_event(&log, 0, &fdle,
                                        opt_master_verify_checksum)) &&
         ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
         ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
     {
       sql_print_information("Recovering after a crash using %s", opt_name);
-      error= recover(&log, (Format_description_log_event *)ev);
+      valid_pos= my_b_tell(&log);
+      error= recover(&log, (Format_description_log_event *)ev, &valid_pos);
     }
     else
       error=0;
@@ -3958,6 +4243,51 @@ int MYSQL_BIN_LOG::open(const char *opt_
 
     if (error)
       goto err;
+
+    /* Trim the crashed binlog file to last valid transaction
+      or event (non-transaction) base on valid_pos. */
+    if (valid_pos > 0)
+    {
+      if ((file= mysql_file_open(key_file_binlog, log_name,
+                                 O_RDWR | O_BINARY, MYF(MY_WME))) < 0)
+      {
+        sql_print_error("Failed to open the crashed binlog file "
+                        "when master server is recovering it.");
+        return -1;
+      }
+
+      /* Change binlog file size to valid_pos */
+      if (valid_pos < binlog_size)
+      {
+        if (my_chsize(file, valid_pos, 0, MYF(MY_WME)))
+        {
+          sql_print_error("Failed to trim the crashed binlog file "
+                          "when master server is recovering it.");
+          mysql_file_close(file, MYF(MY_WME));
+          return -1;
+        }
+        else
+        {
+          sql_print_information("Crashed binlog file %s size is %llu, "
+                                "but recovered up to %llu. Binlog trimmed to %llu bytes.",
+                                log_name, binlog_size, valid_pos, valid_pos);
+        }
+      }
+
+      /* Clear LOG_EVENT_BINLOG_IN_USE_F */
+      my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
+      uchar flags= 0;
+      if (mysql_file_pwrite(file, &flags, 1, offset, MYF(0)) != 1)
+      {
+        sql_print_error("Failed to clear LOG_EVENT_BINLOG_IN_USE_F "
+                        "for the crashed binlog file when master "
+                        "server is recovering it.");
+        mysql_file_close(file, MYF(MY_WME));
+        return -1;
+      }
+
+      mysql_file_close(file, MYF(MY_WME));
+    } //end if
   }
 
 err:
@@ -4007,11 +4337,32 @@ void MYSQL_BIN_LOG::unlog(ulong cookie,
   rotate_and_purge(0);     // as ::write() did not rotate
 }
 
-int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
+
+/**
+  MYSQLD server recovers from last crashed binlog.
+
+  @param log           IO_CACHE of the crashed binlog.
+  @param fdle          Format_description_log_event of the crashed binlog.
+  @param valid_pos     The position of the last valid transaction or
+                       event(non-transaction) of the crashed binlog.
+
+  @retval
+    0                  ok
+  @retval
+    1                  error
+*/
+int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle,
+                            my_off_t *valid_pos)
 {
   Log_event  *ev;
   HASH xids;
   MEM_ROOT mem_root;
+  my_off_t last_valid_pos= *valid_pos;
+  /*
+    The flag is used for handling the case that a transaction
+    is partially written to the binlog.
+  */
+  bool in_transaction= TRUE;
 
   if (! fdle->is_valid() ||
       my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
@@ -4020,14 +4371,33 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log
 
   init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
 
-  fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
-
-  while ((ev= Log_event::read_log_event(log, 0, fdle,
-                                        opt_master_verify_checksum))
+  while ((ev= Log_event::read_log_event(log, 0, fdle, TRUE))
          && ev->is_valid())
   {
-    if (ev->get_type_code() == XID_EVENT)
+    /*
+      Recorded valid position for the crashed binlog file
+      which contains incorrect events.
+    */
+    if (ev->get_type_code() == QUERY_EVENT &&
+        !strcmp(((Query_log_event*)ev)->query, "BEGIN"))
     {
+      in_transaction= TRUE;
+      *valid_pos= last_valid_pos;
+    }
+    last_valid_pos= my_b_tell(log);
+
+    sql_print_information("ev->get_type_code()== %d, last_valid_pos== %llu .", ev->get_type_code(), last_valid_pos);
+
+    if (ev->get_type_code() == QUERY_EVENT &&
+        !strcmp(((Query_log_event*)ev)->query, "COMMIT"))
+    {
+      DBUG_ASSERT(in_transaction == TRUE);
+      in_transaction= FALSE;
+    }
+    else if (ev->get_type_code() == XID_EVENT)
+    {
+      DBUG_ASSERT(in_transaction == TRUE);
+      in_transaction= FALSE;
       Xid_log_event *xev=(Xid_log_event *)ev;
       uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
                                       sizeof(xev->xid));
@@ -4037,6 +4407,13 @@ int MYSQL_BIN_LOG::recover(IO_CACHE *log
     delete ev;
   }
 
+  /*
+    Recorded valid position for the crashed binlog file
+    which did not contain incorrect events.
+  */
+  if (!log->error && !in_transaction)
+    *valid_pos= last_valid_pos;
+
   if (ha_recover(&xids))
     goto err2;
 

=== modified file 'sql/binlog.h'
--- a/sql/binlog.h	2010-10-25 19:02:24 +0000
+++ b/sql/binlog.h	2011-01-10 05:18:21 +0000
@@ -35,6 +35,12 @@ class MYSQL_BIN_LOG: public TC_LOG, priv
   IO_CACHE index_file;
   char index_file_name[FN_REFLEN];
   /*
+    crash_safe_index_file is temp file used for guaranteeing
+    index file crash safe when master server restarts.
+  */
+  IO_CACHE crash_safe_index_file;
+  char crash_safe_index_file_name[FN_REFLEN];
+  /*
     purge_file is a temp file used in purge_logs so that the index file
     can be updated before deleting files from disk, yielding better crash
     recovery. It is created on demand the first time purge_logs is called
@@ -153,7 +159,8 @@ public:
   void close();
   int log_xid(THD *thd, my_xid xid);
   void unlog(ulong cookie, my_xid xid);
-  int recover(IO_CACHE *log, Format_description_log_event *fdle);
+  int recover(IO_CACHE *log, Format_description_log_event *fdle,
+              my_off_t *valid_pos);
 #if !defined(MYSQL_CLIENT)
 
   int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
@@ -218,7 +225,7 @@ public:
 
   void make_log_name(char* buf, const char* log_ident);
   bool is_active(const char* log_file_name);
-  int update_log_index(LOG_INFO* linfo, bool need_update_threads);
+  int remove_logs_from_index(LOG_INFO* linfo, bool need_update_threads);
   void rotate_and_purge(uint flags);
   /**
      Flush binlog cache and synchronize to disk.
@@ -240,6 +247,11 @@ public:
                  ulonglong *decrease_log_space);
   int purge_logs_before_date(time_t purge_time);
   int purge_first_log(Relay_log_info* rli, bool included);
+  int set_crash_safe_index_file_name(const char *base_file_name);
+  int open_crash_safe_index_file();
+  int close_crash_safe_index_file();
+  int add_log_to_index(uchar* log_file_name, int name_len, bool need_mutex);
+  int move_crash_safe_index_file_to_index_file(bool need_mutex);
   int set_purge_index_file_name(const char *base_file_name);
   int open_purge_index_file(bool destroy);
   bool is_inited_purge_index_file();

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-11-23 23:10:22 +0000
+++ b/sql/log_event.cc	2011-01-10 05:18:21 +0000
@@ -1306,6 +1306,8 @@ err:
       enough to stop the SQL thread now ; as we are skipping the current event,
       going on with reading and successfully executing other events can
       only corrupt the slave's databases. So stop.
+      The file->error is also checked to record the position of
+      the last valid event when master server recovers.
     */
     file->error= -1;
   }
@@ -1378,20 +1380,15 @@ Log_event* Log_event::read_log_event(con
   if (crc_check &&
       event_checksum_test((uchar *) buf, event_len, alg))
   {
-#ifdef MYSQL_CLIENT
     *error= "Event crc check failed! Most likely there is event corruption.";
+#ifdef MYSQL_CLIENT
     if (force_opt)
     {
       ev= new Unknown_log_event(buf, description_event);
       DBUG_RETURN(ev);
     }
-    else
-      DBUG_RETURN(NULL);
-#else
-    *error= ER(ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE);
-    sql_print_error("%s", ER(ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE));
-    DBUG_RETURN(NULL);
 #endif
+    DBUG_RETURN(NULL);
   }
 
   if (event_type > description_event->number_of_event_types &&


Attachment: [text/bzr-bundle] bzr/daogang.qu@greatopensource.com-20110110051821-e9o2jxctlo2l65jk.bundle
Thread
bzr commit into mysql-trunk branch (daogang.qu:3208) WL#5493daogang.qu10 Jan