Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added memory usage monitoring for the prepared statement cache. #4372

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/MySQL_PreparedStatement.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class MySQL_STMT_Global_info {
uint16_t warning_count;
MYSQL_FIELD **fields;
char* first_comment;
uint64_t total_mem_usage;
// struct {
// int cache_ttl;
// int timeout;
Expand All @@ -70,6 +71,7 @@ class MySQL_STMT_Global_info {
MySQL_STMT_Global_info(uint64_t id, char *u, char *s, char *q, unsigned int ql, char *fc, MYSQL_STMT *stmt, uint64_t _h);
void update_metadata(MYSQL_STMT *stmt);
~MySQL_STMT_Global_info();
void calculate_mem_usage();
};


Expand Down Expand Up @@ -264,6 +266,7 @@ class MySQL_STMT_Manager_v14 {
MySQL_STMT_Global_info * add_prepared_statement(char *u, char *s, char *q, unsigned int ql, char *fc, MYSQL_STMT *stmt, bool lock=true);
void get_metrics(uint64_t *c_unique, uint64_t *c_total, uint64_t *stmt_max_stmt_id, uint64_t *cached, uint64_t *s_unique, uint64_t *s_total);
SQLite3_result * get_prepared_statements_global_infos();
void get_memory_usage(uint64_t& prep_stmt_metadata_mem_usage, uint64_t& prep_stmt_backend_mem_usage);
};

#endif /* CLASS_MYSQL_PREPARED_STATEMENT_H */
2 changes: 2 additions & 0 deletions include/proxysql_admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ struct p_admin_gauge {
stack_memory_mysql_threads,
stack_memory_admin_threads,
stack_memory_cluster_threads,
prepare_stmt_metadata_memory_bytes,
prepare_stmt_backend_memory_bytes,
// stmt metrics
stmt_client_active_total,
stmt_client_active_unique,
Expand Down
56 changes: 56 additions & 0 deletions lib/MySQL_PreparedStatement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ MySQL_STMT_Global_info::MySQL_STMT_Global_info(uint64_t id,
char *fc,
MYSQL_STMT *stmt, uint64_t _h) {
pthread_rwlock_init(&rwlock_, NULL);
total_mem_usage = 0;
statement_id = id;
ref_count_client = 0;
ref_count_server = 0;
Expand Down Expand Up @@ -294,6 +295,33 @@ MySQL_STMT_Global_info::MySQL_STMT_Global_info(uint64_t id,
memset(params[i], 0, sizeof(MYSQL_BIND));
}
}

calculate_mem_usage();
}

void MySQL_STMT_Global_info::calculate_mem_usage() {
total_mem_usage = sizeof(MySQL_STMT_Global_info) +
(num_params * (sizeof(MYSQL_BIND) + sizeof(MYSQL_BIND*))) +
(num_columns * (sizeof(MYSQL_FIELD) + sizeof(MYSQL_FIELD*))) +
query_length + 1;// +
//(ref_count_client * 24) +
//(ref_count_server * 24);

if (username) total_mem_usage += strlen(username) + 1;
if (schemaname) total_mem_usage += strlen(schemaname) + 1;
if (first_comment) total_mem_usage += strlen(first_comment) + 1;
if (digest_text) total_mem_usage += strlen(digest_text) + 1;

for (uint16_t i = 0; i < num_columns; i++) {
const MYSQL_FIELD* fd = fields[i];
if (fd->name) total_mem_usage += strlen(fd->name) + 1;
if (fd->org_name) total_mem_usage += strlen(fd->org_name) + 1;
if (fd->table) total_mem_usage += strlen(fd->table) + 1;
if (fd->org_table) total_mem_usage += strlen(fd->org_table) + 1;
if (fd->db) total_mem_usage += strlen(fd->db) + 1;
if (fd->catalog) total_mem_usage += strlen(fd->catalog) + 1;
if (fd->def) total_mem_usage += strlen(fd->def) + 1;
}
}

void MySQL_STMT_Global_info::update_metadata(MYSQL_STMT *stmt) {
Expand Down Expand Up @@ -478,6 +506,7 @@ void MySQL_STMT_Global_info::update_metadata(MYSQL_STMT *stmt) {
}
}
// till here is copied from constructor
calculate_mem_usage();
}
pthread_rwlock_unlock(&rwlock_);
}
Expand Down Expand Up @@ -892,6 +921,33 @@ MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::add_prepared_statement(
return ret;
}


void MySQL_STMT_Manager_v14::get_memory_usage(uint64_t& prep_stmt_metadata_mem_usage, uint64_t& prep_stmt_backend_mem_usage) {
prep_stmt_backend_mem_usage = 0;
prep_stmt_metadata_mem_usage = sizeof(MySQL_STMT_Manager_v14);
rdlock();
prep_stmt_metadata_mem_usage += map_stmt_id_to_info.size() * (sizeof(uint64_t) + sizeof(MySQL_STMT_Global_info*));
prep_stmt_metadata_mem_usage += map_stmt_hash_to_info.size() * (sizeof(uint64_t) + sizeof(MySQL_STMT_Global_info*));
prep_stmt_metadata_mem_usage += free_stmt_ids.size() * (sizeof(uint64_t));
for (const auto& keyval : map_stmt_id_to_info) {
const MySQL_STMT_Global_info* stmt_global_info = keyval.second;
prep_stmt_metadata_mem_usage += stmt_global_info->total_mem_usage;
prep_stmt_metadata_mem_usage += stmt_global_info->ref_count_server *
((stmt_global_info->num_params * sizeof(MYSQL_BIND)) +
(stmt_global_info->num_columns * sizeof(MYSQL_FIELD))) + 16;
prep_stmt_metadata_mem_usage += stmt_global_info->ref_count_client *
((stmt_global_info->num_params * sizeof(MYSQL_BIND)) +
(stmt_global_info->num_columns * sizeof(MYSQL_FIELD))) + 16;

// backend
prep_stmt_backend_mem_usage += stmt_global_info->ref_count_server * (sizeof(MYSQL_STMT) +
56 + //sizeof(MADB_STMT_EXTENSION)
(stmt_global_info->num_params * sizeof(MYSQL_BIND)) +
(stmt_global_info->num_columns * sizeof(MYSQL_BIND)));
}
unlock();
}

void MySQL_STMT_Manager_v14::get_metrics(uint64_t *c_unique, uint64_t *c_total,
uint64_t *stmt_max_stmt_id, uint64_t *cached,
uint64_t *s_unique, uint64_t *s_total) {
Expand Down
1 change: 1 addition & 0 deletions lib/MySQL_Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4326,6 +4326,7 @@ bool MySQL_Session::handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st,
stmt_info->digest_text=strdup(CurrentQuery.QueryParserArgs.digest_text);
stmt_info->digest=CurrentQuery.QueryParserArgs.digest; // copy digest
stmt_info->MyComQueryCmd=CurrentQuery.MyComQueryCmd; // copy MyComQueryCmd
stmt_info->calculate_mem_usage();
}
}
global_stmtid=stmt_info->statement_id;
Expand Down
2 changes: 1 addition & 1 deletion lib/MySQL_Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2280,7 +2280,7 @@ char ** MySQL_Threads_Handler::get_variables_list() {
VariablesPointers_int["max_allowed_packet"] = make_tuple(&variables.max_allowed_packet, 8192, 1024*1024*1024, false);
VariablesPointers_int["max_connections"] = make_tuple(&variables.max_connections, 1, 1000*1000, false);
VariablesPointers_int["max_stmts_per_connection"] = make_tuple(&variables.max_stmts_per_connection, 1, 1024, false);
VariablesPointers_int["max_stmts_cache"] = make_tuple(&variables.max_stmts_cache, 1024, 1024*1024, false);
VariablesPointers_int["max_stmts_cache"] = make_tuple(&variables.max_stmts_cache, 128, 1024*1024, false);
VariablesPointers_int["max_transaction_idle_time"] = make_tuple(&variables.max_transaction_idle_time, 1000, 20*24*3600*1000, false);
VariablesPointers_int["max_transaction_time"] = make_tuple(&variables.max_transaction_time, 1000, 20*24*3600*1000, false);
VariablesPointers_int["query_cache_size_mb"] = make_tuple(&variables.query_cache_size_MB, 0, 1024*10240, false);
Expand Down
36 changes: 36 additions & 0 deletions lib/ProxySQL_Admin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,18 @@ admin_metrics_map = std::make_tuple(
"This is the number of global prepared statements for which proxysql has metadata.",
metric_tags {}
),
std::make_tuple(
p_admin_gauge::prepare_stmt_metadata_memory_bytes,
"prepare_stmt_metadata_memory_bytes",
"Memory used to store meta data related to prepare statements.",
metric_tags{}
),
std::make_tuple(
p_admin_gauge::prepare_stmt_backend_memory_bytes,
"prepare_stmt_backend_memory_bytes",
"Memory used by backend server related to prepare statements.",
metric_tags{}
),
std::make_tuple (
p_admin_gauge::fds_in_use,
"proxysql_fds_in_use",
Expand Down Expand Up @@ -8979,6 +8991,13 @@ void ProxySQL_Admin::p_stats___memory_metrics() {
__sync_fetch_and_add(&GloVars.statuses.stack_memory_cluster_threads, 0);
this->metrics.p_gauge_array[p_admin_gauge::stack_memory_cluster_threads]->Set(stack_memory_cluster_threads);

// proxysql_prepare_statement_memory metric
uint64_t prepare_stmt_metadata_mem_used;
uint64_t prepare_stmt_backend_mem_used;
GloMyStmt->get_memory_usage(prepare_stmt_metadata_mem_used, prepare_stmt_backend_mem_used);
this->metrics.p_gauge_array[p_admin_gauge::prepare_stmt_metadata_memory_bytes]->Set(prepare_stmt_metadata_mem_used);
this->metrics.p_gauge_array[p_admin_gauge::prepare_stmt_backend_memory_bytes]->Set(prepare_stmt_backend_mem_used);

// Update opened file descriptors
int32_t cur_fds = get_open_fds();
if (cur_fds != -1) {
Expand Down Expand Up @@ -9093,6 +9112,23 @@ void ProxySQL_Admin::stats___memory_metrics() {
statsdb->execute(query);
free(query);
}
if (GloMyStmt) {
uint64_t prep_stmt_metadata_mem_usage;
uint64_t prep_stmt_backend_mem_usage;
GloMyStmt->get_memory_usage(prep_stmt_metadata_mem_usage, prep_stmt_backend_mem_usage);
vn = (char*)"prepare_statement_metadata_memory";
sprintf(bu, "%llu", prep_stmt_metadata_mem_usage);
query=(char*)malloc(strlen(a)+strlen(vn)+strlen(bu)+16);
sprintf(query, a, vn, bu);
statsdb->execute(query);
free(query);
vn = (char*)"prepare_statement_backend_memory";
sprintf(bu, "%llu", prep_stmt_backend_mem_usage);
query=(char*)malloc(strlen(a)+strlen(vn)+strlen(bu)+16);
sprintf(query, a, vn, bu);
statsdb->execute(query);
free(query);
}
if (GloQPro) {
unsigned long long mu = 0;
mu = GloQPro->get_mysql_firewall_memory_users_table();
Expand Down
151 changes: 151 additions & 0 deletions test/tap/tests/test_prepare_statement_memory_usage-t.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* @file test_prepare_statement_memory_usage-t.cpp
* @brief Examines the memory consumption of the prepared statement cache..
* @details This test assesses the memory utilization of prepared statement metadata/backend cache memory.
*/

#include <string>
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
#include "proxysql_utils.h"
#include "utils.h"

enum ComparisonOperator {
kEqual = 0x00000001,
kGreaterThan = 0x00000002,
kLessThan = 0x00000004
};

int get_prepare_stmt_mem_usage(MYSQL* admin, uint64_t& prep_stmt_metadata_mem, uint64_t& prep_stmt_backend_mem) {
prep_stmt_metadata_mem = prep_stmt_backend_mem = 0;
MYSQL_QUERY_T(admin, "SELECT variable_name, variable_value FROM stats_memory_metrics WHERE \
variable_name IN ('prepare_statement_metadata_memory', 'prepare_statement_backend_memory')");
MYSQL_RES* myres = mysql_store_result(admin);
while (MYSQL_ROW myrow = mysql_fetch_row(myres)) {
if (strncmp(myrow[0], "prepare_statement_metadata_memory", sizeof("prepare_statement_metadata_memory") - 1) == 0) {
prep_stmt_metadata_mem = std::stoull(myrow[1], nullptr, 10);
} else if (strncmp(myrow[0], "prepare_statement_backend_memory", sizeof("prepare_statement_backend_memory") - 1) == 0) {
prep_stmt_backend_mem = std::stoull(myrow[1], nullptr, 10);
} else {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, "Invalid resultset");
mysql_free_result(myres);
return EXIT_FAILURE;
}
}
mysql_free_result(myres);
return EXIT_SUCCESS;
}

int check_prepare_statement_mem_usage(MYSQL* proxysql_admin, MYSQL* proxysql, const char* query, int prep_stmt_metadata_mem_comp,
int prep_stmt_backend_mem_comp) {
uint64_t old_prep_stmt_metadata_mem, old_prep_stmt_backend_mem;
if (get_prepare_stmt_mem_usage(proxysql_admin, old_prep_stmt_metadata_mem, old_prep_stmt_backend_mem) == EXIT_FAILURE) {
return EXIT_FAILURE;
}
MYSQL_STMT* stmt = mysql_stmt_init(proxysql);
if (!stmt) {
diag("mysql_stmt_init(), out of memory\n");
return EXIT_FAILURE;
}
if (mysql_stmt_prepare(stmt, query, strlen(query))) {
diag("query: %s", query);
diag("mysql_stmt_prepare at line %d failed: %s", __LINE__, mysql_error(proxysql));
mysql_stmt_close(stmt);
return EXIT_FAILURE;
} else {
ok(true, "Prepare succeeded: %s", query);
}
uint64_t new_prep_stmt_metadata_mem, new_prep_stmt_backend_mem;
if (get_prepare_stmt_mem_usage(proxysql_admin, new_prep_stmt_metadata_mem, new_prep_stmt_backend_mem) == EXIT_FAILURE) {
mysql_stmt_close(stmt);
return EXIT_FAILURE;
}
auto fnCompare = [](const uint64_t& val1, const uint64_t& val2, int co) -> bool {
bool res = false;
if ((co & kLessThan) == kLessThan) {
if ((co & kEqual) == kEqual) {
res = (val1 >= val2);
} else {
res = (val1 > val2);
}
} else if ((co & kGreaterThan) == kGreaterThan) {
if ((co & kEqual) == kEqual) {
res = (val1 <= val2);
} else {
res = (val1 < val2);
}
} else {
res = (val1 == val2);
}
return res;
};

ok(fnCompare(old_prep_stmt_metadata_mem, new_prep_stmt_metadata_mem, prep_stmt_metadata_mem_comp),
"Memory usage check [%d]. 'prepare_statement_metadata_memory':[%lu] [%lu]", prep_stmt_metadata_mem_comp,
old_prep_stmt_metadata_mem, new_prep_stmt_metadata_mem);

ok(fnCompare(old_prep_stmt_backend_mem, new_prep_stmt_backend_mem, prep_stmt_backend_mem_comp),
"Memory usage check [%d]. 'prepare_statement_backend_memory':[%lu] [%lu]", prep_stmt_backend_mem_comp,
old_prep_stmt_backend_mem, new_prep_stmt_backend_mem);

mysql_stmt_close(stmt);
return EXIT_SUCCESS;
}

int main(int argc, char** argv) {

CommandLine cl;

if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return -1;
}

plan(4 * // query
3 // checks
);

// Initialize Admin connection
MYSQL* proxysql_admin = mysql_init(NULL);
if (!proxysql_admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
return -1;
}
// Connnect to ProxySQL Admin
if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
return exit_status();
}

// Initialize ProxySQL connection
MYSQL* proxysql = mysql_init(NULL);
if (!proxysql) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql));
return exit_status();
}

// Connect to ProxySQL
if (!mysql_real_connect(proxysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql));
return exit_status();
}

if (check_prepare_statement_mem_usage(proxysql_admin, proxysql, "SELECT 1", kGreaterThan, (kGreaterThan | kEqual)) == EXIT_FAILURE)
goto __cleanup;

if (check_prepare_statement_mem_usage(proxysql_admin, proxysql, "SELECT 2", kGreaterThan, (kGreaterThan | kEqual)) == EXIT_FAILURE)
goto __cleanup;

if (check_prepare_statement_mem_usage(proxysql_admin, proxysql, "SELECT 1", kGreaterThan, kEqual) == EXIT_FAILURE)
goto __cleanup;

if (check_prepare_statement_mem_usage(proxysql_admin, proxysql, "SELECT 2", kGreaterThan, kEqual) == EXIT_FAILURE)
goto __cleanup;

__cleanup:
mysql_close(proxysql);
mysql_close(proxysql_admin);

return exit_status();
}
Loading