205 lines
6.7 KiB
C++
205 lines
6.7 KiB
C++
//
|
|
// Created by wolverindev on 04.09.18.
|
|
//
|
|
|
|
#include <sql/SqlQuery.h>
|
|
#include <misc/std_unique_ptr.h>
|
|
|
|
#include <utility>
|
|
#include "StatisticManager.h"
|
|
#include "DatabaseHandler.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(std::shared_ptr<license::server::database::DatabaseHandler> manager) : license_manager{std::move(manager)} {}
|
|
StatisticManager::~StatisticManager() = default;
|
|
|
|
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(std::deque<std::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;
|
|
|
|
//TODO: Calculate web clients!
|
|
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->empty_instances += entry->clients == 0;
|
|
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;
|
|
} |