You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/lib/ProxySQL_Statistics.cpp

1970 lines
74 KiB

//#include <thread>
#include "proxysql.h"
#include "cpp.h"
#include "ProxySQL_Statistics.hpp"
#include "MySQL_HostGroups_Manager.h"
#include "PgSQL_HostGroups_Manager.h"
#include "../deps/json/json.hpp"
using json = nlohmann::json;
//#include "thread.h"
//#include "wqueue.h"
#include <fcntl.h>
#include <sys/times.h>
#include <poll.h>
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cmath>
#include <sstream>
#include <netdb.h>
#include <future>
namespace {
std::string escape_sql_string_literal(const std::string& value) {
std::string escaped;
escaped.reserve(value.size() + 8);
for (const char c : value) {
if (c == '\'') {
escaped += "''";
} else {
escaped += c;
}
}
return escaped;
}
bool valid_label_key(const std::string& key) {
if (key.empty()) {
return false;
}
for (const unsigned char c : key) {
if (!(std::isalnum(c) || c == '_' || c == '-' || c == ':')) {
return false;
}
}
return true;
}
std::string format_prometheus_label_double(const double value) {
if (std::isnan(value)) {
return "nan";
}
if (std::isinf(value)) {
return value > 0 ? "+Inf" : "-Inf";
}
std::ostringstream oss;
oss.setf(std::ios::fmtflags(0), std::ios::floatfield);
oss.precision(17);
oss << value;
return oss.str();
}
struct probe_target_t {
int hg;
std::string host;
int port;
};
void append_probe_targets(SQLite3_result* resultset, std::vector<probe_target_t>& targets) {
if (!resultset) {
return;
}
for (int i = 0; i < static_cast<int>(resultset->rows_count); i++) {
SQLite3_row* row = resultset->rows[i];
if (!row || !row->fields[0] || !row->fields[1] || !row->fields[2]) {
continue;
}
const int hg = atoi(row->fields[0]);
const std::string host = row->fields[1];
const int port = atoi(row->fields[2]);
if (host.empty() || port <= 0 || port > 65535) {
continue;
}
targets.push_back({hg, host, port});
}
}
} // namespace
#ifdef DEBUG
#define DEB "_DEBUG"
#else
#define DEB ""
#endif /* DEBUG */
#define PROXYSQL_STATISTICS_VERSION "1.4.1027" DEB
using namespace std;
extern ProxySQL_Admin *GloAdmin;
extern MySQL_Threads_Handler *GloMTH;
#define SAFE_SQLITE3_STEP(_stmt) do {\
do {\
rc=(*proxy_sqlite3_step)(_stmt);\
if (rc!=SQLITE_DONE) {\
assert(rc==SQLITE_LOCKED)\
usleep(100);\
}\
} while (rc!=SQLITE_DONE );\
} while (0)
ProxySQL_Statistics::ProxySQL_Statistics() {
statsdb_mem = new SQLite3DB();
statsdb_mem->open((char *)"file:statsdb_mem?mode=memory&cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX);
// statsdb_disk = GloAdmin->statsdb_disk;
statsdb_disk = new SQLite3DB();
// char *dbname = (char *)malloc(strlen(GloVars.statsdb_disk)+50);
// sprintf(dbname,"file:%s?cache=shared",GloVars.statsdb_disk);
statsdb_disk->open((char *)GloVars.statsdb_disk, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX );
// statsdb_disk->open((char *)GloVars.statsdb_disk, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_NOMUTEX);
// statsdb_disk->open(dbname, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_NOMUTEX);
// free(dbname);
statsdb_disk->execute("PRAGMA synchronous=0");
next_timer_MySQL_Threads_Handler = 0;
next_timer_mysql_query_digest_to_disk = 0;
next_timer_system_cpu = 0;
#ifndef NOJEM
next_timer_system_memory = 0;
#endif
next_timer_MySQL_Query_Cache = 0;
next_timer_tsdb_sampler = 0;
next_timer_tsdb_downsample = 0;
next_timer_tsdb_monitor = 0;
next_timer_tsdb_retention = 0;
stmt_insert_tsdb_metric = NULL;
stmt_insert_backend_health = NULL;
variables.tsdb_enabled = 0;
variables.tsdb_sample_interval = 5;
variables.tsdb_retention_days = 7;
variables.tsdb_monitor_enabled = 0;
variables.tsdb_monitor_interval = 10;
}
static const struct {
const char *name;
int min_val;
int max_val;
} tsdb_variable_meta[] = {
{"enabled", 0, 1},
{"sample_interval", 1, 3600},
{"retention_days", 1, 3650},
{"monitor_enabled", 0, 1},
{"monitor_interval", 1, 3600},
{NULL, 0, 0}
};
bool ProxySQL_Statistics::set_variable(const char *name, const char *value) {
if (name == NULL || value == NULL) return false;
char *endptr;
long intv = strtol(value, &endptr, 10);
if (*endptr != '\0') return false; // Not a valid integer
for (int i=0; tsdb_variable_meta[i].name; i++) {
if (!strcasecmp(name, tsdb_variable_meta[i].name)) {
if (intv >= tsdb_variable_meta[i].min_val && intv <= tsdb_variable_meta[i].max_val) {
if (i == 0) variables.tsdb_enabled = (int)intv;
else if (i == 1) variables.tsdb_sample_interval = (int)intv;
else if (i == 2) variables.tsdb_retention_days = (int)intv;
else if (i == 3) variables.tsdb_monitor_enabled = (int)intv;
else if (i == 4) variables.tsdb_monitor_interval = (int)intv;
return true;
}
return false;
}
}
return false;
}
char *ProxySQL_Statistics::get_variable(const char *name) {
if (name == NULL) return NULL;
char buf[32];
if (!strcasecmp(name, "enabled")) {
snprintf(buf, sizeof(buf), "%d", variables.tsdb_enabled);
return strdup(buf);
} else if (!strcasecmp(name, "sample_interval")) {
snprintf(buf, sizeof(buf), "%d", variables.tsdb_sample_interval);
return strdup(buf);
} else if (!strcasecmp(name, "retention_days")) {
snprintf(buf, sizeof(buf), "%d", variables.tsdb_retention_days);
return strdup(buf);
} else if (!strcasecmp(name, "monitor_enabled")) {
snprintf(buf, sizeof(buf), "%d", variables.tsdb_monitor_enabled);
return strdup(buf);
} else if (!strcasecmp(name, "monitor_interval")) {
snprintf(buf, sizeof(buf), "%d", variables.tsdb_monitor_interval);
return strdup(buf);
}
return NULL;
}
char **ProxySQL_Statistics::get_variables_list() {
int count = 0;
while (tsdb_variable_meta[count].name) count++;
char **list = (char **)malloc(sizeof(char *) * (count + 1));
for (int i = 0; i < count; i++) {
list[i] = strdup(tsdb_variable_meta[i].name);
}
list[count] = NULL;
return list;
}
bool ProxySQL_Statistics::has_variable(const char *name) {
if (name == NULL) return false;
for (int i = 0; tsdb_variable_meta[i].name; i++) {
if (!strcasecmp(name, tsdb_variable_meta[i].name)) return true;
}
return false;
}
ProxySQL_Statistics::~ProxySQL_Statistics() {
if (stmt_insert_tsdb_metric) {
(*proxy_sqlite3_finalize)(stmt_insert_tsdb_metric);
}
if (stmt_insert_backend_health) {
(*proxy_sqlite3_finalize)(stmt_insert_backend_health);
}
drop_tables_defs(tables_defs_statsdb_mem);
delete tables_defs_statsdb_mem;
drop_tables_defs(tables_defs_statsdb_disk);
delete tables_defs_statsdb_disk;
delete statsdb_mem;
delete statsdb_disk;
}
void ProxySQL_Statistics::init() {
tables_defs_statsdb_mem = new std::vector<table_def_t *>;
tables_defs_statsdb_disk = new std::vector<table_def_t *>;
insert_into_tables_defs(tables_defs_statsdb_mem,"mysql_connections", STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS);
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_connections", STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_mysql_status_variables", STATSDB_SQLITE_TABLE_HISTORY_MYSQL_STATUS_VARIABLES);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_mysql_status_variables_lookup", STATSDB_SQLITE_TABLE_HISTORY_MYSQL_STATUS_VARIABLES_LOOKUP);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_stats_mysql_connection_pool", STATSDB_SQLITE_TABLE_HISTORY_STATS_MYSQL_CONNECTION_POOL);
insert_into_tables_defs(tables_defs_statsdb_disk,"system_cpu", STATSDB_SQLITE_TABLE_SYSTEM_CPU);
#ifndef NOJEM
insert_into_tables_defs(tables_defs_statsdb_disk,"system_memory", STATSDB_SQLITE_TABLE_SYSTEM_MEMORY);
#endif
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_connections_hour", STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS_HOUR);
insert_into_tables_defs(tables_defs_statsdb_disk,"system_cpu_hour", STATSDB_SQLITE_TABLE_SYSTEM_CPU_HOUR);
#ifndef NOJEM
insert_into_tables_defs(tables_defs_statsdb_disk,"system_memory_hour", STATSDB_SQLITE_TABLE_SYSTEM_MEMORY_HOUR);
#endif
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_connections_day", STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS_DAY);
insert_into_tables_defs(tables_defs_statsdb_disk,"system_cpu_day", STATSDB_SQLITE_TABLE_SYSTEM_CPU_DAY);
#ifndef NOJEM
insert_into_tables_defs(tables_defs_statsdb_disk,"system_memory_day", STATSDB_SQLITE_TABLE_SYSTEM_MEMORY_DAY);
#endif
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_query_cache", STATSDB_SQLITE_TABLE_MYSQL_QUERY_CACHE);
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_query_cache_hour", STATSDB_SQLITE_TABLE_MYSQL_QUERY_CACHE_HOUR);
insert_into_tables_defs(tables_defs_statsdb_disk,"mysql_query_cache_day", STATSDB_SQLITE_TABLE_MYSQL_QUERY_CACHE_DAY);
insert_into_tables_defs(tables_defs_statsdb_disk,"myhgm_connections", STATSDB_SQLITE_TABLE_MYHGM_CONNECTIONS);
insert_into_tables_defs(tables_defs_statsdb_disk,"myhgm_connections_hour", STATSDB_SQLITE_TABLE_MYHGM_CONNECTIONS_HOUR);
insert_into_tables_defs(tables_defs_statsdb_disk,"myhgm_connections_day", STATSDB_SQLITE_TABLE_MYHGM_CONNECTIONS_DAY);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_mysql_query_digest", STATSDB_SQLITE_TABLE_HISTORY_MYSQL_QUERY_DIGEST);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_pgsql_query_digest", STATSDB_SQLITE_TABLE_HISTORY_PGSQL_QUERY_DIGEST);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_pgsql_status_variables", STATSDB_SQLITE_TABLE_HISTORY_PGSQL_STATUS_VARIABLES);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_pgsql_status_variables_lookup", STATSDB_SQLITE_TABLE_HISTORY_PGSQL_STATUS_VARIABLES_LOOKUP);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_mysql_query_events", STATSDB_SQLITE_TABLE_HISTORY_MYSQL_QUERY_EVENTS);
insert_into_tables_defs(tables_defs_statsdb_disk,"history_pgsql_query_events", STATSDB_SQLITE_TABLE_HISTORY_PGSQL_QUERY_EVENTS);
// TSDB tables
insert_into_tables_defs(tables_defs_statsdb_disk,"tsdb_metrics", STATSDB_SQLITE_TABLE_TSDB_METRICS);
insert_into_tables_defs(tables_defs_statsdb_disk,"tsdb_metrics_hour", STATSDB_SQLITE_TABLE_TSDB_METRICS_HOUR);
insert_into_tables_defs(tables_defs_statsdb_disk,"tsdb_backend_health", STATSDB_SQLITE_TABLE_TSDB_BACKEND_HEALTH);
disk_upgrade_mysql_connections();
check_and_build_standard_tables(statsdb_mem, tables_defs_statsdb_disk);
check_and_build_standard_tables(statsdb_disk, tables_defs_statsdb_disk);
// statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_hostgroup ON history_mysql_query_digest (hostgroup)");
// statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_username ON history_mysql_query_digest (username)");
// statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_schemaname ON history_mysql_query_digest (schemaname)");
// statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_digest ON history_mysql_query_digest (digest)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_first_seen ON history_mysql_query_digest (first_seen)");
// statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_last_seen ON history_mysql_query_digest (last_seen)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_digest_dump_time ON history_mysql_query_digest (dump_time)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_status_variable_id_timestamp ON history_mysql_status_variables(variable_id,timestamp)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_pgsql_query_digest_first_seen ON history_pgsql_query_digest (first_seen)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_pgsql_query_digest_dump_time ON history_pgsql_query_digest (dump_time)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_pgsql_status_variable_id_timestamp ON history_pgsql_status_variables(variable_id,timestamp)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_events_start_time ON history_mysql_query_events(start_time)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_mysql_query_events_query_digest ON history_mysql_query_events(query_digest)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_pgsql_query_events_start_time ON history_pgsql_query_events(start_time)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_history_pgsql_query_events_query_digest ON history_pgsql_query_events(query_digest)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_tsdb_metrics_metric_time ON tsdb_metrics(metric_name, timestamp)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_tsdb_metrics_hour_metric_bucket ON tsdb_metrics_hour(metric_name, bucket)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_tsdb_backend_health_time ON tsdb_backend_health(timestamp)");
statsdb_disk->execute("CREATE INDEX IF NOT EXISTS idx_tsdb_backend_health_host_time ON tsdb_backend_health(hostgroup, hostname, port, timestamp)");
}
void ProxySQL_Statistics::disk_upgrade_mysql_connections() {
int rci;
rci=statsdb_disk->check_table_structure((char *)"mysql_connections",(char *)STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS_V1_4);
if (rci) {
proxy_warning("Detected version v1.4 of table mysql_connections\n");
proxy_warning("ONLINE UPGRADE of table mysql_connections in progress\n");
statsdb_disk->execute("ALTER TABLE mysql_connections ADD COLUMN GTID_consistent_queries INT NOT NULL DEFAULT 0");
proxy_warning("ONLINE UPGRADE of table mysql_connections completed\n");
}
rci=statsdb_disk->check_table_structure((char *)"mysql_connections_hour",(char *)STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS_HOUR_V1_4);
if (rci) {
proxy_warning("Detected version v1.4 of table mysql_connections_hour\n");
proxy_warning("ONLINE UPGRADE of table mysql_connections_hour in progress\n");
statsdb_disk->execute("ALTER TABLE mysql_connections_hour ADD COLUMN GTID_consistent_queries INT NOT NULL DEFAULT 0");
proxy_warning("ONLINE UPGRADE of table mysql_connections_hour completed\n");
}
rci=statsdb_disk->check_table_structure((char *)"mysql_connections_day",(char *)STATSDB_SQLITE_TABLE_MYSQL_CONNECTIONS_DAY_V1_4);
if (rci) {
proxy_warning("Detected version v1.4 of table mysql_connections_day\n");
proxy_warning("ONLINE UPGRADE of table mysql_connections_day in progress\n");
statsdb_disk->execute("ALTER TABLE mysql_connections_day ADD COLUMN GTID_consistent_queries INT NOT NULL DEFAULT 0");
proxy_warning("ONLINE UPGRADE of table mysql_connections_day completed\n");
}
const char* tsdb_metrics_old =
"CREATE TABLE tsdb_metrics (timestamp INT NOT NULL, metric_name TEXT NOT NULL, labels TEXT, value REAL, PRIMARY KEY (timestamp, metric_name)) WITHOUT ROWID";
rci = statsdb_disk->check_table_structure((char*)"tsdb_metrics", (char*)tsdb_metrics_old);
if (rci) {
proxy_warning("Detected legacy schema for tsdb_metrics\n");
statsdb_disk->execute("BEGIN IMMEDIATE");
statsdb_disk->execute("ALTER TABLE tsdb_metrics RENAME TO tsdb_metrics_old");
statsdb_disk->execute(STATSDB_SQLITE_TABLE_TSDB_METRICS);
statsdb_disk->execute(
"INSERT OR IGNORE INTO tsdb_metrics(timestamp, metric_name, labels, value) "
"SELECT timestamp, metric_name, COALESCE(labels,'{}'), value FROM tsdb_metrics_old"
);
statsdb_disk->execute("DROP TABLE tsdb_metrics_old");
statsdb_disk->execute("COMMIT");
proxy_warning("ONLINE UPGRADE of table tsdb_metrics completed\n");
}
const char* tsdb_metrics_hour_old =
"CREATE TABLE tsdb_metrics_hour (bucket INT NOT NULL, metric_name TEXT NOT NULL, labels TEXT, avg_value REAL, max_value REAL, min_value REAL, count INT, PRIMARY KEY (bucket, metric_name)) WITHOUT ROWID";
rci = statsdb_disk->check_table_structure((char*)"tsdb_metrics_hour", (char*)tsdb_metrics_hour_old);
if (rci) {
proxy_warning("Detected legacy schema for tsdb_metrics_hour\n");
statsdb_disk->execute("BEGIN IMMEDIATE");
statsdb_disk->execute("ALTER TABLE tsdb_metrics_hour RENAME TO tsdb_metrics_hour_old");
statsdb_disk->execute(STATSDB_SQLITE_TABLE_TSDB_METRICS_HOUR);
statsdb_disk->execute(
"INSERT OR IGNORE INTO tsdb_metrics_hour(bucket, metric_name, labels, avg_value, max_value, min_value, count) "
"SELECT bucket, metric_name, COALESCE(labels,'{}'), avg_value, max_value, min_value, count FROM tsdb_metrics_hour_old"
);
statsdb_disk->execute("DROP TABLE tsdb_metrics_hour_old");
statsdb_disk->execute("COMMIT");
proxy_warning("ONLINE UPGRADE of table tsdb_metrics_hour completed\n");
}
}
void ProxySQL_Statistics::print_version() {
fprintf(stderr,"Standard ProxySQL Statistics rev. %s -- %s -- %s\n", PROXYSQL_STATISTICS_VERSION, __FILE__, __TIMESTAMP__);
}
void ProxySQL_Statistics::check_and_build_standard_tables(SQLite3DB *db, std::vector<table_def_t *> *tables_defs) {
table_def_t *td;
db->execute("PRAGMA foreign_keys = OFF");
for (std::vector<table_def_t *>::iterator it=tables_defs->begin(); it!=tables_defs->end(); ++it) {
td=*it;
db->check_and_build_table(td->table_name, td->table_def);
}
db->execute("PRAGMA foreign_keys = ON");
}
void ProxySQL_Statistics::insert_into_tables_defs(std::vector<table_def_t *> *tables_defs, const char *table_name, const char *table_def) {
table_def_t *td = new table_def_t;
td->table_name=strdup(table_name);
td->table_def=strdup(table_def);
tables_defs->push_back(td);
}
void ProxySQL_Statistics::drop_tables_defs(std::vector<table_def_t *> *tables_defs) {
table_def_t *td;
while (!tables_defs->empty()) {
td=tables_defs->back();
free(td->table_name);
td->table_name=NULL;
free(td->table_def);
td->table_def=NULL;
tables_defs->pop_back();
delete td;
}
}
bool ProxySQL_Statistics::MySQL_Logger_dump_eventslog_timetoget(unsigned long long currentTimeMicros) {
if (variables.stats_mysql_eventslog_sync_buffer_to_disk) { // only proceed if not zero
unsigned long long t = variables.stats_mysql_eventslog_sync_buffer_to_disk; // originally in seconds
t = t * 1000 * 1000;
if (currentTimeMicros > last_timer_mysql_dump_eventslog_to_disk + t) {
last_timer_mysql_dump_eventslog_to_disk = currentTimeMicros;
return true;
}
}
return false;
}
bool ProxySQL_Statistics::PgSQL_Logger_dump_eventslog_timetoget(unsigned long long currentTimeMicros) {
if (variables.stats_pgsql_eventslog_sync_buffer_to_disk) { // only proceed if not zero
unsigned long long t = variables.stats_pgsql_eventslog_sync_buffer_to_disk; // originally in seconds
t = t * 1000 * 1000;
if (currentTimeMicros > last_timer_pgsql_dump_eventslog_to_disk + t) {
last_timer_pgsql_dump_eventslog_to_disk = currentTimeMicros;
return true;
}
}
return false;
}
bool ProxySQL_Statistics::MySQL_Threads_Handler_timetoget(unsigned long long curtime) {
unsigned int i = (unsigned int)variables.stats_mysql_connections;
if (i) {
if (
( curtime > next_timer_MySQL_Threads_Handler )
||
( curtime + i*1000*1000 < next_timer_MySQL_Threads_Handler )
) {
next_timer_MySQL_Threads_Handler = curtime/1000/1000 + i;
next_timer_MySQL_Threads_Handler = next_timer_MySQL_Threads_Handler * 1000 * 1000;
return true;
}
}
return false;
}
bool ProxySQL_Statistics::MySQL_Query_Cache_timetoget(unsigned long long curtime) {
unsigned int i = (unsigned int)variables.stats_mysql_query_cache;
if (i) {
if (
( curtime > next_timer_MySQL_Query_Cache )
||
( curtime + i*1000*1000 < next_timer_MySQL_Query_Cache )
) {
next_timer_MySQL_Query_Cache = curtime/1000/1000 + i;
next_timer_MySQL_Query_Cache = next_timer_MySQL_Query_Cache * 1000 * 1000;
return true;
}
}
return false;
}
bool ProxySQL_Statistics::mysql_query_digest_to_disk_timetoget(unsigned long long curtime) {
unsigned long i = (unsigned long)variables.stats_mysql_query_digest_to_disk;
if (i) {
if (
( curtime > next_timer_mysql_query_digest_to_disk )
||
( curtime + i*1000*1000 < next_timer_mysql_query_digest_to_disk )
) {
next_timer_mysql_query_digest_to_disk = curtime/1000/1000 + i;
next_timer_mysql_query_digest_to_disk = next_timer_mysql_query_digest_to_disk * 1000 * 1000;
return true;
}
}
return false;
}
bool ProxySQL_Statistics::system_cpu_timetoget(unsigned long long curtime) {
unsigned int i = (unsigned int)variables.stats_system_cpu;
if (i) {
if (
( curtime > next_timer_system_cpu )
||
( curtime + i*1000*1000 < next_timer_system_cpu )
) {
next_timer_system_cpu = curtime/1000/1000 + i;
next_timer_system_cpu = next_timer_system_cpu * 1000 * 1000;
return true;
}
}
return false;
}
#ifndef NOJEM
bool ProxySQL_Statistics::system_memory_timetoget(unsigned long long curtime) {
unsigned int i = (unsigned int)variables.stats_system_memory;
if (i) {
if (
( curtime > next_timer_system_memory )
||
( curtime + i*1000*1000 < next_timer_system_memory )
) {
next_timer_system_memory = curtime/1000/1000 + i;
next_timer_system_memory = next_timer_system_memory * 1000 * 1000;
return true;
}
}
return false;
}
#endif
SQLite3_result * ProxySQL_Statistics::get_mysql_metrics(int interval) {
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
char *query1 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, Client_Connections_aborted, Client_Connections_connected, Client_Connections_created, Server_Connections_aborted, Server_Connections_connected, Server_Connections_created, ConnPool_get_conn_failure, ConnPool_get_conn_immediate, ConnPool_get_conn_success, Questions, Slow_queries, GTID_consistent_queries FROM mysql_connections WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
char *query2 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, Client_Connections_aborted, Client_Connections_connected, Client_Connections_created, Server_Connections_aborted, Server_Connections_connected, Server_Connections_created, ConnPool_get_conn_failure, ConnPool_get_conn_immediate, ConnPool_get_conn_success, Questions, Slow_queries, GTID_consistent_queries FROM mysql_connections_hour WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
time_t ts = time(NULL);
switch (interval) {
case 1800:
case 3600:
case 7200:
query = (char *)malloc(strlen(query1)+128);
sprintf(query, query1, ts-interval, ts);
break;
case 28800:
case 86400:
case 259200:
case 604800:
case 2592000:
case 7776000:
query = (char *)malloc(strlen(query2)+128);
sprintf(query, query2, ts-interval, ts);
break;
default:
// LCOV_EXCL_START
assert(0);
break;
// LCOV_EXCL_STOP
}
//fprintf(stderr,"%s\n", query);
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
free(query);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
/*
char *query = (char *)"SELECT * FROM (SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, Client_Connections_aborted, Client_Connections_connected, Client_Connections_created, Server_Connections_aborted, Server_Connections_connected, Server_Connections_created, ConnPool_get_conn_failure, ConnPool_get_conn_immediate, ConnPool_get_conn_success, Questions FROM mysql_connections ORDER BY timestamp DESC LIMIT 100) t ORDER BY ts";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
*/
return resultset;
}
SQLite3_result * ProxySQL_Statistics::get_myhgm_metrics(int interval) {
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
char *query1 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, MyHGM_myconnpoll_destroy, MyHGM_myconnpoll_get, MyHGM_myconnpoll_get_ok, MyHGM_myconnpoll_push, MyHGM_myconnpoll_reset FROM myhgm_connections WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
char *query2 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, MyHGM_myconnpoll_destroy, MyHGM_myconnpoll_get, MyHGM_myconnpoll_get_ok, MyHGM_myconnpoll_push, MyHGM_myconnpoll_reset FROM myhgm_connections_hour WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
time_t ts = time(NULL);
switch (interval) {
case 1800:
case 3600:
case 7200:
query = (char *)malloc(strlen(query1)+128);
sprintf(query, query1, ts-interval, ts);
break;
case 28800:
case 86400:
case 259200:
case 604800:
case 2592000:
case 7776000:
query = (char *)malloc(strlen(query2)+128);
sprintf(query, query2, ts-interval, ts);
break;
default:
// LCOV_EXCL_START
assert(0);
break;
// LCOV_EXCL_STOP
}
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
free(query);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
return resultset;
}
SQLite3_result * ProxySQL_Statistics::get_MySQL_Query_Cache_metrics(int interval) {
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
char *query1 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, count_GET, count_GET_OK, count_SET, bytes_IN, bytes_OUT, Entries_Purged, Entries_In_Cache, Memory_Bytes FROM mysql_query_cache WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
char *query2 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, count_GET, count_GET_OK, count_SET, bytes_IN, bytes_OUT, Entries_Purged, Entries_In_Cache, Memory_Bytes FROM mysql_query_cache_hour WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
time_t ts = time(NULL);
switch (interval) {
case 1800:
case 3600:
case 7200:
query = (char *)malloc(strlen(query1)+128);
sprintf(query, query1, ts-interval, ts);
break;
case 28800:
case 86400:
case 259200:
case 604800:
case 2592000:
case 7776000:
query = (char *)malloc(strlen(query2)+128);
sprintf(query, query2, ts-interval, ts);
break;
default:
// LCOV_EXCL_START
assert(0);
break;
// LCOV_EXCL_STOP
}
//fprintf(stderr,"%s\n", query);
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
free(query);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
return resultset;
}
#ifndef NOJEM
SQLite3_result * ProxySQL_Statistics::get_system_memory_metrics(int interval) {
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
char *query1 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, allocated, resident, active, mapped, metadata, retained FROM system_memory WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
char *query2 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, allocated, resident, active, mapped, metadata, retained FROM system_memory_hour WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
time_t ts = time(NULL);
switch (interval) {
case 1800:
case 3600:
case 7200:
query = (char *)malloc(strlen(query1)+128);
sprintf(query, query1, ts-interval, ts);
break;
case 28800:
case 86400:
case 259200:
case 604800:
case 2592000:
case 7776000:
query = (char *)malloc(strlen(query2)+128);
sprintf(query, query2, ts-interval, ts);
break;
default:
// LCOV_EXCL_START
assert(0);
break;
// LCOV_EXCL_STOP
}
//fprintf(stderr,"%s\n", query);
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
free(query);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
return resultset;
}
#endif
SQLite3_result * ProxySQL_Statistics::get_system_cpu_metrics(int interval) {
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
char *query1 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, tms_utime, tms_stime FROM system_cpu WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
char *query2 = (char *)"SELECT SUBSTR(FROM_UNIXTIME(timestamp),0,20) ts, timestamp, tms_utime, tms_stime FROM system_cpu_hour WHERE timestamp BETWEEN %d AND %d ORDER BY timestamp";
time_t ts = time(NULL);
switch (interval) {
case 1800:
case 3600:
case 7200:
query = (char *)malloc(strlen(query1)+128);
sprintf(query, query1, ts-interval, ts);
break;
case 28800:
case 86400:
case 259200:
case 604800:
case 2592000:
case 7776000:
query = (char *)malloc(strlen(query2)+128);
sprintf(query, query2, ts-interval, ts);
break;
default:
// LCOV_EXCL_START
assert(0);
break;
// LCOV_EXCL_STOP
}
//fprintf(stderr,"%s\n", query);
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
free(query);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
}
return resultset;
}
void ProxySQL_Statistics::system_cpu_sets() {
int rc;
struct tms buf;
if (times(&buf) > -1) {
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement1=NULL;
char *query1=NULL;
query1=(char *)"INSERT INTO system_cpu VALUES (?1, ?2, ?3)";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0);
ASSERT_SQLITE_OK(rc, statsdb_disk);
time_t ts = time(NULL);
rc = (*proxy_sqlite3_bind_int64)(statement1, 1, ts); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 2, buf.tms_utime); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 3, buf.tms_stime); ASSERT_SQLITE_OK(rc, statsdb_disk);
ASSERT_SQLITE_OK(rc, statsdb_disk);
SAFE_SQLITE3_STEP2(statement1);
rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement1);
(*proxy_sqlite3_finalize)(statement1);
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = (char *)"SELECT MAX(timestamp) FROM system_cpu_hour";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
} else {
char buf[256];
if (resultset->rows_count == 0) {
sprintf(buf,"INSERT INTO system_cpu_hour SELECT timestamp/3600*3600 , SUM(tms_utime), SUM(tms_stime) FROM system_cpu WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
} else {
SQLite3_row *r = resultset->rows[0];
if (r->fields[0]) {
time_t t = atol(r->fields[0]);
if (ts >= t + 3600) {
sprintf(buf,"INSERT INTO system_cpu_hour SELECT timestamp/3600*3600 , SUM(tms_utime), SUM(tms_stime) FROM system_cpu WHERE timestamp >= %ld AND timestamp < %ld GROUP BY timestamp/3600", t+3600 , (ts/3600)*3600);
statsdb_disk->execute(buf);
}
} else {
sprintf(buf,"INSERT INTO system_cpu_hour SELECT timestamp/3600*3600 , SUM(tms_utime), SUM(tms_stime) FROM system_cpu WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
}
}
delete resultset;
resultset = NULL;
sprintf(buf,"DELETE FROM system_cpu WHERE timestamp < %ld", ts - 3600*24*7);
statsdb_disk->execute(buf);
sprintf(buf,"DELETE FROM system_cpu_hour WHERE timestamp < %ld", ts - 3600*24*365);
statsdb_disk->execute(buf);
}
}
}
#ifndef NOJEM
void ProxySQL_Statistics::system_memory_sets() {
int rc;
struct tms buf;
if (times(&buf) > -1) {
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement1=NULL;
char *query1=NULL;
query1=(char *)"INSERT INTO system_memory VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
time_t ts = time(NULL);
size_t allocated = 0, resident = 0, active = 0, mapped = 0 , metadata = 0, retained = 0 , sz = sizeof(size_t);
mallctl("stats.resident", &resident, &sz, NULL, 0);
mallctl("stats.active", &active, &sz, NULL, 0);
mallctl("stats.allocated", &allocated, &sz, NULL, 0);
mallctl("stats.mapped", &mapped, &sz, NULL, 0);
mallctl("stats.metadata", &metadata, &sz, NULL, 0);
mallctl("stats.retained", &retained, &sz, NULL, 0);
rc = (*proxy_sqlite3_bind_int64)(statement1, 1, ts); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 2, allocated); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 3, resident); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 4, active); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 5, mapped); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 6, metadata); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int64)(statement1, 7, retained); ASSERT_SQLITE_OK(rc, statsdb_disk);
ASSERT_SQLITE_OK(rc, statsdb_disk);
SAFE_SQLITE3_STEP2(statement1);
rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement1);
(*proxy_sqlite3_finalize)(statement1);
SQLite3_result *resultset = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = (char *)"SELECT MAX(timestamp) FROM system_memory_hour";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset);
if (error) {
if (resultset) {
delete resultset;
resultset = NULL;
}
free(error);
} else {
char buf[512];
if (resultset->rows_count == 0) {
sprintf(buf,"INSERT INTO system_memory_hour SELECT timestamp/3600*3600 , AVG(allocated), AVG(resident), AVG(active), AVG(mapped), AVG(metadata), AVG(retained) FROM system_memory WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
} else {
SQLite3_row *r = resultset->rows[0];
if (r->fields[0]) {
time_t t = atol(r->fields[0]);
if (ts >= t + 3600) {
sprintf(buf,"INSERT INTO system_memory_hour SELECT timestamp/3600*3600 , AVG(allocated), AVG(resident), AVG(active), AVG(mapped), AVG(metadata), AVG(retained) FROM system_memory WHERE timestamp >= %ld AND timestamp < %ld GROUP BY timestamp/3600", t+3600 , (ts/3600)*3600);
statsdb_disk->execute(buf);
}
} else {
sprintf(buf,"INSERT INTO system_memory_hour SELECT timestamp/3600*3600 , AVG(allocated), AVG(resident), AVG(active), AVG(mapped), AVG(metadata), AVG(retained) FROM system_memory WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
}
}
delete resultset;
resultset = NULL;
sprintf(buf,"DELETE FROM system_memory WHERE timestamp < %ld", ts - 3600*24*7);
statsdb_disk->execute(buf);
sprintf(buf,"DELETE FROM system_memory_hour WHERE timestamp < %ld", ts - 3600*24*365);
statsdb_disk->execute(buf);
}
}
}
#endif
void ProxySQL_Statistics::MyHGM_Handler_sets(SQLite3_result *resultset1, SQLite3_result *resultset2) {
MyHGM_Handler_sets_v1(resultset1);
// In debug, enable metrics features for debugging and testing even if the web interface plugin is not loaded.
#ifdef DEBUG
if (resultset2) {
#else
if (GloVars.web_interface_plugin && resultset2) {
#endif
MySQL_Threads_Handler_sets_v2(resultset1);
MyHGM_Handler_sets_connection_pool(resultset2);
}
}
void ProxySQL_Statistics::MyHGM_Handler_sets_connection_pool(SQLite3_result *resultset) {
int rc;
if (resultset == NULL)
return;
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement=NULL;
string query;
if (resultset->rows_count == 0) {
return;
}
time_t ts = time(NULL);
query = "INSERT INTO history_stats_mysql_connection_pool VALUES (?1";
for (int i=0; i < resultset->columns; i++) {
query += ",?" + to_string(i+2);
}
query += ")";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query.c_str(), -1, &statement, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
for (std::vector<SQLite3_row *>::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) {
SQLite3_row *r=*it;
rc=(*proxy_sqlite3_bind_int64)(statement, 1, ts); ASSERT_SQLITE_OK(rc, statsdb_disk);
for (int i=0 ; i < resultset->columns ; i++) {
rc=(*proxy_sqlite3_bind_text)(statement, i+2, r->fields[i] , -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
SAFE_SQLITE3_STEP2(statement);
rc=(*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement); //ASSERT_SQLITE_OK(rc, statsdb_disk);
}
(*proxy_sqlite3_finalize)(statement);
}
void ProxySQL_Statistics::MyHGM_Handler_sets_v1(SQLite3_result *resultset) {
int rc;
if (resultset == NULL)
return;
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement1=NULL;
//sqlite3_stmt *statement2=NULL;
//sqlite3_stmt *statement3=NULL;
char *query1=NULL;
//char *query2=NULL;
//char *query3=NULL;
query1=(char *)"INSERT INTO myhgm_connections VALUES (?1, ?2, ?3, ?4, ?5, ?6)";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
//rc=(*proxy_sqlite3_prepare_v2)(mydb3, query2, -1, &statement2, 0);
//ASSERT_SQLITE_OK(rc, statsdb_disk);
//rc=(*proxy_sqlite3_prepare_v2)(mydb3, query3, -1, &statement3, 0);
//ASSERT_SQLITE_OK(rc, statsdb_disk);
time_t ts = time(NULL);
uint64_t myhgm_connections_values[6];
for (int i=0; i<6; i++) {
myhgm_connections_values[i]=0;
}
myhgm_connections_values[0] = ts;
for (std::vector<SQLite3_row *>::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) {
SQLite3_row *r1=*it;
if (!strcasecmp(r1->fields[0],"MyHGM_myconnpoll_destroy")) {
myhgm_connections_values[1]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"MyHGM_myconnpoll_get")) {
myhgm_connections_values[2]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"MyHGM_myconnpoll_get_ok")) {
myhgm_connections_values[3]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"MyHGM_myconnpoll_push")) {
myhgm_connections_values[4]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"MyHGM_myconnpoll_reset")) {
myhgm_connections_values[5]=atoi(r1->fields[1]);
continue;
}
}
for (int i=0; i<6; i++) {
rc=(*proxy_sqlite3_bind_int64)(statement1, i+1, myhgm_connections_values[i]); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
SAFE_SQLITE3_STEP2(statement1);
rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement1); //ASSERT_SQLITE_OK(rc, statsdb_disk);
(*proxy_sqlite3_finalize)(statement1);
SQLite3_result *resultset2 = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
query = (char *)"SELECT MAX(timestamp) FROM myhgm_connections_hour";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset2);
if (error) {
if (resultset2) {
delete resultset2;
resultset2 = NULL;
}
free(error);
} else {
char buf[1024];
if (resultset2->rows_count == 0) {
sprintf(buf,"INSERT INTO myhgm_connections_hour SELECT timestamp/3600*3600 , MAX(MyHGM_myconnpoll_destroy), MAX(MyHGM_myconnpoll_get), MAX(MyHGM_myconnpoll_get_ok), MAX(MyHGM_myconnpoll_push), MAX(MyHGM_myconnpoll_reset) FROM myhgm_connections WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
} else {
SQLite3_row *r = resultset2->rows[0];
if (r->fields[0]) {
time_t t = atol(r->fields[0]);
if (ts >= t + 3600) {
sprintf(buf,"INSERT INTO myhgm_connections_hour SELECT timestamp/3600*3600 , MAX(MyHGM_myconnpoll_destroy), MAX(MyHGM_myconnpoll_get), MAX(MyHGM_myconnpoll_get_ok), MAX(MyHGM_myconnpoll_push), MAX(MyHGM_myconnpoll_reset) FROM myhgm_connections WHERE timestamp >= %ld AND timestamp < %ld GROUP BY timestamp/3600", t+3600 , (ts/3600)*3600);
statsdb_disk->execute(buf);
}
} else {
sprintf(buf,"INSERT INTO myhgm_connections_hour SELECT timestamp/3600*3600 , MAX(MyHGM_myconnpoll_destroy), MAX(MyHGM_myconnpoll_get), MAX(MyHGM_myconnpoll_get_ok), MAX(MyHGM_myconnpoll_push), MAX(MyHGM_myconnpoll_reset) FROM myhgm_connections WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
}
}
delete resultset2;
resultset2 = NULL;
sprintf(buf,"DELETE FROM myhgm_connections WHERE timestamp < %ld", ts - 3600*24*7);
statsdb_disk->execute(buf);
sprintf(buf,"DELETE FROM myhgm_connections_hour WHERE timestamp < %ld", ts - 3600*24*365);
statsdb_disk->execute(buf);
}
}
void ProxySQL_Statistics::MySQL_Threads_Handler_sets(SQLite3_result *resultset) {
MySQL_Threads_Handler_sets_v1(resultset);
// In debug, enable metrics features for debugging and testing even if the web interface plugin is not loaded.
#ifdef DEBUG
if (true) {
#else
if (GloVars.web_interface_plugin) {
#endif
MySQL_Threads_Handler_sets_v2(resultset);
}
}
void ProxySQL_Statistics::MySQL_Threads_Handler_sets_v2(SQLite3_result *resultset) {
int rc;
if (resultset == NULL)
return;
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement=NULL;
string query;
if (resultset->rows_count == 0) {
return;
}
time_t ts = time(NULL);
load_variable_name_id_map_if_empty();
query = "INSERT INTO history_mysql_status_variables VALUES ";
int idx = 0;
for (int i=0; i < resultset->rows_count; i++) {
query += "(?" + to_string(idx+1) + ",?" + to_string(idx+2) + ",?" + to_string(idx+3) + "),";
idx+=3;
}
query.pop_back();
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query.c_str(), -1, &statement, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
idx=0;
for (std::vector<SQLite3_row *>::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) {
SQLite3_row *r=*it;
rc=(*proxy_sqlite3_bind_int64)(statement, idx+1, ts); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_bind_int64)(statement, idx+2, get_variable_id_for_name(r->fields[0])); ASSERT_SQLITE_OK(rc, statsdb_disk); // variable_id
rc=(*proxy_sqlite3_bind_text)(statement, idx+3, r->fields[1] , -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb_disk); // value
idx+=3;
}
SAFE_SQLITE3_STEP2(statement);
rc=(*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement); //ASSERT_SQLITE_OK(rc, statsdb_disk);
(*proxy_sqlite3_finalize)(statement);
}
void ProxySQL_Statistics::MySQL_Threads_Handler_sets_v1(SQLite3_result *resultset) {
int rc;
if (resultset == NULL)
return;
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement1=NULL;
//sqlite3_stmt *statement2=NULL;
//sqlite3_stmt *statement3=NULL;
char *query1=NULL;
//char *query2=NULL;
//char *query3=NULL;
query1=(char *)"INSERT INTO mysql_connections VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)";
//query2=(char *)"INSERT INTO myhgm_connections VALUES (?1, ?2, ?3, ?4, ?5, ?6)";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
//rc=(*proxy_sqlite3_prepare_v2)(mydb3, query2, -1, &statement2, 0);
//ASSERT_SQLITE_OK(rc, statsdb_disk);
//rc=(*proxy_sqlite3_prepare_v2)(mydb3, query3, -1, &statement3, 0);
//ASSERT_SQLITE_OK(rc, statsdb_disk);
time_t ts = time(NULL);
uint64_t mysql_connections_values[13];
for (int i=0; i<13; i++) {
mysql_connections_values[i]=0;
}
mysql_connections_values[0] = ts;
for (std::vector<SQLite3_row *>::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) {
SQLite3_row *r1=*it;
if (!strcasecmp(r1->fields[0],"Client_Connections_aborted")) {
mysql_connections_values[1]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Client_Connections_connected")) {
mysql_connections_values[2]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Client_Connections_created")) {
mysql_connections_values[3]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Server_Connections_aborted")) {
mysql_connections_values[4]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Server_Connections_connected")) {
mysql_connections_values[5]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Server_Connections_created")) {
mysql_connections_values[6]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"ConnPool_get_conn_failure")) {
mysql_connections_values[7]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"ConnPool_get_conn_immediate")) {
mysql_connections_values[8]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"ConnPool_get_conn_success")) {
mysql_connections_values[9]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Questions")) {
mysql_connections_values[10]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"Slow_queries")) {
mysql_connections_values[11]=atoi(r1->fields[1]);
continue;
}
if (!strcasecmp(r1->fields[0],"GTID_consistent_queries")) {
mysql_connections_values[12]=atoi(r1->fields[1]);
continue;
}
}
for (int i=0; i<13; i++) {
rc=(*proxy_sqlite3_bind_int64)(statement1, i+1, mysql_connections_values[i]); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
SAFE_SQLITE3_STEP2(statement1);
rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement1); //ASSERT_SQLITE_OK(rc, statsdb_disk);
(*proxy_sqlite3_finalize)(statement1);
SQLite3_result *resultset2 = NULL;
int cols;
int affected_rows;
char *error = NULL;
char *query = NULL;
query = (char *)"SELECT MAX(timestamp) FROM mysql_connections_hour";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset2);
if (error) {
if (resultset2) {
delete resultset2;
resultset2 = NULL;
}
free(error);
} else {
char buf[1024];
if (resultset2->rows_count == 0) {
sprintf(buf,"INSERT INTO mysql_connections_hour SELECT timestamp/3600*3600 , MAX(Client_Connections_aborted), AVG(Client_Connections_connected), MAX(Client_Connections_created), MAX(Server_Connections_aborted), AVG(Server_Connections_connected), MAX(Server_Connections_created), MAX(ConnPool_get_conn_failure), MAX(ConnPool_get_conn_immediate), MAX(ConnPool_get_conn_success), MAX(Questions), MAX(Slow_queries), MAX(GTID_consistent_queries) FROM mysql_connections WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
} else {
SQLite3_row *r = resultset2->rows[0];
if (r->fields[0]) {
time_t t = atol(r->fields[0]);
if (ts >= t + 3600) {
sprintf(buf,"INSERT INTO mysql_connections_hour SELECT timestamp/3600*3600 , MAX(Client_Connections_aborted), AVG(Client_Connections_connected), MAX(Client_Connections_created), MAX(Server_Connections_aborted), AVG(Server_Connections_connected), MAX(Server_Connections_created), MAX(ConnPool_get_conn_failure), MAX(ConnPool_get_conn_immediate), MAX(ConnPool_get_conn_success), MAX(Questions), MAX(Slow_queries), MAX(GTID_consistent_queries) FROM mysql_connections WHERE timestamp >= %ld AND timestamp < %ld GROUP BY timestamp/3600", t+3600 , (ts/3600)*3600);
statsdb_disk->execute(buf);
}
} else {
sprintf(buf,"INSERT INTO mysql_connections_hour SELECT timestamp/3600*3600 , MAX(Client_Connections_aborted), AVG(Client_Connections_connected), MAX(Client_Connections_created), MAX(Server_Connections_aborted), AVG(Server_Connections_connected), MAX(Server_Connections_created), MAX(ConnPool_get_conn_failure), MAX(ConnPool_get_conn_immediate), MAX(ConnPool_get_conn_success), MAX(Questions), MAX(Slow_queries), MAX(GTID_consistent_queries) FROM mysql_connections WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
}
}
delete resultset2;
resultset2 = NULL;
sprintf(buf,"DELETE FROM mysql_connections WHERE timestamp < %ld", ts - 3600*24*7);
statsdb_disk->execute(buf);
sprintf(buf,"DELETE FROM mysql_connections_hour WHERE timestamp < %ld", ts - 3600*24*365);
statsdb_disk->execute(buf);
}
}
void ProxySQL_Statistics::MySQL_Query_Cache_sets(SQLite3_result *resultset) {
int rc;
if (resultset == NULL)
return;
sqlite3 *mydb3=statsdb_disk->get_db();
sqlite3_stmt *statement1=NULL;
char *query1=NULL;
query1=(char *)"INSERT INTO mysql_query_cache VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)";
rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0);
if (rc!=SQLITE_OK) {
proxy_error("SQLITE CRITICAL error: %s . Shutting down.\n", (*proxy_sqlite3_errmsg)(mydb3));
exit(EXIT_SUCCESS);
}
uint64_t qc_values[9];
for (int i=0; i<9; i++) {
qc_values[i]=0;
}
qc_values[0] = time(NULL);
for (std::vector<SQLite3_row *>::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) {
SQLite3_row *r1=*it;
if (!strcasecmp(r1->fields[0],"Query_Cache_count_GET")) {
qc_values[1]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_count_GET_OK")) {
qc_values[2]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_count_SET")) {
qc_values[3]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_bytes_IN")) {
qc_values[4]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_bytes_OUT")) {
qc_values[5]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_Purged")) {
qc_values[6]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_Entries")) {
qc_values[7]=strtoull(r1->fields[1], NULL, 10);
continue;
}
if (!strcasecmp(r1->fields[0],"Query_Cache_Memory_bytes")) {
qc_values[8]=strtoull(r1->fields[1], NULL, 10);
continue;
}
}
for (int i=0; i<9; i++) {
rc=(*proxy_sqlite3_bind_int64)(statement1, i+1, qc_values[i]); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
SAFE_SQLITE3_STEP2(statement1);
rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc=(*proxy_sqlite3_reset)(statement1); //ASSERT_SQLITE_OK(rc, statsdb_disk);
(*proxy_sqlite3_finalize)(statement1);
SQLite3_result *resultset2 = NULL;
int cols;
int affected_rows;
char *error = NULL;
time_t ts = time(NULL);
char *query = (char *)"SELECT MAX(timestamp) FROM mysql_query_cache_hour";
statsdb_disk->execute_statement(query, &error , &cols , &affected_rows , &resultset2);
if (error) {
if (resultset2) {
delete resultset2;
resultset2 = NULL;
}
free(error);
} else {
char buf[1024];
if (resultset2->rows_count == 0) {
sprintf(buf,"INSERT INTO mysql_query_cache_hour SELECT timestamp/3600*3600 , MAX(count_GET), MAX(count_GET_OK), MAX(count_SET), MAX(bytes_IN), MAX(bytes_OUT), MAX(Entries_Purged), AVG(Entries_In_Cache), AVG(Memory_bytes) FROM mysql_query_cache WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
} else {
SQLite3_row *r = resultset2->rows[0];
if (r->fields[0]) {
time_t t = atol(r->fields[0]);
if (ts >= t + 3600) {
sprintf(buf,"INSERT INTO mysql_query_cache_hour SELECT timestamp/3600*3600 , MAX(count_GET), MAX(count_GET_OK), MAX(count_SET), MAX(bytes_IN), MAX(bytes_OUT), MAX(Entries_Purged), AVG(Entries_In_Cache), AVG(Memory_bytes) FROM mysql_query_cache WHERE timestamp >= %ld AND timestamp < %ld GROUP BY timestamp/3600", t+3600 , (ts/3600)*3600);
statsdb_disk->execute(buf);
}
} else {
sprintf(buf,"INSERT INTO mysql_query_cache_hour SELECT timestamp/3600*3600 , MAX(count_GET), MAX(count_GET_OK), MAX(count_SET), MAX(bytes_IN), MAX(bytes_OUT), MAX(Entries_Purged), AVG(Entries_In_Cache), AVG(Memory_bytes) FROM mysql_query_cache WHERE timestamp < %ld GROUP BY timestamp/3600", (ts/3600)*3600);
statsdb_disk->execute(buf);
}
}
delete resultset2;
resultset2 = NULL;
sprintf(buf,"DELETE FROM mysql_query_cache WHERE timestamp < %ld", ts - 3600*24*7);
statsdb_disk->execute(buf);
sprintf(buf,"DELETE FROM mysql_query_cache_hour WHERE timestamp < %ld", ts - 3600*24*365);
statsdb_disk->execute(buf);
}
}
bool ProxySQL_Statistics::knows_variable_name(const std::string & variable_name) const
{
return (variable_name_id_map.find(variable_name) != variable_name_id_map.end());
}
int64_t ProxySQL_Statistics::get_variable_id_for_name(const std::string & variable_name) {
lock_guard<mutex> lock(mu);
int64_t variable_id = -1; // Negative value indicates not yet found.
auto it = variable_name_id_map.find(variable_name);
if (it != variable_name_id_map.end()) {
variable_id = it->second;
} else {
// No matching variable_id found in map. Try loading from the SQLite lookup table on disk
SQLite3_result *result = NULL;
int cols;
int affected_rows;
char *error = NULL;
auto select_var_id = [&]() -> int64_t {
const string var_id_query = "SELECT variable_id FROM history_mysql_status_variables_lookup WHERE variable_name=\"" + variable_name + "\"";
statsdb_disk->execute_statement(var_id_query.c_str(), &error , &cols , &affected_rows , &result);
if (error) {
proxy_error("SQLITE CRITICAL ERROR %s. Shutting down\n", error);
free(error);
error = NULL;
exit(EXIT_SUCCESS);
}
if (result) {
if (result->rows_count > 0) {
// matching variable_id for variable_name in lookup table
SQLite3_row *r = result->rows[0];
int64_t found_variable_id = strtoll(r->fields[0], NULL, 10);
delete result;
return found_variable_id;
}
delete result;
result = NULL;
}
return -1;
};
variable_id = select_var_id(); // Check for variable name present in the lookup
if (variable_id < 0) {
// No match found, create a new record in the lookup table and then select the newly generated id
string insert_var_query = "INSERT INTO history_mysql_status_variables_lookup(variable_name) VALUES(\"" + variable_name + "\")";
if (statsdb_disk->execute(insert_var_query.c_str())) {
variable_id = select_var_id();
}
}
// Update the map if a lookup record id was found, or if a new record was generated.
if (variable_id > 0) {
variable_name_id_map[variable_name] = variable_id;
} else {
proxy_error("CRITICAL ERROR: Statistics could not find or generate variable_id for variable_name: %s. Shutting down\n", variable_name.c_str());
exit(EXIT_SUCCESS);
}
}
return variable_id;
}
void ProxySQL_Statistics::load_variable_name_id_map_if_empty() {
lock_guard<mutex> lock(mu);
if (!variable_name_id_map.empty())
return;
// Load id and name records from the lookup table and store in the map
SQLite3_result *result = NULL;
int cols;
int affected_rows;
char *error = NULL;
string query = "SELECT variable_id, variable_name FROM history_mysql_status_variables_lookup";
statsdb_disk->execute_statement(query.c_str(), &error , &cols , &affected_rows , &result);
if (error) {
proxy_error("SQLITE CRITICAL ERROR: %s. Shutting down\n", error);
if (result)
delete result;
free(error);
error = NULL;
exit(EXIT_SUCCESS);
}
if (result) {
for (int i = 0; i < result->rows_count; i++) {
SQLite3_row *r = result->rows[i];
int64_t variable_id = strtoll(r->fields[0], NULL, 10);
string variable_name = r->fields[1];
variable_name_id_map[variable_name] = variable_id;
}
delete result;
}
}
// TSDB Metric Insertion
void ProxySQL_Statistics::insert_tsdb_metric(const std::string& metric_name,
const std::map<std::string, std::string>& labels,
double value,
time_t timestamp) {
if (!statsdb_disk) return;
sqlite3 *mydb3 = statsdb_disk->get_db();
int rc;
if (stmt_insert_tsdb_metric == NULL) {
const char* query = "INSERT OR REPLACE INTO tsdb_metrics(timestamp, metric_name, labels, value) VALUES (?1, ?2, ?3, ?4)";
rc = (*proxy_sqlite3_prepare_v2)(mydb3, query, -1, &stmt_insert_tsdb_metric, 0);
if (rc != SQLITE_OK) {
proxy_error("Failed to prepare statement: %s\n", (*proxy_sqlite3_errmsg)(mydb3));
return;
}
}
sqlite3_stmt *statement = stmt_insert_tsdb_metric;
// Convert labels map to JSON string
json j_labels(labels);
std::string labels_str = j_labels.dump();
if (labels_str.empty()) {
labels_str = "{}";
}
rc = (*proxy_sqlite3_bind_int64)(statement, 1, timestamp); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_text)(statement, 2, metric_name.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_text)(statement, 3, labels_str.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_double)(statement, 4, value); ASSERT_SQLITE_OK(rc, statsdb_disk);
SAFE_SQLITE3_STEP2(statement);
rc = (*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_reset)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
// TSDB Backend Health Insertion
void ProxySQL_Statistics::insert_backend_health(int hostgroup,
const std::string& hostname,
int port,
bool probe_up,
int connect_ms,
time_t timestamp) {
if (!statsdb_disk) return;
sqlite3 *mydb3 = statsdb_disk->get_db();
int rc;
if (stmt_insert_backend_health == NULL) {
const char* query = "INSERT OR REPLACE INTO tsdb_backend_health(timestamp, hostgroup, hostname, port, probe_up, connect_ms) VALUES (?1, ?2, ?3, ?4, ?5, ?6)";
rc = (*proxy_sqlite3_prepare_v2)(mydb3, query, -1, &stmt_insert_backend_health, 0);
if (rc != SQLITE_OK) {
proxy_error("Failed to prepare statement: %s\n", (*proxy_sqlite3_errmsg)(mydb3));
return;
}
}
sqlite3_stmt *statement = stmt_insert_backend_health;
rc = (*proxy_sqlite3_bind_int64)(statement, 1, timestamp); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int)(statement, 2, hostgroup); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_text)(statement, 3, hostname.c_str(), -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int)(statement, 4, port); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int)(statement, 5, probe_up ? 1 : 0); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_bind_int)(statement, 6, connect_ms); ASSERT_SQLITE_OK(rc, statsdb_disk);
SAFE_SQLITE3_STEP2(statement);
rc = (*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
rc = (*proxy_sqlite3_reset)(statement); ASSERT_SQLITE_OK(rc, statsdb_disk);
}
// TSDB Downsampling
void ProxySQL_Statistics::tsdb_downsample_metrics() {
if (!variables.tsdb_enabled) return;
if (!statsdb_disk) return;
time_t ts = time(NULL);
time_t current_hour = (ts / 3600) * 3600;
// Get last processed hour
char *query = (char *)"SELECT MAX(bucket) FROM tsdb_metrics_hour";
SQLite3_result *resultset = NULL;
int cols, affected_rows;
char *error = NULL;
statsdb_disk->execute_statement(query, &error, &cols, &affected_rows, &resultset);
if (error) {
proxy_error("tsdb_downsample_metrics: %s\n", error);
free(error);
if (resultset) delete resultset;
return;
}
time_t last_hour = 0;
if (resultset && resultset->rows_count > 0 && resultset->rows[0]->fields[0]) {
last_hour = atol(resultset->rows[0]->fields[0]);
}
if (resultset) delete resultset;
// Process new hours
if (last_hour < current_hour - 3600) {
char buf[2048];
snprintf(buf, sizeof(buf),
"INSERT OR REPLACE INTO tsdb_metrics_hour "
"SELECT "
" (timestamp/3600)*3600 as bucket,"
" metric_name,"
" labels,"
" AVG(value) as avg_value,"
" MAX(value) as max_value,"
" MIN(value) as min_value,"
" COUNT(*) as count "
"FROM tsdb_metrics "
"WHERE timestamp >= %ld AND timestamp < %ld "
"GROUP BY bucket, metric_name, labels",
last_hour > 0 ? last_hour + 3600 : 0,
current_hour);
statsdb_disk->execute(buf);
}
}
void ProxySQL_Statistics::tsdb_retention_cleanup() {
if (!variables.tsdb_enabled) return;
if (!statsdb_disk) return;
time_t ts = time(NULL);
const int retention_days = std::max(1, variables.tsdb_retention_days);
char delete_buf[256];
// Retention: delete raw data older than configured days
snprintf(delete_buf, sizeof(delete_buf),
"DELETE FROM tsdb_metrics WHERE timestamp < %ld",
ts - 86400 * retention_days);
statsdb_disk->execute(delete_buf);
// Retention: delete hourly data older than 1 year
snprintf(delete_buf, sizeof(delete_buf),
"DELETE FROM tsdb_metrics_hour WHERE bucket < %ld",
ts - 86400 * 365);
statsdb_disk->execute(delete_buf);
// Retention: delete backend probe data older than configured days
snprintf(delete_buf, sizeof(delete_buf),
"DELETE FROM tsdb_backend_health WHERE timestamp < %ld",
ts - 86400 * retention_days);
statsdb_disk->execute(delete_buf);
}
// TSDB Status
ProxySQL_Statistics::tsdb_status_t ProxySQL_Statistics::get_tsdb_status() {
tsdb_status_t status = {0, 0, 0, 0, 0};
if (!statsdb_disk) return status;
char *error = NULL;
int cols = 0;
int affected_rows = 0;
SQLite3_result *resultset = NULL;
// Count total series (unique metric_name + labels combinations)
statsdb_disk->execute_statement(
"SELECT COUNT(DISTINCT metric_name || CHAR(31) || labels) FROM tsdb_metrics",
&error, &cols, &affected_rows, &resultset);
if (error) {
free(error);
error = NULL;
}
if (resultset) {
if (resultset->rows_count > 0 && resultset->rows[0]->fields[0]) {
status.total_series = atol(resultset->rows[0]->fields[0]);
}
delete resultset;
}
// Count total datapoints
resultset = NULL;
statsdb_disk->execute_statement(
"SELECT COUNT(*) FROM tsdb_metrics",
&error, &cols, &affected_rows, &resultset);
if (error) {
free(error);
error = NULL;
}
if (resultset) {
if (resultset->rows_count > 0 && resultset->rows[0]->fields[0]) {
status.total_datapoints = atol(resultset->rows[0]->fields[0]);
}
delete resultset;
}
// Get oldest and newest datapoint timestamps
resultset = NULL;
statsdb_disk->execute_statement(
"SELECT MIN(timestamp), MAX(timestamp) FROM tsdb_metrics",
&error, &cols, &affected_rows, &resultset);
if (error) {
free(error);
error = NULL;
}
if (resultset) {
if (resultset->rows_count > 0) {
if (resultset->rows[0]->fields[0]) {
status.oldest_datapoint = atol(resultset->rows[0]->fields[0]);
}
if (resultset->rows[0]->fields[1]) {
status.newest_datapoint = atol(resultset->rows[0]->fields[1]);
}
}
delete resultset;
}
long long page_count = statsdb_disk->return_one_int((char*)"PRAGMA page_count");
long long page_size = statsdb_disk->return_one_int((char*)"PRAGMA page_size");
if (page_count > 0 && page_size > 0) {
status.disk_size_bytes = page_count * page_size;
}
return status;
}
// TSDB Timer Checks
bool ProxySQL_Statistics::tsdb_sampler_timetoget(unsigned long long curtime) {
if (!variables.tsdb_enabled || variables.tsdb_sample_interval <= 0) {
return false;
}
if (curtime > next_timer_tsdb_sampler) {
next_timer_tsdb_sampler = curtime + (unsigned long long)variables.tsdb_sample_interval * 1000000ULL;
return true;
}
return false;
}
bool ProxySQL_Statistics::tsdb_downsample_timetoget(unsigned long long curtime) {
if (!variables.tsdb_enabled) {
return false;
}
if (curtime > next_timer_tsdb_downsample) {
next_timer_tsdb_downsample = curtime + 3600ULL * 1000000ULL; // Hourly
return true;
}
return false;
}
bool ProxySQL_Statistics::tsdb_monitor_timetoget(unsigned long long curtime) {
if (!variables.tsdb_enabled || !variables.tsdb_monitor_enabled || variables.tsdb_monitor_interval <= 0) {
return false;
}
if (curtime > next_timer_tsdb_monitor) {
next_timer_tsdb_monitor = curtime + (unsigned long long)variables.tsdb_monitor_interval * 1000000ULL;
return true;
}
return false;
}
bool ProxySQL_Statistics::tsdb_retention_timetoget(unsigned long long curtime) {
if (!variables.tsdb_enabled) {
return false;
}
if (curtime > next_timer_tsdb_retention) {
next_timer_tsdb_retention = curtime + 3600ULL * 1000000ULL; // Once per hour
return true;
}
return false;
}
// TSDB Query with Label Filtering
SQLite3_result* ProxySQL_Statistics::query_tsdb_metrics(
const std::string& metric_name,
const std::map<std::string, std::string>& label_filters,
time_t from,
time_t to,
const std::string& aggregation) {
if (!statsdb_disk) return NULL;
if (to < from) {
std::swap(from, to);
}
const bool use_hourly = (to - from > 86400);
const std::string agg = aggregation.empty() ? "raw" : aggregation;
std::string query;
if (use_hourly) {
std::string value_expr = "avg_value";
if (agg == "max") {
value_expr = "max_value";
} else if (agg == "min") {
value_expr = "min_value";
} else if (agg == "count") {
value_expr = "count";
}
query =
"SELECT bucket AS ts, metric_name, labels, " + value_expr + " AS value "
"FROM tsdb_metrics_hour "
"WHERE metric_name='" + escape_sql_string_literal(metric_name) + "' "
"AND bucket BETWEEN " + std::to_string(from) + " AND " + std::to_string(to);
} else {
query =
"SELECT timestamp AS ts, metric_name, labels, value "
"FROM tsdb_metrics "
"WHERE metric_name='" + escape_sql_string_literal(metric_name) + "' "
"AND timestamp BETWEEN " + std::to_string(from) + " AND " + std::to_string(to);
}
for (const auto& kv : label_filters) {
if (!valid_label_key(kv.first)) {
proxy_error("Invalid TSDB label key: %s\n", kv.first.c_str());
return NULL;
}
query += " AND json_extract(labels, '$.\"" + kv.first + "\"')='" + escape_sql_string_literal(kv.second) + "'";
}
query += std::string(" ORDER BY ") + (use_hourly ? "bucket" : "timestamp");
char* error = NULL;
int cols = 0;
int affected_rows = 0;
SQLite3_result* resultset = NULL;
statsdb_disk->execute_statement((char*)query.c_str(), &error, &cols, &affected_rows, &resultset);
if (error) {
proxy_error("query_tsdb_metrics failed: %s -- sql: %s\n", error, query.c_str());
free(error);
if (resultset) {
delete resultset;
}
return NULL;
}
return resultset;
}
// TSDB Backend Health Query
SQLite3_result* ProxySQL_Statistics::get_backend_health_metrics(time_t from, time_t to, int hostgroup) {
if (!statsdb_disk) return NULL;
if (to < from) {
std::swap(from, to);
}
std::string query =
"SELECT timestamp, hostgroup, hostname, port, probe_up, connect_ms "
"FROM tsdb_backend_health "
"WHERE timestamp BETWEEN " + std::to_string(from) + " AND " + std::to_string(to);
if (hostgroup >= 0) {
query += " AND hostgroup = " + std::to_string(hostgroup);
}
query += " ORDER BY timestamp";
char* error = NULL;
int cols = 0;
int affected_rows = 0;
SQLite3_result* resultset = NULL;
statsdb_disk->execute_statement((char*)query.c_str(), &error, &cols, &affected_rows, &resultset);
if (error) {
proxy_error("get_backend_health_metrics failed: %s -- sql: %s\n", error, query.c_str());
free(error);
if (resultset) {
delete resultset;
}
return NULL;
}
return resultset;
}
// TSDB Sampler Loop
void ProxySQL_Statistics::tsdb_sampler_loop() {
if (!variables.tsdb_enabled) return;
// Sample Prometheus metrics if registry exists
if (GloVars.prometheus_registry) {
auto metrics = GloVars.prometheus_registry->Collect();
time_t now = time(NULL);
statsdb_disk->execute("BEGIN");
for (const auto& family : metrics) {
for (const auto& metric : family.metric) {
std::map<std::string, std::string> labels;
for (const auto& lp : metric.label) {
labels[lp.name] = lp.value;
}
switch (family.type) {
case prometheus::MetricType::Counter:
insert_tsdb_metric(family.name, labels, metric.counter.value, now);
break;
case prometheus::MetricType::Gauge:
insert_tsdb_metric(family.name, labels, metric.gauge.value, now);
break;
case prometheus::MetricType::Summary: {
insert_tsdb_metric(family.name + "_count", labels, static_cast<double>(metric.summary.sample_count), now);
insert_tsdb_metric(family.name + "_sum", labels, metric.summary.sample_sum, now);
for (const auto& q : metric.summary.quantile) {
std::map<std::string, std::string> q_labels(labels);
q_labels["quantile"] = format_prometheus_label_double(q.quantile);
insert_tsdb_metric(family.name, q_labels, q.value, now);
}
break;
}
case prometheus::MetricType::Histogram: {
insert_tsdb_metric(family.name + "_count", labels, static_cast<double>(metric.histogram.sample_count), now);
insert_tsdb_metric(family.name + "_sum", labels, metric.histogram.sample_sum, now);
for (const auto& b : metric.histogram.bucket) {
std::map<std::string, std::string> b_labels(labels);
b_labels["le"] = format_prometheus_label_double(b.upper_bound);
insert_tsdb_metric(
family.name + "_bucket",
b_labels,
static_cast<double>(b.cumulative_count),
now
);
}
break;
}
case prometheus::MetricType::Info:
insert_tsdb_metric(family.name, labels, metric.info.value, now);
break;
case prometheus::MetricType::Untyped:
default:
insert_tsdb_metric(family.name, labels, metric.untyped.value, now);
break;
}
}
}
statsdb_disk->execute("COMMIT");
}
}
struct probe_result_t {
int hg;
std::string host;
int port;
bool probe_up;
int connect_ms;
time_t timestamp;
};
probe_result_t probe_backend(int hg, std::string host, int port, time_t now) {
bool probe_up = false;
int connect_ms = -1;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
struct addrinfo* res = NULL;
char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", port);
if (getaddrinfo(host.c_str(), port_str, &hints, &res) == 0 && res) {
for (struct addrinfo* ai = res; ai != NULL; ai = ai->ai_next) {
int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
continue;
}
// Set non-blocking
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
auto start = std::chrono::steady_clock::now();
int cres = connect(sock, ai->ai_addr, ai->ai_addrlen);
if (cres < 0) {
if (errno == EINPROGRESS) {
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLOUT;
int p_res = poll(fds, 1, 1000); // 1s timeout
if (p_res > 0) {
int error = 0;
socklen_t len = sizeof(error);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
if (error == 0) {
cres = 0;
}
}
}
}
auto end = std::chrono::steady_clock::now();
connect_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
close(sock);
if (cres == 0) {
probe_up = true;
break;
}
}
freeaddrinfo(res);
}
return {hg, host, port, probe_up, connect_ms, now};
}
// TSDB Monitor Loop
void ProxySQL_Statistics::tsdb_monitor_loop() {
if (!variables.tsdb_enabled || !variables.tsdb_monitor_enabled || !statsdb_disk) return;
std::vector<probe_target_t> targets;
// Pull backend servers through the same hostgroup-manager paths used to build runtime tables.
if (MyHGM) {
SQLite3_result* mysql_servers = MyHGM->dump_table_mysql("mysql_servers");
append_probe_targets(mysql_servers, targets);
if (mysql_servers) {
delete mysql_servers;
}
}
if (PgHGM) {
SQLite3_result* pgsql_servers = PgHGM->dump_table_pgsql("pgsql_servers");
append_probe_targets(pgsql_servers, targets);
if (pgsql_servers) {
delete pgsql_servers;
}
}
if (targets.empty()) {
return;
}
time_t now = time(NULL);
std::vector<std::future<probe_result_t>> futures;
futures.reserve(targets.size());
for (const auto& target : targets) {
futures.push_back(std::async(std::launch::async, probe_backend, target.hg, target.host, target.port, now));
}
statsdb_disk->execute("BEGIN");
for (auto& f : futures) {
probe_result_t res = f.get();
insert_backend_health(res.hg, res.host, res.port, res.probe_up, res.connect_ms, res.timestamp);
}
statsdb_disk->execute("COMMIT");
}