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/include/Stats_Tool_Handler.h

269 lines
9.6 KiB

#ifndef CLASS_STATS_TOOL_HANDLER_H
#define CLASS_STATS_TOOL_HANDLER_H
#ifdef PROXYSQLGENAI
#include <map>
#include <string>
#include <vector>
#include "MCP_Tool_Handler.h"
#include "MCP_Thread.h"
/**
* @brief Stats Tool Handler for /mcp/stats endpoint
*
* This handler provides MCP tools for monitoring ProxySQL statistics and metrics.
* Tools are organized into three categories:
*
* Live Data Tools (12):
* - show_status: Global status variables and metrics
* - show_processlist: Currently active sessions
* - show_queries: Query digest performance statistics
* - show_commands: Command execution counters with latency histograms
* - show_connections: Backend connection pool metrics
* - show_errors: Error tracking and frequency
* - show_users: User connection statistics
* - show_client_cache: Client host error cache
* - show_query_rules: Query rule hit counts
* - show_prepared_statements: Prepared statement information
* - show_gtid: GTID replication status (MySQL only)
* - show_cluster: ProxySQL cluster node health
*
* Historical Data Tools (4):
* - show_system_history: CPU and memory trends over time
* - show_query_cache_history: Query cache performance trends
* - show_connection_history: Connection metrics history
* - show_query_history: Query digest snapshots over time
*
* Utility Tools (3):
* - flush_query_log: Flush query events from buffer to tables
* - show_query_log: View query event audit log
* - flush_queries: Save query digest statistics to disk
*/
class Stats_Tool_Handler : public MCP_Tool_Handler {
private:
MCP_Threads_Handler* mcp_handler; ///< Pointer to MCP handler
pthread_mutex_t handler_lock; ///< Mutex for thread-safe operations
// =========================================================================
// Live Data Tool Handlers
// =========================================================================
/**
* @brief Returns global status variables and metrics
* @param arguments JSON with db_type, category, variable_name
*/
json handle_show_status(const json& arguments);
/**
* @brief Shows all currently active sessions
* @param arguments JSON with db_type, filters, sort_by, sort_order, limit, offset
*/
json handle_show_processlist(const json& arguments);
/**
* @brief Returns aggregated query performance statistics
* @param arguments JSON with db_type, sort_by, limit, offset, filters
*/
json handle_show_queries(const json& arguments);
/**
* @brief Returns command execution statistics with latency histograms
* @param arguments JSON with db_type, command, limit, offset
*/
json handle_show_commands(const json& arguments);
/**
* @brief Returns backend connection pool metrics
* @param arguments JSON with db_type, hostgroup, server, status, detail
*/
json handle_show_connections(const json& arguments);
/**
* @brief Returns error tracking statistics
* @param arguments JSON with db_type, errno, sqlstate, username, database, hostgroup, filters
*/
json handle_show_errors(const json& arguments);
/**
* @brief Returns connection statistics per user
* @param arguments JSON with db_type, username, limit, offset
*/
json handle_show_users(const json& arguments);
/**
* @brief Returns client host error cache
* @param arguments JSON with db_type, client_address, min_error_count, limit, offset
*/
json handle_show_client_cache(const json& arguments);
/**
* @brief Returns query rule hit statistics
* @param arguments JSON with db_type, rule_id, min_hits, include_zero_hits, limit, offset
*/
json handle_show_query_rules(const json& arguments);
/**
* @brief Returns prepared statement information
* @param arguments JSON with db_type, username, database
*/
json handle_show_prepared_statements(const json& arguments);
/**
* @brief Returns GTID replication information (MySQL only)
* @param arguments JSON with hostname, port
*/
json handle_show_gtid(const json& arguments);
/**
* @brief Returns ProxySQL cluster node health and sync status
* @param arguments JSON with hostname, include_checksums
*/
json handle_show_cluster(const json& arguments);
// =========================================================================
// Historical Data Tool Handlers
// =========================================================================
/**
* @brief Returns historical CPU and memory usage trends
* @param arguments JSON with metric, interval
*/
json handle_show_system_history(const json& arguments);
/**
* @brief Returns historical query cache performance metrics
* @param arguments JSON with db_type, interval
*/
json handle_show_query_cache_history(const json& arguments);
/**
* @brief Returns historical connection metrics
* @param arguments JSON with db_type, interval, scope, hostgroup, server
*/
json handle_show_connection_history(const json& arguments);
/**
* @brief Returns historical query digest snapshots
* @param arguments JSON with db_type, dump_time, start_time, end_time, filters, limit, offset
*/
json handle_show_query_history(const json& arguments);
// =========================================================================
// Utility Tool Handlers
// =========================================================================
/**
* @brief Flushes query events from buffer to tables (MySQL only)
* @param arguments JSON with destination (memory, disk, both)
*/
json handle_flush_query_log(const json& arguments);
/**
* @brief Returns query event audit log (MySQL only)
* @param arguments JSON with source, filters, limit, offset
*/
json handle_show_query_log(const json& arguments);
/**
* @brief Saves query digest statistics to disk
* @param arguments JSON with db_type
*/
json handle_flush_queries(const json& arguments);
// =========================================================================
// Helper Methods
// =========================================================================
/**
* @brief Execute a SQL query against `GloAdmin->admindb` with Admin-session semantics.
*
* This helper intentionally mirrors the critical part of the Admin SQL execution path
* used by `ProxySQL_Admin::admin_session_handler()`:
*
* 1. Acquire `GloAdmin->sql_query_global_mutex` to serialize access to the internal
* in-memory SQLite databases.
* 2. Optionally invoke `ProxySQL_Admin::GenericRefreshStatistics()` on the current SQL
* statement so `stats_*` tables are repopulated before they are read.
* 3. Execute the SQL statement via `admindb->execute_statement()`.
* 4. Release `sql_query_global_mutex`.
*
* Using this flow avoids stale reads from runtime-populated `stats.*` tables when tools
* access them directly through `admindb`.
*
* @param sql SQL statement to execute. Must not be `nullptr` or empty.
* @param resultset Output pointer receiving the result set on success. Caller owns it.
* @param cols Output pointer receiving the number of columns on success.
* @param refresh_before_query
* If true (default), call `GenericRefreshStatistics()` before executing @p sql.
* Set to false for secondary queries (for example `COUNT(*)` immediately after a
* primary read) to avoid redundant expensive refresh passes.
*
* @return Empty string on success, otherwise a human-readable error message.
*/
std::string execute_admin_query(const char* sql, SQLite3_result** resultset, int* cols, bool refresh_before_query = true);
/**
* @brief Execute a SQL query against GloAdmin->statsdb_disk (historical data)
* @param sql The SQL query to execute
* @param resultset Output pointer for the result set (caller must delete)
* @param cols Output for number of columns
* @return Empty string on success, error message on failure
*/
std::string execute_statsdb_disk_query(const char* sql, SQLite3_result** resultset, int* cols);
/**
* @brief Parse key-value pairs from stats_*_global tables
* @param resultset The result set from a global stats query
* @return Map of variable name to variable value
*/
std::map<std::string, std::string> parse_global_stats(SQLite3_result* resultset);
/**
* @brief Get interval configuration for historical queries
* @param interval User-friendly interval string (e.g. "1h", "1d")
* @param seconds Output: interval in seconds
* @param use_hourly Output: whether to use hourly aggregated tables
* @return true if interval is valid
*/
bool get_interval_config(const std::string& interval, int& seconds, bool& use_hourly);
/**
* @brief Estimate a latency percentile from histogram buckets.
*
* This helper clamps percentile bounds, handles the p0 edge case by
* returning the first non-empty bucket, and uses 64-bit accumulation
* to avoid overflow when counters are very large.
*
* @param buckets Bucket counts (aligned by index with @p thresholds).
* @param thresholds Bucket upper bounds in microseconds.
* @param percentile Requested percentile in `[0.0, 1.0]`.
* @return Estimated percentile latency in microseconds, or 0 when empty/invalid.
*/
int calculate_percentile(const std::vector<int>& buckets, const std::vector<int>& thresholds, double percentile);
public:
/**
* @brief Constructor
* @param handler Pointer to MCP_Threads_Handler
*/
Stats_Tool_Handler(MCP_Threads_Handler* handler);
/**
* @brief Destructor
*/
~Stats_Tool_Handler() override;
// MCP_Tool_Handler interface implementation
json get_tool_list() override;
json get_tool_description(const std::string& tool_name) override;
json execute_tool(const std::string& tool_name, const json& arguments) override;
int init() override;
void close() override;
std::string get_handler_name() const override { return "stats"; }
};
#endif /* PROXYSQLGENAI */
#endif /* CLASS_STATS_TOOL_HANDLER_H */