From: Li-Bing.Song Date: January 27 2010 7:49am Subject: bzr commit into mysql-5.1-rep-semisync branch (Li-Bing.Song:3127) Bug#50157 List-Archive: http://lists.mysql.com/commits/98279 X-Bug: 50157 Message-Id: <201001270750.o0R7oCMF016642@anders-server> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8451211494547313260==" --===============8451211494547313260== 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-27 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 insertted into the active list each time when some log events are written into binlog file and is flushed. The memory for TranxNode is allocted with thd_alloc and will be freed after at the end of the statement. The after_commit/after_rollback callback was supposed to be call 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. CREATE TEMPORARY ... SELECT), 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, The memory for TranxNode will be allocated by my_malloc. @ 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-27 07:48:44 +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-27 07:48:44 +0000 @@ -193,8 +193,36 @@ 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. +CREATE TEMPORARY TABLE t3 SELECT c1 FROM t2 where 1=1; +--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-27 07:48:44 +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-27 07:48:44 +0000 @@ -20,6 +20,196 @@ #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 */ +}; + +/* + TranxNodeAllocator provides memory allocating and freeing methods for + TranxNode. + + BLOCK_TRANX_NODES + The number of TranxNodes which are in a block. + Block + A sequence memory which contain BLOCK_TRANX_NODES TranxNodes. + The Block link table is composed of number of Blocks. + last_node + it always points to the last node in current_block which is in use. + current_block + Part of its nodes are free and others are used. The Blocks before it are + all in use and the blocks after it are all free. + allocate_node + The pointer of the first free node in current_block is returned to the + caller. 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. At first, there is no any Block in + the Block link table. A Block is allocated and is put into the Block link + table; + free_nodes_before(TranxNode *node) + All blocks before the block which owneres the node gaving by the caller are + put into the rear of the Block link table. The block containing + the node gaving by the caller is not move to the rear of the Block link + table, for some nodes of its are still in use. We only say a block is free + if all its node are free. This will waste at most one block, but it is more + efficient. + */ +#define BLOCK_TRANX_NODES 16 +class TranxNodeAllocator +{ +public: + /* + init_nodes + How many TranxNodes are usually need. + max_reserved_block_num + How many blocks are need to cantain the TranxNodes of init_nodes. + Some free Blocks will be freed if there are too many Blocks in the + Block link table. + */ + TranxNodeAllocator(uint init_nodes) : + max_reserved_block_num(init_nodes/BLOCK_TRANX_NODES + + (init_nodes % BLOCK_TRANX_NODES > 1 ? 2 : 1)), + last_node(-1), first_block(NULL), last_block(NULL), + current_block(NULL), block_num(0) {} + + ~TranxNodeAllocator() + { + Block *block= first_block; + while (block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + } + + 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; + } + + int free_all_nodes() + { + current_block= first_block; + last_node= -1; + free_blocks(); + return 0; + } + + int free_nodes_before(TranxNode* node) + { + Block *block; + Block *prev_block; + + block= first_block; + while (block != current_block->next) + { + if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) + { + 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 max_reserved_block_num; + int last_node; + + struct Block { + Block *next; + TranxNode nodes[BLOCK_TRANX_NODES]; + }; + Block *first_block; + Block *last_block; + Block *current_block; + uint block_num; + + 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 always is put at the rear */ + last_block= block; + /* New block always is the current_block */ + current_block= block; + ++block_num; + return 0; + } + return 1; + } + + void free_block(Block *block) + { + my_free(block, MYF(0)); + --block_num; + } + + 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 > max_reserved_block_num && block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + current_block->next= block; + if (block == NULL) + last_block= current_block; + } +}; + + /** This class manages memory for active transaction list. @@ -31,13 +221,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 +233,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 +271,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 +282,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-27 07:48:44 +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 --===============8451211494547313260== 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: f0c99e5f68e6298b608cd260b0f77274cfd40860 # timestamp: 2010-01-27 15:49:12 +0800 # base_revision_id: zhenxing.he@stripped\ # a4nbo9003sb8j9gu # # Begin bundle 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 EXhcGl+BuhoQoN8qJlWkEf8XckU4UJCZBWgf --===============8451211494547313260==--