List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:August 13 2008 12:18pm
Subject:RE: bzr commit into mysql-6.0-falcon branch (vvaintroub:2772) Bug#22165
View as plain text  
Hi Olav,
Thanks for the comments.

Order of updates of triggers is  no problem for MySQL - in MySQL they belong
to server and not to storage engine.
It is  possible I screwed netfrastructure slightly, but I also doubt it -
Table::updateRecords does something completely
unrelated to records itself and seems to be just another kind of trigger.
Basically, all operations are already done to all 
involved records and this code does nothing else just updates the
cardinalities (and in Netfrastructure inserts Images(?) into the
hash table - if you read the  code path from Table::updateRecords() you'll
can be amazed).


>   * Question: You have moved updates of cardinalities outside of the
> region where you had a lock on
> transactionManager->committedTransactions.syncObject. Does this
> increase
> the likelihood that the cardinalities can be updated concurrently by
> multiple threads? (and if yes, does it really matter?)

Possibly it increases the likelihood , but it does not really matter.
Those cardinalities are for optimizer and it is ok if they are off by 1 or 2
(the optimizer really needs only an estimation)


Thanks,
Vlad



> -----Original Message-----
> From: Olav.Sandstaa@stripped [mailto:Olav.Sandstaa@stripped]
> Sent: Wednesday, August 13, 2008 8:17 AM
> To: Vladislav Vaintroub
> Cc: commits@stripped
> Subject: Re: bzr commit into mysql-6.0-falcon branch (vvaintroub:2772)
> Bug#22165
> 
> Hi Vlad,
> 
> Thanks for fixing this high priority bug. The patch looks very good and
> I have only minor comments:
> 
> Table.cpp:
> 
>   * The new method waitForWriteComplete() is declared to not return any
> value but in the implementation you have included a return statement.
> 
> Transaction.cpp:
> 
>   * Question: Before your change to Transaction::commit() all records
> were updated first and then all triggers were fired. After your change
> you do both in a single loop. This is an area where I neither know the
> code or what is correct but I have some vague memories about that an
> AFTER trigger should run "after all operations have been done to the
> involved records". Does your change here change semantic for triggers?
> As I said this is something I do not know very much about so I am just
> asking...
> 
>   * Question: You have moved updates of cardinalities outside of the
> region where you had a lock on
> transactionManager->committedTransactions.syncObject. Does this
> increase
> the likelihood that the cardinalities can be updated concurrently by
> multiple threads? (and if yes, does it really matter?)
> 
>   * Transaction;;hadRecords: the have text passed to the Sync object is
> "Transaction::releaseDependency". Should this be
> "Transaction::hasRecords" instead?
> 
> TransactionManager.cpp:
> 
>   * In the method waitForWriteComplete() I the string added to the Sync
> object should be "TransactionManager::waitForWriteComplete", not just
> "waitForWriteComplete".
> 
> If you think the answer to the question I had about order of updates
> and
> execution of triggers is "no problem" then this patch is "OK to push".
> 
> Regards,
> Olav
> 
> Vladislav Vaintroub wrote:
> > #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