mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-21 01:37:37 +01:00
Core/DBLayer:
- Implement DatabaseWorkerPool::DirectCommitTransaction for synchronous transaction execution (as opposed to asynchronous/enqueued). - Add MySQL errno 1213 "Deadlock found when trying to get lock; try restarting transaction" handler. If 1213 is called the core will retry to directly execute the transaction a maximum of 5 times.
This commit is contained in:
@@ -353,6 +353,30 @@ class DatabaseWorkerPool
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
|
||||
//! Directly executes a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
void DirectCommitTransaction(SQLTransaction& transaction)
|
||||
{
|
||||
MySQLConnection* con = GetFreeConnection();
|
||||
if (con->ExecuteTransaction(transaction))
|
||||
{
|
||||
con->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
if (con->GetLastError() == 1213)
|
||||
{
|
||||
uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (con->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
con->Unlock();
|
||||
}
|
||||
|
||||
//! Method used to execute prepared statements in a diverse context.
|
||||
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
|
||||
void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt)
|
||||
|
||||
@@ -369,6 +369,52 @@ void MySQLConnection::CommitTransaction()
|
||||
Execute("COMMIT");
|
||||
}
|
||||
|
||||
bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
|
||||
{
|
||||
std::queue<SQLElementData> &queries = transaction->m_queries;
|
||||
if (queries.empty())
|
||||
return false;
|
||||
|
||||
BeginTransaction();
|
||||
while (!queries.empty())
|
||||
{
|
||||
SQLElementData data = queries.front();
|
||||
switch (data.type)
|
||||
{
|
||||
case SQL_ELEMENT_PREPARED:
|
||||
{
|
||||
PreparedStatement* stmt = data.element.stmt;
|
||||
ASSERT(stmt);
|
||||
if (!Execute(stmt))
|
||||
{
|
||||
sLog->outSQLDriver("[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
|
||||
RollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
delete data.element.stmt;
|
||||
}
|
||||
break;
|
||||
case SQL_ELEMENT_RAW:
|
||||
{
|
||||
const char* sql = data.element.query;
|
||||
ASSERT(sql);
|
||||
if (!Execute(sql))
|
||||
{
|
||||
sLog->outSQLDriver("[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
|
||||
RollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
free((void*)const_cast<char*>(sql));
|
||||
}
|
||||
break;
|
||||
}
|
||||
queries.pop();
|
||||
}
|
||||
|
||||
CommitTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
|
||||
{
|
||||
ASSERT(index < m_stmts.size());
|
||||
@@ -462,6 +508,9 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
return _HandleMySQLErrno(lErrno); // Call self (recursive)
|
||||
}
|
||||
|
||||
case 1213: // "Deadlock found when trying to get lock; try restarting transaction"
|
||||
return true; // Implemented in TransactionTask::Execute and DatabaseWorkerPool<T>::DirectCommitTransaction
|
||||
|
||||
// Query related errors - skip query
|
||||
case 1058: // "Column count doesn't match value count"
|
||||
case 1062: // "Duplicate entry '%s' for key '%d'"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <ace/Activation_Queue.h>
|
||||
|
||||
#include "DatabaseWorkerPool.h"
|
||||
#include "Transaction.h"
|
||||
#include "Util.h"
|
||||
|
||||
#ifndef _MYSQLCONNECTION_H
|
||||
@@ -91,10 +92,13 @@ class MySQLConnection
|
||||
void BeginTransaction();
|
||||
void RollbackTransaction();
|
||||
void CommitTransaction();
|
||||
bool ExecuteTransaction(SQLTransaction& transaction);
|
||||
|
||||
operator bool () const { return m_Mysql != NULL; }
|
||||
void Ping() { mysql_ping(m_Mysql); }
|
||||
|
||||
uint32 GetLastError() { return mysql_errno(m_Mysql); }
|
||||
|
||||
protected:
|
||||
bool LockIfReady()
|
||||
{
|
||||
|
||||
@@ -67,45 +67,16 @@ void Transaction::Cleanup()
|
||||
|
||||
bool TransactionTask::Execute()
|
||||
{
|
||||
std::queue<SQLElementData> &queries = m_trans->m_queries;
|
||||
if (queries.empty())
|
||||
return false;
|
||||
if (m_conn->ExecuteTransaction(m_trans))
|
||||
return true;
|
||||
|
||||
m_conn->BeginTransaction();
|
||||
while (!queries.empty())
|
||||
if (m_conn->GetLastError() == 1213)
|
||||
{
|
||||
SQLElementData data = queries.front();
|
||||
switch (data.type)
|
||||
{
|
||||
case SQL_ELEMENT_PREPARED:
|
||||
{
|
||||
PreparedStatement* stmt = data.element.stmt;
|
||||
ASSERT(stmt);
|
||||
if (!m_conn->Execute(stmt))
|
||||
{
|
||||
sLog->outSQLDriver("[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
|
||||
m_conn->RollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
delete data.element.stmt;
|
||||
}
|
||||
break;
|
||||
case SQL_ELEMENT_RAW:
|
||||
{
|
||||
const char* sql = data.element.query;
|
||||
ASSERT(sql);
|
||||
if (!m_conn->Execute(sql))
|
||||
{
|
||||
sLog->outSQLDriver("[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
|
||||
m_conn->RollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
free((void*)const_cast<char*>(sql));
|
||||
}
|
||||
break;
|
||||
}
|
||||
queries.pop();
|
||||
uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
if (m_conn->ExecuteTransaction(m_trans))
|
||||
return true;
|
||||
}
|
||||
m_conn->CommitTransaction();
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class PreparedStatement;
|
||||
class Transaction
|
||||
{
|
||||
friend class TransactionTask;
|
||||
friend class MySQLConnection;
|
||||
|
||||
public:
|
||||
Transaction() {}
|
||||
~Transaction() { Cleanup(); }
|
||||
@@ -49,7 +51,7 @@ class TransactionTask : public SQLOperation
|
||||
{
|
||||
template <class T> friend class DatabaseWorkerPool;
|
||||
friend class DatabaseWorker;
|
||||
|
||||
|
||||
public:
|
||||
TransactionTask(SQLTransaction trans) : m_trans(trans) {} ;
|
||||
~TransactionTask(){};
|
||||
|
||||
Reference in New Issue
Block a user