Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
DatabaseWorkerPool.h
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#ifndef _DATABASEWORKERPOOL_H
7#define _DATABASEWORKERPOOL_H
8
9#include "AdhocStatement.h"
10#include "Callback.h"
11#include "Common.h"
12#include "DatabaseQueue.h"
13#include "DatabaseWorker.h"
14#include "Log.h"
15#include "MySQLConnection.h"
16#include "PreparedStatement.h"
17#include "QueryHolder.h"
18#include "QueryResult.h"
19#include "Transaction.h"
20
21#define MIN_MYSQL_SERVER_VERSION 50100u
22#define MIN_MYSQL_CLIENT_VERSION 50100u
23
25{
27 bool Execute()
28 {
29 m_conn->Ping();
30 return true;
31 }
32};
33
34template <class T>
36{
37public:
38 /* Activity state */
39 DatabaseWorkerPool() : _queue(new Skyfire::DatabaseQueue()), _connectionInfo(NULL)
40 {
41 memset(_connectionCount, 0, sizeof(_connectionCount));
42 _connections.resize(IDX_SIZE);
43
44 WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
45 WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "SkyFire does not support MySQL versions below 5.1");
46 }
47
51
52 bool Open(const char* host, const char* port, const char* user, const char* password, const char* database, uint8 async_threads, uint8 synch_threads)
53 {
54 bool res = true;
55 _connectionInfo = new MySQLConnectionInfo(host, port, user, password, database);
56
57 SF_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
58 GetDatabaseName(), async_threads, synch_threads);
59
61 _connections[IDX_ASYNC].resize(async_threads);
62 for (uint8 i = 0; i < async_threads; ++i)
63 {
64 T* t = new T(_queue, *_connectionInfo);
65 res &= t->Open();
66 if (res) // only check mysql version if connection is valid
67 WPFatal(mysql_get_server_version(t->GetHandle()) >= MIN_MYSQL_SERVER_VERSION, "SkyFire does not support MySQL versions below 5.1");
68 _connections[IDX_ASYNC][i] = t;
70 }
71
73 _connections[IDX_SYNCH].resize(synch_threads);
74 for (uint8 i = 0; i < synch_threads; ++i)
75 {
76 T* t = new T(*_connectionInfo);
77 res &= t->Open();
78 _connections[IDX_SYNCH][i] = t;
80 }
81
82 if (res)
83 SF_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
85 else
86 SF_LOG_ERROR("sql.driver", "DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
87 "for specific errors.", GetDatabaseName());
88 return res;
89 }
90
91 bool Open(const std::string& infoString, uint8 async_threads, uint8 synch_threads)
92 {
93 bool res = true;
94 _connectionInfo = new MySQLConnectionInfo(infoString);
95
96 SF_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
97 GetDatabaseName(), async_threads, synch_threads);
98
100 _connections[IDX_ASYNC].resize(async_threads);
101 for (uint8 i = 0; i < async_threads; ++i)
102 {
103 T* t = new T(_queue, *_connectionInfo);
104 res &= t->Open();
105 if (res) // only check mysql version if connection is valid
106 WPFatal(mysql_get_server_version(t->GetHandle()) >= MIN_MYSQL_SERVER_VERSION, "Skyfire does not support MySQL versions below 5.1");
107 _connections[IDX_ASYNC][i] = t;
109 }
110
112 _connections[IDX_SYNCH].resize(synch_threads);
113 for (uint8 i = 0; i < synch_threads; ++i)
114 {
115 T* t = new T(*_connectionInfo);
116 res &= t->Open();
117 _connections[IDX_SYNCH][i] = t;
119 }
120
121 if (res)
122 SF_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
124 else
125 SF_LOG_ERROR("sql.driver", "DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
126 "for specific errors.", GetDatabaseName());
127 return res;
128 }
129
130 void Close()
131 {
132 SF_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
133
136 _queue->close();
137
138 for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i)
139 {
140 T* t = _connections[IDX_ASYNC][i];
141 DatabaseWorker* worker = t->m_worker;
142 worker->wait();
143 delete worker;
144 t->Close();
145 }
146
147 SF_LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. Proceeding with synchronous connections.",
149
154 for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
156
157 delete _queue;
158
159 SF_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
160 delete _connectionInfo;
161 _connectionInfo = NULL;
162 }
163
167
170 void Execute(const char* sql)
171 {
172 if (!sql)
173 return;
174
175 BasicStatementTask* task = new BasicStatementTask(sql);
176 Enqueue(task);
177 }
178
181 void PExecute(const char* sql, ...)
182 {
183 if (!sql)
184 return;
185
186 va_list ap;
187 char szQuery[MAX_QUERY_LEN];
188 va_start(ap, sql);
189 vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
190 va_end(ap);
191
192 Execute(szQuery);
193 }
194
198 {
200 Enqueue(task);
201 }
202
206
209 void DirectExecute(const char* sql)
210 {
211 if (!sql)
212 return;
213
214 T* t = GetFreeConnection();
215 t->Execute(sql);
216 t->Unlock();
217 }
218
221 void DirectPExecute(const char* sql, ...)
222 {
223 if (!sql)
224 return;
225
226 va_list ap;
227 char szQuery[MAX_QUERY_LEN];
228 va_start(ap, sql);
229 vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
230 va_end(ap);
231
232 return DirectExecute(szQuery);
233 }
234
238 {
239 T* t = GetFreeConnection();
240 t->Execute(stmt);
241 t->Unlock();
242
244 delete stmt;
245 }
246
250
253 QueryResult Query(const char* sql, T* conn = NULL)
254 {
255 if (!conn)
256 conn = GetFreeConnection();
257
258 ResultSet* result = conn->Query(sql);
259 conn->Unlock();
260 if (!result || !result->GetRowCount())
261 {
262 delete result;
263 return QueryResult(NULL);
264 }
265
266 result->NextRow();
267 return QueryResult(result);
268 }
269
272 QueryResult PQuery(const char* sql, T* conn, ...)
273 {
274 if (!sql)
275 return QueryResult(NULL);
276
277 va_list ap;
278 char szQuery[MAX_QUERY_LEN];
279 va_start(ap, conn);
280 vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
281 va_end(ap);
282
283 return Query(szQuery, conn);
284 }
285
288 QueryResult PQuery(const char* sql, ...)
289 {
290 if (!sql)
291 return QueryResult(NULL);
292
293 va_list ap;
294 char szQuery[MAX_QUERY_LEN];
295 va_start(ap, sql);
296 vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
297 va_end(ap);
298
299 return Query(szQuery);
300 }
301
306 {
307 T* t = GetFreeConnection();
308 PreparedResultSet* ret = t->Query(stmt);
309 t->Unlock();
310
312 delete stmt;
313
314 if (!ret || !ret->GetRowCount())
315 {
316 delete ret;
317 return PreparedQueryResult(NULL);
318 }
319
320 return PreparedQueryResult(ret);
321 }
322
326
330 {
332 BasicStatementTask* task = new BasicStatementTask(sql, res);
333 Enqueue(task);
334 return res;
335 }
336
339 QueryResultFuture AsyncPQuery(const char* sql, ...)
340 {
341 va_list ap;
342 char szQuery[MAX_QUERY_LEN];
343 va_start(ap, sql);
344 vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
345 va_end(ap);
346
347 return AsyncQuery(szQuery);
348 }
349
354 {
356 PreparedStatementTask* task = new PreparedStatementTask(stmt, res);
357 Enqueue(task);
358 return res;
359 }
360
366 {
368 SQLQueryHolderTask* task = new SQLQueryHolderTask(holder, res);
369 Enqueue(task);
370 return res;
371 }
372
376
382
386 {
387#ifdef SKYFIRE_DEBUG
391 switch (transaction->GetSize())
392 {
393 case 0:
394 SF_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
395 return;
396 case 1:
397 SF_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
398 break;
399 default:
400 break;
401 }
402#endif // SKYFIRE_DEBUG
403
404 Enqueue(new TransactionTask(transaction));
405 }
406
408 void Wait()
409 {
410 _queue->wait();
411 }
412
416 {
417 T* con = GetFreeConnection();
418 if (con->ExecuteTransaction(transaction))
419 {
420 con->Unlock(); // OK, operation succesful
421 return;
422 }
423
426 if (con->GetLastError() == 1213)
427 {
428 uint8 loopBreaker = 5;
429 for (uint8 i = 0; i < loopBreaker; ++i)
430 {
431 if (con->ExecuteTransaction(transaction))
432 break;
433 }
434 }
435
437 transaction->Cleanup();
438
439 con->Unlock();
440 }
441
445 {
446 if (trans.null())
447 Execute(stmt);
448 else
449 trans->Append(stmt);
450 }
451
454 void ExecuteOrAppend(SQLTransaction& trans, const char* sql)
455 {
456 if (trans.null())
457 Execute(sql);
458 else
459 trans->Append(sql);
460 }
461
465
470 {
471 return new PreparedStatement(index);
472 }
473
475 void EscapeString(std::string& str)
476 {
477 if (str.empty())
478 return;
479
480 char* buf = new char[str.size() * 2 + 1];
481 EscapeString(buf, str.c_str(), str.size());
482 str = buf;
483 delete[] buf;
484 }
485
488 {
490 for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
491 {
492 T* t = _connections[IDX_SYNCH][i];
493 if (t->LockIfReady())
494 {
495 t->Ping();
496 t->Unlock();
497 }
498 }
499
503 for (size_t i = 0; i < _connections[IDX_ASYNC].size(); ++i)
505 }
506
507private:
508 unsigned long EscapeString(char* to, const char* from, unsigned long length)
509 {
510 if (!to || !from || !length)
511 return 0;
512
513 return mysql_real_escape_string(_connections[IDX_SYNCH][0]->GetHandle(), to, from, length);
514 }
515
517 {
518 _queue->enqueue(op);
519 }
520
524 {
525 uint8 i = 0;
526 size_t num_cons = _connectionCount[IDX_SYNCH];
527 T* t = NULL;
529 for (;;)
530 {
531 t = _connections[IDX_SYNCH][++i % num_cons];
533 if (t->LockIfReady())
534 break;
535 }
536
537 return t;
538 }
539
540 char const* GetDatabaseName() const
541 {
542 return _connectionInfo->_database.c_str();
543 }
544
545private:
552
554 std::vector< std::vector<T*> > _connections;
556 MySQLConnectionInfo* _connectionInfo;
557};
558
559#endif
Skyfire::Future< QueryResult > QueryResultFuture
#define vsnprintf
Definition Common.h:100
#define MAX_QUERY_LEN
Definition Common.h:195
#define MIN_MYSQL_CLIENT_VERSION
#define MIN_MYSQL_SERVER_VERSION
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
#define WPFatal(cond, msg)
Definition Errors.h:25
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
Skyfire::Future< PreparedQueryResult > PreparedQueryResultFuture
Skyfire::Future< SQLQueryHolder * > QueryResultHolderFuture
Definition QueryHolder.h:30
Skyfire::AutoPtr< PreparedResultSet, Skyfire::Mutex > PreparedQueryResult
Definition QueryResult.h:94
Skyfire::AutoPtr< ResultSet, Skyfire::Mutex > QueryResult
Definition QueryResult.h:48
Skyfire::AutoPtr< Transaction, Skyfire::Mutex > SQLTransaction
Definition Transaction.h:42
void ExecuteOrAppend(SQLTransaction &trans, const char *sql)
QueryResult PQuery(const char *sql, T *conn,...)
void ExecuteOrAppend(SQLTransaction &trans, PreparedStatement *stmt)
PreparedQueryResultFuture AsyncQuery(PreparedStatement *stmt)
void PExecute(const char *sql,...)
bool Open(const std::string &infoString, uint8 async_threads, uint8 synch_threads)
void Enqueue(SQLOperation *op)
unsigned long EscapeString(char *to, const char *from, unsigned long length)
PreparedStatement * GetPreparedStatement(uint32 index)
QueryResult Query(const char *sql, T *conn=NULL)
void Execute(const char *sql)
void DirectCommitTransaction(SQLTransaction &transaction)
QueryResultFuture AsyncQuery(const char *sql)
bool Open(const char *host, const char *port, const char *user, const char *password, const char *database, uint8 async_threads, uint8 synch_threads)
void Execute(PreparedStatement *stmt)
std::vector< std::vector< CharacterDatabaseConnection * > > _connections
void KeepAlive()
Keeps all our MySQL connections alive, prevent the server from disconnecting us.
QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder *holder)
char const * GetDatabaseName() const
void Wait()
Blocks until all currently queued asynchronous operations have finished.
void DirectPExecute(const char *sql,...)
SQLTransaction BeginTransaction()
Begins an automanaged transaction pointer that will automatically rollback if not commited....
void DirectExecute(PreparedStatement *stmt)
void EscapeString(std::string &str)
Apply escape string'ing for current collation. (utf8).
void CommitTransaction(SQLTransaction transaction)
PreparedQueryResult Query(PreparedStatement *stmt)
void DirectExecute(const char *sql)
QueryResult PQuery(const char *sql,...)
QueryResultFuture AsyncPQuery(const char *sql,...)
bool Execute()
Operation for idle delaythreads.
uint64 GetRowCount() const
Definition QueryResult.h:57
uint64 GetRowCount() const
Definition QueryResult.h:25
bool NextRow()
MySQLConnection * m_conn
bool null() const
Definition AutoPtr.h:34