1
0

RankMgr: Fixed multithreading issues.

Only one thread is allowed to interact with a SQLite::Database object at a time.
Additionally, improved performance of the migration by wrapping the entire thing in a transaction.
This commit is contained in:
madmaxoft 2014-08-13 12:33:31 +02:00
parent e110f72268
commit 5e415c5b95
2 changed files with 110 additions and 50 deletions

View File

@ -5,7 +5,6 @@
#include "Globals.h" #include "Globals.h"
#include "RankManager.h" #include "RankManager.h"
#include "SQLiteCpp/Transaction.h"
#include "inifile/iniFile.h" #include "inifile/iniFile.h"
#include "Protocol/MojangAPI.h" #include "Protocol/MojangAPI.h"
@ -237,6 +236,8 @@ public:
/** Performs the complete migration from INI files to DB. */ /** Performs the complete migration from INI files to DB. */
bool Migrate(void) bool Migrate(void)
{ {
cRankManager::cMassChangeLock Lock(m_RankManager);
LOGD("Reading groups..."); LOGD("Reading groups...");
if (!ReadGroups()) if (!ReadGroups())
{ {
@ -615,6 +616,7 @@ void cRankManager::Initialize(cMojangAPI & a_MojangAPI)
AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID) AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -642,6 +644,7 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID) AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -677,6 +680,7 @@ AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID) AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -712,6 +716,7 @@ AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
AStringVector cRankManager::GetRankGroups(const AString & a_RankName) AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -742,6 +747,7 @@ AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName) AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -771,6 +777,7 @@ AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
AStringVector cRankManager::GetRankPermissions(const AString & a_RankName) AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -801,6 +808,7 @@ AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
AStringVector cRankManager::GetAllRanks(void) AStringVector cRankManager::GetAllRanks(void)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -825,6 +833,7 @@ AStringVector cRankManager::GetAllRanks(void)
AStringVector cRankManager::GetAllGroups(void) AStringVector cRankManager::GetAllGroups(void)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -849,6 +858,7 @@ AStringVector cRankManager::GetAllGroups(void)
AStringVector cRankManager::GetAllPermissions(void) AStringVector cRankManager::GetAllPermissions(void)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -878,6 +888,7 @@ bool cRankManager::GetPlayerMsgVisuals(
) )
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
@ -923,6 +934,7 @@ void cRankManager::AddRank(
) )
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -965,10 +977,11 @@ void cRankManager::AddRank(
void cRankManager::AddGroup(const AString & a_GroupName) void cRankManager::AddGroup(const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Check if such a rank name is already used: // Check if such a group name is already used:
{ {
SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?"); SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?");
stmt.bind(1, a_GroupName); stmt.bind(1, a_GroupName);
@ -982,7 +995,7 @@ void cRankManager::AddGroup(const AString & a_GroupName)
} }
} }
// Insert a new rank: // Insert a new group:
SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)"); SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)");
stmt.bind(1, a_GroupName); stmt.bind(1, a_GroupName);
if (stmt.exec() <= 0) if (stmt.exec() <= 0)
@ -1001,16 +1014,58 @@ void cRankManager::AddGroup(const AString & a_GroupName)
bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a_RankName) void cRankManager::AddGroups(const AStringVector & a_GroupNames)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try
{
for (AStringVector::const_iterator itr = a_GroupNames.begin(), end = a_GroupNames.end(); itr != end; ++itr)
{
// Check if such the group name is already used:
{
SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?");
stmt.bind(1, *itr);
if (stmt.executeStep())
{
if (stmt.getColumn(0).getInt() > 0)
{
// Group already exists, do nothing:
return;
}
}
}
// Insert a new group:
SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)");
stmt.bind(1, *itr);
if (stmt.exec() <= 0)
{
LOGWARNING("%s: Failed to add a new group \"%s\".", __FUNCTION__, itr->c_str());
return;
}
} // for itr - a_GroupNames[]
}
catch (const SQLite::Exception & ex)
{
LOGWARNING("%s: Failed to add new groups: %s", __FUNCTION__, ex.what());
}
}
bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a_RankName)
{
ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
SQLite::Transaction trans(m_DB);
int GroupID, RankID;
// Get the group's ID: // Get the group's ID:
int GroupID;
{ {
SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
stmt.bind(1, a_GroupName); stmt.bind(1, a_GroupName);
@ -1023,6 +1078,7 @@ bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a
} }
// Get the rank's ID: // Get the rank's ID:
int RankID;
{ {
SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
stmt.bind(1, a_RankName); stmt.bind(1, a_RankName);
@ -1066,7 +1122,6 @@ bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a
} }
// Adding succeeded: // Adding succeeded:
trans.commit();
return true; return true;
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
@ -1083,12 +1138,10 @@ bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a
bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName) bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrapp the entire operation into a transaction:
SQLite::Transaction trans(m_DB);
// Get the group's ID: // Get the group's ID:
int GroupID; int GroupID;
{ {
@ -1134,7 +1187,6 @@ bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AStr
} }
// Adding succeeded: // Adding succeeded:
trans.commit();
return true; return true;
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
@ -1153,12 +1205,10 @@ bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AStr
bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName) bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrapp the entire operation into a transaction:
SQLite::Transaction trans(m_DB);
// Get the group's ID: // Get the group's ID:
int GroupID; int GroupID;
{ {
@ -1207,7 +1257,6 @@ bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, co
} // for itr - a_Permissions[] } // for itr - a_Permissions[]
// Adding succeeded: // Adding succeeded:
trans.commit();
return true; return true;
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
@ -1226,13 +1275,11 @@ bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, co
void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName) void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
AStringVector res; AStringVector res;
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Get the RankID for the rank being removed: // Get the RankID for the rank being removed:
int RemoveRankID; int RemoveRankID;
{ {
@ -1287,8 +1334,6 @@ void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_Repl
stmt.bind(1, RemoveRankID); stmt.bind(1, RemoveRankID);
stmt.exec(); stmt.exec();
} }
trans.commit();
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
{ {
@ -1303,12 +1348,10 @@ void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_Repl
void cRankManager::RemoveGroup(const AString & a_GroupName) void cRankManager::RemoveGroup(const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Get the ID of the group: // Get the ID of the group:
int GroupID; int GroupID;
{ {
@ -1342,8 +1385,6 @@ void cRankManager::RemoveGroup(const AString & a_GroupName)
stmt.bind(1, GroupID); stmt.bind(1, GroupID);
stmt.exec(); stmt.exec();
} }
trans.commit();
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
{ {
@ -1358,12 +1399,10 @@ void cRankManager::RemoveGroup(const AString & a_GroupName)
void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName) void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Get the IDs of the group and the rank: // Get the IDs of the group and the rank:
int GroupID, RankID; int GroupID, RankID;
{ {
@ -1398,8 +1437,6 @@ void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AStrin
stmt.bind(1, RankID); stmt.bind(1, RankID);
stmt.exec(); stmt.exec();
} }
trans.commit();
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
{ {
@ -1414,12 +1451,10 @@ void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AStrin
void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName) void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Get the ID of the group: // Get the ID of the group:
int GroupID; int GroupID;
{ {
@ -1440,8 +1475,6 @@ void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const
stmt.bind(2, a_Permission); stmt.bind(2, a_Permission);
stmt.exec(); stmt.exec();
} }
trans.commit();
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
{ {
@ -1458,12 +1491,10 @@ void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const
bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName) bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Check that NewName doesn't exist: // Check that NewName doesn't exist:
{ {
SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?"); SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
@ -1484,7 +1515,6 @@ bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewNa
res = (stmt.exec() > 0); res = (stmt.exec() > 0);
} }
trans.commit();
return res; return res;
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
@ -1502,12 +1532,10 @@ bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewNa
bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewName) bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap everything into a transaction:
SQLite::Transaction trans(m_DB);
// Check that NewName doesn't exist: // Check that NewName doesn't exist:
{ {
SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
@ -1528,7 +1556,6 @@ bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewN
res = (stmt.exec() > 0); res = (stmt.exec() > 0);
} }
trans.commit();
return res; return res;
} }
catch (const SQLite::Exception & ex) catch (const SQLite::Exception & ex)
@ -1546,12 +1573,10 @@ bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewN
void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName) void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
// Wrap the entire operation into a transaction:
SQLite::Transaction trans(m_DB);
// Get the rank ID: // Get the rank ID:
int RankID; int RankID;
{ {
@ -1574,7 +1599,6 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
if (stmt.exec() > 0) if (stmt.exec() > 0)
{ {
// Successfully updated the player's rank // Successfully updated the player's rank
trans.commit();
return; return;
} }
} }
@ -1587,7 +1611,6 @@ void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a
if (stmt.exec() > 0) if (stmt.exec() > 0)
{ {
// Successfully added the player // Successfully added the player
trans.commit();
return; return;
} }
@ -1615,6 +1638,7 @@ void cRankManager::SetRankVisuals(
) )
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1646,6 +1670,7 @@ bool cRankManager::GetRankVisuals(
) )
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1675,6 +1700,7 @@ bool cRankManager::GetRankVisuals(
bool cRankManager::RankExists(const AString & a_RankName) bool cRankManager::RankExists(const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1700,6 +1726,7 @@ bool cRankManager::RankExists(const AString & a_RankName)
bool cRankManager::GroupExists(const AString & a_GroupName) bool cRankManager::GroupExists(const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1725,6 +1752,7 @@ bool cRankManager::GroupExists(const AString & a_GroupName)
bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID) bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1750,6 +1778,7 @@ bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_RankName) bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_RankName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {
@ -1781,6 +1810,7 @@ bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_
bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName) bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName)
{ {
ASSERT(m_IsInitialized); ASSERT(m_IsInitialized);
cCSLock Lock(m_CS);
try try
{ {

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include "SQLiteCpp/Database.h" #include "SQLiteCpp/Database.h"
#include "SQLiteCpp/Transaction.h"
@ -23,6 +24,29 @@ class cMojangAPI;
class cRankManager class cRankManager
{ {
public: public:
/** Acquire this lock to perform mass changes.
Improves performance by wrapping everything into a transaction.
Makes sure that no other thread is accessing the DB. */
class cMassChangeLock
{
public:
cMassChangeLock(cRankManager & a_RankManager) :
m_Lock(a_RankManager.m_CS),
m_Transaction(a_RankManager.m_DB)
{
}
~cMassChangeLock()
{
m_Transaction.commit();
}
protected:
cCSLock m_Lock;
SQLite::Transaction m_Transaction;
};
/** Creates the rank manager. Needs to be initialized before other use. */ /** Creates the rank manager. Needs to be initialized before other use. */
cRankManager(void); cRankManager(void);
@ -80,6 +104,9 @@ public:
/** Adds a new permission group. No action if such a group already exists. */ /** Adds a new permission group. No action if such a group already exists. */
void AddGroup(const AString & a_GroupName); void AddGroup(const AString & a_GroupName);
/** Bulk-adds groups. Group names that already exist are silently skipped. */
void AddGroups(const AStringVector & a_GroupNames);
/** Adds the specified permission group to the specified rank. /** Adds the specified permission group to the specified rank.
Fails if the rank or group names are not found. Fails if the rank or group names are not found.
Returns true if successful, false on error. */ Returns true if successful, false on error. */
@ -163,9 +190,12 @@ public:
protected: protected:
/** The database storage for all the data. */ /** The database storage for all the data. Protected by m_CS. */
SQLite::Database m_DB; SQLite::Database m_DB;
/** The mutex protecting m_DB against multi-threaded access. */
cCriticalSection m_CS;
/** Set to true once the manager is initialized. */ /** Set to true once the manager is initialized. */
bool m_IsInitialized; bool m_IsInitialized;