#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. */ explicit 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 */