List:Commits« Previous MessageNext Message »
From:Li-Bing.Song Date:January 22 2010 2:59pm
Subject:bzr commit into mysql-5.1-rep-semisync branch (Li-Bing.Song:3128) Bug#50157
View as plain text  
#At file:///home/anders/work/bzrroot/mysql-5.1-rep-semisync/ based on revid:li-bing.song@stripped

 3128 Li-Bing.Song@stripped	2010-01-22
      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 created when each time some log events are written into binlog file
      and is synchronized. TranxNodes' memories are allocted from mem_root of the current
      thread, and will be freed immediately after current statement ending.
      from TranxNode list. So the Pointer of the TranxNode in TranxNode list becomes a 
      wild pointer.
            
      After this patch, TranxNodes are not allocated from mem_root.

    added:
      plugin/semisync/semisync_node_allocator.h
    modified:
      mysql-test/suite/rpl/r/rpl_semi_sync_mixed_tables.result
      mysql-test/suite/rpl/t/rpl_semi_sync_mixed_tables.test
      plugin/semisync/semisync_master.cc
      plugin/semisync/semisync_master.h
      plugin/semisync/semisync_master_plugin.cc
=== modified file 'mysql-test/suite/rpl/r/rpl_semi_sync_mixed_tables.result'
--- a/mysql-test/suite/rpl/r/rpl_semi_sync_mixed_tables.result	2010-01-20 08:37:18 +0000
+++ b/mysql-test/suite/rpl/r/rpl_semi_sync_mixed_tables.result	2010-01-22 14:59:03 +0000
@@ -15,22 +15,26 @@ CREATE TABLE t2(c1 INT) ENGINE=innodb;
 call mtr.add_suppression(".*");
 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
 SET GLOBAL rpl_semi_sync_master_enabled = 1;
-SET GLOBAL rpl_semi_sync_master_trace_level= 80;
+SET GLOBAL rpl_semi_sync_master_trace_level= 0;
 STOP SLAVE;
 INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
 SET GLOBAL rpl_semi_sync_slave_enabled = 1;
+SET GLOBAL rpl_semi_sync_slave_trace_level= 0;
 START SLAVE;
 BEGIN;
 
 # Even though it is in a transaction, this statement is binlogged into binlog
 # file immediately.
-CREATE TEMPORARY TABLE t1 LIKE t2;
+CREATE TEMPORARY TABLE t1 SELECT c1 FROM t2 where 1=1;
+CREATE TEMPORARY TABLE t3 (c1 INT);
 
 # These statements will not binlogged until the transaction is committed
 INSERT INTO t2 VALUES(11);
 INSERT INTO t2 VALUES(22);
 COMMIT;
 
+SET GLOBAL rpl_semi_sync_slave_enabled = 0;
 UNINSTALL PLUGIN rpl_semi_sync_slave;
+SET GLOBAL rpl_semi_sync_master_enabled = 0;
 UNINSTALL PLUGIN rpl_semi_sync_master;
-DROP TABLE t1, t2;
+DROP TABLE t2;

=== modified file 'mysql-test/suite/rpl/t/rpl_semi_sync_mixed_tables.test'
--- a/mysql-test/suite/rpl/t/rpl_semi_sync_mixed_tables.test	2010-01-20 08:37:18 +0000
+++ b/mysql-test/suite/rpl/t/rpl_semi_sync_mixed_tables.test	2010-01-22 14:59:03 +0000
@@ -19,7 +19,7 @@ connection master;
 call mtr.add_suppression(".*");
 eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN';
 SET GLOBAL rpl_semi_sync_master_enabled = 1;
-SET GLOBAL rpl_semi_sync_master_trace_level= 80;
+SET GLOBAL rpl_semi_sync_master_trace_level= 0;
 
 connection slave;
 STOP SLAVE;
@@ -27,6 +27,7 @@ source include/wait_for_slave_to_stop.in
 
 eval INSTALL PLUGIN rpl_semi_sync_slave SONAME '$SEMISYNC_SLAVE_PLUGIN';
 SET GLOBAL rpl_semi_sync_slave_enabled = 1;
+SET GLOBAL rpl_semi_sync_slave_trace_level= 0;
 
 START SLAVE;
 source include/wait_for_slave_to_start.inc;
@@ -37,6 +38,7 @@ BEGIN;
 --echo # Even though it is in a transaction, this statement is binlogged into binlog
 --echo # file immediately.
 CREATE TEMPORARY TABLE t1 SELECT c1 FROM t2 where 1=1;
+CREATE TEMPORARY TABLE t3 (c1 INT);
 --echo
 --echo # These statements will not binlogged until the transaction is committed
 INSERT INTO t2 VALUES(11);
@@ -45,9 +47,12 @@ COMMIT;
 sync_slave_with_master;
 
 --echo
+
+SET GLOBAL rpl_semi_sync_slave_enabled = 0;
 UNINSTALL PLUGIN rpl_semi_sync_slave;
 connection master;
+SET GLOBAL rpl_semi_sync_master_enabled = 0;
 UNINSTALL PLUGIN rpl_semi_sync_master;
 
-DROP TABLE t1, t2;
+DROP TABLE t2;
 source include/master-slave-end.inc;

=== 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-22 14:59:03 +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 */
@@ -123,7 +123,7 @@ ActiveTranx::TranxNode* ActiveTranx::all
      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));
+  TranxNode *trx_node = allocator_.add_node();
   if (trx_node)
   {
     trx_node->log_name_[0] = '\0';
@@ -271,6 +271,7 @@ int ActiveTranx::clear_active_tranx_node
 
     /* Clear the hash table. */
     memset(trx_htb_, 0, num_entries_ * sizeof(TranxNode *));
+    allocator_.delete_all_nodes();
 
     /* Clear the active transaction list. */
     if (trx_front_ != NULL)
@@ -311,6 +312,7 @@ int ActiveTranx::clear_active_tranx_node
     }
 
     trx_front_ = new_front;
+    allocator_.delete_node_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-22 14:59:03 +0000
@@ -19,6 +19,7 @@
 #define SEMISYNC_MASTER_H
 
 #include "semisync.h"
+#include "semisync_node_allocator.h"
 
 /**
    This class manages memory for active transaction list.
@@ -38,6 +39,7 @@ private:
     struct TranxNode *hash_next_;    /* the next node during hash collision */
   };
 
+  NodeAllocator<TranxNode> allocator_;
   /* These two record the active transaction list in sort order. */
   TranxNode       *trx_front_, *trx_rear_;
 

=== modified file 'plugin/semisync/semisync_master_plugin.cc'
--- a/plugin/semisync/semisync_master_plugin.cc	2010-01-20 08:37:18 +0000
+++ b/plugin/semisync/semisync_master_plugin.cc	2010-01-22 14:59:03 +0000
@@ -46,7 +46,10 @@ int repl_semi_request_commit(Trans_param
 
 int repl_semi_report_commit(Trans_param *param)
 {
-  if (param->log_pos)
+
+  bool is_real_trans= param->flags & TRANS_IS_REAL_TRANS;
+
+  if (is_real_trans && param->log_pos)
   {
     const char *binlog_name= param->log_file;
     return repl_semisync.commitTrx(binlog_name, param->log_pos);

=== added file 'plugin/semisync/semisync_node_allocator.h'
--- a/plugin/semisync/semisync_node_allocator.h	1970-01-01 00:00:00 +0000
+++ b/plugin/semisync/semisync_node_allocator.h	2010-01-22 14:59:03 +0000
@@ -0,0 +1,174 @@
+/* Copyright (C) 2010 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
+
+#ifndef SEMISYNC_NODE_ALLOCATOR
+#define SEMISYNC_NODE_ALLOCATOR
+
+/*
+ */
+template <class T> class NodeAllocator
+{
+public:
+  /* init_nodes_ means how many nodes(class T) are in one block */
+  NodeAllocator(uint init_nodes) :
+    init_nodes_(init_nodes), block_size_(init_nodes * sizeof(T)),
+    last_node_(0), head_(0), rear_(0), current_block_(0), block_num_(0) {}
+
+  ~NodeAllocator()
+  {
+    Block *block= head_;
+    while (block != NULL)
+    {
+      Block *next= block->next;
+      free_block(block);
+      block= next;
+    }
+  }
+
+  /*
+    Return a node pointer which follows the last_node_ immediately.
+   */
+  T *add_node()
+  {
+    /* A block is allocated first, if there is no any block existing */
+    if (block_num_ == 0 && allocate_block())
+      return NULL;
+
+    last_node_+= sizeof(T);
+    if (last_node_ == current_block_->end)
+    {
+      current_block_= current_block_->next;
+      /* All blocks are in use, so a new block is allocated */
+      if (current_block_ == NULL && allocate_block())
+      {
+        /* Recovery the last_node_ if allocate_block() fails */
+        last_node_-= sizeof(T);
+        return NULL;
+      }
+      else
+        last_node_= current_block_->begin;
+    }
+    return static_cast<T*>(last_node_);
+  }
+
+  int delete_all_nodes()
+  {
+     last_node_= head_ ? head_->begin : NULL;
+     //free_blocks();
+     return 0;
+  }
+
+  int delete_node_before(T* node)
+  {
+    Block *block;
+    Block *prev_block;
+
+    assert(node != NULL);
+    block= head_;
+    while (block != current_block_->next)
+    {
+      void *p = static_cast<void *>(node);
+      if (block->begin <= p and block->end > p)
+      {
+        if (head_ != block)
+        {
+          rear_->next= head_;
+          head_= block;
+          rear_= prev_block;
+          rear_->next= NULL;
+          //free_blocks();
+        }
+        return 0;
+      }
+      prev_block= block;
+      block= block->next;
+    }
+    return 1;
+  }
+
+private:
+  uint init_nodes_;
+  uint block_size_;
+  void* last_node_;
+
+  struct Block {
+    void *begin;
+    void *end;
+    Block *next;
+  };
+  Block *head_;
+  Block *rear_;
+  Block *current_block_;
+  uint block_num_;
+
+  int allocate_block()
+  {
+    Block *block= (Block *)my_malloc(sizeof(Block), MYF(0));
+    if (block)
+    {
+      block->begin= my_malloc(block_size_, MYF(0));
+      if (block->begin)
+      {
+        block->end= block->begin + block_size_;
+        block->next= NULL;
+
+        if (head_ == NULL)
+          head_= block;
+        else
+          rear_->next= block;
+
+        /* New block always is put at the rear */
+        rear_= block;
+        /* New block always is the current_block_ */
+        current_block_= block;
+        last_node_= block->begin;
+        ++block_num_;
+        return 0;
+      }
+      else
+        my_free(block, MYF(0));
+    }
+    return 1;
+  }
+
+  void free_block(Block *block)
+  {
+    assert(block != NULL || block->begin != NULL);
+    my_free(block->begin, MYF(0));
+    my_free(block, MYF(0));
+    --block_num_;
+  }
+
+  /*
+   */
+  void free_blocks()
+  {
+    if (current_block_ == NULL)
+      return;
+
+    Block *block= current_block_->next;
+    while (block_num_ > 3 && block != NULL)
+    {
+      Block *next= block->next;
+      free_block(block);
+      block= next;
+    }
+    current_block_->next= block;
+    if (block == NULL)
+      rear_= NULL;
+  }
+};
+
+#endif


Attachment: [text/bzr-bundle] bzr/li-bing.song@sun.com-20100122145903-30xn2fzh88sspd49.bundle
Thread
bzr commit into mysql-5.1-rep-semisync branch (Li-Bing.Song:3128) Bug#50157Li-Bing.Song22 Jan
  • Re: bzr commit into mysql-5.1-rep-semisync branch (Li-Bing.Song:3128)Bug#50157He Zhenxing25 Jan