feat(Core/Database): implement db loader (#4431)
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DatabaseEnv.h"
|
||||||
|
|
||||||
|
WorldDatabaseWorkerPool WorldDatabase;
|
||||||
|
CharacterDatabaseWorkerPool CharacterDatabase;
|
||||||
|
LoginDatabaseWorkerPool LoginDatabase;
|
||||||
@@ -22,12 +22,17 @@
|
|||||||
#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )"
|
#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )"
|
||||||
#define _OFFSET_ "LIMIT %d, 1"
|
#define _OFFSET_ "LIMIT %d, 1"
|
||||||
|
|
||||||
#include "Implementation/LoginDatabase.h"
|
#include "LoginDatabase.h"
|
||||||
#include "Implementation/CharacterDatabase.h"
|
#include "CharacterDatabase.h"
|
||||||
#include "Implementation/WorldDatabase.h"
|
#include "WorldDatabase.h"
|
||||||
|
|
||||||
|
/// Accessor to the world database
|
||||||
extern WorldDatabaseWorkerPool WorldDatabase;
|
extern WorldDatabaseWorkerPool WorldDatabase;
|
||||||
|
|
||||||
|
/// Accessor to the character database
|
||||||
extern CharacterDatabaseWorkerPool CharacterDatabase;
|
extern CharacterDatabaseWorkerPool CharacterDatabase;
|
||||||
|
|
||||||
|
/// Accessor to the realm/login database
|
||||||
extern LoginDatabaseWorkerPool LoginDatabase;
|
extern LoginDatabaseWorkerPool LoginDatabase;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DatabaseLoader.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "DatabaseEnv.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Duration.h"
|
||||||
|
#include <mysqld_error.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name)
|
||||||
|
{
|
||||||
|
_open.push([this, name, &pool]() -> bool
|
||||||
|
{
|
||||||
|
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", "");
|
||||||
|
if (dbString.empty())
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("Database %s not specified in configuration file!", name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 const asyncThreads = sConfigMgr->GetOption<uint8>(name + "Database.WorkerThreads", 1);
|
||||||
|
if (asyncThreads < 1 || asyncThreads > 32)
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("%s database: invalid number of worker threads specified. Please pick a value between 1 and 32.", name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 const synchThreads = sConfigMgr->GetOption<uint8>(name + "Database.SynchThreads", 1);
|
||||||
|
|
||||||
|
pool.SetConnectionInfo(dbString, asyncThreads, synchThreads);
|
||||||
|
|
||||||
|
if (uint32 error = pool.Open())
|
||||||
|
{
|
||||||
|
// Try reconnect
|
||||||
|
if (error == CR_CONNECTION_ERROR)
|
||||||
|
{
|
||||||
|
// Possible improvement for future: make ATTEMPTS and SECONDS configurable values
|
||||||
|
uint32 const ATTEMPTS = 5;
|
||||||
|
Seconds durationSecs = 5s;
|
||||||
|
uint32 count = 1;
|
||||||
|
|
||||||
|
auto sleepThread = [&]()
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("> Retrying after %u seconds", durationSecs.count());
|
||||||
|
std::this_thread::sleep_for(durationSecs);
|
||||||
|
};
|
||||||
|
|
||||||
|
sleepThread();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
error = pool.Open();
|
||||||
|
|
||||||
|
if (error == CR_CONNECTION_ERROR)
|
||||||
|
{
|
||||||
|
sleepThread();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (count < ATTEMPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the error wasn't handled quit
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile for specific errors", name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the close operation
|
||||||
|
_close.push([&pool]
|
||||||
|
{
|
||||||
|
pool.Close();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
_prepare.push([name, &pool]() -> bool
|
||||||
|
{
|
||||||
|
if (!pool.PrepareStatements())
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("Could not prepare statements of the %s database, see log for details.", name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseLoader::Load()
|
||||||
|
{
|
||||||
|
return OpenDatabases() && PrepareStatements();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseLoader::OpenDatabases()
|
||||||
|
{
|
||||||
|
return Process(_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseLoader::PrepareStatements()
|
||||||
|
{
|
||||||
|
return Process(_prepare);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseLoader::Process(std::queue<Predicate>& queue)
|
||||||
|
{
|
||||||
|
while (!queue.empty())
|
||||||
|
{
|
||||||
|
if (!queue.front()())
|
||||||
|
{
|
||||||
|
// Close all open databases which have a registered close operation
|
||||||
|
while (!_close.empty())
|
||||||
|
{
|
||||||
|
_close.top()();
|
||||||
|
_close.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>&, std::string const&);
|
||||||
|
template DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&);
|
||||||
|
template DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DatabaseLoader_h__
|
||||||
|
#define DatabaseLoader_h__
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <queue>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class DatabaseWorkerPool;
|
||||||
|
|
||||||
|
// A helper class to initiate all database worker pools,
|
||||||
|
// handles updating, delays preparing of statements and cleans up on failure.
|
||||||
|
class DatabaseLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Register a database to the loader (lazy implemented)
|
||||||
|
template <class T>
|
||||||
|
DatabaseLoader& AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name);
|
||||||
|
|
||||||
|
// Load all databases
|
||||||
|
bool Load();
|
||||||
|
|
||||||
|
enum DatabaseTypeFlags
|
||||||
|
{
|
||||||
|
DATABASE_NONE = 0,
|
||||||
|
|
||||||
|
DATABASE_LOGIN = 1,
|
||||||
|
DATABASE_CHARACTER = 2,
|
||||||
|
DATABASE_WORLD = 4,
|
||||||
|
|
||||||
|
DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool OpenDatabases();
|
||||||
|
bool PrepareStatements();
|
||||||
|
|
||||||
|
using Predicate = std::function<bool()>;
|
||||||
|
using Closer = std::function<void()>;
|
||||||
|
|
||||||
|
// Invokes all functions in the given queue and closes the databases on errors.
|
||||||
|
// Returns false when there was an error.
|
||||||
|
bool Process(std::queue<Predicate>& queue);
|
||||||
|
|
||||||
|
std::queue<Predicate> _open, _prepare;
|
||||||
|
std::stack<Closer> _close;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DatabaseLoader_h__
|
||||||
@@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
template <class T> DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
template <class T> DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
||||||
_mqueue(new ACE_Message_Queue<ACE_SYNCH>(2 * 1024 * 1024, 2 * 1024 * 1024)),
|
_mqueue(new ACE_Message_Queue<ACE_SYNCH>(2 * 1024 * 1024, 2 * 1024 * 1024)),
|
||||||
_queue(new ACE_Activation_Queue(_mqueue))
|
_queue(new ACE_Activation_Queue(_mqueue)),
|
||||||
|
_async_threads(0),
|
||||||
|
_synch_threads(0)
|
||||||
{
|
{
|
||||||
memset(_connectionCount, 0, sizeof(_connectionCount));
|
memset(_connectionCount, 0, sizeof(_connectionCount));
|
||||||
_connections.resize(IDX_SIZE);
|
_connections.resize(IDX_SIZE);
|
||||||
@@ -22,45 +24,39 @@ template <class T> DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool DatabaseWorkerPool<T>::Open(const std::string& infoString, uint8 async_threads, uint8 synch_threads)
|
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString,
|
||||||
|
uint8 const asyncThreads, uint8 const synchThreads)
|
||||||
{
|
{
|
||||||
bool res = true;
|
_connectionInfo = std::make_unique<MySQLConnectionInfo>(infoString);
|
||||||
_connectionInfo = MySQLConnectionInfo(infoString);
|
|
||||||
|
_async_threads = asyncThreads;
|
||||||
|
_synch_threads = synchThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
uint32 DatabaseWorkerPool<T>::Open()
|
||||||
|
{
|
||||||
|
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||||
|
|
||||||
sLog->outSQLDriver("Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
|
sLog->outSQLDriver("Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
|
||||||
GetDatabaseName(), async_threads, synch_threads);
|
GetDatabaseName(), _async_threads, _synch_threads);
|
||||||
|
|
||||||
//! Open asynchronous connections (delayed operations)
|
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
|
||||||
_connections[IDX_ASYNC].resize(async_threads);
|
|
||||||
for (uint8 i = 0; i < async_threads; ++i)
|
if (error)
|
||||||
{
|
{
|
||||||
T* t = new T(_queue, _connectionInfo);
|
return error;
|
||||||
res &= t->Open();
|
|
||||||
if (res) // only check mysql version if connection is valid
|
|
||||||
WPFatal(mysql_get_server_version(t->GetHandle()) >= MIN_MYSQL_SERVER_VERSION, "AzerothCore does not support MySQL versions below 5.7");
|
|
||||||
|
|
||||||
_connections[IDX_ASYNC][i] = t;
|
|
||||||
++_connectionCount[IDX_ASYNC];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Open synchronous connections (direct, blocking operations)
|
error = OpenConnections(IDX_SYNCH, _synch_threads);
|
||||||
_connections[IDX_SYNCH].resize(synch_threads);
|
|
||||||
for (uint8 i = 0; i < synch_threads; ++i)
|
if (!error)
|
||||||
{
|
{
|
||||||
T* t = new T(_connectionInfo);
|
sLog->outSQLDriver("DatabasePool '%s' opened successfully. %u total connections running.",
|
||||||
res &= t->Open();
|
GetDatabaseName(), (_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
|
||||||
_connections[IDX_SYNCH][i] = t;
|
|
||||||
++_connectionCount[IDX_SYNCH];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res)
|
return error;
|
||||||
sLog->outSQLDriver("DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
|
|
||||||
(_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
|
|
||||||
else
|
|
||||||
sLog->outError("DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
|
|
||||||
"for specific errors.", GetDatabaseName());
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -99,6 +95,91 @@ void DatabaseWorkerPool<T>::Close()
|
|||||||
sLog->outSQLDriver("All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
sLog->outSQLDriver("All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConnections)
|
||||||
|
{
|
||||||
|
_connections[type].resize(numConnections);
|
||||||
|
for (uint8 i = 0; i < numConnections; ++i)
|
||||||
|
{
|
||||||
|
T* t;
|
||||||
|
|
||||||
|
if (type == IDX_ASYNC)
|
||||||
|
{
|
||||||
|
t = new T(_queue, *_connectionInfo);
|
||||||
|
}
|
||||||
|
else if (type == IDX_SYNCH)
|
||||||
|
{
|
||||||
|
t = new T(*_connectionInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT(false, "> Incorrect InternalIndex (%u)", static_cast<uint32>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
_connections[type][i] = t;
|
||||||
|
++_connectionCount[type];
|
||||||
|
|
||||||
|
uint32 error = t->Open();
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
|
||||||
|
{
|
||||||
|
sLog->outSQLDriver("Not support MySQL versions below 5.7");
|
||||||
|
error = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to open a connection or invalid version, abort and cleanup
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
while (_connectionCount[type] != 0)
|
||||||
|
{
|
||||||
|
T* t = _connections[type][i--];
|
||||||
|
delete t;
|
||||||
|
--_connectionCount[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything is fine
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool DatabaseWorkerPool<T>::PrepareStatements()
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < IDX_SIZE; ++i)
|
||||||
|
{
|
||||||
|
for (uint32 c = 0; c < _connectionCount[i]; ++c)
|
||||||
|
{
|
||||||
|
T* t = _connections[i][c];
|
||||||
|
t->LockIfReady();
|
||||||
|
|
||||||
|
if (!t->PrepareStatements())
|
||||||
|
{
|
||||||
|
t->Unlock();
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t->Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
char const* DatabaseWorkerPool<T>::GetDatabaseName() const
|
||||||
|
{
|
||||||
|
return _connectionInfo->database.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void DatabaseWorkerPool<T>::Execute(const char* sql)
|
void DatabaseWorkerPool<T>::Execute(const char* sql)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#ifndef _DATABASEWORKERPOOL_H
|
#ifndef _DATABASEWORKERPOOL_H
|
||||||
#define _DATABASEWORKERPOOL_H
|
#define _DATABASEWORKERPOOL_H
|
||||||
|
|
||||||
#include <ace/Thread_Mutex.h>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Callback.h"
|
#include "Callback.h"
|
||||||
#include "MySQLConnection.h"
|
#include "MySQLConnection.h"
|
||||||
@@ -20,6 +18,7 @@
|
|||||||
#include "QueryHolder.h"
|
#include "QueryHolder.h"
|
||||||
#include "AdhocStatement.h"
|
#include "AdhocStatement.h"
|
||||||
#include "StringFormat.h"
|
#include "StringFormat.h"
|
||||||
|
#include <ace/Thread_Mutex.h>
|
||||||
|
|
||||||
class PingOperation : public SQLOperation
|
class PingOperation : public SQLOperation
|
||||||
{
|
{
|
||||||
@@ -37,13 +36,20 @@ class DatabaseWorkerPool
|
|||||||
public:
|
public:
|
||||||
/* Activity state */
|
/* Activity state */
|
||||||
DatabaseWorkerPool();
|
DatabaseWorkerPool();
|
||||||
|
|
||||||
~DatabaseWorkerPool() = default;
|
~DatabaseWorkerPool() = default;
|
||||||
|
|
||||||
bool Open(const std::string& infoString, uint8 async_threads, uint8 synch_threads);
|
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
|
||||||
|
uint32 Open();
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
//! Prepares all prepared statements
|
||||||
|
bool PrepareStatements();
|
||||||
|
|
||||||
|
inline MySQLConnectionInfo const* GetConnectionInfo() const
|
||||||
|
{
|
||||||
|
return _connectionInfo.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Delayed one-way statement methods.
|
Delayed one-way statement methods.
|
||||||
*/
|
*/
|
||||||
@@ -199,11 +205,6 @@ public:
|
|||||||
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
|
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
|
||||||
void KeepAlive();
|
void KeepAlive();
|
||||||
|
|
||||||
[[nodiscard]] char const* GetDatabaseName() const
|
|
||||||
{
|
|
||||||
return _connectionInfo.database.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EscapeString(std::string& str)
|
void EscapeString(std::string& str)
|
||||||
{
|
{
|
||||||
if (str.empty())
|
if (str.empty())
|
||||||
@@ -216,28 +217,33 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Enqueue(SQLOperation* op)
|
enum InternalIndex
|
||||||
{
|
|
||||||
_queue->enqueue(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets a free connection in the synchronous connection pool.
|
|
||||||
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
|
|
||||||
T* GetFreeConnection();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum _internalIndex
|
|
||||||
{
|
{
|
||||||
IDX_ASYNC,
|
IDX_ASYNC,
|
||||||
IDX_SYNCH,
|
IDX_SYNCH,
|
||||||
IDX_SIZE
|
IDX_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
ACE_Message_Queue<ACE_SYNCH>* _mqueue;
|
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
|
||||||
ACE_Activation_Queue* _queue; //! Queue shared by async worker threads.
|
|
||||||
std::vector<std::vector<T*>> _connections;
|
void Enqueue(SQLOperation* op)
|
||||||
uint32 _connectionCount[2]; //! Counter of MySQL connections;
|
{
|
||||||
MySQLConnectionInfo _connectionInfo;
|
_queue->enqueue(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] char const* GetDatabaseName() const;
|
||||||
|
|
||||||
|
//! Gets a free connection in the synchronous connection pool.
|
||||||
|
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
|
||||||
|
T* GetFreeConnection();
|
||||||
|
|
||||||
|
ACE_Message_Queue<ACE_SYNCH>* _mqueue;
|
||||||
|
ACE_Activation_Queue* _queue; //! Queue shared by async worker threads.
|
||||||
|
std::vector<std::vector<T*>> _connections;
|
||||||
|
uint32 _connectionCount[IDX_SIZE]; //! Counter of MySQL connections;
|
||||||
|
std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
|
||||||
|
std::vector<uint8> _preparedStatementSize;
|
||||||
|
uint8 _async_threads, _synch_threads;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ MySQLConnection::MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInf
|
|||||||
|
|
||||||
MySQLConnection::~MySQLConnection()
|
MySQLConnection::~MySQLConnection()
|
||||||
{
|
{
|
||||||
ASSERT (m_Mysql); /// MySQL context must be present at this point
|
for (auto stmt : m_stmts)
|
||||||
|
delete stmt;
|
||||||
|
|
||||||
for (size_t i = 0; i < m_stmts.size(); ++i)
|
if (m_Mysql)
|
||||||
delete m_stmts[i];
|
{
|
||||||
|
mysql_close(m_Mysql);
|
||||||
mysql_close(m_Mysql);
|
m_Mysql = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLConnection::Close()
|
void MySQLConnection::Close()
|
||||||
@@ -61,10 +63,9 @@ void MySQLConnection::Close()
|
|||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MySQLConnection::Open()
|
uint32 MySQLConnection::Open()
|
||||||
{
|
{
|
||||||
MYSQL* mysqlInit;
|
MYSQL* mysqlInit = mysql_init(nullptr);
|
||||||
mysqlInit = mysql_init(nullptr);
|
|
||||||
if (!mysqlInit)
|
if (!mysqlInit)
|
||||||
{
|
{
|
||||||
sLog->outError("Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str());
|
sLog->outError("Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str());
|
||||||
@@ -106,59 +107,44 @@ bool MySQLConnection::Open()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Possible improvement for future: make ATTEMPTS and SECONDS configurable values
|
m_Mysql = mysql_real_connect(
|
||||||
uint32 const ATTEMPTS = 180;
|
mysqlInit,
|
||||||
|
m_connectionInfo.host.c_str(),
|
||||||
|
m_connectionInfo.user.c_str(),
|
||||||
|
m_connectionInfo.password.c_str(),
|
||||||
|
m_connectionInfo.database.c_str(),
|
||||||
|
port,
|
||||||
|
unix_socket,
|
||||||
|
0);
|
||||||
|
|
||||||
uint32 count = 0;
|
if (m_Mysql)
|
||||||
do
|
|
||||||
{
|
{
|
||||||
m_Mysql = mysql_real_connect(
|
if (!m_reconnecting)
|
||||||
mysqlInit,
|
|
||||||
m_connectionInfo.host.c_str(),
|
|
||||||
m_connectionInfo.user.c_str(),
|
|
||||||
m_connectionInfo.password.c_str(),
|
|
||||||
m_connectionInfo.database.c_str(),
|
|
||||||
port,
|
|
||||||
unix_socket,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (m_Mysql)
|
|
||||||
{
|
{
|
||||||
if (!m_reconnecting)
|
sLog->outSQLDriver("MySQL client library: %s", mysql_get_client_info());
|
||||||
|
sLog->outSQLDriver("MySQL server ver: %s ", mysql_get_server_info(m_Mysql));
|
||||||
|
|
||||||
|
if (mysql_get_server_version(m_Mysql) != mysql_get_client_version())
|
||||||
{
|
{
|
||||||
sLog->outSQLDriver("MySQL client library: %s", mysql_get_client_info());
|
sLog->outSQLDriver("[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements.");
|
||||||
sLog->outSQLDriver("MySQL server ver: %s ", mysql_get_server_info(m_Mysql));
|
|
||||||
// MySQL version above 5.1 IS required in both client and server and there is no known issue with different versions above 5.1
|
|
||||||
// if (mysql_get_server_version(m_Mysql) != mysql_get_client_version())
|
|
||||||
// sLog->outInfo(LOG_FILTER_SQL, "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements.");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||||
sLog->outDetail("Connected to MySQL database at %s", m_connectionInfo.host.c_str());
|
sLog->outDetail("Connected to MySQL database at %s", m_connectionInfo.host.c_str());
|
||||||
#endif
|
#endif
|
||||||
mysql_autocommit(m_Mysql, 1);
|
mysql_autocommit(m_Mysql, 1);
|
||||||
|
|
||||||
// set connection properties to UTF8 to properly handle locales for different
|
// set connection properties to UTF8 to properly handle locales for different
|
||||||
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
|
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
|
||||||
mysql_set_character_set(m_Mysql, "utf8");
|
mysql_set_character_set(m_Mysql, "utf8");
|
||||||
return PrepareStatements();
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
sLog->outError("Could not connect to MySQL database at %s: %s\n", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
|
|
||||||
sLog->outError("Retrying in 10 seconds...\n\n");
|
|
||||||
std::this_thread::sleep_for(10s);
|
|
||||||
}
|
|
||||||
} while (!m_Mysql && count < ATTEMPTS);
|
|
||||||
|
|
||||||
sLog->outError(
|
sLog->outError("Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
|
||||||
"Could not connect to MySQL database at %s: %s after %d attempts\n",
|
uint32 errorCode = mysql_errno(mysqlInit);
|
||||||
m_connectionInfo.host.c_str(),
|
|
||||||
mysql_error(mysqlInit),
|
|
||||||
ATTEMPTS);
|
|
||||||
mysql_close(mysqlInit);
|
mysql_close(mysqlInit);
|
||||||
return false;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MySQLConnection::PrepareStatements()
|
bool MySQLConnection::PrepareStatements()
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ public:
|
|||||||
MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
|
MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
|
||||||
virtual ~MySQLConnection();
|
virtual ~MySQLConnection();
|
||||||
|
|
||||||
virtual bool Open();
|
virtual uint32 Open();
|
||||||
void Close();
|
void Close();
|
||||||
|
bool PrepareStatements();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Execute(const char* sql);
|
bool Execute(const char* sql);
|
||||||
@@ -102,7 +103,6 @@ protected:
|
|||||||
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
|
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
|
||||||
void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags);
|
void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags);
|
||||||
|
|
||||||
bool PrepareStatements();
|
|
||||||
virtual void DoPrepareStatements() = 0;
|
virtual void DoPrepareStatements() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -121,6 +121,9 @@ private:
|
|||||||
MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging)
|
MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging)
|
||||||
ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements)
|
ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements)
|
||||||
ACE_Thread_Mutex m_Mutex;
|
ACE_Thread_Mutex m_Mutex;
|
||||||
|
|
||||||
|
MySQLConnection(MySQLConnection const& right) = delete;
|
||||||
|
MySQLConnection& operator=(MySQLConnection const& right) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,21 +12,22 @@
|
|||||||
* authentication server
|
* authentication server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ace/Dev_Poll_Reactor.h>
|
|
||||||
#include <ace/TP_Reactor.h>
|
|
||||||
#include <ace/ACE.h>
|
|
||||||
#include <openssl/opensslv.h>
|
|
||||||
#include <openssl/crypto.h>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Database/DatabaseEnv.h"
|
#include "DatabaseEnv.h"
|
||||||
#include "Configuration/Config.h"
|
#include "Config.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "GitRevision.h"
|
#include "GitRevision.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "SignalHandler.h"
|
#include "SignalHandler.h"
|
||||||
#include "RealmList.h"
|
#include "RealmList.h"
|
||||||
#include "RealmAcceptor.h"
|
#include "RealmAcceptor.h"
|
||||||
|
#include "DatabaseLoader.h"
|
||||||
|
#include <ace/Dev_Poll_Reactor.h>
|
||||||
|
#include <ace/TP_Reactor.h>
|
||||||
|
#include <ace/ACE.h>
|
||||||
|
#include <ace/Sig_Handler.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <openssl/crypto.h>
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
@@ -43,8 +44,6 @@ void StopDB();
|
|||||||
|
|
||||||
bool stopEvent = false; // Setting it to true stops the server
|
bool stopEvent = false; // Setting it to true stops the server
|
||||||
|
|
||||||
LoginDatabaseWorkerPool LoginDatabase; // Accessor to the authserver database
|
|
||||||
|
|
||||||
/// Print out the usage string for this program on the console.
|
/// Print out the usage string for this program on the console.
|
||||||
void usage(const char* prog)
|
void usage(const char* prog)
|
||||||
{
|
{
|
||||||
@@ -281,33 +280,15 @@ bool StartDB()
|
|||||||
{
|
{
|
||||||
MySQL::Library_Init();
|
MySQL::Library_Init();
|
||||||
|
|
||||||
std::string dbstring = sConfigMgr->GetOption<std::string>("LoginDatabaseInfo", "");
|
// Load databases
|
||||||
if (dbstring.empty())
|
// NOTE: While authserver is singlethreaded you should keep synch_threads == 1.
|
||||||
{
|
// Increasing it is just silly since only 1 will be used ever.
|
||||||
sLog->outError("Database not specified");
|
DatabaseLoader loader;
|
||||||
|
loader
|
||||||
|
.AddDatabase(LoginDatabase, "Login");
|
||||||
|
|
||||||
|
if (!loader.Load())
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
int32 worker_threads = sConfigMgr->GetOption<int32>("LoginDatabase.WorkerThreads", 1);
|
|
||||||
if (worker_threads < 1 || worker_threads > 32)
|
|
||||||
{
|
|
||||||
sLog->outError("Improper value specified for LoginDatabase.WorkerThreads, defaulting to 1.");
|
|
||||||
worker_threads = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 synch_threads = sConfigMgr->GetOption<int32>("LoginDatabase.SynchThreads", 1);
|
|
||||||
if (synch_threads < 1 || synch_threads > 32)
|
|
||||||
{
|
|
||||||
sLog->outError("Improper value specified for LoginDatabase.SynchThreads, defaulting to 1.");
|
|
||||||
synch_threads = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: While authserver is singlethreaded you should keep synch_threads == 1. Increasing it is just silly since only 1 will be used ever.
|
|
||||||
if (!LoginDatabase.Open(dbstring.c_str(), uint8(worker_threads), uint8(synch_threads)))
|
|
||||||
{
|
|
||||||
sLog->outError("Cannot connect to database");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sLog->outString("Started auth database connection pool.");
|
sLog->outString("Started auth database connection pool.");
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -19852,7 +19852,6 @@ bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
|
Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
|
||||||
MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty);
|
MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty);
|
||||||
if (LevelMin || LevelMax || ilvlRequirementNotMet
|
if (LevelMin || LevelMax || ilvlRequirementNotMet
|
||||||
|
|||||||
@@ -6342,7 +6342,6 @@ void ObjectMgr::LoadAccessRequirements()
|
|||||||
currentRequirementsList->push_back(progression_requirement);
|
currentRequirementsList->push_back(progression_requirement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} while (progression_requirements_results->NextRow());
|
} while (progression_requirements_results->NextRow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6360,7 +6359,6 @@ void ObjectMgr::LoadAccessRequirements()
|
|||||||
_accessRequirementStore[mapid][difficulty] = ar;
|
_accessRequirementStore[mapid][difficulty] = ar;
|
||||||
} while (access_template_result->NextRow());
|
} while (access_template_result->NextRow());
|
||||||
|
|
||||||
|
|
||||||
sLog->outString(">> Loaded %u rows from dungeon_access_template and %u rows from dungeon_access_requirements in %u ms", count, countProgressionRequirements, GetMSTimeDiffToNow(oldMSTime));
|
sLog->outString(">> Loaded %u rows from dungeon_access_template and %u rows from dungeon_access_requirements in %u ms", count, countProgressionRequirements, GetMSTimeDiffToNow(oldMSTime));
|
||||||
sLog->outString();
|
sLog->outString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,5 +53,4 @@ inline AI* GetAhnkahetAI(T* obj)
|
|||||||
return GetInstanceAI<AI>(obj, AhnahetScriptName);
|
return GetInstanceAI<AI>(obj, AhnahetScriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "WorldRunnable.h"
|
#include "WorldRunnable.h"
|
||||||
#include "WorldSocket.h"
|
#include "WorldSocket.h"
|
||||||
#include "WorldSocketMgr.h"
|
#include "WorldSocketMgr.h"
|
||||||
|
#include "DatabaseLoader.h"
|
||||||
#include <ace/Sig_Handler.h>
|
#include <ace/Sig_Handler.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -388,80 +389,16 @@ bool Master::_StartDB()
|
|||||||
MySQL::Library_Init();
|
MySQL::Library_Init();
|
||||||
|
|
||||||
sLog->SetLogDB(false);
|
sLog->SetLogDB(false);
|
||||||
std::string dbstring;
|
|
||||||
uint8 async_threads, synch_threads;
|
|
||||||
|
|
||||||
dbstring = sConfigMgr->GetOption<std::string>("WorldDatabaseInfo", "");
|
// Load databases
|
||||||
if (dbstring.empty())
|
DatabaseLoader loader;
|
||||||
{
|
loader
|
||||||
sLog->outError("World database not specified in configuration file");
|
.AddDatabase(LoginDatabase, "Login")
|
||||||
|
.AddDatabase(CharacterDatabase, "Character")
|
||||||
|
.AddDatabase(WorldDatabase, "World");
|
||||||
|
|
||||||
|
if (!loader.Load())
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
async_threads = uint8(sConfigMgr->GetOption<int32>("WorldDatabase.WorkerThreads", 1));
|
|
||||||
if (async_threads < 1 || async_threads > 32)
|
|
||||||
{
|
|
||||||
sLog->outError("World database: invalid number of worker threads specified. "
|
|
||||||
"Please pick a value between 1 and 32.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
synch_threads = uint8(sConfigMgr->GetOption<int32>("WorldDatabase.SynchThreads", 1));
|
|
||||||
///- Initialise the world database
|
|
||||||
if (!WorldDatabase.Open(dbstring, async_threads, synch_threads))
|
|
||||||
{
|
|
||||||
sLog->outError("Cannot connect to world database %s", dbstring.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Get character database info from configuration file
|
|
||||||
dbstring = sConfigMgr->GetOption<std::string>("CharacterDatabaseInfo", "");
|
|
||||||
if (dbstring.empty())
|
|
||||||
{
|
|
||||||
sLog->outError("Character database not specified in configuration file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async_threads = uint8(sConfigMgr->GetOption<int32>("CharacterDatabase.WorkerThreads", 1));
|
|
||||||
if (async_threads < 1 || async_threads > 32)
|
|
||||||
{
|
|
||||||
sLog->outError("Character database: invalid number of worker threads specified. "
|
|
||||||
"Please pick a value between 1 and 32.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
synch_threads = uint8(sConfigMgr->GetOption<int32>("CharacterDatabase.SynchThreads", 2));
|
|
||||||
|
|
||||||
///- Initialise the Character database
|
|
||||||
if (!CharacterDatabase.Open(dbstring, async_threads, synch_threads))
|
|
||||||
{
|
|
||||||
sLog->outError("Cannot connect to Character database %s", dbstring.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Get login database info from configuration file
|
|
||||||
dbstring = sConfigMgr->GetOption<std::string>("LoginDatabaseInfo", "");
|
|
||||||
if (dbstring.empty())
|
|
||||||
{
|
|
||||||
sLog->outError("Login database not specified in configuration file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async_threads = uint8(sConfigMgr->GetOption<int32>("LoginDatabase.WorkerThreads", 1));
|
|
||||||
if (async_threads < 1 || async_threads > 32)
|
|
||||||
{
|
|
||||||
sLog->outError("Login database: invalid number of worker threads specified. "
|
|
||||||
"Please pick a value between 1 and 32.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
synch_threads = uint8(sConfigMgr->GetOption<int32>("LoginDatabase.SynchThreads", 1));
|
|
||||||
///- Initialise the login database
|
|
||||||
if (!LoginDatabase.Open(dbstring, async_threads, synch_threads))
|
|
||||||
{
|
|
||||||
sLog->outError("Cannot connect to login database %s", dbstring.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///- Get the realm Id from the configuration file
|
///- Get the realm Id from the configuration file
|
||||||
realmID = sConfigMgr->GetOption<int32>("RealmID", 0);
|
realmID = sConfigMgr->GetOption<int32>("RealmID", 0);
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ bool Model::ConvertToVMAPModel(const char* outfilename)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Vec3D fixCoordSystem(Vec3D const& v)
|
Vec3D fixCoordSystem(Vec3D const& v)
|
||||||
{
|
{
|
||||||
return Vec3D(v.x, v.z, -v.y);
|
return Vec3D(v.x, v.z, -v.y);
|
||||||
@@ -250,4 +249,3 @@ void Doodad::ExtractSet(WMODoodadData const& doodadData, ADT::MODF const& wmo, u
|
|||||||
fwrite(ModelInstName, sizeof(char), nlen, pDirfile);
|
fwrite(ModelInstName, sizeof(char), nlen, pDirfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -558,5 +558,4 @@ void MapObject::Extract(ADT::MODF const& mapObjDef, char const* WmoInstName, uin
|
|||||||
fwrite(&nlen, sizeof(uint32), 1, pDirfile);
|
fwrite(&nlen, sizeof(uint32), 1, pDirfile);
|
||||||
fwrite(WmoInstName, sizeof(char), nlen, pDirfile);
|
fwrite(WmoInstName, sizeof(char), nlen, pDirfile);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user