From bc0133107a38bc3fe7403475ef44860ad6993933 Mon Sep 17 00:00:00 2001 From: Wazir Ahmed Date: Mon, 21 Apr 2025 01:38:21 +0530 Subject: [PATCH] feat(stats): Purge query digest based on last seen - Add handler for purge query digest command - Handle last_seen argument in purge query digest operations - Make purge logic common for both sync & async operations Signed-off-by: Wazir Ahmed --- include/gen_utils.h | 2 + include/query_processor.h | 6 +- lib/Admin_Bootstrap.cpp | 3 - lib/Admin_FlushVariables.cpp | 3 - lib/Admin_Handler.cpp | 96 ++++++++++++-- lib/ProxySQL_Admin.cpp | 4 - lib/ProxySQL_Admin_Tests.cpp | 11 +- lib/ProxySQL_Admin_Tests2.cpp | 11 +- lib/QP_query_digest_stats.cpp | 9 +- lib/Query_Processor.cpp | 238 ++++++++++++++++++++++++---------- lib/gen_utils.cpp | 39 ++++++ 11 files changed, 309 insertions(+), 113 deletions(-) diff --git a/include/gen_utils.h b/include/gen_utils.h index 233e37a0b..7b7bef561 100644 --- a/include/gen_utils.h +++ b/include/gen_utils.h @@ -339,6 +339,8 @@ bool mywildcmp(const char *p, const char *str); std::string trim(const std::string& s); char* escape_string_single_quotes_and_backslashes(char* input, bool free_it); const char* escape_string_backslash_spaces(const char* input); +time_t monotonic_time_to_realtime(time_t mt); +time_t realtime_to_monotonic_time(time_t rt); /** * @brief Helper function that converts a MYSQL_RES into a 'SQLite3_result'. diff --git a/include/query_processor.h b/include/query_processor.h index d9a4bd075..b67a39d06 100644 --- a/include/query_processor.h +++ b/include/query_processor.h @@ -282,7 +282,7 @@ class Query_Processor { std::pair get_query_digests_v2(const bool use_resultset = true); std::pair get_query_digests_reset_v2(const bool copy, const bool use_resultset = true); void get_query_digests_reset(umap_query_digest* uqd, umap_query_digest_text* uqdt); - unsigned long long purge_query_digests(bool async_purge, bool parallel, char** msg); + unsigned long long purge_query_digests(bool async_purge, bool parallel, time_t last_seen = 0); void save_query_rules(SQLite3_result* resultset); @@ -394,8 +394,8 @@ class Query_Processor { DEFINE_HAS_METHOD_STRUCT(query_parser_first_comment_extended); DEFINE_HAS_METHOD_STRUCT(process_query_extended); - unsigned long long purge_query_digests_async(char** msg); - unsigned long long purge_query_digests_sync(bool parallel); + unsigned long long purge_query_digests_async(time_t last_seen = 0); + unsigned long long purge_query_digests_sync(bool parallel, time_t last_seen = 0); /** * @brief Searches for a matching rule in the supplied map, returning the destination hostgroup. diff --git a/lib/Admin_Bootstrap.cpp b/lib/Admin_Bootstrap.cpp index 67dbdda97..be2cfaee0 100644 --- a/lib/Admin_Bootstrap.cpp +++ b/lib/Admin_Bootstrap.cpp @@ -112,9 +112,6 @@ extern struct MHD_Daemon *Admin_HTTP_Server; extern ProxySQL_Statistics *GloProxyStats; -template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); - extern char *ssl_key_fp; extern char *ssl_cert_fp; extern char *ssl_ca_fp; diff --git a/lib/Admin_FlushVariables.cpp b/lib/Admin_FlushVariables.cpp index 4efefb9f2..3f76b2205 100644 --- a/lib/Admin_FlushVariables.cpp +++ b/lib/Admin_FlushVariables.cpp @@ -112,9 +112,6 @@ extern struct MHD_Daemon *Admin_HTTP_Server; extern ProxySQL_Statistics *GloProxyStats; -template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); - extern char *ssl_key_fp; extern char *ssl_cert_fp; extern char *ssl_ca_fp; diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index b653f0bef..a38b8ccee 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -114,9 +114,6 @@ extern struct MHD_Daemon *Admin_HTTP_Server; extern ProxySQL_Statistics *GloProxyStats; -template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); - extern char *ssl_key_fp; extern char *ssl_cert_fp; extern char *ssl_ca_fp; @@ -276,6 +273,17 @@ const std::vector LOAD_COREDUMP_FROM_MEMORY = { "LOAD COREDUMP TO RUNTIME" , "LOAD COREDUMP TO RUN" }; +const std::vector CMD_PREFIX_PURGE_QUERY_DIGESTS = { + "PURGE TABLE stats.stats_mysql_query_digest TO ", + "PURGE TABLE stats_mysql_query_digest TO ", + "PURGE stats.stats_mysql_query_digest TO ", + "PURGE stats_mysql_query_digest TO ", + "PURGE TABLE stats.stats_pgsql_query_digest TO ", + "PURGE TABLE stats_pgsql_query_digest TO ", + "PURGE stats.stats_pgsql_query_digest TO ", + "PURGE stats_pgsql_query_digest TO ", +}; + extern unordered_map, vector>> load_save_disk_commands; bool is_admin_command_or_alias(const std::vector& cmds, char *query_no_space, int query_no_space_length) { @@ -288,6 +296,17 @@ bool is_admin_command_or_alias(const std::vector& cmds, char *query return false; } +const char * match_command_prefix(const std::vector& cmd_prefix, char *query, int query_len) { + for (auto &prefix : cmd_prefix) { + if ((unsigned int) query_len >= prefix.length() + && !strncasecmp(prefix.c_str(), query, prefix.length())) + { + return prefix.c_str(); + } + } + + return nullptr; +} template bool FlushCommandWrapper(S* sess, const std::vector& cmds, char *query_no_space, int query_no_space_length, const string& name, const string& direction) { @@ -322,6 +341,33 @@ bool FlushCommandWrapper(S* sess, const string& modname, char *query_no_space, i return false; } +std::tuple parse_command_purge_query_digests(char *query, int query_len) { + bool match = false; + enum SERVER_TYPE server_type = SERVER_TYPE_MYSQL; + time_t last_seen = 0; + + const char *prefix = match_command_prefix(CMD_PREFIX_PURGE_QUERY_DIGESTS, query, query_len); + if (prefix) { + match = true; + + if (strstr(prefix, "_pgsql_") != nullptr) { + server_type = SERVER_TYPE_PGSQL; + } + + // parse timestamp + mf_unique_ptr ts_str(strdup(query + strlen(prefix))); + char *ts_end = nullptr; + long long ts = strtoll(trim_spaces_in_place(ts_str.get()), &ts_end, 10); + + // ts_str should only contain digits and respresent a valid timestamp + if ((*ts_end == 0) && (ts > 0)) { + last_seen = realtime_to_monotonic_time(ts); + } + } + + return std::make_tuple(match, server_type, last_seen); +} + template bool admin_handler_command_kill_connection(char *query_no_space, unsigned int query_no_space_length, S* sess, ProxySQL_Admin *pa) { uint32_t id=atoi(query_no_space+16); @@ -2498,10 +2544,8 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { SPA->admindb->execute("DELETE FROM stats.stats_mysql_query_digest_reset"); SPA->vacuum_stats(true); // purge the digest map, asynchronously, in single thread - char *msg = NULL; - int r1 = ProxySQL_Test___PurgeDigestTable(true, false, &msg); - SPA->send_ok_msg_to_client(sess, msg, r1, query_no_space); - free(msg); + int r1 = GloMyQPro->purge_query_digests(true, false); + SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); run_query=false; goto __run_query; } @@ -2534,16 +2578,45 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { SPA->admindb->execute("DELETE FROM stats.stats_pgsql_query_digest_reset"); SPA->vacuum_stats(true); // purge the digest map, asynchronously, in single thread - char* msg = NULL; - int r1 = ProxySQL_Test___PurgeDigestTable(true, false, &msg); - SPA->send_ok_msg_to_client(sess, msg, r1, query_no_space); - free(msg); + int r1 = GloPgQPro->purge_query_digests(true, false); + SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); run_query = false; goto __run_query; } } } } + + // handles 'PURGE stats_mysql_query_digest TO '. + // any entry in stats_mysql_query_digest where last_seen is less than will be deleted. + if (!strncasecmp("PURGE ", query_no_space, strlen("PURGE ")) + && sess->session_type == PROXYSQL_SESSION_ADMIN + ) { + auto result = parse_command_purge_query_digests(query_no_space, query_no_space_length); + bool match = std::get<0>(result); + + if (match == true) { + int ret = 0; + enum SERVER_TYPE type = std::get<1>(result); + time_t last_seen = std::get<2>(result); + + if (last_seen > 0) { + if (type == SERVER_TYPE_MYSQL) { + ret = GloMyQPro->purge_query_digests(true, false, last_seen); + } else if (type == SERVER_TYPE_PGSQL) { + ret = GloPgQPro->purge_query_digests(true, false, last_seen); + } + + pa->send_ok_msg_to_client(sess, NULL, ret, query_no_space); + } else { + pa->send_error_msg_to_client(sess, "Invalid timestamp"); + } + + run_query = false; + goto __run_query; + } + } + #ifdef DEBUG /** * @brief Handles the 'PROXYSQL_SIMULATOR' command. Performing the operation specified in the payload @@ -3837,4 +3910,3 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { // Explicitly instantiate the required template class and member functions template void admin_session_handler(MySQL_Session* sess, void *_pa, PtrSize_t *pkt); template void admin_session_handler(PgSQL_Session* sess, void *_pa, PtrSize_t *pkt); - diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index a2fcfb23b..2156e210b 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -114,9 +114,6 @@ struct MHD_Daemon *Admin_HTTP_Server; extern ProxySQL_Statistics *GloProxyStats; -template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); - extern char *ssl_key_fp; extern char *ssl_cert_fp; extern char *ssl_ca_fp; @@ -8465,4 +8462,3 @@ void ProxySQL_Admin::enable_replicationlag_testing() { mysql_servers_wrunlock(); } #endif // TEST_REPLICATIONLAG - diff --git a/lib/ProxySQL_Admin_Tests.cpp b/lib/ProxySQL_Admin_Tests.cpp index c7fd89831..4dc7af3d2 100644 --- a/lib/ProxySQL_Admin_Tests.cpp +++ b/lib/ProxySQL_Admin_Tests.cpp @@ -1,3 +1,4 @@ +#include #include // std::cout #include // std::stringstream #include @@ -56,12 +57,12 @@ bool ProxySQL_Test___Refresh_MySQL_Variables(unsigned int cnt) { } template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg) { +int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, time_t last_seen) { int r = 0; if constexpr (ST == SERVER_TYPE_MYSQL) { - r = GloMyQPro->purge_query_digests(async_purge, parallel, msg); + r = GloMyQPro->purge_query_digests(async_purge, parallel, last_seen); } else if constexpr (ST == SERVER_TYPE_PGSQL) { - r = GloPgQPro->purge_query_digests(async_purge, parallel, msg); + r = GloPgQPro->purge_query_digests(async_purge, parallel, last_seen); } return r; } @@ -147,5 +148,5 @@ int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n) { return n*1000; } -template int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char** msg); -template int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char** msg); +template int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, time_t last_seen); +template int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, time_t last_seen); diff --git a/lib/ProxySQL_Admin_Tests2.cpp b/lib/ProxySQL_Admin_Tests2.cpp index d14edec39..55bfb4937 100644 --- a/lib/ProxySQL_Admin_Tests2.cpp +++ b/lib/ProxySQL_Admin_Tests2.cpp @@ -43,7 +43,7 @@ static void init_rand_del() { int ProxySQL_Test___GetDigestTable(bool reset, bool use_swap); bool ProxySQL_Test___Refresh_MySQL_Variables(unsigned int cnt); template -int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, char **msg); +int ProxySQL_Test___PurgeDigestTable(bool async_purge, bool parallel, time_t last_seen); int ProxySQL_Test___GenerateRandomQueryInDigestTable(int n); void ProxySQL_Admin::map_test_mysql_firewall_whitelist_rules_cleanup() { @@ -590,21 +590,20 @@ void ProxySQL_Admin::ProxySQL_Test_Handler(ProxySQL_Admin *SPA, S* sess, char *q break; case 4: // purge the digest map, synchronously, in single thread - r1 = ProxySQL_Test___PurgeDigestTable(false, false, NULL); + r1 = ProxySQL_Test___PurgeDigestTable(false, false, test_arg1); SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); run_query=false; break; case 5: // purge the digest map, synchronously, in multiple threads - r1 = ProxySQL_Test___PurgeDigestTable(false, true, NULL); + r1 = ProxySQL_Test___PurgeDigestTable(false, true, test_arg1); SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); run_query=false; break; case 6: // purge the digest map, asynchronously, in single thread - r1 = ProxySQL_Test___PurgeDigestTable(true, false, &msg); - SPA->send_ok_msg_to_client(sess, msg, r1, query_no_space); - free(msg); + r1 = ProxySQL_Test___PurgeDigestTable(true, false, test_arg1); + SPA->send_ok_msg_to_client(sess, NULL, r1, query_no_space); run_query=false; break; case 7: diff --git a/lib/QP_query_digest_stats.cpp b/lib/QP_query_digest_stats.cpp index ff0750a79..3d4fd8268 100644 --- a/lib/QP_query_digest_stats.cpp +++ b/lib/QP_query_digest_stats.cpp @@ -1,3 +1,4 @@ +#include "gen_utils.h" #include "query_processor.h" // reverse: reverse string s in place @@ -151,16 +152,13 @@ char **QP_query_digest_stats::get_row(umap_query_digest_text *digest_text_umap, my_itoa(qdsp->count_star, count_star); pta[5]=qdsp->count_star; - time_t __now; - time(&__now); - unsigned long long curtime=monotonic_time(); time_t seen_time; - seen_time= __now - curtime/1000000 + first_seen/1000000; + seen_time=monotonic_time_to_realtime(first_seen); //sprintf(qdsp->first_seen,"%ld", seen_time); my_itoa(qdsp->first_seen, seen_time); pta[6]=qdsp->first_seen; - seen_time= __now - curtime/1000000 + last_seen/1000000; + seen_time=monotonic_time_to_realtime(last_seen); //sprintf(qdsp->last_seen,"%ld", seen_time); my_itoa(qdsp->last_seen, seen_time); pta[7]=qdsp->last_seen; @@ -187,4 +185,3 @@ char **QP_query_digest_stats::get_row(umap_query_digest_text *digest_text_umap, pta[13]=qdsp->rows_sent; return pta; } - diff --git a/lib/Query_Processor.cpp b/lib/Query_Processor.cpp index eb01382e8..b6b3c4004 100644 --- a/lib/Query_Processor.cpp +++ b/lib/Query_Processor.cpp @@ -620,123 +620,219 @@ void * get_query_digests_parallel(void *_arg) { return NULL; } -void * purge_query_digests_parallel(void *_arg) { - get_query_digests_parallel_args *arg = (get_query_digests_parallel_args *)_arg; - unsigned long long i = 0; - unsigned long long r = 0; - unsigned long long m = arg->m; - set_thread_name("PurgeQueryDgest", GloVars.set_thread_name); - for (std::unordered_map::iterator it=arg->gu->begin(); it!=arg->gu->end(); ++it) { - if ((i%DIGEST_STATS_FAST_THREADS)==m) { - QP_query_digest_stats *qds=(QP_query_digest_stats *)it->second; +struct purge_query_digests_args { + umap_query_digest *digest_umap = nullptr; + umap_query_digest_text *digest_text_umap = nullptr; + std::function::iterator it)> match_delete_entry = nullptr; + bool erase_umap = true; + + // only used for synchronous multi-threaded purge operation + int scan_idx = -1; + int digest_deleted = 0; +}; + +unsigned long long purge_query_digest_entry(purge_query_digests_args* args) { + unsigned long long digest_deleted = 0; + + umap_query_digest *digest_umap = args->digest_umap; + umap_query_digest_text *digest_text_umap = args->digest_text_umap; + + for (auto it = digest_umap->begin(); it != digest_umap->end();) { + // by default, delete all entries + bool delete_entry = true; + + if (args->match_delete_entry) { + delete_entry = args->match_delete_entry(it); + } + + if (delete_entry) { + auto text_it = digest_text_umap->find(it->first); + if (text_it != digest_text_umap->end()) { + free(text_it->second); + + if (args->erase_umap) { + digest_text_umap->erase(text_it); + } + } + + QP_query_digest_stats *qds = (QP_query_digest_stats *)it->second; delete qds; - r++; + + digest_deleted++; } - i++; - } - arg->ret = r; - i = 0; - for (std::unordered_map::iterator it=arg->gtu->begin(); it!=arg->gtu->end(); ++it) { - if ((i%DIGEST_STATS_FAST_THREADS)==m) { - free(it->second); + + if (delete_entry && args->erase_umap) { + it = digest_umap->erase(it); + } else { + it++; } } - return NULL; + + return digest_deleted; +} + + +void * purge_query_digests_parallel(void *_args) { + set_thread_name("PurgeQueryDgest", GloVars.set_thread_name); + + auto args = (purge_query_digests_args *)_args; + args->digest_deleted = purge_query_digest_entry(args); + + return nullptr; } template -unsigned long long Query_Processor::purge_query_digests(bool async_purge, bool parallel, char **msg) { +unsigned long long Query_Processor::purge_query_digests(bool async_purge, bool parallel, time_t last_seen) { unsigned long long ret = 0; if (async_purge) { - ret = purge_query_digests_async(msg); + ret = purge_query_digests_async(last_seen); } else { - ret = purge_query_digests_sync(parallel); + ret = purge_query_digests_sync(parallel, last_seen); } return ret; } template -unsigned long long Query_Processor::purge_query_digests_async(char **msg) { - unsigned long long ret = 0; +unsigned long long Query_Processor::purge_query_digests_async(time_t last_seen) { umap_query_digest digest_umap_aux; umap_query_digest_text digest_text_umap_aux; + pthread_rwlock_wrlock(&digest_rwlock); digest_umap.swap(digest_umap_aux); digest_text_umap.swap(digest_text_umap_aux); pthread_rwlock_unlock(&digest_rwlock); - unsigned long long curtime1=monotonic_time(); - size_t map1_size = digest_umap_aux.size(); - size_t map2_size = digest_text_umap_aux.size(); - ret = map1_size + map2_size; + unsigned long long digest_deleted = 0; + unsigned long long curtime1 = monotonic_time(); + bool selective_purge = (last_seen > 0); - for ( - std::unordered_map::iterator it = digest_umap_aux.begin(); - it != digest_umap_aux.end(); - ++it - ) { - QP_query_digest_stats *qds = (QP_query_digest_stats *)it->second; - delete qds; - } - digest_umap_aux.clear(); - for (std::unordered_map::iterator it=digest_text_umap_aux.begin(); it!=digest_text_umap_aux.end(); ++it) { - free(it->second); + purge_query_digests_args args; + args.digest_umap = &digest_umap_aux; + args.digest_text_umap = &digest_text_umap_aux; + + if (selective_purge) { + args.match_delete_entry = [&](std::unordered_map::iterator it) { + QP_query_digest_stats *qds = (QP_query_digest_stats *)it->second; + return (last_seen >= qds->last_seen); + }; } - digest_text_umap_aux.clear(); + digest_deleted = purge_query_digest_entry(&args); + + size_t map_size = digest_umap.size(); + if (map_size >= DIGEST_STATS_FAST_MINSIZE) { + const char *cmd = (selective_purge) ? "PURGE" : "TRUNCATE"; + + unsigned long long curtime2 = monotonic_time(); + curtime1 = curtime1 / 1000; + curtime2 = curtime2 / 1000; - if (map1_size >= DIGEST_STATS_FAST_MINSIZE) { - unsigned long long curtime2=monotonic_time(); - curtime1 = curtime1/1000; - curtime2 = curtime2/1000; if constexpr (std::is_same_v) { - proxy_info("TRUNCATE stats_mysql_query_digest: (not locked) %llums to remove %lu entries\n", curtime2 - curtime1, map1_size); + proxy_info("%s stats_mysql_query_digest: (not locked) %llums to remove %lu entries\n", cmd, curtime2 - curtime1, map_size); } else if constexpr (std::is_same_v) { - proxy_info("TRUNCATE stats_pgsql_query_digest: (not locked) %llums to remove %lu entries\n", curtime2 - curtime1, map1_size); + proxy_info("%s stats_pgsql_query_digest: (not locked) %llums to remove %lu entries\n", cmd, curtime2 - curtime1, map_size); } } - return ret; + + if (selective_purge) { + pthread_rwlock_wrlock(&digest_rwlock); + digest_umap_aux.swap(digest_umap); + + // merge stats entries in the aux map with digest_umap + for (const auto& aux_entry : digest_umap_aux) { + unsigned long aux_digest = aux_entry.first; + QP_query_digest_stats *aux_qds = (QP_query_digest_stats *)aux_entry.second; + + auto it = digest_umap.find(aux_digest); + if (it != digest_umap.end()) { + QP_query_digest_stats *digest_qds = (QP_query_digest_stats *)it->second; + + digest_qds->add_time( + aux_qds->min_time, + aux_qds->last_seen, + aux_qds->rows_affected, + aux_qds->rows_sent, + aux_qds->count_star + ); + + delete aux_qds; + } else { + digest_umap.insert(aux_entry); + } + } + + // merge text entries in the aux map with digest_text_umap + digest_text_umap.insert(digest_text_umap_aux.begin(), digest_text_umap_aux.end()); + + pthread_rwlock_unlock(&digest_rwlock); + digest_umap_aux.clear(); + digest_text_umap_aux.clear(); + } + + return digest_deleted; } template -unsigned long long Query_Processor::purge_query_digests_sync(bool parallel) { - unsigned long long ret = 0; +unsigned long long Query_Processor::purge_query_digests_sync(bool parallel, time_t last_seen) { + unsigned long long digest_deleted = 0; + pthread_rwlock_wrlock(&digest_rwlock); + size_t map_size = digest_umap.size(); - if (parallel && map_size >= DIGEST_STATS_FAST_MINSIZE) { // parallel purge - int n=DIGEST_STATS_FAST_THREADS; - get_query_digests_parallel_args args[n]; - for (int i=0; i= DIGEST_STATS_FAST_MINSIZE) + ) { + pthread_t tid[DIGEST_STATS_FAST_THREADS]; + purge_query_digests_args args[DIGEST_STATS_FAST_THREADS]; + + for (int i = 0; i < DIGEST_STATS_FAST_THREADS; i++) { + args[i].digest_umap = &digest_umap; + args[i].digest_text_umap = &digest_text_umap; + args[i].erase_umap = false; + args[i].scan_idx = -1; + + args[i].match_delete_entry = [&, i](std::unordered_map::iterator it) { + args[i].scan_idx++; + return (args[i].scan_idx % DIGEST_STATS_FAST_THREADS == i); + }; } - for (int i=0; i::iterator it=digest_umap.begin(); it!=digest_umap.end(); ++it) { - QP_query_digest_stats *qds=(QP_query_digest_stats *)it->second; - delete qds; - ret++; - } - for (std::unordered_map::iterator it=digest_text_umap.begin(); it!=digest_text_umap.end(); ++it) { - free(it->second); + purge_query_digests_args args; + args.digest_umap = &digest_umap; + args.digest_text_umap = &digest_text_umap; + + if (last_seen > 0) { + args.match_delete_entry = [&](std::unordered_map::iterator it) { + QP_query_digest_stats *qds = (QP_query_digest_stats *)it->second; + return (last_seen >= qds->last_seen); + }; } + + digest_deleted = purge_query_digest_entry(&args); } - digest_umap.erase(digest_umap.begin(),digest_umap.end()); - digest_text_umap.erase(digest_text_umap.begin(),digest_text_umap.end()); + pthread_rwlock_unlock(&digest_rwlock); - return ret; + + return digest_deleted; } template diff --git a/lib/gen_utils.cpp b/lib/gen_utils.cpp index 81c78d167..e141e0a64 100644 --- a/lib/gen_utils.cpp +++ b/lib/gen_utils.cpp @@ -350,3 +350,42 @@ const char* escape_string_backslash_spaces(const char* input) { *(p++) = '\0'; return output; } + +/** + * Converts monotonic clock time (in μs) to realtime clock time (in sec). + * This function assumes that during bootup initial value for the monotonic + * clock in the operation system will be set based on realtime clock value. + * This function should only be used in non-critical business logic, such as + * input and output conversion. + * + * @param mt monotonic clock value in microseconds. + * @return realtime clock time in seconds. + */ +time_t monotonic_time_to_realtime(time_t mt) { + time_t mt_now = monotonic_time() / 1000000; + mt = mt / 1000000; + + time_t rt_now; + time(&rt_now); + + return (rt_now - mt_now + mt); +} + +/** + * Converts realtime clock time (in sec) to monotonic clock time (in μs). + * This function assumes that during bootup initial value for the monotonic + * clock in the operation system will be set based on realtime clock value. + * This function should only be used in non-critical business logic, such as + * input and output conversion. + * + * @param rt realtime clock time in seconds. + * @return monotonic clock value in microseconds. + */ +time_t realtime_to_monotonic_time(time_t rt) { + time_t mt_now = monotonic_time() / 1000000; + + time_t rt_now; + time(&rt_now); + + return ((mt_now - rt_now + rt) * 1000000); +}