From: Li-Bing.Song Date: January 30 2010 6:26pm Subject: bzr commit into mysql-5.1-rep-semisync branch (Li-Bing.Song:3127) Bug#50157 List-Archive: http://lists.mysql.com/commits/98725 X-Bug: 50157 Message-Id: <201001301828.o0UIS12T028221@anders-server> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============3187931754114650525==" --===============3187931754114650525== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/anders/work/bzrroot/mysql-5.1-rep-semisync/ based on revid:zhenxing.he@stripped 3127 Li-Bing.Song@stripped 2010-01-31 BUG#50157 Assertion !active_tranxs_->is_tranx_end_pos(..) in ReplSemiSyncMaster::commitTrx The root cause of the crash is that a TranxNode is freed before it is used. A TranxNode is allocated and inserted into the active list each time a log event is written and flushed into the binlog file. The memory for TranxNode is allocated with thd_alloc and will be freed at the end of the statement. The after_commit/after_rollback callback was supposed to be called before the end of each statement and remove the node from the active list. However this assumption is not correct in all cases(e.g. call 'CREATE TEMPORARY TABLE myisam_t SELECT * FROM innodb_t' in a transaction and delete all temporary tables automatically when a session closed), and can cause the memory allocated for TranxNode be freed before it was removed from the active list. So The TranxNode pointer in the active list would become a wild pointer and cause the crash. After this patch, We have a class called a TranxNodeAllocate which manages the memory for allocating and freeing TranxNode. It uses my_malloc to allocate memory. @ sql/rpl_handler.cc params are not initialized. modified: mysql-test/suite/rpl/r/rpl_semi_sync.result mysql-test/suite/rpl/t/rpl_semi_sync.test plugin/semisync/semisync_master.cc plugin/semisync/semisync_master.h sql/rpl_handler.cc === modified file 'mysql-test/suite/rpl/r/rpl_semi_sync.result' --- a/mysql-test/suite/rpl/r/rpl_semi_sync.result 2009-10-23 04:56:30 +0000 +++ b/mysql-test/suite/rpl/r/rpl_semi_sync.result 2010-01-30 18:26:51 +0000 @@ -120,8 +120,27 @@ min(a) select max(a) from t1; max(a) 300 + +# BUG#50157 +# semi-sync replication crashes when replicating a transaction which +# include 'CREATE TEMPORARY TABLE `MyISAM_t` SELECT * FROM `Innodb_t` ; +[ on master ] +SET SESSION AUTOCOMMIT= 0; +CREATE TABLE t2(c1 INT) ENGINE=innodb; +BEGIN; + +# Even though it is in a transaction, this statement is binlogged into binlog +# file immediately. +CREATE TEMPORARY TABLE t3 SELECT c1 FROM t2 where 1=1; + +# These statements will not be binlogged until the transaction is committed +INSERT INTO t2 VALUES(11); +INSERT INTO t2 VALUES(22); +COMMIT; +DROP TABLE t2, t3; +SET SESSION AUTOCOMMIT= 1; # -# Test semi-sync master will switch OFF after one transacton +# Test semi-sync master will switch OFF after one transaction # timeout waiting for slave reply. # include/stop_slave.inc @@ -135,7 +154,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 0 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 1 @@ -150,7 +169,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 1 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 insert into t1 values (100); [ master status should be OFF ] show status like 'Rpl_semi_sync_master_status'; @@ -161,7 +180,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 # # Test semi-sync status on master will be ON again when slave catches up # @@ -194,7 +213,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 301 +Rpl_semi_sync_master_yes_tx 304 show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 1 @@ -213,7 +232,7 @@ Variable_name Value Rpl_semi_sync_master_no_tx 302 SHOW STATUS LIKE 'Rpl_semi_sync_master_yes_tx'; Variable_name Value -Rpl_semi_sync_master_yes_tx 302 +Rpl_semi_sync_master_yes_tx 305 FLUSH NO_WRITE_TO_BINLOG STATUS; [ Semi-sync master status variables after FLUSH STATUS ] SHOW STATUS LIKE 'Rpl_semi_sync_master_no_tx'; === modified file 'mysql-test/suite/rpl/t/rpl_semi_sync.test' --- a/mysql-test/suite/rpl/t/rpl_semi_sync.test 2009-10-23 04:56:30 +0000 +++ b/mysql-test/suite/rpl/t/rpl_semi_sync.test 2010-01-30 18:26:51 +0000 @@ -11,6 +11,7 @@ disable_query_log; connection master; call mtr.add_suppression("Timeout waiting for reply of binlog"); call mtr.add_suppression("Read semi-sync reply"); +call mtr.add_suppression("Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT."); connection slave; call mtr.add_suppression("Master server does not support semi-sync"); call mtr.add_suppression("Semi-sync slave .* reply"); @@ -193,8 +194,38 @@ select count(distinct a) from t1; select min(a) from t1; select max(a) from t1; +--echo +--echo # BUG#50157 +--echo # semi-sync replication crashes when replicating a transaction which +--echo # include 'CREATE TEMPORARY TABLE `MyISAM_t` SELECT * FROM `Innodb_t` ; + +connection master; +echo [ on master ]; +SET SESSION AUTOCOMMIT= 0; +CREATE TABLE t2(c1 INT) ENGINE=innodb; +sync_slave_with_master; + +connection master; +BEGIN; +--echo +--echo # Even though it is in a transaction, this statement is binlogged into binlog +--echo # file immediately. +--disable_warnings +CREATE TEMPORARY TABLE t3 SELECT c1 FROM t2 where 1=1; +--enable_warnings +--echo +--echo # These statements will not be binlogged until the transaction is committed +INSERT INTO t2 VALUES(11); +INSERT INTO t2 VALUES(22); +COMMIT; + +DROP TABLE t2, t3; +SET SESSION AUTOCOMMIT= 1; +sync_slave_with_master; + + --echo # ---echo # Test semi-sync master will switch OFF after one transacton +--echo # Test semi-sync master will switch OFF after one transaction --echo # timeout waiting for slave reply. --echo # connection slave; === modified file 'plugin/semisync/semisync_master.cc' --- a/plugin/semisync/semisync_master.cc 2009-12-04 05:43:38 +0000 +++ b/plugin/semisync/semisync_master.cc 2010-01-30 18:26:51 +0000 @@ -65,7 +65,7 @@ static int gettimeofday(struct timeval * ActiveTranx::ActiveTranx(pthread_mutex_t *lock, unsigned long trace_level) - : Trace(trace_level), + : Trace(trace_level), allocator_(max_connections), num_entries_(max_connections << 1), /* Transaction hash table size * is set to double the size * of max_connections */ @@ -115,25 +115,6 @@ unsigned int ActiveTranx::get_hash_value return (hash1 + hash2) % num_entries_; } -ActiveTranx::TranxNode* ActiveTranx::alloc_tranx_node() -{ - MYSQL_THD thd= (MYSQL_THD)current_thd; - /* The memory allocated for TranxNode will be automatically freed at - the end of the command of current THD. And because - ha_autocommit_or_rollback() will always be called before that, so - we are sure that the node will be removed from the active list - before it get freed. */ - TranxNode *trx_node = (TranxNode *)thd_alloc(thd, sizeof(TranxNode)); - if (trx_node) - { - trx_node->log_name_[0] = '\0'; - trx_node->log_pos_= 0; - trx_node->next_= 0; - trx_node->hash_next_= 0; - } - return trx_node; -} - int ActiveTranx::compare(const char *log_file_name1, my_off_t log_file_pos1, const char *log_file_name2, my_off_t log_file_pos2) { @@ -159,7 +140,7 @@ int ActiveTranx::insert_tranx_node(const function_enter(kWho); - ins_node = alloc_tranx_node(); + ins_node = allocator_.allocate_node(); if (!ins_node) { sql_print_error("%s: transaction node allocation failed for: (%s, %lu)", @@ -271,6 +252,7 @@ int ActiveTranx::clear_active_tranx_node /* Clear the hash table. */ memset(trx_htb_, 0, num_entries_ * sizeof(TranxNode *)); + allocator_.free_all_nodes(); /* Clear the active transaction list. */ if (trx_front_ != NULL) @@ -311,6 +293,7 @@ int ActiveTranx::clear_active_tranx_node } trx_front_ = new_front; + allocator_.free_nodes_before(trx_front_); if (trace_level_ & kTraceDetail) sql_print_information("%s: cleared %d nodes back until pos (%s, %lu)", === modified file 'plugin/semisync/semisync_master.h' --- a/plugin/semisync/semisync_master.h 2009-12-04 01:46:33 +0000 +++ b/plugin/semisync/semisync_master.h 2010-01-30 18:26:51 +0000 @@ -20,6 +20,267 @@ #include "semisync.h" +struct TranxNode { + char log_name_[FN_REFLEN]; + my_off_t log_pos_; + struct TranxNode *next_; /* the next node in the sorted list */ + struct TranxNode *hash_next_; /* the next node during hash collision */ +}; + +/** + @class TranxNodeAllocator + + This class provides memory allocating and freeing methods for + TranxNode. The main target is performance. + + @section ALLOCATE How to allocate a node + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + The list starts up empty (ie, there is no allocated Block). + + After some nodes are freed, there probably are some free nodes before + the sequence of the allocated nodes, but we do not reuse it. It is better + to keep the allocated nodes are in the sequence, for it is more efficient + for allocating and freeing TranxNode. + + @section FREENODE How to free nodes + There are two methods for freeing nodes. They are free_all_nodes and + free_nodes_before. + + 'A Block is free' means all of its nodes are free. + @subsection free_nodes_before + As all allocated nodes are in the sequence, 'Before one node' means all + nodes before given node in the same Block and all Blocks before the Block + which containing the given node. As such, all Blocks before the given one + ('node') are free Block and moved into the rear of the Block link table. + The Block containing the given 'node', however, is not. For at least the + given 'node' is still in use. This will waste at most one Block, but it is + more efficient. + */ +#define BLOCK_TRANX_NODES 16 +class TranxNodeAllocator +{ +public: + /** + @param reserved_nodes + The number of reserved TranxNodes. It is used to set 'reserved_blocks' + which can contain at least 'reserved_nodes' number of TranxNodes. When + freeing memory, we will reserve at least reserved_blocks of Blocks not + freed. + */ + TranxNodeAllocator(uint reserved_nodes) : + reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES + + (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)), + first_block(NULL), last_block(NULL), + current_block(NULL), last_node(-1), block_num(0) {} + + ~TranxNodeAllocator() + { + Block *block= first_block; + while (block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + } + + /** + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + @return Return a TranxNode *, or NULL if an error occured. + */ + TranxNode *allocate_node() + { + TranxNode *trx_node; + Block *block= current_block; + + if (last_node == BLOCK_TRANX_NODES-1) + { + current_block= current_block->next; + last_node= -1; + } + + if (current_block == NULL && allocate_block()) + { + current_block= block; + if (current_block) + last_node= BLOCK_TRANX_NODES-1; + return NULL; + } + + trx_node= &(current_block->nodes[++last_node]); + trx_node->log_name_[0] = '\0'; + trx_node->log_pos_= 0; + trx_node->next_= 0; + trx_node->hash_next_= 0; + return trx_node; + } + + /** + All nodes are freed. + + @return Return 0, or 1 if an error occured. + */ + int free_all_nodes() + { + current_block= first_block; + last_node= -1; + free_blocks(); + return 0; + } + + /** + All Blocks before the given 'node' are free Block and moved into the rear + of the Block link table. + + @param node All nodes before 'node' will be freed + + @return Return 0, or 1 if an error occured. + */ + int free_nodes_before(TranxNode* node) + { + Block *block; + Block *prev_block; + + block= first_block; + while (block != current_block->next) + { + /* Find the Block containing the given node */ + if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) + { + /* All Blocks before the given node are put into the rear */ + if (first_block != block) + { + last_block->next= first_block; + first_block= block; + last_block= prev_block; + last_block->next= NULL; + free_blocks(); + } + return 0; + } + prev_block= block; + block= block->next; + } + + /* Node does not find should never happen */ + DBUG_ASSERT(0); + return 1; + } + +private: + uint reserved_blocks; + + /** + A sequence memory which contains BLOCK_TRANX_NODES TranxNodes. + + BLOCK_TRANX_NODES The number of TranxNodes which are in a Block. + + next Every Block has a 'next' pointer which points to the next Block. + These linking Blocks constitute a Block link table. + */ + struct Block { + Block *next; + TranxNode nodes[BLOCK_TRANX_NODES]; + }; + + /** + The 'first_block' is the head of the Block link table; + */ + Block *first_block; + /** + The 'last_block' is the rear of the Block link table; + */ + Block *last_block; + + /** + current_block always points the Block in the Block link table in + which the last allocated node is. The Blocks before it are all in use + and the Blocks after it are all free. + */ + Block *current_block; + + /** + It always points to the last node which has been allocated in the + current_block. + */ + int last_node; + + /** + How many Blocks are in the Block link table. + */ + uint block_num; + + /** + Allocate a block and then assign it to current_block. + */ + int allocate_block() + { + Block *block= (Block *)my_malloc(sizeof(Block), MYF(0)); + if (block) + { + block->next= NULL; + + if (first_block == NULL) + first_block= block; + else + last_block->next= block; + + /* New Block is always put into the rear */ + last_block= block; + /* New Block is always the current_block */ + current_block= block; + ++block_num; + return 0; + } + return 1; + } + + /** + Free a given Block. + @param block The Block will be freed. + */ + void free_block(Block *block) + { + my_free(block, MYF(0)); + --block_num; + } + + + /** + If there are some free Blocks and the total number of the Blocks in the + Block link table is larger than the 'reserved_blocks', Some free Blocks + will be freed until the total number of the Blocks is equal to the + 'reserved_blocks' or there is only one free Block behind the + 'current_block'. + */ + void free_blocks() + { + if (current_block == NULL || current_block->next == NULL) + return; + + /* One free Block is always kept behind the current block */ + Block *block= current_block->next->next; + while (block_num > reserved_blocks && block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + current_block->next->next= block; + if (block == NULL) + last_block= current_block->next; + } +}; + + /** This class manages memory for active transaction list. @@ -31,13 +292,8 @@ class ActiveTranx :public Trace { private: - struct TranxNode { - char log_name_[FN_REFLEN]; - my_off_t log_pos_; - struct TranxNode *next_; /* the next node in the sorted list */ - struct TranxNode *hash_next_; /* the next node during hash collision */ - }; + TranxNodeAllocator allocator_; /* These two record the active transaction list in sort order. */ TranxNode *trx_front_, *trx_rear_; @@ -48,24 +304,22 @@ private: inline void assert_lock_owner(); - inline TranxNode* alloc_tranx_node(); - inline unsigned int calc_hash(const unsigned char *key,unsigned int length); unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos); int compare(const char *log_file_name1, my_off_t log_file_pos1, - const TranxNode *node2) { + const TranxNode *node2) { return compare(log_file_name1, log_file_pos1, - node2->log_name_, node2->log_pos_); + node2->log_name_, node2->log_pos_); } int compare(const TranxNode *node1, - const char *log_file_name2, my_off_t log_file_pos2) { + const char *log_file_name2, my_off_t log_file_pos2) { return compare(node1->log_name_, node1->log_pos_, - log_file_name2, log_file_pos2); + log_file_name2, log_file_pos2); } int compare(const TranxNode *node1, const TranxNode *node2) { return compare(node1->log_name_, node1->log_pos_, - node2->log_name_, node2->log_pos_); + node2->log_name_, node2->log_pos_); } public: @@ -88,7 +342,7 @@ public: * 0: success; non-zero: error */ int clear_active_tranx_nodes(const char *log_file_name, - my_off_t log_file_pos); + my_off_t log_file_pos); /* Given a position, check to see whether the position is an active * transaction's ending position by probing the hash table. @@ -99,7 +353,7 @@ public: * (file_name, file_position). */ static int compare(const char *log_file_name1, my_off_t log_file_pos1, - const char *log_file_name2, my_off_t log_file_pos2); + const char *log_file_name2, my_off_t log_file_pos2); }; === modified file 'sql/rpl_handler.cc' --- a/sql/rpl_handler.cc 2009-12-04 01:46:33 +0000 +++ b/sql/rpl_handler.cc 2010-01-30 18:26:51 +0000 @@ -190,8 +190,8 @@ int Trans_delegate::after_commit(THD *th { Trans_param param; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - if (is_real_trans) - param.flags |= TRANS_IS_REAL_TRANS; + + param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; Trans_binlog_info *log_info= my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); @@ -218,8 +218,8 @@ int Trans_delegate::after_rollback(THD * { Trans_param param; bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - if (is_real_trans) - param.flags |= TRANS_IS_REAL_TRANS; + + param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; Trans_binlog_info *log_info= my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO); @@ -228,7 +228,7 @@ int Trans_delegate::after_rollback(THD * param.log_pos= log_info ? log_info->log_pos : 0; int ret= 0; - FOREACH_OBSERVER(ret, after_commit, thd, (¶m)); + FOREACH_OBSERVER(ret, after_rollback, thd, (¶m)); /* This is the end of a real transaction or autocommit statement, we --===============3187931754114650525== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/li-bing.song@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: li-bing.song@stripped # target_branch: file:///home/anders/work/bzrroot/mysql-5.1-rep-\ # semisync/ # testament_sha1: 0b2cf248d873c9dad6b9972092e1ba744f4eca2d # timestamp: 2010-01-31 02:27:01 +0800 # base_revision_id: zhenxing.he@stripped\ # a4nbo9003sb8j9gu # # Begin bundle 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 yRThQkBmkqIo --===============3187931754114650525==--