feat: add database debug logging
This commit is contained in:
parent
f15bf41bfe
commit
02b7b41b63
|
|
@ -43,7 +43,7 @@ cd ..
|
||||||
./build/bin/cards /path/to/config.json
|
./build/bin/cards /path/to/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Для подробного вывода этапов запуска и итоговой конфигурации используйте флаг `--debug`. Пароль базы данных в таком выводе не раскрывается.
|
Для подробного вывода этапов запуска и итоговой конфигурации используйте флаг `--debug`. Он также включает диагностику работы с БД: создание схемы, запуск и остановку воркеров, запросы, выбранную стратегию выборки, результаты и переподключения после ошибок. Пароль базы данных в таком выводе не раскрывается.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build/bin/cards --debug
|
./build/bin/cards --debug
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <syncstream>
|
||||||
|
|
||||||
Database::Database(const DatabaseConfig& cfg, int workers)
|
Database::Database(const DatabaseConfig& cfg, int workers, bool debug)
|
||||||
: connStr(
|
: connStr(
|
||||||
"host=" + cfg.host +
|
"host=" + cfg.host +
|
||||||
" port=" + std::to_string(cfg.port) +
|
" port=" + std::to_string(cfg.port) +
|
||||||
" dbname=" + cfg.dbname +
|
" dbname=" + cfg.dbname +
|
||||||
" user=" + cfg.user +
|
" user=" + cfg.user +
|
||||||
" password=" + cfg.password) {
|
" password=" + cfg.password),
|
||||||
|
debug(debug) {
|
||||||
|
logDebug("Creating schema for random_data");
|
||||||
{
|
{
|
||||||
pqxx::connection probe(connStr);
|
pqxx::connection probe(connStr);
|
||||||
pqxx::work txn(probe);
|
pqxx::work txn(probe);
|
||||||
|
|
@ -22,6 +26,7 @@ Database::Database(const DatabaseConfig& cfg, int workers)
|
||||||
);
|
);
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
logDebug("Schema is ready; starting " + std::to_string(workers) + " database worker(s)");
|
||||||
for (int i = 0; i < workers; ++i) {
|
for (int i = 0; i < workers; ++i) {
|
||||||
threadPool.emplace_back([this, i]() { worker(i); });
|
threadPool.emplace_back([this, i]() { worker(i); });
|
||||||
}
|
}
|
||||||
|
|
@ -36,16 +41,20 @@ void Database::stop() {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
stopFlag = true;
|
stopFlag = true;
|
||||||
}
|
}
|
||||||
|
logDebug("Stopping database workers");
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
for (auto& t : threadPool) {
|
for (auto& t : threadPool) {
|
||||||
if (t.joinable()) {
|
if (t.joinable()) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logDebug("All database workers stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::worker(int id) {
|
void Database::worker(int id) {
|
||||||
|
logDebug("Worker " + std::to_string(id) + " connecting to database");
|
||||||
auto conn = std::make_unique<pqxx::connection>(connStr);
|
auto conn = std::make_unique<pqxx::connection>(connStr);
|
||||||
|
logDebug("Worker " + std::to_string(id) + " connected to database");
|
||||||
std::mt19937 rng(std::random_device{}());
|
std::mt19937 rng(std::random_device{}());
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -53,7 +62,10 @@ void Database::worker(int id) {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
cv.wait(lock, [&] { return stopFlag || !queue.empty(); });
|
cv.wait(lock, [&] { return stopFlag || !queue.empty(); });
|
||||||
if (stopFlag && queue.empty()) return;
|
if (stopFlag && queue.empty()) {
|
||||||
|
logDebug("Worker " + std::to_string(id) + " stopped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
task = std::move(queue.front());
|
task = std::move(queue.front());
|
||||||
queue.pop();
|
queue.pop();
|
||||||
}
|
}
|
||||||
|
|
@ -61,23 +73,36 @@ void Database::worker(int id) {
|
||||||
json res;
|
json res;
|
||||||
try {
|
try {
|
||||||
if (task.op == Op::RandomRecord) {
|
if (task.op == Op::RandomRecord) {
|
||||||
|
logDebug("Worker " + std::to_string(id) + " fetching one random record");
|
||||||
res = getRandomRecord(*conn, rng);
|
res = getRandomRecord(*conn, rng);
|
||||||
} else {
|
} else {
|
||||||
|
logDebug("Worker " + std::to_string(id) + " fetching " + std::to_string(task.count)
|
||||||
|
+ " random record(s)");
|
||||||
res = getRandomRecords(*conn, rng, task.count, task.med, task.max);
|
res = getRandomRecords(*conn, rng, task.count, task.med, task.max);
|
||||||
}
|
}
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception& e) {
|
||||||
|
logDebug("Worker " + std::to_string(id) + " database error: " + e.what()
|
||||||
|
+ "; reconnecting");
|
||||||
conn = std::make_unique<pqxx::connection>(connStr);
|
conn = std::make_unique<pqxx::connection>(connStr);
|
||||||
|
logDebug("Worker " + std::to_string(id) + " reconnected to database");
|
||||||
res = json{{"error", "Database error"}};
|
res = json{{"error", "Database error"}};
|
||||||
}
|
}
|
||||||
|
logDebug("Worker " + std::to_string(id) + " completed request"
|
||||||
|
+ (res.contains("error") ? ": " + res["error"].get<std::string>() : ""));
|
||||||
task.cb(std::move(res));
|
task.cb(std::move(res));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::enqueue(Task task) {
|
void Database::enqueue(Task task) {
|
||||||
|
std::size_t queueSize;
|
||||||
|
const char* operation = task.op == Op::RandomRecord ? "one random record" : "random records";
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
queue.push(std::move(task));
|
queue.push(std::move(task));
|
||||||
|
queueSize = queue.size();
|
||||||
}
|
}
|
||||||
|
logDebug("Queued request for " + std::string(operation)
|
||||||
|
+ "; pending requests: " + std::to_string(queueSize));
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +116,7 @@ void Database::getRandomRecords(int count, int med, int max, Callback cb) {
|
||||||
|
|
||||||
json Database::getRandomRecord(pqxx::connection& conn, std::mt19937& rng) {
|
json Database::getRandomRecord(pqxx::connection& conn, std::mt19937& rng) {
|
||||||
int totalCount = getTotalCount(conn);
|
int totalCount = getTotalCount(conn);
|
||||||
|
logDebug("random_data contains " + std::to_string(totalCount) + " record(s)");
|
||||||
|
|
||||||
if (totalCount == 0) {
|
if (totalCount == 0) {
|
||||||
return json{
|
return json{
|
||||||
|
|
@ -100,6 +126,7 @@ json Database::getRandomRecord(pqxx::connection& conn, std::mt19937& rng) {
|
||||||
|
|
||||||
std::uniform_int_distribution<int> idDist(1, totalCount);
|
std::uniform_int_distribution<int> idDist(1, totalCount);
|
||||||
int randomId = idDist(rng);
|
int randomId = idDist(rng);
|
||||||
|
logDebug("Selected random record ID " + std::to_string(randomId));
|
||||||
|
|
||||||
pqxx::work txn(conn);
|
pqxx::work txn(conn);
|
||||||
|
|
||||||
|
|
@ -111,6 +138,7 @@ json Database::getRandomRecord(pqxx::connection& conn, std::mt19937& rng) {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
|
logDebug("Selected record no longer exists; falling back to the first available record");
|
||||||
result = txn.exec("SELECT id, name, text, description, created_at FROM random_data LIMIT 1");
|
result = txn.exec("SELECT id, name, text, description, created_at FROM random_data LIMIT 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +165,7 @@ json Database::getRandomRecord(pqxx::connection& conn, std::mt19937& rng) {
|
||||||
}
|
}
|
||||||
|
|
||||||
json Database::getRandomRecordsFast(pqxx::connection& conn, const int count) {
|
json Database::getRandomRecordsFast(pqxx::connection& conn, const int count) {
|
||||||
|
logDebug("Using recursive CTE strategy for " + std::to_string(count) + " record(s)");
|
||||||
pqxx::work txn(conn);
|
pqxx::work txn(conn);
|
||||||
|
|
||||||
std::string query =
|
std::string query =
|
||||||
|
|
@ -210,6 +239,7 @@ json Database::getRandomRecordsFast(pqxx::connection& conn, const int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
logDebug("Recursive CTE returned " + std::to_string(response.size()) + " record(s)");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{"data", response},
|
{"data", response},
|
||||||
|
|
@ -220,6 +250,8 @@ json Database::getRandomRecordsFast(pqxx::connection& conn, const int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
json Database::getRandomRecordsSlow(pqxx::connection& conn, std::mt19937& rng, const int count, const int totalCount) {
|
json Database::getRandomRecordsSlow(pqxx::connection& conn, std::mt19937& rng, const int count, const int totalCount) {
|
||||||
|
logDebug("Using individual-ID strategy for " + std::to_string(count) + " of "
|
||||||
|
+ std::to_string(totalCount) + " record(s)");
|
||||||
std::set<int> ids;
|
std::set<int> ids;
|
||||||
{
|
{
|
||||||
std::uniform_int_distribution<int> idDist(1, totalCount);
|
std::uniform_int_distribution<int> idDist(1, totalCount);
|
||||||
|
|
@ -259,6 +291,7 @@ json Database::getRandomRecordsSlow(pqxx::connection& conn, std::mt19937& rng, c
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
logDebug("Individual-ID query returned " + std::to_string(response.size()) + " record(s)");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{"data", response},
|
{"data", response},
|
||||||
|
|
@ -277,6 +310,8 @@ json Database::getRandomRecords(pqxx::connection& conn, std::mt19937& rng, int c
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalCount = getTotalCount(conn);
|
int totalCount = getTotalCount(conn);
|
||||||
|
logDebug("Requested " + std::to_string(count) + " random record(s); "
|
||||||
|
+ std::to_string(totalCount) + " available");
|
||||||
|
|
||||||
if (totalCount == 0) {
|
if (totalCount == 0) {
|
||||||
return json{
|
return json{
|
||||||
|
|
@ -286,10 +321,12 @@ json Database::getRandomRecords(pqxx::connection& conn, std::mt19937& rng, int c
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > totalCount) {
|
if (count > totalCount) {
|
||||||
|
logDebug("Reducing requested count to available record count");
|
||||||
count = totalCount;
|
count = totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > max) {
|
if (count > max) {
|
||||||
|
logDebug("Reducing requested count to configured maximum of " + std::to_string(max));
|
||||||
count = max;
|
count = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,3 +366,9 @@ std::string Database::getQueryRandomIds(const int count) {
|
||||||
query += ") ORDER BY id";
|
query += ") ORDER BY id";
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::logDebug(const std::string& message) const {
|
||||||
|
if (debug) {
|
||||||
|
std::osyncstream(std::cout) << "[debug][database] " << message << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class Database {
|
||||||
public:
|
public:
|
||||||
using Callback = std::function<void(json)>;
|
using Callback = std::function<void(json)>;
|
||||||
|
|
||||||
explicit Database(const DatabaseConfig& config, int workers = 2);
|
explicit Database(const DatabaseConfig& config, int workers = 2, bool debug = false);
|
||||||
~Database();
|
~Database();
|
||||||
|
|
||||||
Database(const Database&) = delete;
|
Database(const Database&) = delete;
|
||||||
|
|
@ -42,6 +42,7 @@ private:
|
||||||
void worker(int id);
|
void worker(int id);
|
||||||
void enqueue(Task task);
|
void enqueue(Task task);
|
||||||
void stop();
|
void stop();
|
||||||
|
void logDebug(const std::string& message) const;
|
||||||
|
|
||||||
json getRandomRecord(pqxx::connection& conn, std::mt19937& rng);
|
json getRandomRecord(pqxx::connection& conn, std::mt19937& rng);
|
||||||
json getRandomRecords(pqxx::connection& conn, std::mt19937& rng, int count, int med, int max);
|
json getRandomRecords(pqxx::connection& conn, std::mt19937& rng, int count, int med, int max);
|
||||||
|
|
@ -56,4 +57,5 @@ private:
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::queue<Task> queue;
|
std::queue<Task> queue;
|
||||||
bool stopFlag = false;
|
bool stopFlag = false;
|
||||||
|
bool debug;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ int main(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Connecting to database..." << std::endl;
|
std::cout << "Connecting to database..." << std::endl;
|
||||||
auto db = std::make_shared<Database>(config.database);
|
auto db = std::make_shared<Database>(config.database, 2, debug);
|
||||||
std::cout << "Database connected successfully!" << std::endl;
|
std::cout << "Database connected successfully!" << std::endl;
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue