#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 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::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::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::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; ilen) { fdb_hashes[i].ptrArray->remove_index_fast(0); } delete fdb_hashes[i].ptrArray; g_hash_table_destroy(fdb_hashes[i].hash); } */ for (i=0; iref_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 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; ilen) { 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*);