Separate query cache metrics for MySQL and PostgreSQL

Previously, query cache metrics were shared between MySQL and PostgreSQL,
causing both to reflect the same values when performing cache operations.
This change isolates the metrics for each database type.
v3.0_disable_pgsql_prometheus_5159
Rahim Kanji 4 months ago
parent cdfd1543a4
commit 818025a3b3

@ -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;

@ -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;

@ -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 <typename QC_DERIVED>
class Query_Cache {
static_assert(std::is_same_v<QC_DERIVED,MySQL_Query_Cache> || std::is_same_v<QC_DERIVED,PgSQL_Query_Cache>,
"Invalid QC_DERIVED Query Cache type");
using TypeQCEntry = typename std::conditional<std::is_same_v<QC_DERIVED, MySQL_Query_Cache>,
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<QC_entry_t> 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<std::shared_ptr<QC_entry_t>> entries;
using BtMap_cache = btree::btree_map<uint64_t, std::weak_ptr<QC_entry_t>>;
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<QC_entry_t>& 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<QC_entry_t> 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<prometheus::Counter*, p_qc_counter::__size> p_counter_array{};
std::array<prometheus::Gauge*, p_qc_gauge::__size> p_gauge_array{};
} metrics;
};
#endif /* __CLASS_QUERY_CACHE_H */

@ -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 <typename QC_DERIVED>
class Query_Cache {
static_assert(std::is_same_v<QC_DERIVED,MySQL_Query_Cache> || std::is_same_v<QC_DERIVED,PgSQL_Query_Cache>,
"Invalid QC_DERIVED Query Cache type");
using TypeQCEntry = typename std::conditional<std::is_same_v<QC_DERIVED, MySQL_Query_Cache>,
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<QC_entry_t> 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<prometheus::Counter*, p_qc_counter::__size> p_counter_array{};
std::array<prometheus::Gauge*, p_qc_gauge::__size> p_gauge_array{};
} metrics;
};
#endif /* __CLASS_QUERY_CACHE_H */

@ -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<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_cntSet = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_cntGet = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_cntGetOK = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_num_entries = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_dataIN = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_dataOUT = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_cntPurge = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_size_values = 0;
template<typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::Glo_total_freed_memory = 0;
template<typename QC_DERIVED>
bool Query_Cache<QC_DERIVED>::shutting_down = false;
@ -59,112 +75,6 @@ bool Query_Cache<QC_DERIVED>::shutting_down = false;
template<typename QC_DERIVED>
pthread_t Query_Cache<QC_DERIVED>::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<QC_entry_t> 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<std::shared_ptr<QC_entry_t>> entries;
using BtMap_cache = btree::btree_map<uint64_t,std::weak_ptr<QC_entry_t>>;
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<QC_entry_t>& 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 <typename QC_DERIVED>
Query_Cache<QC_DERIVED>::KV_BtreeArray::KV_BtreeArray(unsigned int entry_size) : qc_entry_size(entry_size) {
pthread_rwlock_init(&lock, NULL);
};
KV_BtreeArray::~KV_BtreeArray() {
template <typename QC_DERIVED>
Query_Cache<QC_DERIVED>::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<QC_entry_t>& entry) {
template <typename QC_DERIVED>
void Query_Cache<QC_DERIVED>::KV_BtreeArray::add_to_entries(const std::shared_ptr<QC_entry_t>& 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<QC_entry_t>& entry) {
entries.push_back(entry);
}
void KV_BtreeArray::remove_from_entries_by_index(size_t index) {
template <typename QC_DERIVED>
void Query_Cache<QC_DERIVED>::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 <typename QC_DERIVED>
uint64_t Query_Cache<QC_DERIVED>::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 <typename QC_DERIVED>
void Query_Cache<QC_DERIVED>::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<uint64_t>::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 <typename QC_DERIVED>
bool Query_Cache<QC_DERIVED>::KV_BtreeArray::replace(uint64_t key, QC_entry_t *entry) {
std::shared_ptr<QC_entry_t> 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<QC_entry_t> KV_BtreeArray::lookup(uint64_t key) {
template <typename QC_DERIVED>
std::weak_ptr<QC_entry_t> Query_Cache<QC_DERIVED>::KV_BtreeArray::lookup(uint64_t key) {
std::weak_ptr<QC_entry_t> entry_ptr;
rdlock();
THR_UPDATE_CNT(__thr_cntGet,Glo_cntGet,1,1);
@ -361,7 +271,8 @@ std::weak_ptr<QC_entry_t> KV_BtreeArray::lookup(uint64_t key) {
return entry_ptr;
};
void KV_BtreeArray::clear(bool release_entries) {
template <typename QC_DERIVED>
void Query_Cache<QC_DERIVED>::KV_BtreeArray::clear(bool release_entries) {
wrlock();
btree::btree_map<uint64_t,std::weak_ptr<QC_entry_t>>::iterator lookup;

@ -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

Loading…
Cancel
Save