bool assoc_prefix_isvalid(struct default_engine *engine, hash_item *it) { rel_time_t current_time = engine->server.core->get_current_time(); prefix_t *pt; if (it->nprefix == it->nkey) { /* the prefix of key: null */ assert(root_pt != NULL); pt = root_pt; if (pt->oldest_live != 0 && pt->oldest_live <= current_time && it->time <= pt->oldest_live) { return false; } } else { /* the prifix of key: given */ pt = assoc_prefix_find(engine, engine->server.core->hash(item_get_key(it), it->nprefix, 0), item_get_key(it), it->nprefix); while (pt != NULL && pt != root_pt) { // validation check between prefix and hash_item if (pt->oldest_live != 0 && pt->oldest_live <= current_time && it->time <= pt->oldest_live) { return false; } // traversal parent prefixes to validate pt = pt->parent_prefix; } } return true; }
void assoc_prefix_unlink(struct default_engine *engine, hash_item *it, const size_t item_size, bool drop_if_empty) { prefix_t *pt; assert(it->nprefix != 0); if (it->nprefix == it->nkey) { pt = root_pt; } else { pt = assoc_prefix_find(engine, engine->server.core->hash(item_get_key(it), it->nprefix, 0), item_get_key(it), it->nprefix); } assert(pt != NULL); // update prefix information if ((it->iflag & ITEM_IFLAG_LIST) != 0) { pt->list_hash_items--; pt->list_hash_items_bytes -= item_size; } else if ((it->iflag & ITEM_IFLAG_SET) != 0) { pt->set_hash_items--; pt->set_hash_items_bytes -= item_size; } else if ((it->iflag & ITEM_IFLAG_BTREE) != 0) { pt->btree_hash_items--; pt->btree_hash_items_bytes -= item_size; } else { pt->hash_items--; pt->hash_items_bytes -= item_size; } #if 0 // might be used later if (1) { prefix_t *curr_pt = pt->parent_prefix; while (curr_pt != NULL) { curr_pt->tot_hash_items--; curr_pt->tot_hash_items_bytes -= item_size; curr_pt = curr_pt->parent_prefix; } } #endif if (drop_if_empty) { while (pt != NULL) { prefix_t *parent_pt = pt->parent_prefix; if (pt != root_pt && pt->prefix_items == 0 && pt->hash_items == 0 && pt->list_hash_items == 0 && pt->set_hash_items == 0 && pt->btree_hash_items == 0) { assert(pt->hash_items_bytes == 0 && pt->list_hash_items_bytes == 0 && pt->set_hash_items_bytes == 0 && pt->btree_hash_items_bytes == 0); _prefix_delete(engine, engine->server.core->hash(_get_prefix(pt), pt->nprefix, 0), _get_prefix(pt), pt->nprefix); } else { break; } pt = parent_pt; } } }
static int _prefix_insert(struct default_engine *engine, uint32_t hash, prefix_t *pt) { assert(assoc_prefix_find(engine, hash, _get_prefix(pt), pt->nprefix) == NULL); pt->h_next = engine->assoc.prefix_hashtable[hash & hashmask(DEFAULT_PREFIX_HASHPOWER)]; engine->assoc.prefix_hashtable[hash & hashmask(DEFAULT_PREFIX_HASHPOWER)] = pt; assert(pt->parent_prefix != NULL); pt->parent_prefix->prefix_items++; engine->assoc.tot_prefix_items++; return 1; }
static ENGINE_ERROR_CODE do_assoc_get_prefix_stats(struct default_engine *engine, const char *prefix, const int nprefix, void *prefix_data) { prefix_t *pt; if (nprefix < 0) { // all prefix information char *buf; struct tm *t; const char *format = "PREFIX %s itm %llu kitm %llu litm %llu sitm %llu bitm %llu " "tsz %llu ktsz %llu ltsz %llu stsz %llu btsz %llu time %04d%02d%02d%02d%02d%02d\r\n"; uint32_t i, hsize = hashsize(DEFAULT_PREFIX_HASHPOWER); uint32_t num_prefixes = engine->assoc.tot_prefix_items; uint32_t tot_prefix_name_len = 0; uint32_t msize, pos, written; pt = root_pt; if (pt != NULL && (pt->hash_items > 0 || pt->list_hash_items > 0 || pt->set_hash_items > 0 || pt->btree_hash_items > 0)) { /* including null prefix */ num_prefixes += 1; tot_prefix_name_len = strlen("<null>"); } for (i = 0; i < hsize; i++) { pt = engine->assoc.prefix_hashtable[i]; while (pt) { tot_prefix_name_len += pt->nprefix; pt = pt->h_next; } } msize = sizeof(uint32_t) + strlen(format) + tot_prefix_name_len + num_prefixes * (strlen(format) - 2 /* %s */ + (10 * (20 - 4))) /* %llu replaced by 20-digit num */ - (5 * (4 - 2)) /* %02d replaced by 2-digit num */ + sizeof("END\r\n"); buf = malloc(msize); if (buf == NULL) { return ENGINE_ENOMEM; } pos = sizeof(uint32_t); pt = root_pt; if (pt != NULL && (pt->hash_items > 0 || pt->list_hash_items > 0 || pt->set_hash_items > 0 || pt->btree_hash_items > 0)) { /* including null prefix */ t = localtime(&pt->create_time); written = snprintf(buf+pos, msize-pos, format, "<null>", pt->hash_items+pt->list_hash_items+pt->set_hash_items+pt->btree_hash_items, pt->hash_items,pt->list_hash_items,pt->set_hash_items,pt->btree_hash_items, pt->hash_items_bytes+pt->list_hash_items_bytes+pt->set_hash_items_bytes+pt->btree_hash_items_bytes, pt->hash_items_bytes,pt->list_hash_items_bytes,pt->set_hash_items_bytes,pt->btree_hash_items_bytes, t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); pos += written; } for (i = 0; i < hsize; i++) { pt = engine->assoc.prefix_hashtable[i]; while (pt) { t = localtime(&pt->create_time); written = snprintf(buf+pos, msize-pos, format, _get_prefix(pt), pt->hash_items+pt->list_hash_items+pt->set_hash_items+pt->btree_hash_items, pt->hash_items,pt->list_hash_items,pt->set_hash_items,pt->btree_hash_items, pt->hash_items_bytes+pt->list_hash_items_bytes+pt->set_hash_items_bytes+pt->btree_hash_items_bytes, pt->hash_items_bytes,pt->list_hash_items_bytes,pt->set_hash_items_bytes,pt->btree_hash_items_bytes, t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); pos += written; assert(pos < msize); pt = pt->h_next; } } memcpy(buf+pos, "END\r\n", 6); *(uint32_t*)buf = pos + 5 - sizeof(uint32_t); *(char**)prefix_data = buf; } else { prefix_engine_stats *prefix_stats = (prefix_engine_stats*)prefix_data; if (prefix != NULL) { pt = assoc_prefix_find(engine, engine->server.core->hash(prefix,nprefix,0), prefix, nprefix); } else { pt = root_pt; } if (pt == NULL) { return ENGINE_PREFIX_ENOENT; } prefix_stats->hash_items = pt->hash_items; prefix_stats->hash_items_bytes = pt->hash_items_bytes; prefix_stats->prefix_items = pt->prefix_items; if (prefix != NULL) prefix_stats->tot_prefix_items = pt->prefix_items; else prefix_stats->tot_prefix_items = engine->assoc.tot_prefix_items; } return ENGINE_SUCCESS; }
ENGINE_ERROR_CODE assoc_prefix_link(struct default_engine *engine, hash_item *it, const size_t item_size, prefix_t **pfx_item) { assert(it->nprefix == 0); const char *key = item_get_key(it); size_t nkey = it->nkey; int prefix_depth = 0; int i = 0; char *token; prefix_t *pt = NULL; prefix_t_list_elem prefix_list[DEFAULT_PREFIX_MAX_DEPTH]; // prefix discovering: we don't even know prefix existence at this time while ((token = memchr(key + i + 1, engine->config.prefix_delimiter, nkey - i - 1)) != NULL) { i = token - key; prefix_list[prefix_depth].nprefix = i; prefix_depth++; if (prefix_depth >= DEFAULT_PREFIX_MAX_DEPTH) { break; } } if (prefix_depth == 0) { pt = root_pt; time(&pt->create_time); it->nprefix = nkey; } else { for (i = prefix_depth - 1; i >= 0; i--) { prefix_list[i].hash = engine->server.core->hash(key, prefix_list[i].nprefix, 0); pt = assoc_prefix_find(engine, prefix_list[i].hash, key, prefix_list[i].nprefix); if (pt != NULL) break; } if (i < (prefix_depth - 1)) { if (prefix_depth == 1) { if (!mc_isvalidname(key, prefix_list[0].nprefix)) { return ENGINE_PREFIX_ENAME; /* Invalid prefix name */ } } // need building prefixes if (pt != NULL && i >= 0) { prefix_list[i].pt = pt; // i >= 0 } for (int j = i + 1; j < prefix_depth; j++) { pt = (prefix_t*)malloc(sizeof(prefix_t) + prefix_list[j].nprefix + 1); if (pt == NULL) { for (j = j - 1; j >= i + 1; j--) { assert(prefix_list[j].pt != NULL); _prefix_delete(engine, prefix_list[j].hash, key, prefix_list[j].nprefix); } return ENGINE_ENOMEM; } // building a prefix_t memset(pt, 0, sizeof(prefix_t)); memcpy(pt + 1, key, prefix_list[j].nprefix); memcpy((char*)pt+sizeof(prefix_t)+prefix_list[j].nprefix, "\0", 1); pt->nprefix = prefix_list[j].nprefix; pt->parent_prefix = (j == 0 ? root_pt : prefix_list[j-1].pt); time(&pt->create_time); // registering allocated prefixes to prefix hastable _prefix_insert(engine, prefix_list[j].hash, pt); prefix_list[j].pt = pt; } } // update item information about prefix length it->nprefix = pt->nprefix; } assert(pt != NULL); // update prefix information if ((it->iflag & ITEM_IFLAG_LIST) != 0) { pt->list_hash_items++; pt->list_hash_items_bytes += item_size; } else if ((it->iflag & ITEM_IFLAG_SET) != 0) { pt->set_hash_items++; pt->set_hash_items_bytes += item_size; } else if ((it->iflag & ITEM_IFLAG_BTREE) != 0) { pt->btree_hash_items++; pt->btree_hash_items_bytes += item_size; } else { pt->hash_items++; pt->hash_items_bytes += item_size; } #if 0 // might be used later if (1) { curr_pt = pt->parent_prefix; while (curr_pt != NULL) { curr_pt->tot_hash_items++; curr_pt->tot_hash_items_bytes += item_size; curr_pt = curr_pt->parent_prefix; } } #endif *pfx_item = pt; return ENGINE_SUCCESS; }
static ENGINE_ERROR_CODE do_assoc_get_prefix_stats(struct default_engine *engine, const char *prefix, const int nprefix, void *prefix_data) { struct assoc *assoc = &engine->assoc; prefix_t *pt; if (nprefix < 0) /* all prefix stats */ { const char *format = "PREFIX %s " "itm %llu kitm %llu litm %llu sitm %llu mitm %llu bitm %llu " /* total item count */ "tsz %llu ktsz %llu ltsz %llu stsz %llu mtsz %llu btsz %llu " /* total item bytes */ "time %04d%02d%02d%02d%02d%02d\r\n"; /* create time */ char *buffer; struct tm *t; uint32_t prefix_hsize = hashsize(DEFAULT_PREFIX_HASHPOWER); uint32_t num_prefixes = assoc->tot_prefix_items; uint32_t sum_nameleng = 0; /* sum of prefix name length */ uint32_t i, buflen, pos; /* get # of prefixes and num of prefix names */ assert(root_pt != NULL); if (root_pt->total_count_exclusive > 0) { /* Include the valid null prefix (that is root prefix) */ num_prefixes += 1; sum_nameleng += strlen("<null>"); } for (i = 0; i < prefix_hsize; i++) { pt = assoc->prefix_hashtable[i]; while (pt) { sum_nameleng += pt->nprefix; pt = pt->h_next; } } /* Allocate stats buffer: <length, prefix stats list, tail>. * Check the count of "%llu" and "%02d" in the above format string. * - 10 : the count of "%llu" strings. * - 5 : the count of "%02d" strings. */ buflen = sizeof(uint32_t) /* length */ + sum_nameleng + num_prefixes * (strlen(format) - 2 /* %s replaced by prefix name */ + (12 * (20 - 4)) /* %llu replaced by 20-digit num */ - ( 5 * ( 4 - 2))) /* %02d replaced by 2-digit num */ + sizeof("END\r\n"); /* tail string */ if ((buffer = malloc(buflen)) == NULL) { return ENGINE_ENOMEM; } /* write prefix stats in the buffer */ pos = sizeof(uint32_t); if (num_prefixes > assoc->tot_prefix_items) { /* include root prefix */ pt = root_pt; t = localtime(&pt->create_time); pos += snprintf(buffer+pos, buflen-pos, format, "<null>", pt->total_count_exclusive, pt->items_count[ITEM_TYPE_KV], pt->items_count[ITEM_TYPE_LIST], pt->items_count[ITEM_TYPE_SET], pt->items_count[ITEM_TYPE_MAP], pt->items_count[ITEM_TYPE_BTREE], pt->total_bytes_exclusive, pt->items_bytes[ITEM_TYPE_KV], pt->items_bytes[ITEM_TYPE_LIST], pt->items_bytes[ITEM_TYPE_SET], pt->items_bytes[ITEM_TYPE_MAP], pt->items_bytes[ITEM_TYPE_BTREE], t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); assert(pos < buflen); } for (i = 0; i < prefix_hsize; i++) { pt = assoc->prefix_hashtable[i]; while (pt) { t = localtime(&pt->create_time); pos += snprintf(buffer+pos, buflen-pos, format, _get_prefix(pt), pt->total_count_exclusive, pt->items_count[ITEM_TYPE_KV], pt->items_count[ITEM_TYPE_LIST], pt->items_count[ITEM_TYPE_SET], pt->items_count[ITEM_TYPE_MAP], pt->items_count[ITEM_TYPE_BTREE], pt->total_bytes_exclusive, pt->items_bytes[ITEM_TYPE_KV], pt->items_bytes[ITEM_TYPE_LIST], pt->items_bytes[ITEM_TYPE_SET], pt->items_bytes[ITEM_TYPE_MAP], pt->items_bytes[ITEM_TYPE_BTREE], t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); assert(pos < buflen); pt = pt->h_next; } } memcpy(buffer+pos, "END\r\n", 6); *(uint32_t*)buffer = pos + 5 - sizeof(uint32_t); *(char**)prefix_data = buffer; } else /* prefix stats on the given prefix */ { prefix_engine_stats *prefix_stats = (prefix_engine_stats*)prefix_data; if (prefix != NULL) { pt = assoc_prefix_find(engine, engine->server.core->hash(prefix,nprefix,0), prefix, nprefix); } else { pt = root_pt; } if (pt == NULL) { return ENGINE_PREFIX_ENOENT; } prefix_stats->hash_items = pt->items_count[ITEM_TYPE_KV]; prefix_stats->hash_items_bytes = pt->items_bytes[ITEM_TYPE_KV]; prefix_stats->prefix_items = pt->prefix_items; if (prefix != NULL) prefix_stats->tot_prefix_items = pt->prefix_items; else prefix_stats->tot_prefix_items = assoc->tot_prefix_items; } return ENGINE_SUCCESS; }
ENGINE_ERROR_CODE assoc_prefix_link(struct default_engine *engine, hash_item *it, const size_t item_size) { const char *key = item_get_key(it); size_t nkey = it->nkey; int prefix_depth = 0; int i = 0; char *token; prefix_t *pt = NULL; prefix_t_list_elem prefix_list[DEFAULT_PREFIX_MAX_DEPTH]; // prefix discovering: we don't even know prefix existence at this time while ((token = memchr(key+i+1, engine->config.prefix_delimiter, nkey-i-1)) != NULL) { i = token - key; prefix_list[prefix_depth].nprefix = i; prefix_depth++; if (prefix_depth >= DEFAULT_PREFIX_MAX_DEPTH) { break; } } if (prefix_depth == 0) { pt = root_pt; time(&pt->create_time); /* save prefix pointer in hash_item */ it->pfxptr = pt; } else { for (i = prefix_depth - 1; i >= 0; i--) { prefix_list[i].hash = engine->server.core->hash(key, prefix_list[i].nprefix, 0); pt = assoc_prefix_find(engine, prefix_list[i].hash, key, prefix_list[i].nprefix); if (pt != NULL) break; } if (i < (prefix_depth - 1)) { if (prefix_depth == 1) { if (!mc_isvalidname(key, prefix_list[0].nprefix)) { return ENGINE_PREFIX_ENAME; /* Invalid prefix name */ } } // need building prefixes if (pt != NULL && i >= 0) { prefix_list[i].pt = pt; // i >= 0 } for (int j = i + 1; j < prefix_depth; j++) { pt = (prefix_t*)malloc(sizeof(prefix_t) + prefix_list[j].nprefix + 1); if (pt == NULL) { for (j = j - 1; j >= i + 1; j--) { assert(prefix_list[j].pt != NULL); _prefix_delete(engine, prefix_list[j].hash, key, prefix_list[j].nprefix); } return ENGINE_ENOMEM; } // building a prefix_t memset(pt, 0, sizeof(prefix_t)); memcpy(pt + 1, key, prefix_list[j].nprefix); memcpy((char*)pt+sizeof(prefix_t)+prefix_list[j].nprefix, "\0", 1); pt->nprefix = prefix_list[j].nprefix; if (PREFIX_IS_RSVD(key, pt->nprefix)) { pt->internal = 1; /* internal prefix */ } pt->parent_prefix = (j == 0 ? root_pt : prefix_list[j-1].pt); time(&pt->create_time); // registering allocated prefixes to prefix hastable _prefix_insert(engine, prefix_list[j].hash, pt); prefix_list[j].pt = pt; } } /* save prefix pointer in hash_item */ it->pfxptr = pt; } assert(pt != NULL); /* update prefix information */ int item_type = GET_ITEM_TYPE(it); pt->items_count[item_type] += 1; pt->items_bytes[item_type] += item_size; pt->total_count_exclusive += 1; pt->total_bytes_exclusive += item_size; #if 0 // might be used later if (1) { prefix_t *curr_pt = pt->parent_prefix; while (curr_pt != NULL) { curr_pt->total_count_inclusive += 1; curr_pt->total_bytes_inclusive += item_size; curr_pt = curr_pt->parent_prefix; } } #endif return ENGINE_SUCCESS; }