diff --git a/include/MySQL_Query_Cache.h b/include/MySQL_Query_Cache.h index 6bd822fc2..276683b2a 100644 --- a/include/MySQL_Query_Cache.h +++ b/include/MySQL_Query_Cache.h @@ -3,7 +3,7 @@ #include "proxysql.h" #include "cpp.h" -#include "query_cache.hpp" +#include "Query_Cache.h" typedef struct _MySQL_QC_entry : public QC_entry_t { uint32_t column_eof_pkt_offset; diff --git a/include/PgSQL_Query_Cache.h b/include/PgSQL_Query_Cache.h index 2b695f28b..721040003 100644 --- a/include/PgSQL_Query_Cache.h +++ b/include/PgSQL_Query_Cache.h @@ -3,7 +3,7 @@ #include "proxysql.h" #include "cpp.h" -#include "query_cache.hpp" +#include "Query_Cache.h" typedef struct _PgSQL_QC_entry : public QC_entry_t {} PgSQL_QC_entry_t; diff --git a/include/Query_Cache.h b/include/Query_Cache.h new file mode 100644 index 000000000..7ec91e209 --- /dev/null +++ b/include/Query_Cache.h @@ -0,0 +1,219 @@ +#ifndef __CLASS_QUERY_CACHE_H +#define __CLASS_QUERY_CACHE_H +#include "btree_map.h" +#include "proxysql.h" +#include "cpp.h" +#include "prometheus/counter.h" +#include "prometheus/gauge.h" + +#define EXPIRE_DROPIT 0 +#define SHARED_QUERY_CACHE_HASH_TABLES 32 +#define HASH_EXPIRE_MAX 3600*24*365*10 +#define DEFAULT_purge_loop_time 500000 +#define DEFAULT_purge_total_time 10000000 +#define DEFAULT_purge_threshold_pct_min 3 +#define DEFAULT_purge_threshold_pct_max 90 + +struct p_qc_counter { + enum metric { + query_cache_count_get = 0, + query_cache_count_get_ok, + query_cache_count_set, + query_cache_bytes_in, + query_cache_bytes_out, + query_cache_purged, + query_cache_entries, + __size + }; +}; + +struct p_qc_gauge { + enum metric { + query_cache_memory_bytes = 0, + __size + }; +}; + +struct qc_metrics_map_idx { + enum index { + counters = 0, + gauges + }; +}; + +class KV_BtreeArray; +class MySQL_Query_Cache; +class PgSQL_Query_Cache; +struct _MySQL_QC_entry; +struct _PgSQL_QC_entry; +typedef struct _MySQL_QC_entry MySQL_QC_entry_t; +typedef struct _PgSQL_QC_entry PgSQL_QC_entry_t; + +typedef struct _QC_entry { + uint64_t key; // primary key + unsigned char *value; // pointer to value + uint32_t length; // length of the value + uint32_t klen; // length of the key : FIXME: not sure if still relevant + uint64_t create_ms; // when the entry was created, monotonic, millisecond granularity + uint64_t expire_ms; // when the entry will expire, monotonic , millisecond granularity + uint64_t access_ms; // when the entry was read last , monotonic , millisecond granularity + bool refreshing; // true when a client will hit the backend to refresh the entry + void* kv; // pointer to the KV_BtreeArray where the entry is stored (used for troubleshooting) + //struct _QC_entry* self; // pointer to itself +} QC_entry_t; + +template +class Query_Cache { + static_assert(std::is_same_v || std::is_same_v, + "Invalid QC_DERIVED Query Cache type"); + using TypeQCEntry = typename std::conditional, + MySQL_QC_entry_t, PgSQL_QC_entry_t>::type; + + /*The KV_BtreeArray class is a container class that represents a key-value store + implemented using a B-tree data structure. It provides methods for performing various + operations on the key-value pairs stored in the container.*/ + class KV_BtreeArray { + public: + /** + * Constructs a new KV_BtreeArray object with the given entry size. + * + * @param entry_size The size of each entry in the key-value store. + */ + KV_BtreeArray(unsigned int entry_size); + + /** + * Destructs the KV_BtreeArray object. + */ + ~KV_BtreeArray(); + + /** + * Retrieves the entry with the given key from the key-value store in the KV_BtreeArray. + * If an entry with the given key exists in the store, a weak pointer to the entry will be returned. + * If an entry with the given key does not exist in the store, an empty weak pointer will be returned. + * + * @param key The key of the entry to be retrieved. + * @return A weak pointer to the entry with the given key, or an empty weak pointer if the entry does not exist. + */ + std::weak_ptr lookup(uint64_t key); + + /** + * Replaces the entry with the given key in the key-value store in the KV_BtreeArray. + * If an entry with the given key already exists in the store, it will be replaced with the new entry. + * If an entry with the given key does not exist in the store, the new entry will be added to the store. + * + * @param key The key of the entry to be replaced. + * @param entry The new entry to be added to the store. + * @return True if the entry was successfully replaced, false otherwise. (currently always true) + */ + bool replace(uint64_t key, QC_entry_t* entry); + + /** + * Clears the key-value store in the KV_BtreeArray. + * If release_entries is set to true, the entries in the store will be released. + * + * @param release_entries A flag indicating whether to release the entries in the store or not. + */ + void clear(bool release_entries = false); + + /** + * Purges entries from the key-value store in the KV_BtreeArray based on the given criteria. + * If aggressive is set to true, the function will remove entries based on the access time + * of the entries, otherwise it will remove entries based on the expiration time of the entries. + * + * @param QCnow_ms The current time in milliseconds. + * @param aggressive A flag indicating whether to perform aggressive purging or not. + */ + void purge_some(uint64_t QCnow_ms, bool aggressive); + + /** + * Retrieves the total data size of the key-value store in the KV_BtreeArray. + * The data size is calculated by multiplying the number of entries in the store + * with the size of each entry, including the size of the value, pointers, and metadata. + * + * @return The total data size of the key-value store. + */ + uint64_t get_data_size() const; + + /** + * Retrieves the number of entries in the key-value store in the KV_BtreeArray. + * + * @return The number of entries in the key-value store. + */ + inline int count() const { return bt_map.size(); }; + + private: + pthread_rwlock_t lock; + std::vector> entries; + using BtMap_cache = btree::btree_map>; + BtMap_cache bt_map; + const unsigned int qc_entry_size; + + inline void rdlock() { pthread_rwlock_rdlock(&lock); } + inline void wrlock() { pthread_rwlock_wrlock(&lock); } + inline void unlock() { pthread_rwlock_unlock(&lock); } + + /** + * Adds the given entry to the entries vector of the KV_BtreeArray. + * If the capacity of the entries vector is not enough to accommodate the new entry, + * it will be resized to the nearest power of 2 greater than the current size. + * + * @param entry The entry to be added to the entries vector.d: + */ + void add_to_entries(const std::shared_ptr& entry); + + /** + * Removes the entry at the given index from the entries vector of the KV_BtreeArray. + * If the index is out of bounds, this function does nothing. + * + * @param index The index of the entry to be removed from the entries vector. + */ + void remove_from_entries_by_index(size_t index); + }; + +public: + static bool shutting_down; + static pthread_t purge_thread_id; + constexpr static unsigned int purge_loop_time = DEFAULT_purge_loop_time; + + void print_version(); + uint64_t flush(); + void p_update_metrics(); + SQLite3_result* SQL3_getStats(); + void purgeHash(uint64_t max_memory_size); + +protected: + Query_Cache(); + ~Query_Cache(); + + bool set(QC_entry_t* entry, uint64_t user_hash, const unsigned char *kp, uint32_t kl, unsigned char *vp, + uint32_t vl, uint64_t create_ms, uint64_t curtime_ms, uint64_t expire_ms); + std::shared_ptr get(uint64_t user_hash, const unsigned char* kp, const uint32_t kl, + uint64_t curtime_ms, uint64_t cache_ttl); + + constexpr static unsigned int purge_total_time = DEFAULT_purge_total_time; + constexpr static unsigned int purge_threshold_pct_min = DEFAULT_purge_threshold_pct_min; + constexpr static unsigned int purge_threshold_pct_max = DEFAULT_purge_threshold_pct_max; + + static uint64_t Glo_cntSet; + static uint64_t Glo_cntGet; + static uint64_t Glo_cntGetOK; + static uint64_t Glo_num_entries; + static uint64_t Glo_dataIN; + static uint64_t Glo_dataOUT; + static uint64_t Glo_cntPurge; + static uint64_t Glo_size_values; + static uint64_t Glo_total_freed_memory; + +private: + KV_BtreeArray* KVs[SHARED_QUERY_CACHE_HASH_TABLES]; + uint64_t get_data_size_total(); + unsigned int current_used_memory_pct(uint64_t max_memory_size); + void purgeHash(uint64_t QCnow_ms, unsigned int curr_pct); + + struct { + std::array p_counter_array{}; + std::array p_gauge_array{}; + } metrics; +}; + +#endif /* __CLASS_QUERY_CACHE_H */ diff --git a/include/query_cache.hpp b/include/query_cache.hpp deleted file mode 100644 index 1659ceb9c..000000000 --- a/include/query_cache.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef __CLASS_QUERY_CACHE_H -#define __CLASS_QUERY_CACHE_H -#include "proxysql.h" -#include "cpp.h" -#include "prometheus/counter.h" -#include "prometheus/gauge.h" - -#define EXPIRE_DROPIT 0 -#define SHARED_QUERY_CACHE_HASH_TABLES 32 -#define HASH_EXPIRE_MAX 3600*24*365*10 -#define DEFAULT_purge_loop_time 500000 -#define DEFAULT_purge_total_time 10000000 -#define DEFAULT_purge_threshold_pct_min 3 -#define DEFAULT_purge_threshold_pct_max 90 - -struct p_qc_counter { - enum metric { - query_cache_count_get = 0, - query_cache_count_get_ok, - query_cache_count_set, - query_cache_bytes_in, - query_cache_bytes_out, - query_cache_purged, - query_cache_entries, - __size - }; -}; - -struct p_qc_gauge { - enum metric { - query_cache_memory_bytes = 0, - __size - }; -}; - -struct qc_metrics_map_idx { - enum index { - counters = 0, - gauges - }; -}; - -class KV_BtreeArray; -class MySQL_Query_Cache; -class PgSQL_Query_Cache; -struct _MySQL_QC_entry; -struct _PgSQL_QC_entry; -typedef struct _MySQL_QC_entry MySQL_QC_entry_t; -typedef struct _PgSQL_QC_entry PgSQL_QC_entry_t; - -typedef struct _QC_entry { - uint64_t key; // primary key - unsigned char *value; // pointer to value - uint32_t length; // length of the value - uint32_t klen; // length of the key : FIXME: not sure if still relevant - uint64_t create_ms; // when the entry was created, monotonic, millisecond granularity - uint64_t expire_ms; // when the entry will expire, monotonic , millisecond granularity - uint64_t access_ms; // when the entry was read last , monotonic , millisecond granularity - bool refreshing; // true when a client will hit the backend to refresh the entry - KV_BtreeArray* kv; // pointer to the KV_BtreeArray where the entry is stored (used for troubleshooting) - //struct _QC_entry* self; // pointer to itself -} QC_entry_t; - -template -class Query_Cache { - static_assert(std::is_same_v || std::is_same_v, - "Invalid QC_DERIVED Query Cache type"); - using TypeQCEntry = typename std::conditional, - MySQL_QC_entry_t, PgSQL_QC_entry_t>::type; -public: - static bool shutting_down; - static pthread_t purge_thread_id; - constexpr static unsigned int purge_loop_time = DEFAULT_purge_loop_time; - - void print_version(); - uint64_t flush(); - void p_update_metrics(); - SQLite3_result* SQL3_getStats(); - void purgeHash(uint64_t max_memory_size); - -protected: - Query_Cache(); - ~Query_Cache(); - - bool set(QC_entry_t* entry, uint64_t user_hash, const unsigned char *kp, uint32_t kl, unsigned char *vp, - uint32_t vl, uint64_t create_ms, uint64_t curtime_ms, uint64_t expire_ms); - std::shared_ptr get(uint64_t user_hash, const unsigned char* kp, const uint32_t kl, - uint64_t curtime_ms, uint64_t cache_ttl); - - constexpr static unsigned int purge_total_time = DEFAULT_purge_total_time; - constexpr static unsigned int purge_threshold_pct_min = DEFAULT_purge_threshold_pct_min; - constexpr static unsigned int purge_threshold_pct_max = DEFAULT_purge_threshold_pct_max; - //uint64_t max_memory_size; - -private: - KV_BtreeArray* KVs[SHARED_QUERY_CACHE_HASH_TABLES]; - uint64_t get_data_size_total(); - unsigned int current_used_memory_pct(uint64_t max_memory_size); - void purgeHash(uint64_t QCnow_ms, unsigned int curr_pct); - - struct { - std::array p_counter_array{}; - std::array p_gauge_array{}; - } metrics; -}; - -#endif /* __CLASS_QUERY_CACHE_H */ diff --git a/lib/Query_Cache.cpp b/lib/Query_Cache.cpp index 5645b4b4b..4564fcc06 100644 --- a/lib/Query_Cache.cpp +++ b/lib/Query_Cache.cpp @@ -1,8 +1,7 @@ -#include "btree_map.h" #include "proxysql_atomic.h" #include "prometheus/counter.h" #include "prometheus_helpers.h" -#include "query_cache.hpp" +#include "Query_Cache.h" #include "MySQL_Query_Cache.h" #include "PgSQL_Query_Cache.h" @@ -43,15 +42,32 @@ __thread uint64_t __thr_num_entries = 0; __thread uint64_t __thr_num_deleted = 0; __thread uint64_t __thr_size_values = 0; -static uint64_t Glo_cntSet = 0; -static uint64_t Glo_cntGet = 0; -static uint64_t Glo_cntGetOK = 0; -static uint64_t Glo_num_entries = 0; -static uint64_t Glo_dataIN = 0; -static uint64_t Glo_dataOUT = 0; -static uint64_t Glo_cntPurge = 0; -static uint64_t Glo_size_values = 0; -static uint64_t Glo_total_freed_memory = 0; +template +uint64_t Query_Cache::Glo_cntSet = 0; + +template +uint64_t Query_Cache::Glo_cntGet = 0; + +template +uint64_t Query_Cache::Glo_cntGetOK = 0; + +template +uint64_t Query_Cache::Glo_num_entries = 0; + +template +uint64_t Query_Cache::Glo_dataIN = 0; + +template +uint64_t Query_Cache::Glo_dataOUT = 0; + +template +uint64_t Query_Cache::Glo_cntPurge = 0; + +template +uint64_t Query_Cache::Glo_size_values = 0; + +template +uint64_t Query_Cache::Glo_total_freed_memory = 0; template bool Query_Cache::shutting_down = false; @@ -59,112 +75,6 @@ bool Query_Cache::shutting_down = false; template pthread_t Query_Cache::purge_thread_id; -/*The KV_BtreeArray class is a container class that represents a key-value store - implemented using a B-tree data structure. It provides methods for performing various - operations on the key-value pairs stored in the container.*/ -class KV_BtreeArray { -public: - /** - * Constructs a new KV_BtreeArray object with the given entry size. - * - * @param entry_size The size of each entry in the key-value store. - */ - KV_BtreeArray(unsigned int entry_size); - - /** - * Destructs the KV_BtreeArray object. - */ - ~KV_BtreeArray(); - - /** - * Retrieves the entry with the given key from the key-value store in the KV_BtreeArray. - * If an entry with the given key exists in the store, a weak pointer to the entry will be returned. - * If an entry with the given key does not exist in the store, an empty weak pointer will be returned. - * - * @param key The key of the entry to be retrieved. - * @return A weak pointer to the entry with the given key, or an empty weak pointer if the entry does not exist. - */ - std::weak_ptr lookup(uint64_t key); - - /** - * Replaces the entry with the given key in the key-value store in the KV_BtreeArray. - * If an entry with the given key already exists in the store, it will be replaced with the new entry. - * If an entry with the given key does not exist in the store, the new entry will be added to the store. - * - * @param key The key of the entry to be replaced. - * @param entry The new entry to be added to the store. - * @return True if the entry was successfully replaced, false otherwise. (currently always true) - */ - bool replace(uint64_t key, QC_entry_t *entry); - - /** - * Clears the key-value store in the KV_BtreeArray. - * If release_entries is set to true, the entries in the store will be released. - * - * @param release_entries A flag indicating whether to release the entries in the store or not. - */ - void clear(bool release_entries = false); - - /** - * Purges entries from the key-value store in the KV_BtreeArray based on the given criteria. - * If aggressive is set to true, the function will remove entries based on the access time - * of the entries, otherwise it will remove entries based on the expiration time of the entries. - * - * @param QCnow_ms The current time in milliseconds. - * @param aggressive A flag indicating whether to perform aggressive purging or not. - */ - void purge_some(uint64_t QCnow_ms, bool aggressive); - - /** - * Retrieves the total data size of the key-value store in the KV_BtreeArray. - * The data size is calculated by multiplying the number of entries in the store - * with the size of each entry, including the size of the value, pointers, and metadata. - * - * @return The total data size of the key-value store. - */ - uint64_t get_data_size() const; - - /** - * Retrieves the number of entries in the key-value store in the KV_BtreeArray. - * - * @return The number of entries in the key-value store. - */ - int count() const; - -private: - pthread_rwlock_t lock; - std::vector> entries; - using BtMap_cache = btree::btree_map>; - BtMap_cache bt_map; - const unsigned int qc_entry_size; - - // read lock - void rdlock(); - - // write lock - void wrlock(); - - // unlock - void unlock(); - - /** - * Adds the given entry to the entries vector of the KV_BtreeArray. - * If the capacity of the entries vector is not enough to accommodate the new entry, - * it will be resized to the nearest power of 2 greater than the current size. - * - * @param entry The entry to be added to the entries vector. - */ - void add_to_entries(const std::shared_ptr& entry); - - /** - * Removes the entry at the given index from the entries vector of the KV_BtreeArray. - * If the index is out of bounds, this function does nothing. - * - * @param index The index of the entry to be removed from the entries vector. - */ - void remove_from_entries_by_index(size_t index); -}; - void free_QC_Entry(QC_entry_t* entry) { if (entry) { free(entry->value); @@ -172,21 +82,20 @@ void free_QC_Entry(QC_entry_t* entry) { } } -KV_BtreeArray::KV_BtreeArray(unsigned int entry_size) : qc_entry_size(entry_size) { +template +Query_Cache::KV_BtreeArray::KV_BtreeArray(unsigned int entry_size) : qc_entry_size(entry_size) { pthread_rwlock_init(&lock, NULL); }; -KV_BtreeArray::~KV_BtreeArray() { +template +Query_Cache::KV_BtreeArray::~KV_BtreeArray() { proxy_debug(PROXY_DEBUG_QUERY_CACHE, 3, "Size of KVBtreeArray:%d , entries:%lu\n", count(), entries.size()); clear(true); pthread_rwlock_destroy(&lock); }; -inline void KV_BtreeArray::rdlock() { pthread_rwlock_rdlock(&lock); } -inline void KV_BtreeArray::wrlock() { pthread_rwlock_wrlock(&lock); } -inline void KV_BtreeArray::unlock() { pthread_rwlock_unlock(&lock); } - -void KV_BtreeArray::add_to_entries(const std::shared_ptr& entry) { +template +void Query_Cache::KV_BtreeArray::add_to_entries(const std::shared_ptr& entry) { if (entries.capacity() <= (entries.size() + 1)) { const unsigned int new_size = l_near_pow_2(entries.size() + 1); entries.reserve(new_size); @@ -194,7 +103,8 @@ void KV_BtreeArray::add_to_entries(const std::shared_ptr& entry) { entries.push_back(entry); } -void KV_BtreeArray::remove_from_entries_by_index(size_t index) { +template +void Query_Cache::KV_BtreeArray::remove_from_entries_by_index(size_t index) { if (index >= entries.size()) { return; } @@ -210,12 +120,14 @@ void KV_BtreeArray::remove_from_entries_by_index(size_t index) { } } -uint64_t KV_BtreeArray::get_data_size() const { +template +uint64_t Query_Cache::KV_BtreeArray::get_data_size() const { uint64_t data_size = __sync_fetch_and_add(&Glo_num_entries,0) * (qc_entry_size+sizeof(QC_entry_t*)*2+sizeof(uint64_t)*2); // + __sync_fetch_and_add(&Glo_size_values,0) ; return data_size; }; -void KV_BtreeArray::purge_some(uint64_t QCnow_ms, bool aggressive) { +template +void Query_Cache::KV_BtreeArray::purge_some(uint64_t QCnow_ms, bool aggressive) { uint64_t ret = 0; uint64_t freeable_memory = 0; uint64_t access_ms_min = std::numeric_limits::max(); @@ -316,11 +228,8 @@ void KV_BtreeArray::purge_some(uint64_t QCnow_ms, bool aggressive) { } }; -inline int KV_BtreeArray::count() const { - return bt_map.size(); -}; - -bool KV_BtreeArray::replace(uint64_t key, QC_entry_t *entry) { +template +bool Query_Cache::KV_BtreeArray::replace(uint64_t key, QC_entry_t *entry) { std::shared_ptr entry_shared(entry, &free_QC_Entry); wrlock(); @@ -347,7 +256,8 @@ bool KV_BtreeArray::replace(uint64_t key, QC_entry_t *entry) { return true; } -std::weak_ptr KV_BtreeArray::lookup(uint64_t key) { +template +std::weak_ptr Query_Cache::KV_BtreeArray::lookup(uint64_t key) { std::weak_ptr entry_ptr; rdlock(); THR_UPDATE_CNT(__thr_cntGet,Glo_cntGet,1,1); @@ -361,7 +271,8 @@ std::weak_ptr KV_BtreeArray::lookup(uint64_t key) { return entry_ptr; }; -void KV_BtreeArray::clear(bool release_entries) { +template +void Query_Cache::KV_BtreeArray::clear(bool release_entries) { wrlock(); btree::btree_map>::iterator lookup; diff --git a/proxysql_notes.txt b/proxysql_notes.txt index 0d1ce5177..c8204f37b 100644 --- a/proxysql_notes.txt +++ b/proxysql_notes.txt @@ -97,7 +97,7 @@ proxysql_macros.h : a collection of few macros proxysql_mem.h : custom memory allocator using **per-thread** stacks. Memory allocated from per-thread stacks are **never** shared amoung threads. proxysql_session.hpp : unused , FIXME: to delete proxysql_structs.h : defines a lot of structures. The most important are _global_variable_entry_t (used to define any sort of global variable during the parsing of configuration) and _global_variables_t (global variables) -query_cache.hpp : defines virtual class Query_Cache (then implemented as plugin) +Query_Cache.h : defines virtual class Query_Cache (then implemented as plugin) query_processor.hpp : defines virtual class Query_Processor (then implemented as plugin) safe_btree*.h : A btree implementation of the STL set and map interfaces : https://code.google.com/p/cpp-btree/ shared_query_cache.h : currently it only defines some constants . FIXME: move these constants somewhere else