aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMachiavelli <machiavelli.trinity@gmail.com>2011-01-13 20:07:09 +0100
committerMachiavelli <machiavelli.trinity@gmail.com>2011-01-13 20:07:09 +0100
commitcf9250c29f4e66f6a8fe6d14ace798a4252ce59b (patch)
tree00e9983260a4c20f7ffef1e2a2b05ba30d8b4772 /src
parent8d59953f9372c6876e0cd8078b44de6893082cbe (diff)
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.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/server/shared/Database/DatabaseWorkerPool.h24
-rwxr-xr-xsrc/server/shared/Database/MySQLConnection.cpp49
-rwxr-xr-xsrc/server/shared/Database/MySQLConnection.h4
-rwxr-xr-xsrc/server/shared/Database/Transaction.cpp47
-rwxr-xr-xsrc/server/shared/Database/Transaction.h4
5 files changed, 89 insertions, 39 deletions
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h
index 5c7ebfd796e..957d93d449b 100755
--- a/src/server/shared/Database/DatabaseWorkerPool.h
+++ b/src/server/shared/Database/DatabaseWorkerPool.h
@@ -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)
diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp
index 2a07fad7a9b..832a801f94b 100755
--- a/src/server/shared/Database/MySQLConnection.cpp
+++ b/src/server/shared/Database/MySQLConnection.cpp
@@ -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'"
diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h
index f96284de5f8..9cc57000171 100755
--- a/src/server/shared/Database/MySQLConnection.h
+++ b/src/server/shared/Database/MySQLConnection.h
@@ -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()
{
diff --git a/src/server/shared/Database/Transaction.cpp b/src/server/shared/Database/Transaction.cpp
index b09415c6c7c..8604dfc609c 100755
--- a/src/server/shared/Database/Transaction.cpp
+++ b/src/server/shared/Database/Transaction.cpp
@@ -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;
}
diff --git a/src/server/shared/Database/Transaction.h b/src/server/shared/Database/Transaction.h
index 46e13a84a14..da603b8cf50 100755
--- a/src/server/shared/Database/Transaction.h
+++ b/src/server/shared/Database/Transaction.h
@@ -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(){};