From: Li-Bing.Song Date: January 30 2010 7:15pm Subject: bzr commit into mysql-5.5-trunk-bugfixing branch (Li-Bing.Song:2932) Bug#50157 List-Archive: http://lists.mysql.com/commits/98733 X-Bug: 50157 Message-Id: <201001301916.o0UJGdDL005125@anders-server> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============6980007049565000752==" --===============6980007049565000752== 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-trunk-bugfixing/ based on revid:alik@stripped 2932 Li-Bing.Song@stripped 2010-01-31 [merge] Auto Merge fix for bug#50157 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 08:05:35 +0000 +++ b/sql/rpl_handler.cc 2010-01-30 19:14:29 +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 --===============6980007049565000752== 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-trunk-\ # bugfixing/ # testament_sha1: 9383b8d6a332cd178ea67dd9133eb11ccb851a95 # timestamp: 2010-01-31 03:15:39 +0800 # source_branch: file:///home/anders/work/bzrroot/mysql-5.1-rep-\ # semisync/ # base_revision_id: alik@stripped # # Begin bundle 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 IpwoSFTqjZqA --===============6980007049565000752==--