diff options
author | Machiavelli <none@none> | 2010-08-18 02:25:52 +0200 |
---|---|---|
committer | Machiavelli <none@none> | 2010-08-18 02:25:52 +0200 |
commit | 87218eadcdeac5ba86a035edfd079958405cb24f (patch) | |
tree | b72020ed0d390953b70d2026bf4c0b16c8271d11 /src | |
parent | 1ab2bd6d58adf35090ca3a9ef82eee00a14ff507 (diff) |
* HIGHLY EXPERIMENTAL - USE AT OWN RISK *
Database Layer:
- Implement connection pooling: Instead of 1 delay thread per database, you can configure between 1 and 32 worker threads that have a seperate thread in the core and have a seperate connection to the MySQL server (based on raczman/Albator´s database layer for Trinitycore3)
- Implement a configurable thread bundle for synchroneous requests from seperate core threads (see worldserver.conf.dist for more info)
- Every mapupdate thread now has its seperate MySQL connection to the world and characters database
- Drop inconsistent PExecuteLog function - query logging will be implemented CONSISTENTLY later
- Drop current prepared statement interface - this will be done *properly* later
- You´ll need to update your worldserver.conf and authserver.conf
- You´re recommended to make a backup of your databases before using this.
* HIGHLY EXPERIMENTAL - USE AT OWN RISK *
* HIGHLY EXPERIMENTAL - USE AT OWN RISK *
etc.
--HG--
branch : trunk
Diffstat (limited to 'src')
39 files changed, 1565 insertions, 728 deletions
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 88a98cd6cdb..849e1f71248 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -24,7 +24,6 @@ #include "Common.h" #include "Database/DatabaseEnv.h" -#include "Database/PreparedStatements.h" #include "Configuration/Config.h" #include "Log.h" @@ -315,7 +314,7 @@ extern int main(int argc, char **argv) { loopCounter = 0; sLog.outDetail("Ping MySQL to keep connection alive"); - sPreparedStatement.Query(&LoginDatabase, "auth_ping"); + LoginDatabase.Query("SELECT 1 FROM realmlist"); } #ifdef _WIN32 if (m_ServiceStatus == 0) stopEvent = true; @@ -323,9 +322,8 @@ extern int main(int argc, char **argv) #endif } - ///- Wait for the delay thread to exit - LoginDatabase.ThreadEnd(); - LoginDatabase.HaltDelayThread(); + ///- Close the Database Pool + LoginDatabase.Close(); sLog.outString("Halting process..."); return 0; @@ -341,16 +339,18 @@ bool StartDB() return false; } - if (!LoginDatabase.Initialize(dbstring.c_str())) + uint8 num_threads = sConfig.GetIntDefault("LoginDatabase.WorkerThreads", 1); + if (num_threads < 1 || num_threads > 32) + { + sLog.outError("Improper value specified for LoginDatabase.WorkerThreads, defaulting to 1."); + num_threads = 1; + } + + if (!LoginDatabase.Open(dbstring.c_str(), num_threads)) { sLog.outError("Cannot connect to database"); return false; } - LoginDatabase.ThreadStart(); - - uint32 count = 0; - sPreparedStatement.LoadAuthserver(&LoginDatabase, count); - sLog.outString("Loaded %u prepared MySQL statements for auth DB.", count); return true; } diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 0ecb58bf14b..56788381af8 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -7,17 +7,6 @@ ############################################################################### # AUTH SERVER SETTINGS # -# LoginDatabaseInfo -# Database connection settings for the realm server. -# Default: -# hostname;port;username;password;database -# .;somenumber;username;password;database -# - use named pipes in Windows -# Named pipes: mySQL required adding -# "enable-named-pipe" to [mysqld] section my.ini -# .;/path/to/unix_socket;username;password;database -# - use Unix sockets in Unix/Linux -# # LogsDir # Logs directory setting. # Important: Logs dir must exists, or all logs need to be disabled @@ -146,3 +135,30 @@ RealmsStateUpdateDelay = 20 WrongPass.MaxCount = 0 WrongPass.BanTime = 600 WrongPass.BanType = 0 + +############################################################################### +# MYSQL SETTINGS +# +# LoginDatabaseInfo +# Database connection settings for the realm server. +# Default: +# hostname;port;username;password;database +# .;somenumber;username;password;database +# - use named pipes in Windows +# Named pipes: mySQL required adding +# "enable-named-pipe" to [mysqld] section my.ini +# .;/path/to/unix_socket;username;password;database +# - use Unix sockets in Unix/Linux +# +# LoginDatabase.WorkerThreads +# The amount of worker threads spawned to handle +# asynchroneous MySQL statements +# Each worker thread is mirrored with its own +# connection to the MySQL server and their own +# thread on the MySQL server. +# Default: 1 +# +############################################################################### + +LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" +LoginDatabase.WorkerThreads = 1 diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 4c641fa9d87..8a53daa8278 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -48,8 +48,7 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass return AOR_NAME_ALREDY_EXIST; // username does already exist } - if (!LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", username.c_str(), CalculateShaPassHash(username, password).c_str())) - return AOR_DB_INTERNAL_ERROR; // unexpected error + LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", username.c_str(), CalculateShaPassHash(username, password).c_str()); LoginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); return AOR_OK; // everything's fine @@ -89,16 +88,12 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) LoginDatabase.BeginTransaction(); - bool res = - LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && - LoginDatabase.PExecute("DELETE FROM account_access WHERE id ='%d'", accid) && - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); + LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid); + LoginDatabase.PExecute("DELETE FROM account_access WHERE id ='%d'", accid); + LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); LoginDatabase.CommitTransaction(); - if (!res) - return AOR_DB_INTERNAL_ERROR; // unexpected error; - return AOR_OK; } @@ -120,9 +115,8 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string safe_new_uname = new_uname; LoginDatabase.escape_string(safe_new_uname); - if (!LoginDatabase.PExecute("UPDATE account SET v='0',s='0',username='%s',sha_pass_hash='%s' WHERE id='%d'", safe_new_uname.c_str(), - CalculateShaPassHash(new_uname, new_passwd).c_str(), accid)) - return AOR_DB_INTERNAL_ERROR; // unexpected error + LoginDatabase.PExecute("UPDATE account SET v='0',s='0',username='%s',sha_pass_hash='%s' WHERE id='%d'", safe_new_uname.c_str(), + CalculateShaPassHash(new_uname, new_passwd).c_str(), accid); return AOR_OK; } @@ -141,9 +135,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) normalizeString(new_passwd); // also reset s and v to force update at next realmd login - if (!LoginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash='%s' WHERE id='%d'", - CalculateShaPassHash(username, new_passwd).c_str(), accid)) - return AOR_DB_INTERNAL_ERROR; // unexpected error + LoginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash='%s' WHERE id='%d'", + CalculateShaPassHash(username, new_passwd).c_str(), accid); return AOR_OK; } diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index d1001db5ccb..91b7944a4f5 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -84,12 +84,10 @@ Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team) else // save { // _name is already escaped at this point. - if (CharacterDatabase.PExecute("INSERT INTO channels (m_name, m_team, m_announce, m_moderate, m_public, m_password) " - "VALUES ('%s', '%u', '1', '0', '1', '')", _name.c_str(), m_Team)) - { - sLog.outDebug("New Channel(%s) saved", name.c_str()); - m_IsSaved = true; - } + CharacterDatabase.PExecute("INSERT INTO channels (m_name, m_team, m_announce, m_moderate, m_public, m_password) " + "VALUES ('%s', '%u', '1', '0', '1', '')", _name.c_str(), m_Team); + sLog.outDebug("New Channel(%s) saved", name.c_str()); + m_IsSaved = true; } } } @@ -101,8 +99,9 @@ bool Channel::_UpdateStringInDB(const std::string& colName, const std::string& c std::string _colValue(colValue); CharacterDatabase.escape_string(_colValue); CharacterDatabase.escape_string(_name); - return CharacterDatabase.PExecute("UPDATE channels SET %s = '%s' WHERE m_name = '%s' AND m_team = '%u'", + CharacterDatabase.PExecute("UPDATE channels SET %s = '%s' WHERE m_name = '%s' AND m_team = '%u'", colName.c_str(), _colValue.c_str(), _name.c_str(), m_Team); + return true; } bool Channel::_UpdateIntInDB(const std::string& colName, int colValue) const @@ -110,8 +109,9 @@ bool Channel::_UpdateIntInDB(const std::string& colName, int colValue) const // Prevent SQL-injection std::string _name(m_name); CharacterDatabase.escape_string(_name); - return CharacterDatabase.PExecute("UPDATE channels SET %s = '%u' WHERE m_name = '%s' AND m_team = '%u'", + CharacterDatabase.PExecute("UPDATE channels SET %s = '%u' WHERE m_name = '%s' AND m_team = '%u'", colName.c_str(), colValue, _name.c_str(), m_Team); + return true; } void Channel::_UpdateBanListInDB() const diff --git a/src/server/game/Chat/Commands/Level2.cpp b/src/server/game/Chat/Commands/Level2.cpp index cdbdcfb2051..f81c3322916 100644 --- a/src/server/game/Chat/Commands/Level2.cpp +++ b/src/server/game/Chat/Commands/Level2.cpp @@ -1174,7 +1174,7 @@ bool ChatHandler::HandleNpcAddMoveCommand(const char* args) //WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0); // update movement type - WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid); + WorldDatabase.PExecute("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid); if (pCreature && pCreature->GetWaypointPath()) { pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); @@ -1253,7 +1253,7 @@ bool ChatHandler::HandleNpcFlagCommand(const char* args) pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags); - WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry()); + WorldDatabase.PExecute("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry()); SendSysMessage(LANG_VALUE_SAVED_REJOIN); @@ -1372,7 +1372,7 @@ bool ChatHandler::HandleNpcMoveCommand(const char* args) } } - WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid); + WorldDatabase.PExecute("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid); PSendSysMessage(LANG_COMMAND_CREATUREMOVED); return true; } @@ -1583,7 +1583,7 @@ bool ChatHandler::HandleNpcFactionIdCommand(const char* args) } // and DB - WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry()); + WorldDatabase.PExecute("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry()); return true; } @@ -1621,7 +1621,7 @@ bool ChatHandler::HandleNpcSpawnDistCommand(const char* args) pCreature->Respawn(); } - WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow); + WorldDatabase.PExecute("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow); PSendSysMessage(LANG_COMMAND_SPAWNDIST,option); return true; } @@ -1653,7 +1653,7 @@ bool ChatHandler::HandleNpcSpawnTimeCommand(const char* args) else return false; - WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow); + WorldDatabase.PExecute("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow); pCreature->SetRespawnDelay((uint32)i_stime); PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime); @@ -2384,7 +2384,7 @@ bool ChatHandler::HandleWpAddCommand(const char* args) Player* player = m_session->GetPlayer(); //Map *map = player->GetMap(); - WorldDatabase.PExecuteLog("INSERT INTO waypoint_data (id, point, position_x, position_y, position_z) VALUES ('%u','%u','%f', '%f', '%f')", + WorldDatabase.PExecute("INSERT INTO waypoint_data (id, point, position_x, position_y, position_z) VALUES ('%u','%u','%f', '%f', '%f')", pathid, point+1, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); PSendSysMessage("%s%s%u%s%u%s|r", "|cff00ff00", "PathID: |r|cff00ffff", pathid, "|r|cff00ff00: Waypoint |r|cff00ffff", point+1,"|r|cff00ff00 created. "); @@ -2588,7 +2588,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) if (result) { - WorldDatabase.PExecuteLog("DELETE FROM waypoint_scripts WHERE guid = %u", id); + WorldDatabase.PExecute("DELETE FROM waypoint_scripts WHERE guid = %u", id); PSendSysMessage("%s%s%u|r","|cff00ff00","Wp Event: Waypoint script removed: ", id); } else @@ -2647,7 +2647,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) { uint32 newid = atoi(arg_3); PSendSysMessage("%s%s|r|cff00ffff%u|r|cff00ff00%s|r|cff00ffff%u|r","|cff00ff00","Wp Event: Wypoint scipt guid: ", newid," id changed: ", id); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET id='%u' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET id='%u' WHERE guid='%u'", newid, id); return true; } else @@ -2663,7 +2663,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) if (arg_str_2 == "posx") { coord = atof(arg_3); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET x='%f' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET x='%f' WHERE guid='%u'", coord, id); PSendSysMessage("|cff00ff00Waypoint script:|r|cff00ffff %u|r|cff00ff00 position_x updated.|r", id); return true; @@ -2671,7 +2671,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) else if (arg_str_2 == "posy") { coord = atof(arg_3); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET y='%f' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET y='%f' WHERE guid='%u'", coord, id); PSendSysMessage("|cff00ff00Waypoint script: %u position_y updated.|r", id); return true; @@ -2679,7 +2679,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) else if (arg_str_2 == "posz") { coord = atof(arg_3); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET z='%f' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET z='%f' WHERE guid='%u'", coord, id); PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 position_z updated.|r", id); return true; @@ -2687,14 +2687,14 @@ bool ChatHandler::HandleWpEventCommand(const char* args) else if (arg_str_2 == "orientation") { coord = atof(arg_3); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET o='%f' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET o='%f' WHERE guid='%u'", coord, id); PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 orientation updated.|r", id); return true; } else if (arg_str_2 == "dataint") { - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET %s='%u' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET %s='%u' WHERE guid='%u'", arg_2, atoi(arg_3), id); PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 dataint updated.|r", id); return true; @@ -2703,7 +2703,7 @@ bool ChatHandler::HandleWpEventCommand(const char* args) { std::string arg_str_3 = arg_3; WorldDatabase.escape_string(arg_str_3); - WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET %s='%s' WHERE guid='%u'", + WorldDatabase.PExecute("UPDATE waypoint_scripts SET %s='%s' WHERE guid='%u'", arg_2, arg_str_3.c_str(), id); } } @@ -2832,9 +2832,9 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) wpCreature->AddObjectToRemoveList(); } - WorldDatabase.PExecuteLog("DELETE FROM waypoint_data WHERE id='%u' AND point='%u'", + WorldDatabase.PExecute("DELETE FROM waypoint_data WHERE id='%u' AND point='%u'", pathid, point); - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET point=point-1 WHERE id='%u' AND point>'%u'", + WorldDatabase.PExecute("UPDATE waypoint_data SET point=point-1 WHERE id='%u' AND point>'%u'", pathid, point); PSendSysMessage(LANG_WAYPOINT_REMOVED); @@ -2875,7 +2875,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) //sMapMgr.GetMap(npcCreature->GetMapId())->Add(wpCreature2); } - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", + WorldDatabase.PExecute("UPDATE waypoint_data SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), pathid, point); PSendSysMessage(LANG_WAYPOINT_CHANGED); @@ -2888,7 +2888,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) if (text == 0) { // show_str check for present in list of correct values, no sql injection possible - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET %s=NULL WHERE id='%u' AND point='%u'", + WorldDatabase.PExecute("UPDATE waypoint_data SET %s=NULL WHERE id='%u' AND point='%u'", show_str, pathid, point); } else @@ -2896,7 +2896,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) // show_str check for present in list of correct values, no sql injection possible std::string text2 = text; WorldDatabase.escape_string(text2); - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET %s='%s' WHERE id='%u' AND point='%u'", + WorldDatabase.PExecute("UPDATE waypoint_data SET %s='%s' WHERE id='%u' AND point='%u'", show_str, text2.c_str(), pathid, point); } @@ -3031,7 +3031,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) { PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid); hasError = true; - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", wpguid); + WorldDatabase.PExecute("DELETE FROM creature WHERE guid = '%u'", wpguid); } else { @@ -3075,7 +3075,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) sLog.outDebug("DEBUG: UPDATE waypoint_data SET wpguid = '%u"); // set "wpguid" column to the visual waypoint - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), pathid, point); + WorldDatabase.PExecute("UPDATE waypoint_data SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), pathid, point); wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); @@ -3205,7 +3205,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) { PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); hasError = true; - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", guid); + WorldDatabase.PExecute("DELETE FROM creature WHERE guid = '%u'", guid); } else { @@ -3216,8 +3216,8 @@ bool ChatHandler::HandleWpShowCommand(const char* args) } while (result->NextRow()); // set "wpguid" column to "empty" - no visual waypoint spawned - WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '0'"); - //WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0' WHERE wpguid <> '0'"); + WorldDatabase.PExecute("UPDATE waypoint_data SET wpguid = '0'"); + //WorldDatabase.PExecute("UPDATE creature_movement SET wpguid = '0' WHERE wpguid <> '0'"); if (hasError) { @@ -4210,7 +4210,7 @@ bool ChatHandler::HandleNpcAddFormationCommand(const char* args) CreatureGroupMap[lowguid] = group_member; pCreature->SearchFormation(); - WorldDatabase.PExecuteLog("INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES ('%u','%u','%f', '%f', '%u')", + WorldDatabase.PExecute("INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES ('%u','%u','%f', '%f', '%u')", leaderGUID, lowguid, group_member->follow_dist, group_member->follow_angle, group_member->groupAI); PSendSysMessage("Creature %u added to formation with leader %u", lowguid, leaderGUID); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index bf1b77edc36..f64cdb57e5f 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1054,7 +1054,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) // updated in DB WorldDatabase.BeginTransaction(); - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); std::ostringstream ss; ss << "INSERT INTO creature VALUES (" @@ -1077,7 +1077,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) << (m_isDeadByDefault ? 1 : 0) << "," //is_dead << GetDefaultMovementType() << ")"; //default movement generator type - WorldDatabase.PExecuteLog(ss.str().c_str()); + WorldDatabase.PExecute(ss.str().c_str()); WorldDatabase.CommitTransaction(); } @@ -1349,10 +1349,10 @@ void Creature::DeleteFromDB() sObjectMgr.DeleteCreatureData(m_DBTableGuid); WorldDatabase.BeginTransaction(); - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid); WorldDatabase.CommitTransaction(); } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index c89f2b866e3..89f17450a83 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -645,8 +645,8 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) << uint32(GetGoState()) << ")"; WorldDatabase.BeginTransaction(); - WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog(ss.str().c_str()); + WorldDatabase.PExecute("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute(ss.str().c_str()); WorldDatabase.CommitTransaction(); } @@ -722,8 +722,8 @@ void GameObject::DeleteFromDB() { sObjectMgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); sObjectMgr.DeleteGOData(m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); + WorldDatabase.PExecute("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid); } GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9a78bafeba9..ade749671e8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -59,7 +59,7 @@ #include "OutdoorPvPMgr.h" #include "ArenaTeam.h" #include "Chat.h" -#include "DatabaseImpl.h" +#include "AsyncDatabaseImpl.h" #include "Spell.h" #include "SocialMgr.h" #include "GameEventMgr.h" @@ -15811,7 +15811,7 @@ float Player::GetFloatValueFromArray(Tokens const& data, uint16 index) return result; } -bool Player::LoadFromDB(uint32 guid, SqlQueryHolder *holder) +bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) { //// 0 1 2 3 4 5 6 7 8 9 10 11 //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account,name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags," diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 65cc4783ff7..002e1314365 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1407,7 +1407,7 @@ class Player : public Unit, public GridObject<Player> /*** LOAD SYSTEM ***/ /*********************************************************/ - bool LoadFromDB(uint32 guid, SqlQueryHolder *holder); + bool LoadFromDB(uint32 guid, SQLQueryHolder *holder); bool isBeingLoaded() const { return GetSession()->PlayerLoading();} void Initialize(uint32 guid); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 6c434219a1e..d83f7447f4c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -5822,7 +5822,7 @@ bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inD // add link to DB if (inDB) { - WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone (id,ghost_zone,faction) " + WorldDatabase.PExecute("INSERT INTO game_graveyard_zone (id,ghost_zone,faction) " "VALUES ('%u', '%u','%u')",id,zoneId,team); } @@ -8033,8 +8033,9 @@ bool ObjectMgr::AddGameTele(GameTele& tele) m_GameTeleMap[new_id] = tele; - return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')", + WorldDatabase.PExecute("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')", new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str()); + return true; } bool ObjectMgr::DeleteGameTele(const std::string& name) @@ -8051,7 +8052,7 @@ bool ObjectMgr::DeleteGameTele(const std::string& name) { if (itr->second.wnameLow == wname) { - WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str()); + WorldDatabase.PExecute("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str()); m_GameTeleMap.erase(itr); return true; } @@ -8577,7 +8578,7 @@ void ObjectMgr::AddVendorItem(uint32 entry,uint32 item, int32 maxcount, uint32 i vList.AddItem(item, maxcount, incrtime, extendedcost); if (savetodb) - WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')", entry, item, maxcount, incrtime, extendedcost); + WorldDatabase.PExecute("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')", entry, item, maxcount, incrtime, extendedcost); } bool ObjectMgr::RemoveVendorItem(uint32 entry,uint32 item, bool savetodb) @@ -8589,7 +8590,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry,uint32 item, bool savetodb) if(!iter->second.RemoveItem(item)) return false; - if (savetodb) WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item); + if (savetodb) WorldDatabase.PExecute("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item); return true; } diff --git a/src/server/game/Maps/MapUpdater.cpp b/src/server/game/Maps/MapUpdater.cpp index 5720ed1eb50..7e1b82f2275 100644 --- a/src/server/game/Maps/MapUpdater.cpp +++ b/src/server/game/Maps/MapUpdater.cpp @@ -16,7 +16,8 @@ class WDBThreadStartReq1 : public ACE_Method_Request virtual int call() { - WorldDatabase.ThreadStart(); + WorldDatabase.Init_MySQL_Connection(); + CharacterDatabase.Init_MySQL_Connection(); return 0; } }; @@ -31,7 +32,8 @@ class WDBThreadEndReq1 : public ACE_Method_Request virtual int call() { - WorldDatabase.ThreadEnd(); + WorldDatabase.End_MySQL_Connection(); + CharacterDatabase.End_MySQL_Connection(); return 0; } }; diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp index f25dd5824dc..2a764a366e8 100644 --- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp @@ -27,7 +27,7 @@ #include "WorldSession.h" #include "MD5.h" #include "DatabaseEnv.h" -#include "DatabaseImpl.h" +#include "AsyncDatabaseImpl.h" #include "ArenaTeam.h" #include "Chat.h" @@ -44,7 +44,7 @@ #include "Util.h" #include "ScriptMgr.h" -class LoginQueryHolder : public SqlQueryHolder +class LoginQueryHolder : public SQLQueryHolder { private: uint32 m_accountId; @@ -117,7 +117,7 @@ class CharacterHandler return; session->HandleCharEnum(result); } - void HandlePlayerLoginCallback(QueryResult_AutoPtr /*dummy*/, SqlQueryHolder * holder) + void HandlePlayerLoginCallback(QueryResult_AutoPtr /*dummy*/, SQLQueryHolder * holder) { if (!holder) return; WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId()); diff --git a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp index 2656478f899..79e3374c975 100644 --- a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp @@ -21,7 +21,7 @@ #include "Common.h" #include "Language.h" #include "DatabaseEnv.h" -#include "DatabaseImpl.h" +#include "AsyncDatabaseImpl.h" #include "WorldPacket.h" #include "Opcodes.h" #include "Log.h" diff --git a/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp b/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp index 0aa6ded77f0..7cb60435d69 100644 --- a/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/QueryHandler.cpp @@ -21,7 +21,7 @@ #include "Common.h" #include "Language.h" #include "DatabaseEnv.h" -#include "DatabaseImpl.h" +#include "AsyncDatabaseImpl.h" #include "WorldPacket.h" #include "WorldSession.h" #include "Opcodes.h" diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 9a50456919f..1293849c496 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -158,7 +158,21 @@ class ReactorRunnable : protected ACE_Task_Base { DEBUG_LOG ("Network Thread Starting"); - WorldDatabase.ThreadStart(); + bool needInit = true; + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + { + LoginDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + { + CharacterDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (needInit) + MySQL::Thread_Init(); ACE_ASSERT (m_Reactor); @@ -195,7 +209,14 @@ class ReactorRunnable : protected ACE_Task_Base } } - WorldDatabase.ThreadEnd(); + ///- Free MySQL thread resources and deallocate lingering connections + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + LoginDatabase.End_MySQL_Connection(); + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + CharacterDatabase.End_MySQL_Connection(); + + if (needInit) + MySQL::Thread_End(); DEBUG_LOG ("Network Thread Exitting"); diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index 402a274f3cf..4fed5faa7dd 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -629,8 +629,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s fixNULLfields(line); - if (!CharacterDatabase.Execute(line.c_str())) - ROLLBACK(DUMP_FILE_BROKEN); + CharacterDatabase.Execute(line.c_str()); } CharacterDatabase.CommitTransaction(); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 0e5bd88fbb8..15794718491 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -54,7 +54,7 @@ #include "VMapFactory.h" #include "GameEventMgr.h" #include "PoolMgr.h" -#include "DatabaseImpl.h" +#include "AsyncDatabaseImpl.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "InstanceSaveMgr.h" @@ -1223,6 +1223,11 @@ void World::LoadConfigSettings(bool reload) // Dungeon finder m_configs[CONFIG_DUNGEON_FINDER_ENABLE] = sConfig.GetBoolDefault("DungeonFinder.Enable", false); + // MySQL thread bundling config for other runnable tasks + m_configs[CONFIG_MYSQL_BUNDLE_LOGINDB] = sConfig.GetIntDefault("LoginDatabase.ThreadBundleMask", MYSQL_BUNDLE_ALL); + m_configs[CONFIG_MYSQL_BUNDLE_CHARDB] = sConfig.GetIntDefault("CharacterDatabase.ThreadBundleMask", MYSQL_BUNDLE_ALL); + m_configs[CONFIG_MYSQL_BUNDLE_WORLDDB] = sConfig.GetIntDefault("WorldDatabase.ThreadBundleMask", MYSQL_BUNDLE_ALL); + sScriptMgr.OnConfigLoad(reload); } @@ -2478,7 +2483,7 @@ void World::SendRNDBroadcast() void World::InitResultQueue() { - m_resultQueue = new SqlResultQueue; + m_resultQueue = new SQLResultQueue; CharacterDatabase.SetResultQueue(m_resultQueue); } diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 817e8253fe8..8a480d0ccc6 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -42,7 +42,7 @@ class WorldSession; class Player; struct ScriptAction; struct ScriptInfo; -class SqlResultQueue; +class SQLResultQueue; class QueryResult; class WorldSocket; class SystemMgr; @@ -286,6 +286,9 @@ enum WorldConfigs CONFIG_CHARDELETE_MIN_LEVEL, CONFIG_CLEAN_CHARACTER_DB, CONFIG_DUNGEON_FINDER_ENABLE, + CONFIG_MYSQL_BUNDLE_LOGINDB, + CONFIG_MYSQL_BUNDLE_CHARDB, + CONFIG_MYSQL_BUNDLE_WORLDDB, CONFIG_VALUE_COUNT }; @@ -761,7 +764,7 @@ class World // CLI command holder to be thread safe ACE_Based::LockedQueue<CliCommandHolder*,ACE_Thread_Mutex> cliCmdQueue; - SqlResultQueue *m_resultQueue; + SQLResultQueue *m_resultQueue; // next daily quests and random bg reset time time_t m_NextDailyQuestReset; diff --git a/src/server/shared/Database/AsyncDatabaseImpl.h b/src/server/shared/Database/AsyncDatabaseImpl.h new file mode 100644 index 00000000000..f85540a8016 --- /dev/null +++ b/src/server/shared/Database/AsyncDatabaseImpl.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "Database/DatabaseWorkerPool.h" +#include "Database/SQLOperation.h" + + +/// Function body definitions for the template function members of the Database class + +#define ASYNC_QUERY_BODY(sql, queue_itr) \ + if (!sql) return false; \ + \ + QueryQueues::iterator queue_itr; \ + \ + { \ + ACE_Based::Thread * queryThread = ACE_Based::Thread::current(); \ + queue_itr = m_queryQueues.find(queryThread); \ + if (queue_itr == m_queryQueues.end()) return false; \ + } + +#define ASYNC_PQUERY_BODY(format, szQuery) \ + if(!format) return false; \ + \ + char szQuery [MAX_QUERY_LEN]; \ + \ + { \ + va_list ap; \ + \ + va_start(ap, format); \ + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); \ + va_end(ap); \ + \ + if(res==-1) \ + { \ + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); \ + return false; \ + } \ + } + + +#define ASYNC_DELAYHOLDER_BODY(holder, queue_itr) \ + if (!holder) return false; \ + \ + QueryQueues::iterator queue_itr; \ + \ + { \ + ACE_Based::Thread * queryThread = ACE_Based::Thread::current(); \ + queue_itr = m_queryQueues.find(queryThread); \ + if (queue_itr == m_queryQueues.end()) return false; \ + } + + +// -- Query / member -- + + +template<class Class> +bool +DatabaseWorkerPool::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::QueryCallback<Class>(object, method), itr->second); + Enqueue(task); + return true; +} + + +template<class Class, typename ParamType1> +bool +DatabaseWorkerPool::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::QueryCallback<Class, ParamType1>(object, method, (QueryResult_AutoPtr)NULL, param1), itr->second); + Enqueue(task); + return true; +} + + +template<class Class, typename ParamType1, typename ParamType2> +bool +DatabaseWorkerPool::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2>(object, method, (QueryResult_AutoPtr)NULL, param1, param2), itr->second); + Enqueue(task); + return true; +} + + +template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> +bool +DatabaseWorkerPool::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2, ParamType3>(object, method, (QueryResult_AutoPtr)NULL, param1, param2, param3), itr->second); + Enqueue(task); + return true; +} + + +// -- Query / static -- + + +template<typename ParamType1> +bool +DatabaseWorkerPool::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::SQueryCallback<ParamType1>(method, (QueryResult_AutoPtr)NULL, param1), itr->second); + Enqueue(task); + return true; +} + + +template<typename ParamType1, typename ParamType2> +bool +DatabaseWorkerPool::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::SQueryCallback<ParamType1, ParamType2>(method, (QueryResult_AutoPtr)NULL, param1, param2), itr->second); + Enqueue(task); + return true; +} + + +template<typename ParamType1, typename ParamType2, typename ParamType3> +bool +DatabaseWorkerPool::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) +{ + ASYNC_QUERY_BODY(sql, itr) + SQLQueryTask* task = new SQLQueryTask(sql, new Trinity::SQueryCallback<ParamType1, ParamType2, ParamType3>(method, (QueryResult_AutoPtr)NULL, param1, param2, param3), itr->second); + Enqueue(task); + return true; +} + + +// -- PQuery / member -- + + +template<class Class> +bool +DatabaseWorkerPool::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(object, method, szQuery); +} + + +template<class Class, typename ParamType1> +bool +DatabaseWorkerPool::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(object, method, param1, szQuery); +} + + +template<class Class, typename ParamType1, typename ParamType2> +bool +DatabaseWorkerPool::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(object, method, param1, param2, szQuery); +} + + +template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> +bool +DatabaseWorkerPool::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(object, method, param1, param2, param3, szQuery); +} + + +// -- PQuery / static -- + + +template<typename ParamType1> +bool +DatabaseWorkerPool::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(method, param1, szQuery); +} + + +template<typename ParamType1, typename ParamType2> +bool +DatabaseWorkerPool::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(method, param1, param2, szQuery); +} + + +template<typename ParamType1, typename ParamType2, typename ParamType3> +bool +DatabaseWorkerPool::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) +{ + ASYNC_PQUERY_BODY(format, szQuery) + return AsyncQuery(method, param1, param2, param3, szQuery); +} + + +// -- QueryHolder -- + + +template<class Class> +bool +DatabaseWorkerPool::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SQLQueryHolder*), SQLQueryHolder *holder) +{ + ASYNC_DELAYHOLDER_BODY(holder, itr) + SQLQueryHolderTask *task = new SQLQueryHolderTask(holder, new Trinity::QueryCallback<Class, SQLQueryHolder*>(object, method, (QueryResult_AutoPtr)NULL, holder), itr->second); + Enqueue(task); + return true; +} + + +template<class Class, typename ParamType1> +bool +DatabaseWorkerPool::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SQLQueryHolder*, ParamType1), SQLQueryHolder *holder, ParamType1 param1) +{ + ASYNC_DELAYHOLDER_BODY(holder, itr) + SQLQueryHolderTask *task = new SQLQueryHolderTask(holder, new Trinity::QueryCallback<Class, SQLQueryHolder*, ParamType1>(object, method, (QueryResult_AutoPtr)NULL, holder, param1), itr->second); + Enqueue(task); + return true; +} + + +#undef ASYNC_QUERY_BODY +#undef ASYNC_PQUERY_BODY +#undef ASYNC_DELAYHOLDER_BODY diff --git a/src/server/shared/Database/DatabaseEnv.h b/src/server/shared/Database/DatabaseEnv.h index 15c1b1c599e..398ae66f235 100644 --- a/src/server/shared/Database/DatabaseEnv.h +++ b/src/server/shared/Database/DatabaseEnv.h @@ -28,8 +28,10 @@ #include "Database/Field.h" #include "Database/QueryResult.h" -#include "Database/Database.h" -typedef Database DatabaseType; +#include "Database/DatabaseWorkerPool.h" +#include "Database/MySQLThreading.h" + +typedef DatabaseWorkerPool DatabaseType; #define _LIKE_ "LIKE" #define _TABLE_SIM_ "`" #define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )" @@ -39,5 +41,7 @@ extern DatabaseType WorldDatabase; extern DatabaseType CharacterDatabase; extern DatabaseType LoginDatabase; +#define MAX_QUERY_LEN 32*1024 + #endif diff --git a/src/server/shared/Database/DatabaseImpl.h b/src/server/shared/Database/DatabaseImpl.h deleted file mode 100644 index f0ba9c84a30..00000000000 --- a/src/server/shared/Database/DatabaseImpl.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "Database/Database.h" -#include "Database/SqlOperations.h" - - -/// Function body definitions for the template function members of the Database class - - -#define ASYNC_QUERY_BODY(sql, queue_itr) \ - if (!sql) return false; \ - \ - QueryQueues::iterator queue_itr; \ - \ - { \ - ACE_Based::Thread * queryThread = ACE_Based::Thread::current(); \ - queue_itr = m_queryQueues.find(queryThread); \ - if (queue_itr == m_queryQueues.end()) return false; \ - } - - -#define ASYNC_PQUERY_BODY(format, szQuery) \ - if(!format) return false; \ - \ - char szQuery [MAX_QUERY_LEN]; \ - \ - { \ - va_list ap; \ - \ - va_start(ap, format); \ - int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); \ - va_end(ap); \ - \ - if(res==-1) \ - { \ - sLog.outError("SQL Query truncated (and not execute) for format: %s",format); \ - return false; \ - } \ - } - - -#define ASYNC_DELAYHOLDER_BODY(holder, queue_itr) \ - if (!holder) return false; \ - \ - QueryQueues::iterator queue_itr; \ - \ - { \ - ACE_Based::Thread * queryThread = ACE_Based::Thread::current(); \ - queue_itr = m_queryQueues.find(queryThread); \ - if (queue_itr == m_queryQueues.end()) return false; \ - } - - -// -- Query / member -- - - -template<class Class> -bool -Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class>(object, method), itr->second)); -} - - -template<class Class, typename ParamType1> -bool -Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1>(object, method, (QueryResult_AutoPtr)NULL, param1), itr->second)); -} - - -template<class Class, typename ParamType1, typename ParamType2> -bool -Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2>(object, method, (QueryResult_AutoPtr)NULL, param1, param2), itr->second)); -} - - -template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> -bool -Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2, ParamType3>(object, method, (QueryResult_AutoPtr)NULL, param1, param2, param3), itr->second)); -} - - -// -- Query / static -- - - -template<typename ParamType1> -bool -Database::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1>(method, (QueryResult_AutoPtr)NULL, param1), itr->second)); -} - - -template<typename ParamType1, typename ParamType2> -bool -Database::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2>(method, (QueryResult_AutoPtr)NULL, param1, param2), itr->second)); -} - - -template<typename ParamType1, typename ParamType2, typename ParamType3> -bool -Database::AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) -{ - ASYNC_QUERY_BODY(sql, itr) - return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2, ParamType3>(method, (QueryResult_AutoPtr)NULL, param1, param2, param3), itr->second)); -} - - -// -- PQuery / member -- - - -template<class Class> -bool -Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(object, method, szQuery); -} - - -template<class Class, typename ParamType1> -bool -Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(object, method, param1, szQuery); -} - - -template<class Class, typename ParamType1, typename ParamType2> -bool -Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(object, method, param1, param2, szQuery); -} - - -template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> -bool -Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(object, method, param1, param2, param3, szQuery); -} - - -// -- PQuery / static -- - - -template<typename ParamType1> -bool -Database::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(method, param1, szQuery); -} - - -template<typename ParamType1, typename ParamType2> -bool -Database::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(method, param1, param2, szQuery); -} - - -template<typename ParamType1, typename ParamType2, typename ParamType3> -bool -Database::AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) -{ - ASYNC_PQUERY_BODY(format, szQuery) - return AsyncQuery(method, param1, param2, param3, szQuery); -} - - -// -- QueryHolder -- - - -template<class Class> -bool -Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SqlQueryHolder*), SqlQueryHolder *holder) -{ - ASYNC_DELAYHOLDER_BODY(holder, itr) - return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult_AutoPtr)NULL, holder), m_threadBody, itr->second); -} - - -template<class Class, typename ParamType1> -bool -Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1) -{ - ASYNC_DELAYHOLDER_BODY(holder, itr) - return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult_AutoPtr)NULL, holder, param1), m_threadBody, itr->second); -} - - -#undef ASYNC_QUERY_BODY -#undef ASYNC_PQUERY_BODY -#undef ASYNC_DELAYHOLDER_BODY diff --git a/src/server/shared/Database/DatabaseWorker.cpp b/src/server/shared/Database/DatabaseWorker.cpp new file mode 100644 index 00000000000..17fc1d75a5d --- /dev/null +++ b/src/server/shared/Database/DatabaseWorker.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" +#include "DatabaseWorker.h" +#include "SQLOperation.h" +#include "MySQLConnection.h" + +DatabaseWorker::DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con) : +m_queue(new_queue), +m_conn(con) +{ + /// Assign thread to task + activate(); +} + +int DatabaseWorker::svc() +{ + if (!m_queue) + return -1; + + SQLOperation *request = NULL; + while (1) + { + request = (SQLOperation*)(m_queue->dequeue()); + if (!request) + break; + + request->SetConnection(m_conn); + request->call(); + delete request; + } + + delete m_conn; + delete this; + return 0; +} + +int DatabaseWorker::activate() +{ + /* THR_DETACHED: + Create an asynchronous thread. The exit status of the thread would not be available to any other threads. + The thread resources are reclaimed by the operating system whenever the thread is terminated. */ + + /* THR_NEW_LWP: + Create an explicit kernel-level thread (as opposed to a user-level thread). */ + + ACE_Task_Base::activate(THR_NEW_LWP | THR_DETACHED, 1); + return 0; //^ - Spawn one thread to handle this task. + // However more of these tasks may be activated + // See DatabaseWorkerPool ctor. +}
\ No newline at end of file diff --git a/src/server/shared/Database/DatabaseWorker.h b/src/server/shared/Database/DatabaseWorker.h new file mode 100644 index 00000000000..ad5102d28ca --- /dev/null +++ b/src/server/shared/Database/DatabaseWorker.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WORKERTHREAD_H +#define _WORKERTHREAD_H + +#include <ace/Task.h> +#include <ace/Activation_Queue.h> + +class MySQLConnection; + +class DatabaseWorker : protected ACE_Task_Base +{ + public: + DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con); + + ///- Inherited from ACE_Task_Base + int svc(); + int activate(); + + private: + DatabaseWorker() : ACE_Task_Base() {} + ACE_Activation_Queue* m_queue; + MySQLConnection* m_conn; +}; + +#endif
\ No newline at end of file diff --git a/src/server/shared/Database/DatabaseWorkerPool.cpp b/src/server/shared/Database/DatabaseWorkerPool.cpp new file mode 100644 index 00000000000..532d9cad7df --- /dev/null +++ b/src/server/shared/Database/DatabaseWorkerPool.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseWorkerPool.h" +#include "MySQLConnection.h" +#include "DatabaseEnv.h" +#include "SQLOperation.h" + +DatabaseWorkerPool::DatabaseWorkerPool() : +m_queue(new ACE_Activation_Queue(new ACE_Message_Queue<ACE_MT_SYNCH>)), +m_connections(0) +{ + m_infoString = ""; + + mysql_library_init(-1, NULL, NULL); + WPFatal (mysql_thread_safe(), "Used MySQL library isn't thread-safe."); +} + +DatabaseWorkerPool::~DatabaseWorkerPool() +{ +} + +bool DatabaseWorkerPool::Open(const std::string& infoString, uint8 num_threads) +{ + sLog.outDebug("Creating bundled/master MySQL connection."); + m_bundle_conn = new MySQLConnection(); + m_bundle_conn->Open(infoString); + ++m_connections; + + m_async_connections.resize(num_threads); + + /// Open the Async pool + for (uint8 i = 0; i < num_threads; i++) + { + m_async_connections[i] = new MySQLConnection(m_queue); + m_async_connections[i]->Open(infoString); + ++m_connections; + sLog.outDebug("Async database thread pool opened. Worker thread count: %u", num_threads); + } + + m_infoString = infoString; + return true; +} + +void DatabaseWorkerPool::Close() +{ + /// Shuts down worker threads for this connection pool. + for (uint8 i = 0; i < m_async_connections.size(); i++) + Enqueue(NULL); + + //- MySQL::Thread_End() should be called manually from the aborting calling threads + + delete m_bundle_conn; + m_bundle_conn = NULL; +} + +/*! This function creates a new MySQL connection for every MapUpdate thread + and every unbundled task. + */ +void DatabaseWorkerPool::Init_MySQL_Connection() +{ + MySQLConnection* conn = new MySQLConnection(); + conn->Open(m_infoString); + + { + ACE_Guard<ACE_Thread_Mutex> guard(m_connectionMap_mtx); + m_sync_connections[ACE_Based::Thread::current()] = conn; + } + + sLog.outDebug("Core thread with ID ["UI64FMTD"] initializing MySQL connection.", + (uint64)ACE_Based::Thread::currentId()); +} + +void DatabaseWorkerPool::End_MySQL_Connection() +{ + MySQLConnection* conn; + { + ACE_Guard<ACE_Thread_Mutex> guard(m_connectionMap_mtx); + conn = m_sync_connections[ACE_Based::Thread::current()]; + } + delete conn; + conn = NULL; +} + +void DatabaseWorkerPool::Execute(const char* sql) +{ + if (!sql) + return; + + BasicStatementTask* task = new BasicStatementTask(sql); + Enqueue(task); +} + +void DatabaseWorkerPool::PExecute(const char* sql, ...) +{ + if (!sql) + return; + + va_list ap; + char szQuery[MAX_QUERY_LEN]; + va_start(ap, sql); + int res = vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap); + va_end(ap); + + Execute(szQuery); +} + +void DatabaseWorkerPool::DirectExecute(const char* sql) +{ + if (sql) + GetConnection()->Execute(sql); +} + +void DatabaseWorkerPool::DirectPExecute(const char* sql, ...) +{ + if (!sql) + return; + + va_list ap; + char szQuery[MAX_QUERY_LEN]; + va_start(ap, sql); + int res = vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap); + va_end(ap); + + return DirectExecute(szQuery); +} + +QueryResult_AutoPtr DatabaseWorkerPool::Query(const char* sql) +{ + return GetConnection()->Query(sql); +} + +QueryResult_AutoPtr DatabaseWorkerPool::PQuery(const char* sql, ...) +{ + if (!sql) + return QueryResult_AutoPtr(NULL); + + va_list ap; + char szQuery[MAX_QUERY_LEN]; + va_start(ap, sql); + int res = vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap); + va_end(ap); + + return Query(szQuery); +} + +void DatabaseWorkerPool::BeginTransaction() +{ + ACE_Guard<ACE_Thread_Mutex> guard(m_transQueues_mtx); + ACE_Based::Thread* tranThread = ACE_Based::Thread::current(); // owner of this transaction + TransactionQueues::iterator itr = m_tranQueues.find(tranThread); + if (itr != m_tranQueues.end() && itr->second != NULL) + { + itr->second->ForcefulDelete(); + delete itr->second; + } + m_tranQueues[tranThread] = new TransactionTask(); + return; +} + +void DatabaseWorkerPool::RollbackTransaction() +{ + ACE_Guard<ACE_Thread_Mutex> guard(m_transQueues_mtx); + ACE_Based::Thread* tranThread = ACE_Based::Thread::current(); // owner of this transaction + TransactionQueues::iterator itr = m_tranQueues.find(tranThread); + if (itr != m_tranQueues.end() && itr->second != NULL) + { + itr->second->ForcefulDelete(); + delete itr->second; + } +} + +void DatabaseWorkerPool::CommitTransaction() +{ + ACE_Guard<ACE_Thread_Mutex> guard(m_transQueues_mtx); + ACE_Based::Thread* tranThread = ACE_Based::Thread::current(); // owner of this transaction + TransactionQueues::iterator itr = m_tranQueues.find(tranThread); + if (itr != m_tranQueues.end() && itr->second != NULL) + { + Enqueue(itr->second); + m_tranQueues.erase(itr); + } +} + + +MySQLConnection* DatabaseWorkerPool::GetConnection() +{ + MySQLConnection* conn; + ConnectionMap::const_iterator itr; + { + /*! MapUpdate + unbundled threads */ + ACE_Guard<ACE_Thread_Mutex> guard(m_connectionMap_mtx); + itr = m_sync_connections.find(ACE_Based::Thread::current()); + if (itr != m_sync_connections.end()) + conn = itr->second; + } + /*! Bundled threads */ + conn = m_bundle_conn; + ASSERT (conn); + return conn; +}
\ No newline at end of file diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h new file mode 100644 index 00000000000..d6275e0307f --- /dev/null +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DATABASEWORKERPOOL_H +#define _DATABASEWORKERPOOL_H + +#include "Common.h" +#include "Logging/Log.h" + +#include <ace/Activation_Queue.h> +#include <ace/Atomic_Op_T.h> +#include <ace/Thread_Mutex.h> + +#include "SQLOperation.h" +#include "QueryResult.h" +#include "MySQLConnection.h" + +enum MySQLThreadBundle +{ + MYSQL_BUNDLE_NONE = 0x00, //- Each task will run their own MySQL connection + MYSQL_BUNDLE_CLI = 0x01, //- Commandline interface thread + MYSQL_BUNDLE_RA = 0x02, //- Remote admin thread + MYSQL_BUNDLE_RAR = 0x04, //- Reactor runnable thread + MYSQL_BUNDLE_WORLD = 0x08, //- WorldRunnable + MYSQL_BUNDLE_ALL = MYSQL_BUNDLE_CLI | MYSQL_BUNDLE_RA | MYSQL_BUNDLE_RAR | MYSQL_BUNDLE_WORLD, +}; + +class DatabaseWorkerPool +{ + public: + DatabaseWorkerPool(); + ~DatabaseWorkerPool(); + + bool Open(const std::string& infoString, uint8 num_threads); + void Close(); + + void Init_MySQL_Connection(); + void End_MySQL_Connection(); + + void Execute(const char* sql); + void PExecute(const char* sql, ...); + void DirectExecute(const char* sql); + void DirectPExecute(const char* sql, ...); + QueryResult_AutoPtr Query(const char* sql); + QueryResult_AutoPtr PQuery(const char* sql, ...); + + /// Async queries and query holders, implemented in DatabaseImpl.h + + // Query / member + template<class Class> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *sql); + template<class Class, typename ParamType1> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql); + template<class Class, typename ParamType1, typename ParamType2> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql); + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql); + // Query / static + template<typename ParamType1> + bool AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *sql); + template<typename ParamType1, typename ParamType2> + bool AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql); + template<typename ParamType1, typename ParamType2, typename ParamType3> + bool AsyncQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql); + // PQuery / member + template<class Class> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr), const char *format,...) ATTR_PRINTF(4,5); + template<class Class, typename ParamType1> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(5,6); + template<class Class, typename ParamType1, typename ParamType2> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) ATTR_PRINTF(6,7); + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) ATTR_PRINTF(7,8); + // PQuery / static + template<typename ParamType1> + bool AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(4,5); + template<typename ParamType1, typename ParamType2> + bool AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) ATTR_PRINTF(5,6); + template<typename ParamType1, typename ParamType2, typename ParamType3> + bool AsyncPQuery(void (*method)(QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) ATTR_PRINTF(6,7); + template<class Class> + // QueryHolder + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SQLQueryHolder*), SQLQueryHolder *holder); + template<class Class, typename ParamType1> + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult_AutoPtr, SQLQueryHolder*, ParamType1), SQLQueryHolder *holder, ParamType1 param1); + + void SetResultQueue(SQLResultQueue * queue) + { + m_queryQueues[ACE_Based::Thread::current()] = queue; + } + + void BeginTransaction(); + void RollbackTransaction(); + void CommitTransaction(); + + void escape_string(std::string& str) + { + if (str.empty()) + return; + + char* buf = new char[str.size()*2+1]; + escape_string(buf,str.c_str(),str.size()); + str = buf; + delete[] buf; + } + + unsigned long escape_string(char *to, const char *from, unsigned long length) + { + if (!to || !from || !length) + return 0; + return (mysql_real_escape_string(GetConnection()->GetHandle(), to, from, length)); + } + + private: + void Enqueue(SQLOperation* op) + { + m_queue->enqueue(op); + } + + MySQLConnection* GetConnection(); + + private: + typedef UNORDERED_MAP<ACE_Based::Thread*, MySQLConnection*> ConnectionMap; + typedef UNORDERED_MAP<ACE_Based::Thread*, TransactionTask*> TransactionQueues; + typedef UNORDERED_MAP<ACE_Based::Thread*, SQLResultQueue*> QueryQueues; + typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, uint32> AtomicUInt; + + private: + ACE_Activation_Queue* m_queue; //! Queue shared by async worker threads. + ACE_Thread_Mutex m_queue_mtx; //! For thread safe enqueues of delayed statements. + std::vector<MySQLConnection*> m_async_connections; + ConnectionMap m_sync_connections; //! Holds a mysql connection+thread per mapUpdate thread and unbundled runnnables. + ACE_Thread_Mutex m_connectionMap_mtx; //! For thread safe access to the synchroneous connection map + MySQLConnection* m_bundle_conn; //! Bundled connection (see Database.ThreadBundleMask config) + AtomicUInt m_connections; //! Counter of MySQL connections; + std::string m_infoString; //! Infostring that is passed on to child connections. + TransactionQueues m_tranQueues; //! Transaction queues from diff. threads + ACE_Thread_Mutex m_transQueues_mtx; //! To guard m_transQueues + QueryQueues m_queryQueues; //! Query queues from diff threads +}; + +#endif
\ No newline at end of file diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp new file mode 100644 index 00000000000..cd28474c63d --- /dev/null +++ b/src/server/shared/Database/MySQLConnection.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "MySQLConnection.h" +#include "DatabaseWorker.h" +#include "Utilities/Util.h" +#include "Utilities/Timer.h" + +MySQLConnection::MySQLConnection() : +m_Mysql(NULL) +{ +} + +MySQLConnection::MySQLConnection(ACE_Activation_Queue* queue) : +m_queue(queue), +m_Mysql(NULL) +{ + m_worker = new DatabaseWorker(m_queue, this); +} + +MySQLConnection::~MySQLConnection() +{ + delete m_worker; +} + +bool MySQLConnection::Open(const std::string& infoString) +{ + MYSQL *mysqlInit; + mysqlInit = mysql_init(NULL); + if (!mysqlInit) + { + sLog.outError("Could not initialize Mysql connection"); + return false; + } + + Tokens tokens = StrSplit(infoString, ";"); + + Tokens::iterator iter; + + std::string host, port_or_socket, user, password, database; + int port; + char const* unix_socket; + + iter = tokens.begin(); + + if (iter != tokens.end()) + host = *iter++; + if (iter != tokens.end()) + port_or_socket = *iter++; + if (iter != tokens.end()) + user = *iter++; + if (iter != tokens.end()) + password = *iter++; + if (iter != tokens.end()) + database = *iter++; + + mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); + #ifdef _WIN32 + if (host==".") // named pipe use option (Windows) + { + unsigned int opt = MYSQL_PROTOCOL_PIPE; + mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt); + port = 0; + unix_socket = 0; + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #else + if (host==".") // socket use option (Unix/Linux) + { + unsigned int opt = MYSQL_PROTOCOL_SOCKET; + mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt); + host = "localhost"; + port = 0; + unix_socket = port_or_socket.c_str(); + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #endif + + m_Mysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(), + password.c_str(), database.c_str(), port, unix_socket, 0); + + if (m_Mysql) + { + sLog.outDetail("Connected to MySQL database at %s", host.c_str()); + sLog.outString("MySQL client library: %s", mysql_get_client_info()); + sLog.outString("MySQL server ver: %s ", mysql_get_server_info( m_Mysql)); + + if (!mysql_autocommit(m_Mysql, 1)) + sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1"); + else + sLog.outDetail("AUTOCOMMIT NOT SET TO 1"); + + // set connection properties to UTF8 to properly handle locales for different + // server configs - core sends data in UTF8, so MySQL must expect UTF8 too + Execute("SET NAMES `utf8`"); + Execute("SET CHARACTER SET `utf8`"); + + #if MYSQL_VERSION_ID >= 50003 + my_bool my_true = (my_bool)1; + if (mysql_options(m_Mysql, MYSQL_OPT_RECONNECT, &my_true)) + sLog.outDetail("Failed to turn on MYSQL_OPT_RECONNECT."); + else + sLog.outDetail("Successfully turned on MYSQL_OPT_RECONNECT."); + #else + #warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem." + #endif + return true; + } + else + { + sLog.outError("Could not connect to MySQL database at %s: %s\n", host.c_str(), mysql_error(mysqlInit)); + mysql_close(mysqlInit); + return false; + } +} + +bool MySQLConnection::Execute(const char* sql) +{ + if (!m_Mysql) + return false; + + { + // guarded block for thread-safe mySQL request + ACE_Guard<ACE_Thread_Mutex> query_connection_guard(m_Mutex); + + #ifdef TRINITY_DEBUG + uint32 _s = getMSTime(); + #endif + if (mysql_query(m_Mysql, sql)) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", mysql_error(m_Mysql)); + return false; + } + else + { + #ifdef TRINITY_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); + #endif + } + } + + return true; +} + +QueryResult_AutoPtr MySQLConnection::Query(const char* sql) +{ + if (!sql) + return QueryResult_AutoPtr(NULL); + + MYSQL_RES *result = NULL; + MYSQL_FIELD *fields = NULL; + uint64 rowCount = 0; + uint32 fieldCount = 0; + + if (!_Query(sql, &result, &fields, &rowCount, &fieldCount)) + return QueryResult_AutoPtr(NULL); + + QueryResult *queryResult = new QueryResult(result, fields, rowCount, fieldCount); + + queryResult->NextRow(); + + return QueryResult_AutoPtr(queryResult); +} + +bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount) +{ + if (!m_Mysql) + return false; + + { + // guarded block for thread-safe mySQL request + ACE_Guard<ACE_Thread_Mutex> query_connection_guard(m_Mutex); + #ifdef TRINITY_DEBUG + uint32 _s = getMSTime(); + #endif + if (mysql_query(m_Mysql, sql)) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("query ERROR: %s", mysql_error(m_Mysql)); + return false; + } + else + { + #ifdef TRINITY_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql); + #endif + } + + *pResult = mysql_store_result(m_Mysql); + *pRowCount = mysql_affected_rows(m_Mysql); + *pFieldCount = mysql_field_count(m_Mysql); + } + + if (!*pResult ) + return false; + + if (!*pRowCount) + { + mysql_free_result(*pResult); + return false; + } + + *pFields = mysql_fetch_fields(*pResult); + return true; +} + +void MySQLConnection::BeginTransaction() +{ + Execute("START TRANSACTION"); +} + +void MySQLConnection::RollbackTransaction() +{ + Execute("ROLLBACK"); +} + +void MySQLConnection::CommitTransaction() +{ + Execute("COMMIT"); +}
\ No newline at end of file diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h new file mode 100644 index 00000000000..08ceaa2860c --- /dev/null +++ b/src/server/shared/Database/MySQLConnection.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MYSQLCONNECTION_H +#define _MYSQLCONNECTION_H + +class DatabaseWorker; + +class MySQLConnection +{ + friend class DatabaseWorkerPool; + + public: + MySQLConnection(); //! Constructor for synchroneous connections. + MySQLConnection(ACE_Activation_Queue* queue); //! Constructor for asynchroneous connections. + ~MySQLConnection(); + + bool Open(const std::string& infoString); //! Connection details. + + public: + bool Execute(const char* sql); + QueryResult_AutoPtr Query(const char* sql); + bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); + + void BeginTransaction(); + void RollbackTransaction(); + void CommitTransaction(); + + operator bool () const { return m_Mysql != NULL; } + + protected: + MYSQL* GetHandle() { return m_Mysql; } + + private: + ACE_Activation_Queue* m_queue; //! Queue shared with other asynchroneous connections. + DatabaseWorker* m_worker; //! Core worker task. + MYSQL * m_Mysql; //! MySQL Handle. + ACE_Thread_Mutex m_Mutex; +}; + +#endif
\ No newline at end of file diff --git a/src/server/shared/Database/MySQLThreading.h b/src/server/shared/Database/MySQLThreading.h new file mode 100644 index 00000000000..3c039a4d165 --- /dev/null +++ b/src/server/shared/Database/MySQLThreading.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MYSQLTHREADING_H +#define _MYSQLTHREADING_H + +#include "Log.h" + +class MySQL +{ + public: + /*! Create a thread on the MySQL server to mirrior the calling thread, + initializes thread-specific variables and allows thread-specific + operations without concurrence from other threads. + This should only be called if multiple core threads are running + on the same MySQL connection. Seperate MySQL connections implicitly + create a mirror thread. + */ + static void Thread_Init() + { + mysql_thread_init(); + printf("Core thread with ID ["UI64FMTD"] initializing MySQL thread.", + (uint64)ACE_Based::Thread::currentId()); + } + + /*! Shuts down MySQL thread and frees resources, should only be called + when we terminate. MySQL threads and connections are not configurable + during runtime. + */ + static void Thread_End() + { + mysql_thread_end(); + printf("Core thread with ID ["UI64FMTD"] shutting down MySQL thread.", + (uint64)ACE_Based::Thread::currentId()); + } +}; + +#endif
\ No newline at end of file diff --git a/src/server/shared/Database/PreparedStatements.cpp b/src/server/shared/Database/PreparedStatements.cpp deleted file mode 100644 index ec57a0f90e8..00000000000 --- a/src/server/shared/Database/PreparedStatements.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "PreparedStatements.h" - -void PreparedStatementHolder::_prepareStatement(const char* name, const char* sql, Database *db, uint32 &count) -{ - const char prefix[] = "PREPARE "; - size_t querySize = 8 + strlen(name) + 6 + strlen(sql) + 2 + 1; - char* query = new char[querySize]; - strcpy(query, prefix); - strcat(query, name); - strcat(query, " FROM "); - strcat(query, "'"); - strcat(query, sql); - strcat(query, "'"); - - DEBUG_LOG("Preparing statement: %s", query); - db->Execute(query); - - delete[] query; - ++count; -} - -void PreparedStatementHolder::LoadAuthserver(Database *db, uint32 &count) -{ - _prepareStatement("auth_ping", "SELECT 1 FROM realmlist LIMIT 1", db, count); -}; - -void PreparedStatementHolder::Execute(Database *db, const char *name) -{ - const char prefix[] = "EXECUTE "; - size_t querySize = 8 + strlen(name) + 1; - char* query = new char[querySize]; - strcpy(query, prefix); - strcat(query, name); - - DEBUG_LOG("Prepared statement: %s", query); - db->Execute(query); - delete[] query; -} - -void PreparedStatementHolder::PExecute(Database *db, const char *name, const char* args) -{ - // NOTE: if args == NULL, we're crashing here. No need to waste performance on checking; - // devs must make sure they use PExecute for args and Execute for no args. - - const char prefix[] = "EXECUTE "; - size_t querySize = 8 + strlen(name) + 7 + strlen(args) + 1; - char* query = new char[querySize]; - strcpy(query, prefix); - strcat(query, name); - strcat(query, " USING "); - strcat(query, args); - - DEBUG_LOG("Prepared statement (parsed args): %s", query); - db->Execute(query); - delete[] query; -} - -QueryResult_AutoPtr PreparedStatementHolder::Query(Database *db, const char *name) -{ - QueryResult_AutoPtr _return = QueryResult_AutoPtr(NULL); - - const char prefix[] = "EXECUTE "; - size_t querySize = 8 + strlen(name) + 1; - char* query = new char[querySize]; - strcpy(query, prefix); - strcat(query, name); - - DEBUG_LOG("Prepared statement with resultset: %s", query); - _return = db->Query(query); - delete[] query; - return _return; -} - -QueryResult_AutoPtr PreparedStatementHolder::PQuery(Database *db, const char *name, const char *args) -{ - // NOTE: if args == NULL, we're crashing here. No need to waste performance on checking; - // devs must make sure they use PQuery for args and Query for no args. - - QueryResult_AutoPtr _return = QueryResult_AutoPtr(NULL); - - const char prefix[] = "EXECUTE "; - size_t querySize = 8 + strlen(name) + 7 + strlen(args) + 1; - char* query = new char[querySize]; - strcpy(query, prefix); - strcat(query, name); - strcat(query, " USING "); - strcat(query, args); - - DEBUG_LOG("Prepared statement with resultset (parsed args): %s", query); - _return = db->Query(query); - delete[] query; - return _return; -}
\ No newline at end of file diff --git a/src/server/shared/Database/PreparedStatements.h b/src/server/shared/Database/PreparedStatements.h deleted file mode 100644 index 277d2b833ec..00000000000 --- a/src/server/shared/Database/PreparedStatements.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef sPreparedStatement - -#include "ace/Singleton.h" -#include "Database/DatabaseEnv.h" - -class PreparedStatementHolder -{ - public: - ///- Load prepare statements on database $db and increase $count for every statement - void LoadCharacters(Database *db, uint32 &count); - void LoadAuthserver(Database *db, uint32 &count); - void LoadWorldserver(Database *db, uint32 &count); - - ///- Executes prepared statement that doesn't require feedback with name $name on database $db - void Execute(Database *db, const char* name); - ///- Executes prepared statement that doesn't require feedback with name $name and args $args - ///- on database $db - void PExecute(Database *db, const char* name, const char* args); - - ///- Executes a prepared statement without args on db $db with name $name and puts the result set in a pointer. - QueryResult_AutoPtr Query(Database* db, const char* name); - ///- Executes a prepared statement with args $args on db $db with name $name and put the result set in a pointer. - QueryResult_AutoPtr PQuery(Database* db, const char* name, const char* args); - - private: - void _prepareStatement(const char* name, const char* sql, Database *db, uint32 &count); - -}; -#define sPreparedStatement (*ACE_Singleton<PreparedStatementHolder, ACE_Null_Mutex>::instance()) -#endif
\ No newline at end of file diff --git a/src/server/shared/Database/SqlOperations.cpp b/src/server/shared/Database/SQLOperation.cpp index 33b96a30533..d3af5949faf 100644 --- a/src/server/shared/Database/SqlOperations.cpp +++ b/src/server/shared/Database/SQLOperation.cpp @@ -1,6 +1,4 @@ /* - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify @@ -18,71 +16,74 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "SqlOperations.h" -#include "SqlDelayThread.h" -#include "DatabaseEnv.h" -#include "DatabaseImpl.h" +#include "SQLOperation.h" +#include "MySQLConnection.h" -/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- +/*! Basic, ad-hoc queries. */ +BasicStatementTask::BasicStatementTask(const char* sql) +{ + m_sql = strdup(sql); +} -void SqlStatement::Execute(Database *db) +BasicStatementTask::~BasicStatementTask() { - /// just do it - db->DirectExecute(m_sql); + free((void*)m_sql); } -void SqlTransaction::Execute(Database *db) +bool BasicStatementTask::Execute() { - const char* sql; + return m_conn->Execute(m_sql); +} + +/*! Transactions. */ +TransactionTask::TransactionTask() +{ +} + +TransactionTask::~TransactionTask() +{ + +} - m_Mutex.acquire(); - if (m_queue.empty()) +void TransactionTask::ForcefulDelete() +{ + while (!m_queries.empty()) { - m_Mutex.release(); - return; + free((void*)const_cast<char*>(m_queries.front())); + m_queries.pop(); } +} - db->DirectExecute("START TRANSACTION"); - while (!m_queue.empty()) - { - sql = m_queue.front(); +bool TransactionTask::Execute() +{ + if (m_queries.empty()) + return false; + + const char* sql; - if (!db->DirectExecute(sql)) + m_conn->BeginTransaction(); + while (!m_queries.empty()) + { + sql = m_queries.front(); + if (!m_conn->Execute(sql)) { free((void*)const_cast<char*>(sql)); - m_queue.pop(); - db->DirectExecute("ROLLBACK"); - while (!m_queue.empty()) - { - free((void*)const_cast<char*>(m_queue.front())); - m_queue.pop(); - } - m_Mutex.release(); - return; + m_queries.pop(); + m_conn->RollbackTransaction(); + ForcefulDelete(); + return false; } free((void*)const_cast<char*>(sql)); - m_queue.pop(); + m_queries.pop(); } - db->DirectExecute("COMMIT"); - m_Mutex.release(); -} - -/// ---- ASYNC QUERIES ---- - -void SqlQuery::Execute(Database *db) -{ - if (!m_callback || !m_queue) - return; - - /// execute the query and store the result in the callback - m_callback->SetResult(db->Query(m_sql)); - /// add the callback to the sql result queue of the thread it originated from - m_queue->add(m_callback); + m_conn->CommitTransaction(); + return true; } -void SqlResultQueue::Update() +/*! Callback statements/holders */ +void SQLResultQueue::Update() { /// execute the callbacks waiting in the synchronization queue Trinity::IQueryCallback* callback; @@ -93,39 +94,27 @@ void SqlResultQueue::Update() } } -bool SqlQueryHolder::Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue) -{ - if (!callback || !thread || !queue) - return false; - - /// delay the execution of the queries, sync them with the delay thread - /// which will in turn resync on execution (via the queue) and call back - SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue); - thread->Delay(holderEx); - return true; -} - -bool SqlQueryHolder::SetQuery(size_t index, const char *sql) +bool SQLQueryHolder::SetQuery(size_t index, const char *sql) { if (m_queries.size() <= index) { - sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,(uint32)m_queries.size(),sql); + sLog.outError("Query index (%u) out of range (size: %u) for query: %s", index, (uint32)m_queries.size(), sql); return false; } if (m_queries[index].first != NULL) { sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])", - index,m_queries[index].first,sql); + index, m_queries[index].first, sql); return false; } /// not executed yet, just stored (it's not called a holder for nothing) - m_queries[index] = SqlResultPair(strdup(sql), QueryResult_AutoPtr(NULL)); + m_queries[index] = SQLResultPair(strdup(sql), QueryResult_AutoPtr(NULL)); return true; } -bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) +bool SQLQueryHolder::SetPQuery(size_t index, const char *format, ...) { if (!format) { @@ -145,10 +134,10 @@ bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) return false; } - return SetQuery(index,szQuery); + return SetQuery(index, szQuery); } -QueryResult_AutoPtr SqlQueryHolder::GetResult(size_t index) +QueryResult_AutoPtr SQLQueryHolder::GetResult(size_t index) { if (index < m_queries.size()) { @@ -165,14 +154,14 @@ QueryResult_AutoPtr SqlQueryHolder::GetResult(size_t index) return QueryResult_AutoPtr(NULL); } -void SqlQueryHolder::SetResult(size_t index, QueryResult_AutoPtr result) +void SQLQueryHolder::SetResult(size_t index, QueryResult_AutoPtr result) { /// store the result in the holder if (index < m_queries.size()) m_queries[index].second = result; } -SqlQueryHolder::~SqlQueryHolder() +SQLQueryHolder::~SQLQueryHolder() { for (size_t i = 0; i < m_queries.size(); i++) { @@ -183,27 +172,41 @@ SqlQueryHolder::~SqlQueryHolder() } } -void SqlQueryHolder::SetSize(size_t size) +void SQLQueryHolder::SetSize(size_t size) { /// to optimize push_back, reserve the number of queries about to be executed m_queries.resize(size); } -void SqlQueryHolderEx::Execute(Database *db) +bool SQLQueryHolderTask::Execute() { if (!m_holder || !m_callback || !m_queue) - return; + return false; /// we can do this, we are friends - std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries; + std::vector<SQLQueryHolder::SQLResultPair> &queries = m_holder->m_queries; for (size_t i = 0; i < queries.size(); i++) { /// execute all queries in the holder and pass the results char const *sql = queries[i].first; - if(sql) m_holder->SetResult(i, db->Query(sql)); + if (sql) + m_holder->SetResult(i, m_conn->Query(sql)); } /// sync with the caller thread m_queue->add(m_callback); + return true; } + +bool SQLQueryTask::Execute() +{ + if (!m_callback || !m_queue) + return false; + + /// execute the query and store the result in the callback + m_callback->SetResult(m_conn->Query(m_sql)); + /// add the callback to the sql result queue of the thread it originated from + m_queue->add(m_callback); + return true; +}
\ No newline at end of file diff --git a/src/server/shared/Database/SQLOperation.h b/src/server/shared/Database/SQLOperation.h new file mode 100644 index 00000000000..8f2a59151a3 --- /dev/null +++ b/src/server/shared/Database/SQLOperation.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SQLOPERATION_H +#define _SQLOPERATION_H + +#include <ace/Method_Request.h> + +#include "Common.h" +#include "Threading/Callback.h" + +class MySQLConnection; + +class SQLOperation : public ACE_Method_Request +{ + public: + SQLOperation(){}; + int call() + { + Execute(); + return 0; + } + virtual bool Execute() = 0; + + virtual void SetConnection(MySQLConnection* con) { m_conn = con; } + + MySQLConnection* m_conn; +}; + +/*! Raw, ad-hoc query. */ +class BasicStatementTask : public SQLOperation +{ + public: + BasicStatementTask(const char* sql); + ~BasicStatementTask(); + + bool Execute(); + + private: + const char* m_sql; //- Raw query to be executed +}; + +/*! Transactions */ +class TransactionTask : public SQLOperation +{ + public: + TransactionTask(); + ~TransactionTask(); + void ForcefulDelete(); + + bool Execute(); + + private: + std::queue<char*> m_queries; +}; + +/*! ResultQueue */ +class SQLResultQueue : public ACE_Based::LockedQueue<Trinity::IQueryCallback* , ACE_Thread_Mutex> +{ + public: + SQLResultQueue() {} + void Update(); +}; + +class SQLQueryHolder +{ + friend class SQLQueryHolderTask; + private: + typedef std::pair<const char*, QueryResult_AutoPtr> SQLResultPair; + std::vector<SQLResultPair> m_queries; + public: + SQLQueryHolder() {} + ~SQLQueryHolder(); + bool SetQuery(size_t index, const char *sql); + bool SetPQuery(size_t index, const char *format, ...) ATTR_PRINTF(3,4); + void SetSize(size_t size); + QueryResult_AutoPtr GetResult(size_t index); + void SetResult(size_t index, QueryResult_AutoPtr result); +}; + +class SQLQueryHolderTask : public SQLOperation +{ + private: + SQLQueryHolder * m_holder; + Trinity::IQueryCallback * m_callback; + SQLResultQueue * m_queue; + public: + SQLQueryHolderTask(SQLQueryHolder *holder, Trinity::IQueryCallback * callback, SQLResultQueue * queue) + : m_holder(holder), m_callback(callback), m_queue(queue) {} + bool Execute(); +}; + +class SQLQueryTask : public SQLOperation +{ + private: + const char *m_sql; + Trinity::IQueryCallback * m_callback; + SQLResultQueue * m_queue; + public: + SQLQueryTask(const char *sql, Trinity::IQueryCallback * callback, SQLResultQueue * queue) + : m_sql(strdup(sql)), m_callback(callback), m_queue(queue) {} + ~SQLQueryTask() { void* tofree = const_cast<char*>(m_sql); free(tofree); } + bool Execute(); +}; + + +#endif
\ No newline at end of file diff --git a/src/server/shared/Database/SQLStorage.cpp b/src/server/shared/Database/SQLStorage.cpp index 1a65824a631..26aecb80fa3 100644 --- a/src/server/shared/Database/SQLStorage.cpp +++ b/src/server/shared/Database/SQLStorage.cpp @@ -21,7 +21,7 @@ #include "SQLStorage.h" #include "SQLStorageImpl.h" -extern Database WorldDatabase; +extern DatabaseType WorldDatabase; const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiifffliiiiiiiliiisi"; const char CreatureInfodstfmt[]="iiiiiiiiiisssibbiiiifffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiifffliiiiiiiliiiii"; diff --git a/src/server/shared/Database/SqlOperations.h b/src/server/shared/Database/SqlOperations.h deleted file mode 100644 index 337790e4c72..00000000000 --- a/src/server/shared/Database/SqlOperations.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __SQLOPERATIONS_H -#define __SQLOPERATIONS_H - -#include "Common.h" - -#include "ace/Thread_Mutex.h" -#include "ace/Method_Request.h" -#include "Threading/LockedQueue.h" -#include <queue> -#include "Threading/Callback.h" -#include "QueryResult.h" - -/// ---- BASE --- - -class Database; -class SqlDelayThread; - -class SqlOperation -{ - public: - virtual void OnRemove() { delete this; } - virtual void Execute(Database *db) = 0; - virtual ~SqlOperation() {} -}; - -/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- - -class SqlStatement : public SqlOperation -{ - private: - const char *m_sql; - public: - SqlStatement(const char *sql) : m_sql(strdup(sql)){} - ~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); } - void Execute(Database *db); -}; - -class SqlTransaction : public SqlOperation -{ - private: - std::queue<const char*> m_queue; - ACE_Thread_Mutex m_Mutex; - public: - SqlTransaction() {} - void DelayExecute(const char *sql) - { - m_Mutex.acquire(); - char* _sql = strdup(sql); - if (_sql) - m_queue.push(_sql); - m_Mutex.release(); - } - void Execute(Database *db); -}; - -/// ---- ASYNC QUERIES ---- - -class SqlQuery; /// contains a single async query -class QueryResult; /// the result of one -class SqlResultQueue; /// queue for thread sync -class SqlQueryHolder; /// groups several async quries -class SqlQueryHolderEx; /// points to a holder, added to the delay thread - -class SqlResultQueue : public ACE_Based::LockedQueue<Trinity::IQueryCallback* , ACE_Thread_Mutex> -{ - public: - SqlResultQueue() {} - void Update(); -}; - -class SqlQuery : public SqlOperation -{ - private: - const char *m_sql; - Trinity::IQueryCallback * m_callback; - SqlResultQueue * m_queue; - public: - SqlQuery(const char *sql, Trinity::IQueryCallback * callback, SqlResultQueue * queue) - : m_sql(strdup(sql)), m_callback(callback), m_queue(queue) {} - ~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); } - void Execute(Database *db); -}; - -class SqlQueryHolder -{ - friend class SqlQueryHolderEx; - private: - typedef std::pair<const char*, QueryResult_AutoPtr> SqlResultPair; - std::vector<SqlResultPair> m_queries; - public: - SqlQueryHolder() {} - ~SqlQueryHolder(); - bool SetQuery(size_t index, const char *sql); - bool SetPQuery(size_t index, const char *format, ...) ATTR_PRINTF(3,4); - void SetSize(size_t size); - QueryResult_AutoPtr GetResult(size_t index); - void SetResult(size_t index, QueryResult_AutoPtr result); - bool Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue); -}; - -class SqlQueryHolderEx : public SqlOperation -{ - private: - SqlQueryHolder * m_holder; - Trinity::IQueryCallback * m_callback; - SqlResultQueue * m_queue; - public: - SqlQueryHolderEx(SqlQueryHolder *holder, Trinity::IQueryCallback * callback, SqlResultQueue * queue) - : m_holder(holder), m_callback(callback), m_queue(queue) {} - void Execute(Database *db); -}; - -class SqlAsyncTask : public ACE_Method_Request -{ -public: - SqlAsyncTask(Database * db, SqlOperation * op) : m_db(db), m_op(op){} - ~SqlAsyncTask() - { - if (!m_op) - return; - - delete m_op; - m_op = NULL; - } - - int call() - { - if (m_db == NULL || m_op == NULL) - return -1; - - try - { - m_op->Execute(m_db); - } - catch(...) - { - return -1; - } - - return 0; - } - -private: - Database * m_db; - SqlOperation * m_op; -}; -#endif //__SQLOPERATIONS_H - diff --git a/src/server/shared/PrecompiledHeaders/sharedPCH.h b/src/server/shared/PrecompiledHeaders/sharedPCH.h index c3e00e5f119..289f31e74bf 100644 --- a/src/server/shared/PrecompiledHeaders/sharedPCH.h +++ b/src/server/shared/PrecompiledHeaders/sharedPCH.h @@ -6,8 +6,8 @@ #include "DatabaseEnv.h" #include "Threading/Threading.h" #include "Database/DatabaseEnv.h" -#include "Database/SqlDelayThread.h" -#include "Database/SqlOperations.h" +#include "Database/DatabaseWorker.h" +#include "Database/SQLOperation.h" #include "Debugging/Errors.h" #include "Dynamic/TypeList.h" #include "Dynamic/UnorderedMap.h" diff --git a/src/server/worldserver/CommandLine/CliRunnable.cpp b/src/server/worldserver/CommandLine/CliRunnable.cpp index d105f29f3c2..e44ec1ada94 100644 --- a/src/server/worldserver/CommandLine/CliRunnable.cpp +++ b/src/server/worldserver/CommandLine/CliRunnable.cpp @@ -690,8 +690,28 @@ int kb_hit_return() /// %Thread start void CliRunnable::run() { - ///- Init new SQL thread for the world database (one connection call enough) - WorldDatabase.ThreadStart(); // let thread do safe mySQL requests + ///- Init MySQL threads or connections + bool needInit = true; + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_WORLDDB) & MYSQL_BUNDLE_RA)) + { + WorldDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + { + LoginDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + { + CharacterDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (needInit) + MySQL::Thread_Init(); char commandbuf[256]; @@ -759,6 +779,14 @@ void CliRunnable::run() } - ///- End the database thread - WorldDatabase.ThreadEnd(); // free mySQL thread resources + ///- Free MySQL thread resources and deallocate lingering connections + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_WORLDDB) & MYSQL_BUNDLE_RA)) + WorldDatabase.End_MySQL_Connection(); + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + LoginDatabase.End_MySQL_Connection(); + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + CharacterDatabase.End_MySQL_Connection(); + if (needInit) + MySQL::Thread_End(); } diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp index ce2f442fa8f..ab906cb9b42 100644 --- a/src/server/worldserver/Master.cpp +++ b/src/server/worldserver/Master.cpp @@ -370,9 +370,9 @@ int Master::Run() clearOnlineAccounts(); ///- Wait for delay threads to end - CharacterDatabase.HaltDelayThread(); - WorldDatabase.HaltDelayThread(); - LoginDatabase.HaltDelayThread(); + CharacterDatabase.Close(); + WorldDatabase.Close(); + LoginDatabase.Close(); sLog.outString( "Halting process..." ); @@ -439,8 +439,8 @@ bool Master::_StartDB() { sLog.SetLogDB(false); std::string dbstring; + uint8 num_threads; - ///- Get world database info from configuration file dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", ""); if (dbstring.empty()) { @@ -448,13 +448,20 @@ bool Master::_StartDB() return false; } - ///- Initialise the world database - if( !WorldDatabase.Initialize(dbstring.c_str())) + num_threads = sConfig.GetIntDefault("WorldDatabase.WorkerThreads", 1); + if (num_threads < 1 || num_threads > 32) { - sLog.outError("Cannot connect to world database %s",dbstring.c_str()); + sLog.outError("World database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); return false; } + ///- Initialise the world database + if (!WorldDatabase.Open(dbstring, num_threads)) + { + sLog.outError("Cannot connect to world database %s", dbstring.c_str()); + return false; + } ///- Get character database info from configuration file dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", ""); if (dbstring.empty()) @@ -463,13 +470,20 @@ bool Master::_StartDB() return false; } - ///- Initialise the Character database - if (!CharacterDatabase.Initialize(dbstring.c_str())) + num_threads = sConfig.GetIntDefault("CharacterDatabase.WorkerThreads", 1); + if (num_threads < 1 || num_threads > 32) { - sLog.outError("Cannot connect to Character database %s",dbstring.c_str()); + sLog.outError("Character database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); return false; } + ///- Initialise the Character database + if (!CharacterDatabase.Open(dbstring, num_threads)) + { + sLog.outError("Cannot connect to Character database %s", dbstring.c_str()); + return false; + } ///- Get login database info from configuration file dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", ""); if (dbstring.empty()) @@ -478,13 +492,20 @@ bool Master::_StartDB() return false; } - ///- Initialise the login database - if (!LoginDatabase.Initialize(dbstring.c_str())) + num_threads = sConfig.GetIntDefault("LoginDatabase.WorkerThreads", 1); + if (num_threads < 1 || num_threads > 32) { - sLog.outError("Cannot connect to login database %s",dbstring.c_str()); + sLog.outError("Login database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); return false; } + ///- Initialise the login database + if (!LoginDatabase.Open(dbstring, num_threads)) + { + sLog.outError("Cannot connect to login database %s", dbstring.c_str()); + return false; + } ///- Get the realm Id from the configuration file realmID = sConfig.GetIntDefault("RealmID", 0); if (!realmID) diff --git a/src/server/worldserver/WorldThread/WorldRunnable.cpp b/src/server/worldserver/WorldThread/WorldRunnable.cpp index 67a3937b97d..555f174b6c8 100644 --- a/src/server/worldserver/WorldThread/WorldRunnable.cpp +++ b/src/server/worldserver/WorldThread/WorldRunnable.cpp @@ -43,8 +43,27 @@ extern int m_ServiceStatus; /// Heartbeat for the World void WorldRunnable::run() { - ///- Init new SQL thread for the world database - WorldDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough) + ///- Init MySQL threads or connections + bool needInit = true; + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_WORLDDB) & MYSQL_BUNDLE_RA)) + { + WorldDatabase.Init_MySQL_Connection(); + needInit = false; + } + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + { + LoginDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + { + CharacterDatabase.Init_MySQL_Connection(); + needInit = false; + } + + if (needInit) + MySQL::Thread_Init(); sWorld.InitResultQueue(); @@ -99,6 +118,16 @@ void WorldRunnable::run() sMapMgr.UnloadAll(); // unload all grids (including locked in memory) - ///- End the database thread - WorldDatabase.ThreadEnd(); // free mySQL thread resources + ///- Free MySQL thread resources and deallocate lingering connections + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_WORLDDB) & MYSQL_BUNDLE_RA)) + WorldDatabase.End_MySQL_Connection(); + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_LOGINDB) & MYSQL_BUNDLE_RA)) + LoginDatabase.End_MySQL_Connection(); + + if (!(sWorld.getConfig(CONFIG_MYSQL_BUNDLE_CHARDB) & MYSQL_BUNDLE_RA)) + CharacterDatabase.End_MySQL_Connection(); + + if (needInit) + MySQL::Thread_End(); } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 90a520529cc..d847a7c0467 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -36,6 +36,30 @@ # .;/path/to/unix_socket;username;password;database # - use Unix sockets in Unix/Linux # +# LoginDatabase.WorkerThreads +# WorldDatabase.WorkerThreads +# CharacterDatabase.WorkerThreads +# The amount of worker threads spawned to handle +# asynchroneous MySQL statements +# Each worker thread is mirrored with its own +# connection to the MySQL server and their own +# thread on the MySQL server. +# Default: 1 +# +# LoginDatabase.ThreadBundleMask +# WorldDatabase.ThreadBundleMask +# CharacterDatabase.ThreadBundleMask +# Defines which runnable threads are bundled into one synchroneous +# connection. Runnables not specified in the mask will have their +# seperate connection to the MySQL server. +# Value is a bitmask consisting of: +# MYSQL_BUNDLE_NONE = 0, Each task will run their own MySQL connection +# MYSQL_BUNDLE_CLI = 1, Commandline interface thread +# MYSQL_BUNDLE_RA = 2, Remote admin thread +# MYSQL_BUNDLE_RAR = 4, Reactor runnable thread +# MYSQL_BUNDLE_WORLD = 8, WorldRunnable +# MYSQL_BUNDLE_ALL = 15, All bundled together +# # MaxPingTime # Settings for maximum database-ping interval (minutes between pings) # @@ -53,6 +77,12 @@ LogsDir = "" LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" WorldDatabaseInfo = "127.0.0.1;3306;trinity;trinity;world" CharacterDatabaseInfo = "127.0.0.1;3306;trinity;trinity;characters" +LoginDatabase.WorkerThreads = 1 +WorldDatabase.WorkerThreads = 1 +CharacterDatabase.WorkerThreads = 1 +LoginDatabase.ThreadBundleMask = 15 +WorldDatabase.ThreadBundleMask = 15 +CharacterDatabase.ThreadBundleMask = 15 MaxPingTime = 30 WorldServerPort = 8085 BindIP = "0.0.0.0" |