List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:August 22 2008 10:34pm
Subject:bzr commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933
View as plain text  
#At file:///C:/bzr/mysql-6.0-falcon-team/

 2798 Vladislav Vaintroub	2008-08-22
      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. When the list is freed, firstRecord  is set to NULL to prevent
      crash in parallel Transaction::hasRecords() can crash.
modified:
  storage/falcon/Transaction.cpp
  storage/falcon/Transaction.h

=== 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-22 20:33:40 +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;
 
+	Sync syncRec(&syncRecords,"Transaction::~Transaction");
+	syncRec.lock(Exclusive);
 	for (RecordVersion *record; (record = firstRecord);)
 		{
-		removeRecord(record);
+		removeRecordNoLock(record);
 		}
+	firstRecord = NULL;
+	syncRec.unlock();
 	
 	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))
@@ -912,9 +918,11 @@ void Transaction::dropTable(Table* table
 	sync.lock(Exclusive);
 
 	releaseDeferredIndexes(table);
+	sync.unlock();
 
-	// 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);
@@ -928,9 +936,10 @@ void Transaction::truncateTable(Table* t
 	sync.lock(Exclusive);
 
 	releaseDeferredIndexes(table);
+	sync.unlock();
 
-	// 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 +949,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;
@@ -957,14 +965,12 @@ void Transaction::writeComplete(void)
 	Sync sync(&syncIndexes, "Transaction::writeComplete");
 	sync.lock(Exclusive);
 	releaseDeferredIndexes();
-
-	// Keep the synIndexes lock to avoid a race condition with dropTable
+	sync.unlock();
 	
 	if (dependencies == 0)
 		commitRecords();
 
 	writePending = false;
-	sync.unlock();
 }
 
 bool Transaction::waitForTransaction(TransId transId)
@@ -1248,6 +1254,7 @@ void Transaction::rollbackSavepoint(int 
 
 	savePoint = savePoints;
 	
+
 	while (savePoint)
 		{
 		//validateRecords();
@@ -1256,6 +1263,8 @@ void Transaction::rollbackSavepoint(int 
 			break;
 
 		// Purge out records from this savepoint
+		Sync syncRec(&syncRecords,"Transaction::rollbackSavepoint(2)");
+		sync.lock(Exclusive);
 
 		RecordVersion *record = *savePoint->records;
 		RecordVersion *stack = NULL;
@@ -1295,6 +1304,7 @@ void Transaction::rollbackSavepoint(int 
 			rec->transaction = NULL;
 			rec->release();
 			}
+		syncRec.unlock();
 
 		// Handle any backlogged records
 		
@@ -1340,6 +1350,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 +1360,7 @@ void Transaction::releaseRecordLocks(voi
 			}
 		else
 			ptr = &record->nextInTrans;
+	syncRec.unlock();
 }
 
 void Transaction::print(void)
@@ -1365,6 +1378,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 +1427,7 @@ void Transaction::printBlocking(int leve
 				   record->recordNumber,
 				   what);
 		}
-
+	syncRec.unlock();
 	database->transactionManager->printBlocking(this, level);
 }
 
@@ -1450,14 +1465,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 +1510,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 +1569,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-22 20:33:40 +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 commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933Vladislav Vaintroub22 Aug
  • RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933Kevin Lewis25 Aug
  • RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933Kevin Lewis25 Aug
    • RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933Vladislav Vaintroub25 Aug
      • RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2798) Bug#38933Kevin Lewis25 Aug