Current section
Files
Jump to
Current section
Files
c_src/mcache_bucket.c
#include "mcache.h"
#include "mcache_metric.h"
#include "mcache_utils.h"
static void init_slots(mc_conf_t conf,/*@out@*/ mc_gen_t *gen) {
dprint("init_slots\r\n");
gen->slots = (mc_slot_t *) mc_alloc(conf.slots * sizeof(mc_slot_t));
for (int b = 0; b < conf.slots; b++) {
mc_slot_t *bkt = &(gen->slots[b]);
for (int i = 0; i < LCOUNT; i++) {
bkt->largest[i] = NULL;
}
#ifdef TAGGED
bkt->tag = TAG_BKT;
#endif
for (int s = 0; s < SUBS; s++) {
mc_sub_slot_t *sub = &(bkt->subs[s]);
sub->size = conf.initial_entries;
sub->count = 0;
sub->metrics = (mc_metric_t **) mc_alloc(sub->size * sizeof(mc_metric_t *));
#ifdef TAGGED
sub->tag = TAG_SUB;
for (int i = 0; i < sub->size; i++) {
sub->metrics[i] = TAG_METRIC_L;
}
#endif
};
}
}
static void free_gen(mc_conf_t conf, mc_gen_t gen) {
dprint("free_gen\r\n");
for (int b = 0; b < conf.slots; b++) {
for (int s = 0; s < SUBS; s++) {
for (int m = 0; m < gen.slots[b].subs[s].count; m++) {
metric_free(gen.slots[b].subs[s].metrics[m]);
};
mc_free(gen.slots[b].subs[s].metrics);
}
}
mc_free(gen.slots);
};
static mc_metric_t *find_metric_g(mc_conf_t conf, mc_gen_t gen, uint64_t hash, uint16_t name_len, uint8_t *name) {
// Itterate over the existig metrics and see if we have already
// seen this one.
uint64_t slot = hash % conf.slots;
uint8_t sub = subid(hash);
for (int i = 0; i < gen.slots[slot].subs[sub].count; i++) {
mc_metric_t *m = gen.slots[slot].subs[sub].metrics[i];
if (m->name_len == name_len
&& m->hash == hash
&& meq(m->name, name, name_len)) {
return m;
}
}
return NULL;
};
static void remove_largest(mc_slot_t *slot, mc_metric_t *metric) {
int i = 0;
dprint("[%p] remove largest: %llu\r\n", slot, metric->hash);
#ifdef DEBUG
dprint("- >>");
for(i = 0; i < LCOUNT; i++){
if (! slot->largest[i]) {
break;
};
dprint(" %llu(%zu)", slot->largest[i]->hash, slot->largest[i]->alloc);
};
dprint("\r\n");
#endif
i = 0;
// short circuite when we have a last element
if (slot->largest[LCOUNT - 1]) {
// if we are the last lement we can set it to null and return
if (slot->largest[LCOUNT - 1] == metric) {
slot->largest[LCOUNT - 1] = NULL;
return;
}
}
//skip all the ones we don't have
while (i < LCOUNT && slot->largest[i] != metric) {
// if we found a null we can end our search
if (!slot->largest[i]) {
dprint("return %d\r\n", i);
return;
}
i++;
};
dprint("found %d\r\n", i);
uint8_t moved = 0;
while (i < LCOUNT - 1) {
dprint("%d <- %d\r\n", i, i + 1);
// found a null we don't need to go on.
if (!slot->largest[i]) {
return;
}
moved = 1;
slot->largest[i] = slot->largest[i+1];
i++;
}
if (moved) {
dprint("%d <- NULL\r\n", LCOUNT - 1);
slot->largest[LCOUNT - 1] = NULL;
}
}
static void insert_largest(mc_slot_t *slot, mc_metric_t *metric) {
remove_largest(slot, metric);
dprint("[%p] insert largest: %llu\r\n", slot, metric->hash);
#ifdef DEBUG
dprint(">>");
for(int j = 0; j < LCOUNT; j++){
if (! slot->largest[j]) {
break;
};
dprint(" %llu(%zu)", slot->largest[j]->hash, slot->largest[j]->alloc);
};
dprint("\r\n");
#endif
for (int i = 0; i < LCOUNT; i++) {
if (!slot->largest[i]) {
dprint("[%d] <- %llu; %zu!\r\n", i, metric->hash, metric->alloc);
slot->largest[i] = metric;
break;
}
if (metric == slot->largest[i]) {
break;
}
if (metric->alloc > slot->largest[i]->alloc) {
dprint("[%d] <- %llu: %zu\r\n", i, metric->hash, metric->alloc);
mc_metric_t *t = slot->largest[i];
slot->largest[i] = metric;
metric = t;
} else {
dprint("[%d] | %llu: %zu\r\n", i, slot->largest[i]->hash, slot->largest[i]->alloc);
}
}
#ifdef DEBUG
dprint("<<");
for(int j = 0; j < LCOUNT; j++){
if (! slot->largest[j]) {
break;
};
dprint(" %llu(%zu)", slot->largest[j]->hash, slot->largest[j]->alloc);
};
dprint("\r\n");
#endif
}
static mc_metric_t *find_metric_and_remove_g(mc_conf_t conf, mc_gen_t *gen, uint64_t hash, uint16_t name_len, uint8_t *name) {
int i = 0;
uint64_t slot = hash % conf.slots;
uint8_t sub = subid(hash);
// Itterate over the existig metrics and see if we have already
// seen this one.
for (i = 0; i < gen->slots[slot].subs[sub].count; i++) {
mc_metric_t *m = gen->slots[slot].subs[sub].metrics[i];
if (m->name_len == name_len
&& m->hash == hash
&& meq(m->name, name, name_len)) {
if (i != gen->slots[slot].subs[sub].count - 1) {
gen->slots[slot].subs[sub].metrics[i] = gen->slots[slot].subs[sub].metrics[gen->slots[slot].subs[sub].count - 1];
}
remove_largest(&(gen->slots[slot]), m);
gen->slots[slot].subs[sub].count--;
gen->alloc -= m->alloc;
return m;
}
}
return NULL;
};
mc_metric_t *bucket_get_metric(mc_bucket_t *bucket, mc_conf_t conf, uint64_t hash, uint16_t name_len, uint8_t *name) {
uint64_t slot = hash % conf.slots;
uint8_t sub = subid(hash);
// Itterate over the existig metrics and see if we have already
// seen this one.
mc_metric_t *metric = find_metric_g(conf, bucket->g0, hash, name_len, name);
if (metric) {
return metric;
}
mc_slot_t *b = &(bucket->g0.slots[slot]);
mc_sub_slot_t *s = &(b->subs[sub]);
// We start with a small bucket and grow it as required, so we need to
// make sure the new index doesn't exceet the count. If it does
// double the size of the bucket.
if (s->count >= s->size) {
mc_metric_t **new_metrics = mc_alloc(s->size * 2 * sizeof(mc_metric_t *));
#ifdef TAGGED
for (int i = 0; i < s->size * 2; i++) {
new_metrics[i] = TAG_METRIC_L;
}
#endif
memcpy(new_metrics, s->metrics, s->count * sizeof(mc_metric_t *));
mc_free(s->metrics);
s->metrics = new_metrics;
s->size = s->size * 2;
}
// we havn't seen the metric so we'll create a new one.
metric = find_metric_and_remove_g(conf, &(bucket->g1), hash, name_len, name);
if (!metric) {
metric = find_metric_and_remove_g(conf, &(bucket->g2), hash, name_len, name);
}
if (!metric) {
metric = (mc_metric_t *) mc_alloc(sizeof(mc_metric_t));
#ifdef TAGGED
metric->tag = TAG_METRIC;
#endif
metric->alloc = name_len + sizeof(mc_metric_t);
metric->name = mc_alloc(name_len * sizeof(uint8_t));
metric->hash = hash;
metric->name_len = name_len;
memcpy(metric->name, name, name_len);
metric->head = NULL;
metric->tail = NULL;
}
bucket->g0.alloc += metric->alloc;
s->metrics[s->count] = metric;
s->count++;
insert_largest(b, metric);
return metric;
};
mc_metric_t * bucket_check_limit(mc_bucket_t *bucket, mc_conf_t conf, uint64_t max_alloc, uint64_t slot) {
if (max_alloc > bucket->g0.alloc +
bucket->g1.alloc +
bucket->g2.alloc) {
return NULL;
}
// If we don't have g2 entries we age so g1 becomes
// g2
mc_gen_t *gen = &(bucket->g2);
if (gen->alloc == 0) {
gen = &(bucket->g1);
}
// If we still have no g2 entries we aga again,
// this way g0 effectively becomes g0
if (gen->alloc == 0) {
gen = &(bucket->g0);
}
// if we still have no g2 entries we know it's
// all for nothing and just give up
if (gen->alloc == 0) {
return NULL;
}
//we know we have at least 1 entrie so we grab the alst one
// and reduce the count and reduce the alloc;
//TODO: This is not good!
mc_metric_t *metric = NULL;
mc_sub_slot_t *largest_sub = NULL;
mc_slot_t *largest_slot = NULL;
int largest_idx = 0;
// try to find a metric using the largest bucket first
for (int b = 0; b < conf.slots; b++) {
if (gen->slots[b].largest[0] &&
(!metric || metric->alloc < gen->slots[b].largest[0]->alloc)) {
largest_slot = &(gen->slots[b]);
metric = largest_slot->largest[0];
largest_sub = &(largest_slot->subs[subid(metric->hash)]);
}
}
if (metric) {
largest_idx = 0;
remove_largest(largest_slot, metric);
// find the index of the metric
while (largest_idx < largest_sub->count && largest_sub->metrics[largest_idx] != metric) {
largest_idx++;
}
// We didn't found anything that is problematic so we pretend it never
// happend, the metric is already removed from the largest index
if (largest_idx == largest_sub->count) {
dprint("Oh my\r\n");
largest_idx = 0;
metric = NULL;
largest_slot = NULL;
largest_sub = NULL;
}
}
for (int i = 0; i < conf.slots; i++) {
// we break if we found a sutiable metric either in this loop or the loop before
if (metric) {
break;
}
// We itterate through all slots starting after the slot we just edited
// that way we avoid always changing the same buket over and over
int b = (i + slot + 1) % conf.slots;
mc_slot_t *slot = &(gen->slots[b]);
for (int sub = 0; sub < SUBS; sub++) {
// If we found a non empty slot we find the largest metric in there to
// evict, that way we can can free up the 'most sensible' thing;
for (int j = 0; j < slot->subs[sub].count; j++) {
if (!metric || slot->subs[sub].metrics[j]->alloc >= metric->alloc) {
largest_idx = j;
largest_sub = &(slot->subs[sub]);
metric = largest_sub->metrics[j];
}
}
}
}
if (metric) {
if (largest_idx != largest_sub->count - 1) {
largest_sub->metrics[largest_idx] = largest_sub->metrics[largest_sub->count - 1];
}
largest_sub->count--;
gen->alloc -= metric->alloc;
return metric;
}
return NULL;
// now we work on exporting the metric
}
mc_metric_t *bucket_find_metric_and_remove(mc_bucket_t *bucket, mc_conf_t conf, uint64_t hash, uint16_t name_len, uint8_t *name) {
mc_metric_t *res = find_metric_and_remove_g(conf, &(bucket->g0), hash, name_len, name);
if(!res) {
res = find_metric_and_remove_g(conf, &(bucket->g1), hash, name_len, name);
if(!res) {
res = find_metric_and_remove_g(conf, &(bucket->g2), hash, name_len, name);
};
};
return res;
}
mc_metric_t* bucket_find_metric(mc_bucket_t *bucket, mc_conf_t conf, uint64_t hash, uint16_t name_len, uint8_t *name) {
mc_metric_t *res = find_metric_g(conf, bucket->g0, hash, name_len, name);
if(!res) {
res = find_metric_g(conf, bucket->g1, hash, name_len, name);
if(!res) {
res = find_metric_g(conf, bucket->g2, hash, name_len, name);
}
}
return res;
}
void bucket_age(mc_bucket_t *bucket, mc_conf_t conf) {
dprint("age\r\n");
for (int b = 0; b < conf.slots; b++) {
// G1 -> G2
for (int s = 0; s < SUBS; s++) {
mc_sub_slot_t *g2s = &(bucket->g2.slots[b].subs[s]);
mc_sub_slot_t *g1s = &(bucket->g1.slots[b].subs[s]);
mc_metric_t **new_metrics = (mc_metric_t **) mc_alloc((g2s->count + g1s->count) * sizeof(mc_metric_t *));
memcpy(new_metrics, g2s->metrics, g2s->count * sizeof(mc_metric_t *));
mc_free(g2s->metrics);
g2s->metrics = new_metrics;
memcpy(
// copy to the end of the current array
&(g2s->metrics[g2s->count]),
// copy the start ofn the lower gens array
g1s->metrics,
// Copy based on the sbuze of the old array
g1s->count * sizeof(mc_metric_t *)
);
mc_free(g1s->metrics);
g2s->count += g1s->count;
g2s->size = g2s->count;
// Free the G1 metric list of thbs slot as we copbed bt all out
}
// make sure to clear the largest
for (int l = 0; l < LCOUNT; l++) {
// move largest over when we have them
if (bucket->g1.slots[b].largest[l]) {
insert_largest(&(bucket->g2.slots[b]), bucket->g1.slots[b].largest[l]);
} else {
// we can stop our loop when we find the first null
break;
}
// then set them to null
bucket->g1.slots[b].largest[l] = NULL;
}
}
// free g1 slots (we copied the content to g2)
mc_free(bucket->g1.slots);
// move g0 slots to g1
bucket->g1.slots = bucket->g0.slots;
bucket->g2.alloc += bucket->g1.alloc;
bucket->g1.alloc = bucket->g0.alloc;
bucket->g0.alloc = 0;
// reinitialize g0
init_slots(conf, &(bucket->g0));
}
mc_metric_t* bucket_insert(mc_bucket_t *bucket, mc_conf_t conf, uint8_t *name, size_t name_len,
uint64_t offset, uint64_t *value, size_t value_len) {
uint64_t slot;
mc_metric_t *metric;
uint64_t hash = XXH64(name, name_len, conf.hash_seed) ;
slot = hash % conf.slots;
dprint("INSERT[%llu]: %llu %lu@%llu\r\n", slot, hash, value_len, offset);
metric = bucket_get_metric(bucket, conf, hash, name_len, name);
// Add the datapoint
metric_add_point(conf, &(bucket->g0), metric, offset, value_len, value);
// update largest
insert_largest(&(bucket->g0.slots[slot]), metric);
bucket->inserts++;
if (bucket->inserts > conf.age_cycle) {
bucket_age(bucket, conf);
bucket->age++;
bucket->inserts = 0;
}
// We now check for overflow note that metric is re-used here!
return bucket_check_limit(bucket, conf, conf.max_alloc, slot);
}
void bucket_free(mc_bucket_t *bucket, mc_conf_t conf) {
dprint("bucket_free\r\n");
free_gen(conf, bucket->g0);
free_gen(conf, bucket->g1);
free_gen(conf, bucket->g2);
mc_free(bucket->name);
mc_free(bucket);
}
uint8_t bucket_is_empty(mc_bucket_t *bucket) {
return bucket->g0.alloc == 0 && bucket->g1.alloc == 0 && bucket->g2.alloc == 0;
};
mc_bucket_t* bucket_init(mc_conf_t config, uint8_t *name, size_t name_len) {
dprint("bucket_init\r\n");
mc_bucket_t *bucket;
bucket = mc_alloc(sizeof(mc_bucket_t));
#ifdef TAGGED
bucket->tag = TAG_BUCKET;
#endif
// some bucket wqide counters
bucket->inserts = 0;
bucket->age = 0;
bucket->name_len = name_len;
bucket->name = mc_alloc(name_len * sizeof(uint8_t));
memcpy(bucket->name, name, name_len);
bucket->hash = XXH64(name, name_len, config.hash_seed);
//now set up the tree genreations
bucket->g0.v = 0;
bucket->g0.alloc = 0;
#ifdef TAGGED
bucket->g0.tag = TAG_GEN;
#endif
init_slots(config, &(bucket->g0));
bucket->g1.v = 1;
bucket->g1.alloc = 0;
#ifdef TAGGED
bucket->g1.tag = TAG_GEN;
#endif
init_slots(config, &(bucket->g1));
bucket->g2.v = 2;
bucket->g2.alloc = 0;
#ifdef TAGGED
bucket->g2.tag = TAG_GEN;
#endif
init_slots(config, &(bucket->g2));
return bucket;
}