List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:August 11 2008 3:23pm
Subject:bzr commit into mysql-6.0-falcon branch (vvaintroub:2772) Bug#22165
View as plain text  
#At file:///C:/bzr/mysql-6.0-falcon-team/

 2772 Vladislav Vaintroub	2008-08-11
      Bug#22165 - crash while doing ALTER in parallel with another transactions
      Eliminated races.
      
      Race condition 1.
      ALTER/DROP/TRUNCATE have to ensure that table has no committed records
      that are not yet "write complete". Otherwise crash is possible if table is 
      deleted, but then accessed during Transaction::writeComplete() in Record::poke()
      
      It is now solved, Storage::external_lock will wait for all transaction on 
      the table  to become write complete, if there is no uncommited records.
      
      Race condition 2.
      In Transaction::commit(), Transaction state is set to Committed to early
      while the transaction is still in the active list. This can produce incorrect
      results in Transaction::hasUncommitedRecords, DROP is not blocked.
      When later Transaction::commit() traverses the record list, it can crash 
      accessing freed memory in Table::updateRecord().
      
      This is fixed and transaction state is changed during the transition from 
      active list to committed list.
modified:
  mysql-test/suite/falcon/t/disabled.def
  storage/falcon/Database.cpp
  storage/falcon/Database.h
  storage/falcon/StorageTable.cpp
  storage/falcon/StorageTable.h
  storage/falcon/Table.cpp
  storage/falcon/Table.h
  storage/falcon/Transaction.cpp
  storage/falcon/Transaction.h
  storage/falcon/TransactionManager.cpp
  storage/falcon/TransactionManager.h
  storage/falcon/ha_falcon.cpp

per-file messages:
  storage/falcon/Database.cpp
    new function waitForWriteComplete
  storage/falcon/Database.h
    new function waitForWriteComplete
  storage/falcon/StorageTable.cpp
    new function waitForWriteComplete
  storage/falcon/StorageTable.h
    new function waitForWriteComplete
  storage/falcon/Table.cpp
    new function waitForWriteComplete
  storage/falcon/Table.h
    new function waitForWriteComplete
  storage/falcon/Transaction.cpp
    Transaction state was set to Committed to early, allowing for DROP to process in
paralell
    moved state change to later point, when transaction is removed from active list and
added 
    to committed list.
    
    Also, removed unnecessary loops : record list is now traversed 
    only once in Transaction::commit() ,not 2 or 3 times
    
    Additionally, Transaction::hasRecords is now lock-protected to
    avoid races with writeComplete() and dropTable()
    
    Renamed hasUncommittedRecords() to hasRecords() - we're sometimes interested 
    in committed no-yet-write-complete transactions
  storage/falcon/Transaction.h
    hasUncommittedRecords->hasRecords
  storage/falcon/TransactionManager.cpp
    new function TransactionManager::waitForWriteComplete() to
    wait for all transactions referencing records from given
    table to become "write complete"
  storage/falcon/TransactionManager.h
    new function waitForWriteComplete()
  storage/falcon/ha_falcon.cpp
    in StorageInterface::external_lock, if there is no uncommitted records
    for the table, also wait while committed records will become write 
    complete. This is done to avoid race with Transaction::writeComplete()
=== modified file 'mysql-test/suite/falcon/t/disabled.def'
--- a/mysql-test/suite/falcon/t/disabled.def	2008-08-07 21:11:55 +0000
+++ b/mysql-test/suite/falcon/t/disabled.def	2008-08-11 13:22:53 +0000
@@ -12,4 +12,3 @@
 
 falcon_bug_28095_I  : Bug#xxxxx 2008-04-22 hakank Disabled until soft restart is in main
tree
 falcon_bug_28095_II : Bug#xxxxx 2008-03-22 hakank Disabled until soft restart is in main
tree
-falcon_bug_22165 : Bug#22165 2008-08-07 wlad Disabled temporarily, because it crashes.
Work in progress

=== modified file 'storage/falcon/Database.cpp'
--- a/storage/falcon/Database.cpp	2008-07-17 13:52:17 +0000
+++ b/storage/falcon/Database.cpp	2008-08-11 13:22:53 +0000
@@ -2282,6 +2282,11 @@ bool Database::hasUncommittedRecords(Tab
 	return transactionManager->hasUncommittedRecords(table, transaction);
 }
 
+void Database::waitForWriteComplete(Table *table)
+{
+	transactionManager->waitForWriteComplete(table);
+}
+
 void Database::commitByXid(int xidLength, const UCHAR* xid)
 {
 	serialLog->commitByXid(xidLength, xid);

=== modified file 'storage/falcon/Database.h'
--- a/storage/falcon/Database.h	2008-05-09 19:58:50 +0000
+++ b/storage/falcon/Database.h	2008-08-11 13:22:53 +0000
@@ -200,6 +200,7 @@ public:
 	virtual void	createDatabase (const char *filename);
 	void			renameTable(Table* table, const char* newSchema, const char* newName);
 	bool			hasUncommittedRecords(Table* table, Transaction *transaction);
+	void			waitForWriteComplete(Table *table);
 	void			validateCache(void);
 	int				recoverGetNextLimbo(int xidSize, unsigned char *xid);
 	void			commitByXid(int xidLength, const UCHAR* xid);

=== modified file 'storage/falcon/StorageTable.cpp'
--- a/storage/falcon/StorageTable.cpp	2008-08-04 15:53:52 +0000
+++ b/storage/falcon/StorageTable.cpp	2008-08-11 13:22:53 +0000
@@ -565,6 +565,11 @@ int StorageTable::alterCheck(void)
 	return 0;
 }
 
+void StorageTable::waitForWriteComplete(void)
+{
+	share->table->waitForWriteComplete();
+}
+
 void StorageTable::unlockRow(void)
 {
 	if (recordLocked)

=== modified file 'storage/falcon/StorageTable.h'
--- a/storage/falcon/StorageTable.h	2008-08-04 15:53:52 +0000
+++ b/storage/falcon/StorageTable.h	2008-08-11 13:22:53 +0000
@@ -61,6 +61,7 @@ public:
 	void			transactionEnded(void);
 	void			setRecord(Record* record, bool locked);
 	int				alterCheck(void);
+	void			waitForWriteComplete();
 	void			clearAlter(void);
 	bool			setAlter(void);
 	

=== modified file 'storage/falcon/Table.cpp'
--- a/storage/falcon/Table.cpp	2008-08-07 21:11:55 +0000
+++ b/storage/falcon/Table.cpp	2008-08-11 13:22:53 +0000
@@ -3331,6 +3331,11 @@ bool Table::hasUncommittedRecords(Transa
 	return database->hasUncommittedRecords(this, transaction);
 }
 
+void Table::waitForWriteComplete()
+{
+	return database->waitForWriteComplete(this);
+}
+
 RecordVersion* Table::lockRecord(Record* record, Transaction* transaction)
 {
 	Record *current = fetch(record->recordNumber);

=== modified file 'storage/falcon/Table.h'
--- a/storage/falcon/Table.h	2008-04-22 21:10:23 +0000
+++ b/storage/falcon/Table.h	2008-08-11 13:22:53 +0000
@@ -185,6 +185,7 @@ public:
 	int			getFormatVersion();
 	void		validateAndInsert(Transaction *transaction, RecordVersion *record);
 	bool		hasUncommittedRecords(Transaction* transaction);
+	void		waitForWriteComplete();
 	void		checkAncestor(Record* current, Record* oldRecord);
 	int64		estimateCardinality(void);
 	void		optimize(Connection *connection);

=== modified file 'storage/falcon/Transaction.cpp'
--- a/storage/falcon/Transaction.cpp	2008-07-31 10:04:30 +0000
+++ b/storage/falcon/Transaction.cpp	2008-08-11 13:22:53 +0000
@@ -275,38 +275,43 @@ void Transaction::commit()
 		releaseRecordLocks();
 
 	database->serialLog->preCommit(this);
-	state = Committed;
 	syncActive.unlock();
 
+	
+
 	for (RecordVersion *record = firstRecord; record; record = record->nextInTrans)
-		if (!record->isSuperceded() && record->state != recLock)
-			record->format->table->updateRecord (record);
+	{
+		Table * table = record->format->table;
 
-	if (commitTriggers)
-		for (RecordVersion *record = firstRecord; record; record = record->nextInTrans)
-			if (!record->isSuperceded() && record->state != recLock)
-				record->format->table->postCommit (this, record);
+		if (!record->isSuperceded() && record->state != recLock)
+			{
+			table->updateRecord (record);
+			if (commitTriggers)
+				table->postCommit (this, record);
+			}
 
+		if (!record->getPriorVersion())
+			++table->cardinality;
+		if (record->state == recDeleted && table->cardinality > 0)
+			--table->cardinality;
+	}
+	
 	releaseDependencies();
 	database->flushInversion(this);
 
+	// Transfer transaction from active list to committed list, set committed state
 	Sync syncCommitted(&transactionManager->committedTransactions.syncObject,
"Transaction::commit(2)");
-	syncCommitted.lock(Exclusive);
-
 	Sync syncActiveTransactions(&transactionManager->activeTransactions.syncObject,
"Transaction::commit(3)");
+	syncCommitted.lock(Exclusive);
 	syncActiveTransactions.lock(Exclusive);
+
 	transactionManager->activeTransactions.remove(this);
-	syncActiveTransactions.unlock();
-	
-	for (RecordVersion *record = firstRecord; record; record = record->nextInTrans)
-		{
-		if (!record->getPriorVersion())
-			++record->format->table->cardinality;
-		if (record->state == recDeleted &&
record->format->table->cardinality > 0)
-			--record->format->table->cardinality;
-		}
 	transactionManager->committedTransactions.append(this);
+	state = Committed;
+
+	syncActiveTransactions.unlock();
 	syncCommitted.unlock();
+
 	database->commit(this);
 
 	delete [] xid;
@@ -925,8 +930,11 @@ void Transaction::truncateTable(Table* t
 			ptr = &rec->nextInTrans;
 }
 
-bool Transaction::hasUncommittedRecords(Table* table)
+bool Transaction::hasRecords(Table* table)
 {
+	// This lock is to avoid race with writeComplete
+	Sync sync(&syncIndexes, "Transaction::releaseDependency");
+	sync.lock(Exclusive);
 	for (RecordVersion *rec = firstRecord; rec; rec = rec->nextInTrans)
 		if (rec->format->table == table)
 			return true;

=== modified file 'storage/falcon/Transaction.h'
--- a/storage/falcon/Transaction.h	2008-07-24 08:45:03 +0000
+++ b/storage/falcon/Transaction.h	2008-08-11 13:22:53 +0000
@@ -105,7 +105,7 @@ public:
 	State		waitForTransaction (Transaction *transaction, TransId transId, bool *deadlock);
 	void		dropTable(Table* table);
 	void		truncateTable(Table* table);
-	bool		hasUncommittedRecords(Table* table);
+	bool		hasRecords(Table* table);
 	void		writeComplete(void);
 	void		releaseDependency(void);
 	int			createSavepoint();

=== modified file 'storage/falcon/TransactionManager.cpp'
--- a/storage/falcon/TransactionManager.cpp	2008-07-24 08:45:03 +0000
+++ b/storage/falcon/TransactionManager.cpp	2008-08-11 13:22:53 +0000
@@ -27,6 +27,7 @@
 #include "Log.h"
 #include "LogLock.h"
 #include "Synchronize.h"
+#include "Thread.h"
 
 static const int EXTRA_TRANSACTIONS = 10;
 
@@ -168,12 +169,40 @@ bool TransactionManager::hasUncommittedR
 	syncTrans.lock (Shared);
 	
 	for (Transaction *trans = activeTransactions.first; trans; trans = trans->next)
-		if (trans != transaction && trans->isActive() &&
trans->hasUncommittedRecords(table))
+		if (trans != transaction && trans->isActive() &&
trans->hasRecords(table))
 			return true;
 
 	return false;
 }
 
+// Wait until all committed records for a table are purged by gophers
+// (their transaction become write complete)
+void TransactionManager::waitForWriteComplete(Table* table)
+{
+	for(;;)
+		{
+		bool again = false;
+		Sync committedTrans (&committedTransactions.syncObject,
+			"waitForWriteComplete");
+		committedTrans.lock (Shared);
+
+		for (Transaction *trans = committedTransactions.first; trans; 
+			 trans = trans->next)
+			{
+				if (trans->hasRecords(table)&& trans->writePending)
+				{
+				again = true;
+				break;
+				}
+			}
+
+		if(!again)
+			return;
+
+		committedTrans.unlock();
+		Thread::getThread("waitForWriteComplete")->sleep(500);
+		}
+}
 void TransactionManager::commitByXid(int xidLength, const UCHAR* xid)
 {
 	Sync sync (&activeTransactions.syncObject, "TransactionManager::commitByXid");

=== modified file 'storage/falcon/TransactionManager.h'
--- a/storage/falcon/TransactionManager.h	2008-04-24 17:26:34 +0000
+++ b/storage/falcon/TransactionManager.h	2008-08-11 13:22:53 +0000
@@ -35,6 +35,7 @@ public:
 	void			dropTable(Table* table, Transaction* transaction);
 	void			truncateTable(Table* table, Transaction* transaction);
 	bool			hasUncommittedRecords(Table* table, Transaction* transaction);
+	void			waitForWriteComplete(Table *table);
 	void			commitByXid(int xidLength, const UCHAR* xid);
 	void			rollbackByXid(int xidLength, const UCHAR* xid);
 	void			print(void);

=== modified file 'storage/falcon/ha_falcon.cpp'
--- a/storage/falcon/ha_falcon.cpp	2008-08-04 15:53:52 +0000
+++ b/storage/falcon/ha_falcon.cpp	2008-08-11 13:22:53 +0000
@@ -1893,6 +1893,7 @@ int StorageInterface::external_lock(THD 
 					DBUG_RETURN(error(ret));
 					}
 				}
+				storageTable->waitForWriteComplete();
 				break;
 			default:
 				break;

Thread
bzr commit into mysql-6.0-falcon branch (vvaintroub:2772) Bug#22165Vladislav Vaintroub11 Aug
  • Re: bzr commit into mysql-6.0-falcon branch (vvaintroub:2772) Bug#22165Olav Sandstaa13 Aug
    • RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2772) Bug#22165Vladislav Vaintroub13 Aug