You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/lib/Standard_Query_Cache.cpp_st...

407 lines
10 KiB

#include "btree_map.h"
#include "proxysql.h"
#include "cpp.h"
#include "proxysql_atomic.h"
#define EXPIRE_DROPIT 0
#define SHARED_QUERY_CACHE_HASH_TABLES 16
#define HASH_EXPIRE_MAX 3600*24*365*10
#define DEFAULT_purge_loop_time 400000
#define DEFAULT_purge_total_time 10000000
#define DEFAULT_purge_threshold_pct_min 50
#define DEFAULT_purge_threshold_pct_max 90
#define DEFAULT_SQC_size 64*1024*1024
/*
static void hash_value_destroy_func(void * hash_entry) {
fdb_hash_entry *entry= (fdb_hash_entry *) hash_entry;
entry->expire=EXPIRE_DROPIT;
}
*/
/*
class Query_Cache {
protected:
int test;
int whatever;
public:
virtual void * purgeHash_thread(void *);
int size;
int shutdown;
time_t QCnow;
pthread_t purge_thread_id;
fdb_hash_t fdb_hashes[SHARED_QUERY_CACHE_HASH_TABLES];
unsigned int purge_loop_time;
unsigned int purge_total_time;
unsigned int purge_threshold_pct_min;
unsigned int purge_threshold_pct_max;
uint64_t max_memory_size;
uint64_t cntDel;
uint64_t cntGet;
uint64_t cntGetOK;
uint64_t cntSet;
uint64_t cntSetERR;
uint64_t cntPurge;
uint64_t size_keys;
uint64_t size_values;
uint64_t size_metas;
uint64_t dataIN;
uint64_t dataOUT;
Query_Cache()
: whatever(0) {}
virtual ~Query_Cache();
void set_side_length(double side_length) {
test = side_length;
}
virtual bool set(unsigned char *, uint32_t, unsigned char *, uint32_t, time_t);
virtual unsigned char * get(const unsigned char *);
virtual uint64_t flush();
virtual uint64_t current_free_memory();
virtual unsigned int current_used_memory_pct();
virtual double area() const = 0;
};
*/
struct btreemapcmp {
bool operator() (const char *a, const char *b) const
{return strcmp(a,b) < 0; }
};
class KV_BtreeArray;
typedef struct __QC_entry_t QC_entry_t;
struct __QC_entry_t {
char *key;
char *value;
KV_BtreeArray *kv;
QC_entry_t *self;
uint32_t klen;
uint32_t length;
time_t expire;
time_t access;
uint32_t ref_count;
};
typedef btree::btree_map<const char *, QC_entry_t *, btreemapcmp> BtMap;
class KV_BtreeArray {
private:
rwlock_t lock;
BtMap bt_map;
PtrArray ptrArray;
uint64_t dataSize;
uint64_t purgeChunkSize;
uint64_t purgeIdx;
bool __insert(const char *, void *);
public:
KV_BtreeArray() {
spinlock_rwlock_init(&lock);
dataSize=0;
}
~KV_BtreeArray() {};
int size() {
return bt_map.size();
};
bool replace(const char *key, QC_entry_t *entry) {
spin_wrlock(&lock);
ptrArray.add(entry);
btree::btree_map<const char *, QC_entry_t *, btreemapcmp>::iterator lookup;
lookup = bt_map.find(key);
if (lookup != bt_map.end()) {
lookup->second->expire=EXPIRE_DROPIT;
bt_map.erase(lookup);
}
bt_map.insert(std::make_pair(key,entry));
spin_wrunlock(&lock);
return true;
}
QC_entry_t *lookup(const char *key) {
QC_entry_t *entry=NULL;
spin_rdlock(&lock);
// fdb_hash_entry *entry=(fdb_hash_entry *)g_hash_table_lookup(fdb_hashes[i].hash, kp);
btree::btree_map<const char *, QC_entry_t *, btreemapcmp>::iterator lookup;
lookup = bt_map.find(key);
if (lookup != bt_map.end()) {
entry=lookup->second;
__sync_fetch_and_add(&entry->ref_count,1);
}
spin_rdunlock(&lock);
return entry;
};
void empty() {
spin_wrlock(&lock);
btree::btree_map<const char *, QC_entry_t *, btreemapcmp>::iterator lookup;
while (bt_map.size()) {
lookup = bt_map.begin();
if ( lookup != bt_map.end() ) {
lookup->second->expire=EXPIRE_DROPIT;
//const char *f=lookup->first;
bt_map.erase(lookup);
}
}
spin_wrunlock(&lock);
};
};
class Standard_Query_Cache: public Query_Cache {
private:
fdb_hash_t fdb_hashes[SHARED_QUERY_CACHE_HASH_TABLES];
KV_BtreeArray KVs[SHARED_QUERY_CACHE_HASH_TABLES];
public:
//Standard_Query_Cache(uint64_t _max_memory_size) {
virtual double area() const {
return max_memory_size*rand();
};
Standard_Query_Cache() {
QCnow=time(NULL);
test=0;
size=SHARED_QUERY_CACHE_HASH_TABLES;
shutdown=0;
purge_loop_time=DEFAULT_purge_loop_time;
purge_total_time=DEFAULT_purge_total_time;
purge_threshold_pct_min=DEFAULT_purge_threshold_pct_min;
purge_threshold_pct_max=DEFAULT_purge_threshold_pct_max;
//max_memory_size=_max_memory_size;
max_memory_size=DEFAULT_SQC_size;
cntDel=0;
cntGet=0;
cntGetOK=0;
cntSet=0;
cntSetERR=0;
cntPurge=0;
size_keys=0;
size_values=0;
size_metas=0;
dataIN=0;
dataOUT=0;
/*
int i;
for (i=0; i<size; i++) {
spinlock_rwlock_init(&fdb_hashes[i].lock);
fdb_hashes[i].hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, hash_value_destroy_func);
fdb_hashes[i].ptrArray=new PtrArray();
fdb_hashes[i].dataSize=0;
}
*/
};
virtual ~Standard_Query_Cache() {
// this function assumes that the QC is not in use . No locks acquired.
unsigned int i;
// shutdown=1; //causes the purge thread to exit
// pthread_join(purge_thread_id,NULL);
/*
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
g_hash_table_remove_all(fdb_hashes[i].hash);
while (fdb_hashes[i].ptrArray->len) {
fdb_hashes[i].ptrArray->remove_index_fast(0);
}
delete fdb_hashes[i].ptrArray;
g_hash_table_destroy(fdb_hashes[i].hash);
}
*/
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
fprintf(stderr,"Size of %d: %d\n", i, KVs[i].size() );
}
};
virtual unsigned char * get(const unsigned char *kp) {
unsigned char *result=NULL;
uint32_t kl=strlen((const char *)kp);
if (kl < 3) return result;
unsigned char i;
i=(*((unsigned char *)kp))+(*((unsigned char *)kp+1))+(*((unsigned char *)kp+2));
i=i%SHARED_QUERY_CACHE_HASH_TABLES;
/*
spin_rdlock(&fdb_hashes[i].lock);
fdb_hash_entry *entry=(fdb_hash_entry *)g_hash_table_lookup(fdb_hashes[i].hash, kp);
if (entry!=NULL) { __sync_fetch_and_add(&entry->ref_count,1); }
spin_rdunlock(&fdb_hashes[i].lock);
*/
QC_entry_t *entry=KVs[i].lookup((char *)kp);
if (entry!=NULL) {
time_t t=QCnow;
if (entry->expire > t) {
result=(unsigned char *)malloc(entry->length);
memcpy(result,entry->value,entry->length);
__sync_fetch_and_add(&cntGetOK,1);
__sync_fetch_and_add(&dataOUT,entry->length);
if (t > entry->access) entry->access=t;
}
__sync_fetch_and_sub(&entry->ref_count,1);
}
__sync_fetch_and_add(&cntGet,1);
return result;
}
virtual bool set(unsigned char *kp, uint32_t kl, unsigned char *vp, uint32_t vl, time_t expire) {
if (kl < 3) return false;
//fdb_hash_entry *entry = (fdb_hash_entry *)malloc(sizeof(fdb_hash_entry));
QC_entry_t *entry = (QC_entry_t *)malloc(sizeof(QC_entry_t));
entry->klen=kl;
entry->length=vl;
entry->ref_count=0;
//entry->key=(unsigned char *)calloc(1,kl);
entry->key=(char *)malloc(kl);
memcpy(entry->key,kp,kl);
entry->value=(char *)malloc(vl);
memcpy(entry->value,vp,vl);
entry->self=entry;
entry->access=QCnow;
if (expire > HASH_EXPIRE_MAX) {
entry->expire=expire; // expire is a unix timestamp
} else {
entry->expire=QCnow+expire; // expire is seconds
}
unsigned char i;
i=(*((unsigned char *)kp))+(*((unsigned char *)kp+1))+(*((unsigned char *)kp+2));
i=i%SHARED_QUERY_CACHE_HASH_TABLES;
/*
spin_wrlock(&fdb_hashes[i].lock);
fdb_hashes[i].ptrArray->add(entry);
g_hash_table_replace(fdb_hashes[i].hash, (void *)entry->key, (void *)entry);
spin_wrunlock(&fdb_hashes[i].lock);
*/
KVs[i].replace((char *)kp, entry);
__sync_fetch_and_add(&cntSet,1);
__sync_fetch_and_add(&size_keys,kl);
__sync_fetch_and_add(&size_values,vl);
__sync_fetch_and_add(&dataIN,vl);
// __sync_fetch_and_add(&size_metas,sizeof(fdb_hash_entry)+sizeof(fdb_hash_entry *));
__sync_fetch_and_add(&size_metas,sizeof(QC_entry_t)+sizeof(QC_entry_t *)*4);
return true;
}
virtual uint64_t flush() {
int i;
uint64_t total_size=0;
/*
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
spin_wrlock(&fdb_hashes[i].lock);
}
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
total_size+=g_hash_table_size(fdb_hashes[i].hash);
g_hash_table_remove_all(fdb_hashes[i].hash);
}
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
spin_wrunlock(&fdb_hashes[i].lock);
}
*/
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
total_size+=KVs[i].size();
//total_size+=g_hash_table_size(fdb_hashes[i].hash);
KVs[i].empty();
//g_hash_table_remove_all(fdb_hashes[i].hash);
}
return total_size;
};
virtual uint64_t current_free_memory() {
uint64_t cur_size=size_keys+size_values+size_metas;
return (cur_size > max_memory_size ? 0 : max_memory_size-cur_size);
}
virtual unsigned int current_used_memory_pct() {
uint64_t cur_size=size_keys+size_values+size_metas;
float pctf = (float) cur_size*100/max_memory_size;
if (pctf > 100) return 100;
int pct=pctf;
return pct;
}
virtual void * purgeHash_thread(void *) {
uint64_t min_idx=0;
unsigned int i;
while (shutdown==0) {
usleep(purge_loop_time);
time_t t=time(NULL);
QCnow=t;
/*
if (current_used_memory_pct() < purge_threshold_pct_min ) continue;
for (i=0; i<SHARED_QUERY_CACHE_HASH_TABLES; i++) {
spin_wrlock(&fdb_hashes[i].lock);
if (fdb_hashes[i].purgeIdx==0) {
if (fdb_hashes[i].ptrArray->len) {
fdb_hashes[i].purgeIdx=fdb_hashes[i].ptrArray->len;
fdb_hashes[i].purgeChunkSize=fdb_hashes[i].ptrArray->len*purge_loop_time/purge_total_time;
if (fdb_hashes[i].purgeChunkSize < 10) { fdb_hashes[i].purgeChunkSize=fdb_hashes[i].ptrArray->len; } // this should prevent a bug with few entries left in the cache
}
}
if (min_idx < fdb_hashes[i].purgeChunkSize ) min_idx=0;
if (fdb_hashes[i].purgeIdx) while( --fdb_hashes[i].purgeIdx > min_idx) {
fdb_hash_entry *entry=(fdb_hash_entry *)fdb_hashes[i].ptrArray->index(fdb_hashes[i].purgeIdx);
if (( entry->expire!=EXPIRE_DROPIT) && entry->expire <= QCnow) {
g_hash_table_remove(fdb_hashes[i].hash,entry->key);
}
if ( (entry->expire==EXPIRE_DROPIT)
&& (__sync_fetch_and_add(&entry->ref_count,0)==0)
) {
__sync_fetch_and_sub(&size_keys,entry->klen);
__sync_fetch_and_sub(&size_values,entry->length);
__sync_fetch_and_sub(&size_metas,sizeof(fdb_hash_entry)+sizeof(fdb_hash_entry *));
free(entry->key);
free(entry->value);
entry->self=NULL;
free(entry);
__sync_fetch_and_add(&cntPurge,1);
fdb_hashes[i].ptrArray->remove_index_fast(fdb_hashes[i].purgeIdx);
}
}
spin_wrunlock(&fdb_hashes[i].lock);
}
*/
}
return NULL;
};
};
extern "C" Query_Cache* create_SQC() {
return new Standard_Query_Cache();
}
extern "C" void destroy_SQC(Query_Cache* qc) {
delete qc;
}
typedef Query_Cache* create_QC_t();
typedef void destroy_QC_t(Query_Cache*);