TrinityCore
Loading...
Searching...
No Matches
Transaction.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Log.h"
19#include "Transaction.h"
20#include "MySQLConnection.h"
21#include "PreparedStatement.h"
22#include "Timer.h"
23#include <mysqld_error.h>
24#include <sstream>
25#include <thread>
26
28
29#define DEADLOCK_MAX_RETRY_TIME_MS 60000
30
31//- Append a raw ad-hoc query to the transaction
32void TransactionBase::Append(char const* sql)
33{
34 SQLElementData data;
35 data.type = SQL_ELEMENT_RAW;
36 data.element.query = strdup(sql);
37 m_queries.push_back(data);
38}
39
40//- Append a prepared statement to the transaction
43 SQLElementData data;
45 data.element.stmt = stmt;
46 m_queries.push_back(data);
47}
48
50{
51 // This might be called by explicit calls to Cleanup or by the auto-destructor
52 if (_cleanedUp)
53 return;
54
55 for (SQLElementData const& data : m_queries)
56 {
57 switch (data.type)
58 {
60 delete data.element.stmt;
61 break;
62 case SQL_ELEMENT_RAW:
63 free((void*)(data.element.query));
64 break;
65 }
66 }
67
68 m_queries.clear();
69 _cleanedUp = true;
70}
71
73{
74 int errorCode = TryExecute();
75 if (!errorCode)
76 return true;
77
78 if (errorCode == ER_LOCK_DEADLOCK)
79 {
80 std::string threadId = []()
81 {
82 // wrapped in lambda to fix false positive analysis warning C26115
83 std::ostringstream threadIdStream;
84 threadIdStream << std::this_thread::get_id();
85 return threadIdStream.str();
86 }();
87
88 // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
89 std::lock_guard<std::mutex> lock(_deadlockLock);
90
91 for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
92 {
93 if (!TryExecute())
94 return true;
95
96 TC_LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration, threadId);
97 }
98
99 TC_LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
100 }
101
102 // Clean up now.
104
105 return false;
106}
107
112
114{
115 m_trans->Cleanup();
116}
117
119{
120 int errorCode = TryExecute();
121 if (!errorCode)
122 {
123 m_result.set_value(true);
124 return true;
125 }
126
127 if (errorCode == ER_LOCK_DEADLOCK)
128 {
129 std::string threadId = []()
130 {
131 // wrapped in lambda to fix false positive analysis warning C26115
132 std::ostringstream threadIdStream;
133 threadIdStream << std::this_thread::get_id();
134 return threadIdStream.str();
135 }();
136
137 // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
138 std::lock_guard<std::mutex> lock(_deadlockLock);
139 for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
140 {
141 if (!TryExecute())
142 {
143 m_result.set_value(true);
144 return true;
145 }
146
147 TC_LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration, threadId);
148 }
149
150 TC_LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
151 }
152
153 // Clean up now.
155 m_result.set_value(false);
156
157 return false;
158}
159
161{
162 if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
163 {
164 m_callback(m_future.get());
165 return true;
166 }
167
168 return false;
169}
uint32_t uint32
Definition Define.h:133
#define TC_LOG_WARN(filterType__,...)
Definition Log.h:162
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
@ SQL_ELEMENT_RAW
@ SQL_ELEMENT_PREPARED
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
#define DEADLOCK_MAX_RETRY_TIME_MS
int ExecuteTransaction(std::shared_ptr< TransactionBase > transaction)
MySQLConnection * m_conn
std::vector< SQLElementData > m_queries
Definition Transaction.h:54
void AppendPreparedStatement(PreparedStatementBase *statement)
void Append(char const *sql)
TransactionFuture m_future
std::function< void(bool)> m_callback
std::shared_ptr< TransactionBase > m_trans
Definition Transaction.h:87
static std::mutex _deadlockLock
Definition Transaction.h:88
bool Execute() override
TransactionPromise m_result
SQLElementUnion element
SQLElementDataType type
PreparedStatementBase * stmt
char const * query