List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:August 25 2008 8:31pm
Subject:bzr push into mysql-6.0-falcon branch (vvaintroub:2797 to 2798) Bug#38933
View as plain text  
 2798 Vladislav Vaintroub	2008-08-25
      Bug#38933 - Falcon crash in Transaction::hasRecords during concurrent DDL
            
      The problem is that Transaction::hasRecords traverses  transaction's record
      list while a concurrent operation (rollback, writeComplete,addRecord) may 
      concurrently modify it.
           
      This patch removed whacko-reuse of the Transaction::syncIndexes to protect
      the record list and introduces new lock Transaction::syncRecords for exactly 
      this purpose.
            
      Hopefully all places where the list is read or modified are now protected 
      by this lock,except Transaction destructor, where there should be nobody 
      accessing the list concurrently. When the list is freed, firstRecord  is 
      set to NULL to prevent crash in parallel Transaction::hasRecords().
modified:
  storage/falcon/Transaction.cpp
  storage/falcon/Transaction.h

 2797 Vladislav Vaintroub	2008-08-22 [merge]
      merge 6.0-falcon -> 6.0-falcon-team
removed:
  mysql-test/include/wait_timeout_basic.inc
  mysql-test/suite/rpl/t/rpl_row_err_daisychain-master.opt
  mysql-test/suite/rpl/t/rpl_row_err_daisychain-slave.opt
  mysql-test/suite/rpl_ndb/t/rpl_truncate_7ndb_2-master.opt
added:
  mysql-test/include/have_nodebug.inc
  mysql-test/r/have_nodebug.require
  mysql-test/r/innodb-autoinc-optimize.result
  mysql-test/r/sp-no-code.result
  mysql-test/r/subselect_nulls.result
  mysql-test/suite/sys_vars/inc/multi_range_count_basic.inc
  mysql-test/suite/sys_vars/r/multi_range_count_basic_32.result
  mysql-test/suite/sys_vars/r/multi_range_count_basic_64.result
  mysql-test/suite/sys_vars/t/multi_range_count_basic_32.test
  mysql-test/suite/sys_vars/t/multi_range_count_basic_64.test
  mysql-test/t/innodb-autoinc-optimize.test
  mysql-test/t/sp-no-code.test
  mysql-test/t/subselect_nulls.test
modified:
  client/Makefile.am
  client/mysql_upgrade.c
  client/mysqltest.c
  config/ac-macros/character_sets.m4
  configure.in
  extra/comp_err.c
  extra/perror.c
  mysql-test/extra/binlog_tests/binlog_insert_delayed.test
  mysql-test/extra/rpl_tests/rpl_row_basic.test
  mysql-test/include/show_binlog_events.inc
  mysql-test/r/constraints.result
  mysql-test/r/func_if.result
  mysql-test/r/perror.result
  mysql-test/r/sp-error.result
  mysql-test/r/subselect.result
  mysql-test/r/subselect_no_mat.result
  mysql-test/r/subselect_no_opts.result
  mysql-test/r/subselect_no_semijoin.result
  mysql-test/r/subselect_sj.result
  mysql-test/r/subselect_sj2.result
  mysql-test/suite/binlog/r/binlog_killed_simulate.result
  mysql-test/suite/binlog/r/binlog_row_binlog.result
  mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result
  mysql-test/suite/binlog/r/binlog_statement_insert_delayed.result
  mysql-test/suite/binlog/r/binlog_stm_binlog.result
  mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result
  mysql-test/suite/funcs_1/r/falcon_storedproc.result
  mysql-test/suite/funcs_1/r/falcon_trig_03.result
  mysql-test/suite/funcs_1/r/falcon_trig_03e.result
  mysql-test/suite/funcs_1/r/innodb_storedproc.result
  mysql-test/suite/funcs_1/r/innodb_trig_03.result
  mysql-test/suite/funcs_1/r/innodb_trig_03e.result
  mysql-test/suite/funcs_1/r/is_columns_mysql.result
  mysql-test/suite/funcs_1/r/is_user_privileges.result
  mysql-test/suite/funcs_1/r/memory_storedproc.result
  mysql-test/suite/funcs_1/r/memory_trig_03.result
  mysql-test/suite/funcs_1/r/memory_trig_03e.result
  mysql-test/suite/funcs_1/r/myisam_storedproc.result
  mysql-test/suite/funcs_1/r/myisam_trig_03.result
  mysql-test/suite/funcs_1/r/myisam_trig_03e.result
  mysql-test/suite/funcs_1/r/ndb_storedproc.result
  mysql-test/suite/funcs_1/r/ndb_trig_03.result
  mysql-test/suite/funcs_1/r/ndb_trig_03e.result
  mysql-test/suite/funcs_1/triggers/triggers_03.inc
  mysql-test/suite/funcs_1/triggers/triggers_08.inc
  mysql-test/suite/funcs_1/triggers/triggers_1011ext.inc
  mysql-test/suite/ndb/r/ndb_basic.result
  mysql-test/suite/ndb/t/disabled.def
  mysql-test/suite/rpl/r/rpl_loaddata_map.result
  mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result
  mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result
  mysql-test/suite/rpl/r/rpl_stm_log.result
  mysql-test/suite/rpl/r/rpl_variables.result
  mysql-test/suite/rpl/t/disabled.def
  mysql-test/suite/rpl/t/rpl_incident.test
  mysql-test/suite/rpl/t/rpl_loaddata_map.test
  mysql-test/suite/rpl/t/rpl_trunc_temp.test
  mysql-test/suite/rpl/t/rpl_variables.test
  mysql-test/suite/rpl_ndb/t/rpl_truncate_7ndb_2.test
  mysql-test/suite/rpl_ndb_big/r/rpl_row_basic_7ndb.result
  mysql-test/t/constraints.test
  mysql-test/t/disabled.def
  mysql-test/t/func_if.test
  mysql-test/t/perror.test
  mysql-test/t/sp-error.test
  mysql-test/t/subselect.test
  mysql-test/t/subselect_sj.test
  mysql-test/t/subselect_sj2.test
  scripts/mysql_system_tables_fix.sql
  sql/field.h
  sql/handler.cc
  sql/handler.h
  sql/item.cc
  sql/item.h
  sql/item_cmpfunc.cc
  sql/item_func.cc
  sql/mysql_priv.h
  sql/mysqld.cc
  sql/share/errmsg.txt
  sql/sp_head.cc
  sql/sp_pcontext.cc
  sql/sql_acl.cc
  sql/sql_base.cc
  sql/sql_binlog.cc
  sql/sql_cursor.cc
  sql/sql_error.cc
  sql/sql_error.h
  sql/sql_lex.h
  sql/sql_list.h
  sql/sql_parse.cc
  sql/sql_select.cc
  sql/sql_select.h
  sql/sql_string.h
  sql/sql_yacc.yy
  sql/structs.h
  sql/table.cc
  sql/table.h
  sql/thr_malloc.cc
  support-files/build-tags
  tests/mysql_client_test.c

=== modified file 'storage/falcon/Transaction.cpp'
--- a/storage/falcon/Transaction.cpp	2008-08-22 15:01:12 +0000
+++ b/storage/falcon/Transaction.cpp	2008-08-25 18:24:59 +0000
@@ -86,6 +86,7 @@ Transaction::Transaction(Connection *cnc
 	syncObject.setName("Transaction::syncObject");
 	syncActive.setName("Transaction::syncActive");
 	syncIndexes.setName("Transaction::syncIndexes");
+	syncRecords.setName("Transaction::syncRecords");
 	syncSavepoints.setName("Transaction::syncSavepoints");
 	firstRecord = NULL;
 	lastRecord = NULL;
@@ -216,20 +217,19 @@ Transaction::~Transaction()
 	delete backloggedRecords;
 	chillPoint = &firstRecord;
 
+	// We modify record list without locking.
+	// It is a destructor and if somebody accesses the list
+	// at this point, he is already lost.
 	for (RecordVersion *record; (record = firstRecord);)
 		{
-		removeRecord(record);
+		removeRecordNoLock(record);
 		}
+	firstRecord = NULL;
 	
 	releaseSavepoints();
 	
 	if (deferredIndexes)
-		{
-		Sync sync(&syncIndexes, "Transaction::~Transaction");
-		sync.lock(Exclusive);
-
 		releaseDeferredIndexes();
-		}
 }
 
 void Transaction::commit()
@@ -279,6 +279,8 @@ void Transaction::commit()
 
 	
 
+	Sync syncRec(&syncRecords,"Transaction::commit(1.5)");
+	syncRec.lock(Shared);
 	for (RecordVersion *record = firstRecord; record; record = record->nextInTrans)
 	{
 		Table * table = record->format->table;
@@ -295,7 +297,8 @@ void Transaction::commit()
 		if (record->state == recDeleted && table->cardinality > 0)
 			--table->cardinality;
 	}
-	
+	syncRec.unlock();
+
 	releaseDependencies();
 	database->flushInversion(this);
 
@@ -341,11 +344,7 @@ void Transaction::commitNoUpdates(void)
 	++transactionManager->committed;
 	
 	if (deferredIndexes)
-		{
-		Sync sync(&syncIndexes, "Transaction::commitNoUpdates(1)");
-		sync.lock(Exclusive);
 		releaseDeferredIndexes();
-		}
 
 	if (hasLocks)
 		releaseRecordLocks();
@@ -387,11 +386,7 @@ void Transaction::rollback()
 		throw SQLEXCEPTION (RUNTIME_ERROR, "transaction is not active");
 
 	if (deferredIndexes)
-		{
-		Sync sync(&syncIndexes, "Transaction::rollback(1)");
-		sync.lock(Exclusive);
 		releaseDeferredIndexes();
-		}
 		
 	releaseSavepoints();
 	TransactionManager *transactionManager = database->transactionManager;
@@ -403,8 +398,8 @@ void Transaction::rollback()
 	// Rollback pending record versions from newest to oldest in case
 	// there are multiple record versions on a prior record chain
 
-	Sync sync(&syncIndexes, "Transaction::rollback(1.5)");
-	sync.lock(Exclusive);
+	Sync syncRec(&syncRecords, "Transaction::rollback(1.5)");
+	syncRec.lock(Exclusive);
 
 	while (firstRecord)
 		{
@@ -432,7 +427,7 @@ void Transaction::rollback()
 		record->release();
 		}
 	firstRecord = NULL;
-	sync.unlock();
+	syncRec.unlock();
 
 	for (SavePoint *savePoint = savePoints; savePoint; savePoint = savePoint->next)
 		if (savePoint->backloggedRecords)
@@ -626,6 +621,8 @@ void Transaction::addRecord(RecordVersio
 
 	record->addRef();
 	
+	Sync syncRec(&syncRecords,"Transaction::addRecord");
+	syncRec.lock(Exclusive);
 	if ( (record->prevInTrans = lastRecord) )
 		lastRecord->nextInTrans = record;
 	else
@@ -633,6 +630,7 @@ void Transaction::addRecord(RecordVersio
 		
 	record->nextInTrans = NULL;
 	lastRecord = record;
+	syncRec.unlock();
 	
 	if (database->lowMemory && deletedRecords > MAX_LOW_MEMORY_RECORDS)
 		backlogRecords();
@@ -640,6 +638,12 @@ void Transaction::addRecord(RecordVersio
 
 void Transaction::removeRecord(RecordVersion *record)
 {
+	Sync syncRec(&syncRecords,"Transaction::removeRecord");
+	syncRec.lock(Exclusive);
+	removeRecordNoLock(record);
+}
+void Transaction::removeRecordNoLock(RecordVersion *record)
+{
 	RecordVersion **ptr;
 
 	if (record->nextInTrans)
@@ -805,6 +809,8 @@ void Transaction::releaseDependencies()
 
 void Transaction::commitRecords()
 {
+	Sync syncRec(&syncRecords,"Transaction::commitRecords");
+	syncRec.lock(Exclusive);
 	for (RecordVersion *recordList; (recordList = firstRecord);)
 		{
 		if (recordList && COMPARE_EXCHANGE_POINTER(&firstRecord, recordList, NULL))
@@ -908,13 +914,10 @@ State Transaction::getRelativeState(Tran
 
 void Transaction::dropTable(Table* table)
 {
-	Sync sync(&syncIndexes, "Transaction::dropTable");
-	sync.lock(Exclusive);
-
 	releaseDeferredIndexes(table);
 
-	// Keep exclusive lock to avoid race condition with writeComplete
-	
+	Sync syncRec(&syncRecords,"Transaction::dropTable(2)");
+	syncRec.lock(Exclusive);
 	for (RecordVersion **ptr = &firstRecord, *rec; (rec = *ptr);)
 		if (rec->format->table == table)
 			removeRecord(rec);
@@ -924,13 +927,9 @@ void Transaction::dropTable(Table* table
 
 void Transaction::truncateTable(Table* table)
 {
-	Sync sync(&syncIndexes, "Transaction::truncateTable");
-	sync.lock(Exclusive);
-
 	releaseDeferredIndexes(table);
-
-	// Keep exclusive lock to avoid race condition with writeComplete
-	
+	Sync syncRec(&syncRecords,"Transaction::truncateTable(2)");
+	syncRec.lock(Exclusive);
 	for (RecordVersion **ptr = &firstRecord, *rec; (rec = *ptr);)
 		if (rec->format->table == table)
 			removeRecord(rec);
@@ -940,9 +939,8 @@ void Transaction::truncateTable(Table* t
 
 bool Transaction::hasRecords(Table* table)
 {
-	// This lock is to avoid race with writeComplete
-	Sync sync(&syncIndexes, "Transaction::hasRecords");
-	sync.lock(Exclusive);
+	Sync syncRec(&syncRecords, "Transaction::hasRecords");
+	syncRec.lock(Shared);
 	for (RecordVersion *rec = firstRecord; rec; rec = rec->nextInTrans)
 		if (rec->format->table == table)
 			return true;
@@ -954,17 +952,12 @@ void Transaction::writeComplete(void)
 {
 	ASSERT(writePending);
 	ASSERT(state == Committed);
-	Sync sync(&syncIndexes, "Transaction::writeComplete");
-	sync.lock(Exclusive);
 	releaseDeferredIndexes();
-
-	// Keep the synIndexes lock to avoid a race condition with dropTable
 	
 	if (dependencies == 0)
 		commitRecords();
 
 	writePending = false;
-	sync.unlock();
 }
 
 bool Transaction::waitForTransaction(TransId transId)
@@ -1248,6 +1241,7 @@ void Transaction::rollbackSavepoint(int 
 
 	savePoint = savePoints;
 	
+
 	while (savePoint)
 		{
 		//validateRecords();
@@ -1256,6 +1250,8 @@ void Transaction::rollbackSavepoint(int 
 			break;
 
 		// Purge out records from this savepoint
+		Sync syncRec(&syncRecords,"Transaction::rollbackSavepoint(2)");
+		syncRec.lock(Exclusive);
 
 		RecordVersion *record = *savePoint->records;
 		RecordVersion *stack = NULL;
@@ -1295,6 +1291,7 @@ void Transaction::rollbackSavepoint(int 
 			rec->transaction = NULL;
 			rec->release();
 			}
+		syncRec.unlock();
 
 		// Handle any backlogged records
 		
@@ -1340,6 +1337,8 @@ void Transaction::releaseRecordLocks(voi
 	RecordVersion **ptr;
 	RecordVersion *record;
 
+	Sync syncRec(&syncRecords,"Transaction::releaseRecordLocks");
+	syncRec.lock(Exclusive);
 	for (ptr = &firstRecord; (record = *ptr);)
 		if (record->state == recLock)
 			{
@@ -1348,6 +1347,7 @@ void Transaction::releaseRecordLocks(voi
 			}
 		else
 			ptr = &record->nextInTrans;
+	syncRec.unlock();
 }
 
 void Transaction::print(void)
@@ -1365,6 +1365,8 @@ void Transaction::printBlocking(int leve
 	int deletes = 0;
 	RecordVersion *record;
 
+	Sync syncRec(&syncRecords,"Transaction::printBlocking");
+	syncRec.lock(Shared);
 	for (record = firstRecord; record; record = record->nextInTrans)
 		if (record->state == recLock)
 			++locks;
@@ -1412,7 +1414,7 @@ void Transaction::printBlocking(int leve
 				   record->recordNumber,
 				   what);
 		}
-
+	syncRec.unlock();
 	database->transactionManager->printBlocking(this, level);
 }
 
@@ -1450,14 +1452,7 @@ void Transaction::releaseDependency(void
 	INTERLOCKED_DECREMENT(dependencies);
 
 	if ((dependencies == 0) && !writePending && firstRecord)
-		{
-		// The Sync is to avoid a race with writeComplete().  It looks whacko, but does the
trick
-		
-		Sync sync(&syncIndexes, "Transaction::releaseDependency");
-		sync.lock(Exclusive);
 		commitRecords();
-		}
-		
 	releaseCommittedTransaction();
 }
 
@@ -1502,6 +1497,8 @@ void Transaction::printBlockage(void)
 
 void Transaction::releaseDeferredIndexes(void)
 {
+	Sync sync(&syncIndexes, "Transaction::releaseDeferredIndexes");
+	sync.lock(Exclusive);
 	for (DeferredIndex *deferredIndex; (deferredIndex = deferredIndexes);)
 		{
 		ASSERT(deferredIndex->transaction == this);
@@ -1559,7 +1556,8 @@ void Transaction::backlogRecords(void)
 void Transaction::validateRecords(void)
 {
 	RecordVersion *record;
-	
+	Sync syncRec(&syncRecords,"Transaction::validateRecords");
+	syncRec.lock(Shared);
 	for (record = firstRecord; record && record->nextInTrans; record =
record->nextInTrans)
 		;
 	

=== modified file 'storage/falcon/Transaction.h'
--- a/storage/falcon/Transaction.h	2008-08-11 13:22:53 +0000
+++ b/storage/falcon/Transaction.h	2008-08-25 18:24:59 +0000
@@ -87,7 +87,8 @@ public:
 
 	State		getRelativeState(Record* record, uint32 flags);
 	State		getRelativeState (Transaction *transaction, TransId transId, uint32 flags);
-	void		removeRecord (RecordVersion *record);
+	void		removeRecordNoLock (RecordVersion *record);
+	void		removeRecord(RecordVersion *record);
 	void		removeRecord (RecordVersion *record, RecordVersion **ptr);
 	void		expungeTransaction (Transaction *transaction);
 	void		commitRecords();
@@ -171,6 +172,7 @@ public:
 	SyncObject		syncObject;
 	SyncObject		syncActive;
 	SyncObject		syncIndexes;
+	SyncObject		syncRecords;
 	SyncObject		syncSavepoints;
 	uint64			totalRecordData;	// total bytes of record data for this transaction (unchilled
+ thawed)
 	uint32			totalRecords;		// total record count

Thread
bzr push into mysql-6.0-falcon branch (vvaintroub:2797 to 2798) Bug#38933Vladislav Vaintroub25 Aug