Teaspeak-Server/license/server/StatisticManager.cpp

201 lines
6.6 KiB
C++

//
// Created by wolverindev on 04.09.18.
//
#include <sql/SqlQuery.h>
#include <misc/std_unique_ptr.h>
#include "StatisticManager.h"
#include "LicenseManager.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::server;
using namespace license::stats;
std::chrono::milliseconds HistoryStatistics::time_period(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return minutes(15);
case HistoryType::LAST_WEEK:
return hours(1);
case HistoryType::LAST_MONTH:
case HistoryType::LAST_HALF_YEAR:
default:
return hours(2);
}
}
std::chrono::milliseconds HistoryStatistics::cache_timeout(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return minutes(15);
case HistoryType::LAST_WEEK:
return hours(1);
case HistoryType::LAST_MONTH:
return hours(2);
case HistoryType::LAST_HALF_YEAR:
default:
return hours(8);
}
}
std::chrono::milliseconds HistoryStatistics::type_duration(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return hours(24);
case HistoryType::LAST_WEEK:
return hours(24) * 7;
case HistoryType::LAST_MONTH:
return hours(24) * 32;
case HistoryType::LAST_HALF_YEAR:
return hours(24) * 32 * 6;
default:
return hours(24);
}
}
system_clock::time_point HistoryStatistics::align_type(license::stats::HistoryStatistics::HistoryType type, const std::chrono::system_clock::time_point &tp) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return system_clock::time_point() + minutes(duration_cast<minutes>(tp.time_since_epoch()).count());
case HistoryType::LAST_WEEK:
case HistoryType::LAST_MONTH:
case HistoryType::LAST_HALF_YEAR:
default:
return system_clock::time_point() + hours(duration_cast<hours>(tp.time_since_epoch()).count());
}
}
StatisticManager::StatisticManager(const std::shared_ptr<license::server::LicenseManager> &manager) : license_manager(manager) {}
StatisticManager::~StatisticManager() {}
struct GeneralStatisticEntry {
std::chrono::system_clock::time_point age;
string unique_id = "";
uint64_t key_id = 0;
uint64_t servers = 0;
uint64_t clients = 0;
uint64_t bots = 0;
};
void StatisticManager::reset_cache_general() {
lock_guard<recursive_mutex> lock(this->_general_statistics_lock);
this->_general_statistics = nullptr;
}
void parse_general_entry(deque<unique_ptr<GeneralStatisticEntry>>& entries, bool unique, int length, string* values, string* names) {
auto entry = make_unique<GeneralStatisticEntry>();
for(int index = 0; index < length; index++) {
if(names[index] == "keyId") {
entry->key_id = stoull(values[index]);
} else if(names[index] == "timestamp") {
entry->age = system_clock::time_point() + milliseconds(stoll(values[index]));
} else if(names[index] == "server") {
entry->servers = stoull(values[index]);
} else if(names[index] == "clients") {
entry->clients = stoull(values[index]);
} else if(names[index] == "music") {
entry->bots = stoull(values[index]);
} else if(names[index] == "unique_id")
entry->unique_id = values[index];
}
if(unique) {
for(auto& e : entries) {
if(e->key_id == entry->key_id && e->unique_id == entry->unique_id) {
if(e->age < entry->age) {
entries.erase(find(entries.begin(), entries.end(), e));
break;
} else {
return;
}
}
}
}
entries.push_back(std::move(entry));
}
std::shared_ptr<GeneralStatistics> StatisticManager::general_statistics() {
unique_lock<recursive_mutex> lock(this->_general_statistics_lock);
if(this->_general_statistics && system_clock::now() < this->_general_statistics->age + seconds(300)) return this->_general_statistics;
lock.unlock();
unique_lock create_lock(this->_general_statistics_generate_lock);
lock.lock();
if(this->_general_statistics && system_clock::now() < this->_general_statistics->age + seconds(300)) return this->_general_statistics;
lock.unlock();
deque<unique_ptr<GeneralStatisticEntry>> entries;
auto result = sql::command(this->license_manager->sql(), "SELECT `keyId`, `unique_id`, `timestamp`,`server`,`clients`,`music` FROM `history_online` WHERE `timestamp` > :time ORDER BY `timestamp` ASC",
variable{":time", duration_cast<milliseconds>(system_clock::now().time_since_epoch() - hours(2) - minutes(10)).count()}) //10min as buffer
.query(std::function<decltype(parse_general_entry)>(parse_general_entry), entries, true);
auto stats = make_shared<GeneralStatistics>();
for(auto& entry : entries) {
stats->bots += entry->bots;
stats->clients += entry->clients;
stats->servers += entry->servers;
stats->instances++;
}
stats->age = system_clock::now();
lock.lock();
this->_general_statistics = stats;
lock.unlock();
create_lock.unlock();
return stats;
}
std::shared_ptr<HistoryStatistics> StatisticManager::history(license::stats::HistoryStatistics::HistoryType type) {
lock_guard<recursive_mutex> lock(this->_history_locks[type]);
auto current_time = system_clock::now();
auto& entry = this->_history[type];
if(entry && entry->evaluated + HistoryStatistics::cache_timeout(type) > current_time)
return entry;
entry = make_shared<HistoryStatistics>();
entry->period = HistoryStatistics::time_period(type);
entry->begin = HistoryStatistics::align_type(type, current_time - HistoryStatistics::type_duration(type));
entry->end = HistoryStatistics::align_type(type, current_time);
if(type == HistoryStatistics::DAY_YESTERDAY || type == HistoryStatistics::DAY_7DAYS_AGO) {
auto& reference = this->_history[HistoryStatistics::LAST_DAY];
if(reference) {
entry->begin = reference->begin;
entry->end = reference->end;
entry->evaluated = reference->evaluated;
}
if(type == HistoryStatistics::DAY_YESTERDAY) {
entry->begin -= hours(24);
entry->end -= hours(24);
} else if(type == HistoryStatistics::DAY_7DAYS_AGO) {
entry->begin -= hours(24) * 7;
entry->end -= hours(24) * 7;
}
}
auto statistics = this->license_manager->list_statistics_user(entry->begin, entry->end, entry->period);
entry->statistics = std::move(statistics);
if(entry->evaluated.time_since_epoch().count() == 0)
entry->evaluated = current_time;
if(type == HistoryStatistics::LAST_DAY) {
this->history(HistoryStatistics::DAY_YESTERDAY);
this->history(HistoryStatistics::DAY_7DAYS_AGO);
}
return entry;
}