polardbxengine/storage/ndb/memcache/extra/memcached/daemon/topkeys.c

181 lines
5.2 KiB
C

#include <sys/types.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <pthread.h>
#include <memcached/genhash.h>
#include "topkeys.h"
static topkey_item_t *topkey_item_init(const void *key, int nkey, rel_time_t ctime) {
topkey_item_t *item = calloc(sizeof(topkey_item_t) + nkey, 1);
assert(item);
assert(key);
assert(nkey > 0);
item->nkey = nkey;
item->ctime = ctime;
item->atime = ctime;
/* Copy the key into the part trailing the struct */
memcpy(item->key, key, nkey);
return item;
}
static inline size_t topkey_item_size(const topkey_item_t *item) {
return sizeof(topkey_item_t) + item->nkey;
}
static inline topkey_item_t* topkeys_tail(topkeys_t *tk) {
return (topkey_item_t*)(tk->list.prev);
}
static int my_hash_eq(const void *k1, size_t nkey1,
const void *k2, size_t nkey2) {
return nkey1 == nkey2 && memcmp(k1, k2, nkey1) == 0;
}
topkeys_t *topkeys_init(int max_keys) {
topkeys_t *tk = calloc(sizeof(topkeys_t), 1);
if (tk == NULL) {
return NULL;
}
pthread_mutex_init(&tk->mutex, NULL);
tk->max_keys = max_keys;
tk->list.next = &tk->list;
tk->list.prev = &tk->list;
static struct hash_ops my_hash_ops = {
.hashfunc = genhash_string_hash,
.hasheq = my_hash_eq,
.dupKey = NULL,
.dupValue = NULL,
.freeKey = NULL,
.freeValue = NULL,
};
tk->hash = genhash_init(max_keys, my_hash_ops);
if (tk->hash == NULL) {
return NULL;
}
return tk;
}
void topkeys_free(topkeys_t *tk) {
pthread_mutex_destroy(&tk->mutex);
genhash_free(tk->hash);
dlist_t *p = tk->list.next;
while (p != &tk->list) {
dlist_t *tmp = p->next;
free(p);
p = tmp;
}
}
static inline void dlist_remove(dlist_t *list) {
assert(list->prev->next == list);
assert(list->next->prev == list);
list->prev->next = list->next;
list->next->prev = list->prev;
}
static inline void dlist_insert_after(dlist_t *list, dlist_t *new) {
new->next = list->next;
new->prev = list;
list->next->prev = new;
list->next = new;
}
static inline void dlist_iter(dlist_t *list,
void (*iterfunc)(dlist_t *item, void *arg),
void *arg)
{
dlist_t *p = list;
while ((p = p->next) != list) {
iterfunc(p, arg);
}
}
static inline void topkeys_item_delete(topkeys_t *tk, topkey_item_t *item) {
genhash_delete(tk->hash, item->key, item->nkey);
dlist_remove(&item->list);
--tk->nkeys;
free(item);
}
topkey_item_t *topkeys_item_get_or_create(topkeys_t *tk, const void *key, size_t nkey, const rel_time_t ctime) {
topkey_item_t *item = genhash_find(tk->hash, key, nkey);
if (item == NULL) {
item = topkey_item_init(key, nkey, ctime);
if (item != NULL) {
if (++tk->nkeys > tk->max_keys) {
topkeys_item_delete(tk, topkeys_tail(tk));
}
genhash_update(tk->hash, item->key, item->nkey,
item, topkey_item_size(item));
} else {
return NULL;
}
} else {
dlist_remove(&item->list);
}
dlist_insert_after(&tk->list, &item->list);
return item;
}
static inline void append_stat(const void *cookie,
const char *name,
size_t namelen,
const char *key,
size_t nkey,
int value,
ADD_STAT add_stats) {
char key_str[128];
char val_str[128];
int klen, vlen;
klen = sizeof(key_str) - namelen - 2;
if (nkey < klen) {
klen = nkey;
}
memcpy(key_str, key, klen);
key_str[klen] = '.';
memcpy(&key_str[klen+1], name, namelen + 1);
klen += namelen + 1;
vlen = snprintf(val_str, sizeof(val_str) - 1, "%d", value);
add_stats(key_str, klen, val_str, vlen, cookie);
}
struct tk_context {
const void *cookie;
ADD_STAT add_stat;
rel_time_t current_time;
};
#define TK_FMT(name) #name "=%d,"
#define TK_ARGS(name) item->name,
static void tk_iterfunc(dlist_t *list, void *arg) {
struct tk_context *c = arg;
topkey_item_t *item = (topkey_item_t*)list;
char val_str[TK_MAX_VAL_LEN];
/* This line is magical. The missing comma before item->ctime is because the TK_ARGS macro ends with a comma. */
int vlen = snprintf(val_str, sizeof(val_str) - 1, TK_OPS(TK_FMT)"ctime=%"PRIu32",atime=%"PRIu32, TK_OPS(TK_ARGS)
c->current_time - item->ctime, c->current_time - item->atime);
c->add_stat(item->key, item->nkey, val_str, vlen, c->cookie);
}
ENGINE_ERROR_CODE topkeys_stats(topkeys_t *tk,
const void *cookie,
const rel_time_t current_time,
ADD_STAT add_stat) {
struct tk_context context;
context.cookie = cookie;
context.add_stat = add_stat;
context.current_time = current_time;
assert(tk);
pthread_mutex_lock(&tk->mutex);
dlist_iter(&tk->list, tk_iterfunc, &context);
pthread_mutex_unlock(&tk->mutex);
return ENGINE_SUCCESS;
}