fix(Core/Database): Gracefully close database workers (#20936)
* Gracefully close database workers * Change init order. Such a silly compiler flag * Fix hang if db connection failed to open
This commit is contained in:
@@ -28,10 +28,11 @@ private:
|
|||||||
std::mutex _queueLock;
|
std::mutex _queueLock;
|
||||||
std::queue<T> _queue;
|
std::queue<T> _queue;
|
||||||
std::condition_variable _condition;
|
std::condition_variable _condition;
|
||||||
|
std::atomic<bool> _cancel;
|
||||||
std::atomic<bool> _shutdown;
|
std::atomic<bool> _shutdown;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ProducerConsumerQueue() : _shutdown(false) { }
|
ProducerConsumerQueue() : _cancel(false), _shutdown(false) { }
|
||||||
|
|
||||||
void Push(const T& value)
|
void Push(const T& value)
|
||||||
{
|
{
|
||||||
@@ -57,10 +58,8 @@ public:
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_queueLock);
|
std::lock_guard<std::mutex> lock(_queueLock);
|
||||||
|
|
||||||
if (_queue.empty() || _shutdown)
|
if (_queue.empty() || _cancel)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
value = _queue.front();
|
value = _queue.front();
|
||||||
|
|
||||||
@@ -75,21 +74,18 @@ public:
|
|||||||
|
|
||||||
// we could be using .wait(lock, predicate) overload here but it is broken
|
// we could be using .wait(lock, predicate) overload here but it is broken
|
||||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1098841
|
// https://connect.microsoft.com/VisualStudio/feedback/details/1098841
|
||||||
while (_queue.empty() && !_shutdown)
|
while (_queue.empty() && !_cancel && !_shutdown)
|
||||||
{
|
|
||||||
_condition.wait(lock);
|
_condition.wait(lock);
|
||||||
}
|
|
||||||
|
|
||||||
if (_queue.empty() || _shutdown)
|
if (_queue.empty() || _cancel)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
value = _queue.front();
|
value = _queue.front();
|
||||||
|
|
||||||
_queue.pop();
|
_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clears the queue and will immediately stop any consumers
|
||||||
void Cancel()
|
void Cancel()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_queueLock);
|
std::unique_lock<std::mutex> lock(_queueLock);
|
||||||
@@ -103,11 +99,18 @@ public:
|
|||||||
_queue.pop();
|
_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
_shutdown = true;
|
_cancel = true;
|
||||||
|
|
||||||
_condition.notify_all();
|
_condition.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Graceful stop, will wait for queue to become empty before stopping consumers
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
_shutdown = true;
|
||||||
|
_condition.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename E = T>
|
template<typename E = T>
|
||||||
typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
|
typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
|
||||||
|
|||||||
@@ -23,16 +23,11 @@ DatabaseWorker::DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, M
|
|||||||
{
|
{
|
||||||
_connection = connection;
|
_connection = connection;
|
||||||
_queue = newQueue;
|
_queue = newQueue;
|
||||||
_cancelationToken = false;
|
|
||||||
_workerThread = std::thread(&DatabaseWorker::WorkerThread, this);
|
_workerThread = std::thread(&DatabaseWorker::WorkerThread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseWorker::~DatabaseWorker()
|
DatabaseWorker::~DatabaseWorker()
|
||||||
{
|
{
|
||||||
_cancelationToken = true;
|
|
||||||
|
|
||||||
_queue->Cancel();
|
|
||||||
|
|
||||||
_workerThread.join();
|
_workerThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +42,7 @@ void DatabaseWorker::WorkerThread()
|
|||||||
|
|
||||||
_queue->WaitAndPop(operation);
|
_queue->WaitAndPop(operation);
|
||||||
|
|
||||||
if (_cancelationToken || !operation)
|
if (!operation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
operation->SetConnection(_connection);
|
operation->SetConnection(_connection);
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ private:
|
|||||||
void WorkerThread();
|
void WorkerThread();
|
||||||
std::thread _workerThread;
|
std::thread _workerThread;
|
||||||
|
|
||||||
std::atomic<bool> _cancelationToken;
|
|
||||||
|
|
||||||
DatabaseWorker(DatabaseWorker const& right) = delete;
|
DatabaseWorker(DatabaseWorker const& right) = delete;
|
||||||
DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
|
DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -112,7 +112,11 @@ uint32 DatabaseWorkerPool<T>::Open()
|
|||||||
template <class T>
|
template <class T>
|
||||||
void DatabaseWorkerPool<T>::Close()
|
void DatabaseWorkerPool<T>::Close()
|
||||||
{
|
{
|
||||||
LOG_INFO("sql.driver", "Closing down DatabasePool '{}'.", GetDatabaseName());
|
LOG_INFO("sql.driver", "Closing down DatabasePool '{}'. Waiting for {} queries to finish...", GetDatabaseName(), _queue->Size());
|
||||||
|
|
||||||
|
// Gracefully close async query queue, worker threads will block when the destructor
|
||||||
|
// is called from the .clear() functions below until the queue is empty
|
||||||
|
_queue->Shutdown();
|
||||||
|
|
||||||
//! Closes the actualy MySQL connection.
|
//! Closes the actualy MySQL connection.
|
||||||
_connections[IDX_ASYNC].clear();
|
_connections[IDX_ASYNC].clear();
|
||||||
@@ -432,6 +436,7 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne
|
|||||||
if (uint32 error = connection->Open())
|
if (uint32 error = connection->Open())
|
||||||
{
|
{
|
||||||
// Failed to open a connection or invalid version, abort and cleanup
|
// Failed to open a connection or invalid version, abort and cleanup
|
||||||
|
_queue->Cancel();
|
||||||
_connections[type].clear();
|
_connections[type].clear();
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,6 @@
|
|||||||
#include <boost/asio/ip/address.hpp>
|
#include <boost/asio/ip/address.hpp>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
TaskScheduler playersSaveScheduler;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::atomic_long World::_stopEvent = false;
|
std::atomic_long World::_stopEvent = false;
|
||||||
uint8 World::_exitCode = SHUTDOWN_EXIT_CODE;
|
uint8 World::_exitCode = SHUTDOWN_EXIT_CODE;
|
||||||
uint32 World::m_worldLoopCounter = 0;
|
uint32 World::m_worldLoopCounter = 0;
|
||||||
@@ -2497,11 +2492,6 @@ void World::Update(uint32 diff)
|
|||||||
sScriptMgr->OnWorldUpdate(diff);
|
sScriptMgr->OnWorldUpdate(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update playersSaveScheduler"));
|
|
||||||
playersSaveScheduler.Update(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update metrics"));
|
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update metrics"));
|
||||||
// Stats logger update
|
// Stats logger update
|
||||||
@@ -2691,31 +2681,6 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode, const std:
|
|||||||
_shutdownMask = options;
|
_shutdownMask = options;
|
||||||
_exitCode = exitcode;
|
_exitCode = exitcode;
|
||||||
|
|
||||||
auto const& playersOnline = GetActiveSessionCount();
|
|
||||||
|
|
||||||
if (time < 5 && playersOnline)
|
|
||||||
{
|
|
||||||
// Set time to 5s for save all players
|
|
||||||
time = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
playersSaveScheduler.CancelAll();
|
|
||||||
|
|
||||||
if (time >= 5)
|
|
||||||
{
|
|
||||||
playersSaveScheduler.Schedule(Seconds(time - 5), [this](TaskContext /*context*/)
|
|
||||||
{
|
|
||||||
if (!GetActiveSessionCount())
|
|
||||||
{
|
|
||||||
LOG_INFO("server", "> No players online. Skip save before shutdown");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO("server", "> Save players before shutdown server");
|
|
||||||
ObjectAccessor::SaveAllPlayers();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_WARN("server", "Time left until shutdown/restart: {}", time);
|
LOG_WARN("server", "Time left until shutdown/restart: {}", time);
|
||||||
|
|
||||||
///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
|
///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
|
||||||
|
|||||||
Reference in New Issue
Block a user