(unsigned)s->svr.infra_cache_count)) return 0;
if(!ssl_printf(ssl, "key.cache.count"SQ"%u\n",
(unsigned)s->svr.key_cache_count)) return 0;
+ /* max collisions */
+ if(!ssl_printf(ssl, "msg.cache.max_collisions"SQ"%u\n",
+ (unsigned)s->svr.msg_cache_max_collisions)) return 0;
+ if(!ssl_printf(ssl, "rrset.cache.max_collisions"SQ"%u\n",
+ (unsigned)s->svr.rrset_cache_max_collisions)) return 0;
/* applied RPZ actions */
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
s->svr.queries_ratelimited = (long long)get_queries_ratelimit(worker, reset);
/* get cache sizes */
- s->svr.msg_cache_count = (long long)count_slabhash_entries(worker->env.msg_cache);
- s->svr.rrset_cache_count = (long long)count_slabhash_entries(&worker->env.rrset_cache->table);
+ get_slabhash_stats(worker->env.msg_cache,
+ &s->svr.msg_cache_count, &s->svr.msg_cache_max_collisions);
+ get_slabhash_stats(&worker->env.rrset_cache->table,
+ &s->svr.rrset_cache_count, &s->svr.rrset_cache_max_collisions);
s->svr.infra_cache_count = (long long)count_slabhash_entries(worker->env.infra_cache->hosts);
if(worker->env.key_cache)
s->svr.key_cache_count = (long long)count_slabhash_entries(worker->env.key_cache->slab);
/** number of key cache entries */
long long key_cache_count;
+ /** maximum number of collisions in the msg cache */
+ long long msg_cache_max_collisions;
+ /** maximum number of collisions in the rrset cache */
+ long long rrset_cache_max_collisions;
+
/** number of queries that used dnscrypt */
long long num_query_dnscrypt_crypted;
/** number of queries that queried dnscrypt certificates */
PR_UL("rrset.cache.count", s->svr.rrset_cache_count);
PR_UL("infra.cache.count", s->svr.infra_cache_count);
PR_UL("key.cache.count", s->svr.key_cache_count);
+ /* max collisions */
+ PR_UL("msg.cache.max_collisions", s->svr.msg_cache_max_collisions);
+ PR_UL("rrset.cache.max_collisions", s->svr.rrset_cache_max_collisions);
/* applied RPZ actions */
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
bin_overflow_remove(&bin, &k->entry);
/* find in empty list */
- unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
+ unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
/* insert */
lock_quick_lock(&bin.lock);
lock_quick_unlock(&bin.lock);
/* find, hash not OK. */
- unit_assert( bin_find_entry(table, &bin, myhash(13), k) == NULL );
+ unit_assert( bin_find_entry(table, &bin, myhash(13), k, NULL) == NULL );
/* find, hash OK, but cmp not */
unit_assert( k->entry.hash == k2->entry.hash );
- unit_assert( bin_find_entry(table, &bin, h, k2) == NULL );
+ unit_assert( bin_find_entry(table, &bin, h, k2, NULL) == NULL );
/* find, hash OK, and cmp too */
- unit_assert( bin_find_entry(table, &bin, h, k) == &k->entry );
+ unit_assert( bin_find_entry(table, &bin, h, k, NULL) == &k->entry );
/* remove the element */
lock_quick_lock(&bin.lock);
bin_overflow_remove(&bin, &k->entry);
lock_quick_unlock(&bin.lock);
- unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
+ unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
/* prepend two different elements; so the list is long */
/* one has the same hash, but different cmp */
lock_quick_unlock(&bin.lock);
/* find, hash not OK. */
- unit_assert( bin_find_entry(table, &bin, myhash(13), k) == NULL );
+ unit_assert( bin_find_entry(table, &bin, myhash(13), k, NULL) == NULL );
/* find, hash OK, but cmp not */
unit_assert( k->entry.hash == k2->entry.hash );
- unit_assert( bin_find_entry(table, &bin, h, k2) == NULL );
+ unit_assert( bin_find_entry(table, &bin, h, k2, NULL) == NULL );
/* find, hash OK, and cmp too */
- unit_assert( bin_find_entry(table, &bin, h, k) == &k->entry );
+ unit_assert( bin_find_entry(table, &bin, h, k, NULL) == &k->entry );
/* remove middle element */
- unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4)
+ unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4, NULL)
== &k4->entry );
lock_quick_lock(&bin.lock);
bin_overflow_remove(&bin, &k4->entry);
lock_quick_unlock(&bin.lock);
- unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4) == NULL);
+ unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4, NULL) == NULL);
/* remove last element */
lock_quick_lock(&bin.lock);
bin_overflow_remove(&bin, &k->entry);
lock_quick_unlock(&bin.lock);
- unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
+ unit_assert( bin_find_entry(table, &bin, h, k, NULL) == NULL );
lock_quick_destroy(&bin.lock);
delkey(k);
table->num = 0;
table->space_used = 0;
table->space_max = maxmem;
+ table->max_collisions = 0;
table->array = calloc(table->size, sizeof(struct lruhash_bin));
if(!table->array) {
lock_quick_destroy(&table->lock);
struct lruhash_entry*
bin_find_entry(struct lruhash* table,
- struct lruhash_bin* bin, hashvalue_type hash, void* key)
+ struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions)
{
+ size_t c = 0;
struct lruhash_entry* p = bin->overflow_list;
while(p) {
if(p->hash == hash && table->compfunc(p->key, key) == 0)
- return p;
+ break;
+ c++;
p = p->overflow_next;
}
- return NULL;
+ if (collisions != NULL)
+ *collisions = c;
+ return p;
}
void
struct lruhash_bin* bin;
struct lruhash_entry* found, *reclaimlist=NULL;
size_t need_size;
+ size_t collisions;
fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
lock_quick_lock(&bin->lock);
/* see if entry exists already */
- if(!(found=bin_find_entry(table, bin, hash, entry->key))) {
+ if(!(found=bin_find_entry(table, bin, hash, entry->key, &collisions))) {
/* if not: add to bin */
entry->overflow_next = bin->overflow_list;
bin->overflow_list = entry;
lru_front(table, entry);
table->num++;
+ if (table->max_collisions < collisions)
+ table->max_collisions = collisions;
table->space_used += need_size;
} else {
/* if so: update data - needs a writelock */
lock_quick_lock(&table->lock);
bin = &table->array[hash & table->size_mask];
lock_quick_lock(&bin->lock);
- if((entry=bin_find_entry(table, bin, hash, key)))
+ if((entry=bin_find_entry(table, bin, hash, key, NULL)))
lru_touch(table, entry);
lock_quick_unlock(&table->lock);
lock_quick_lock(&table->lock);
bin = &table->array[hash & table->size_mask];
lock_quick_lock(&bin->lock);
- if((entry=bin_find_entry(table, bin, hash, key))) {
+ if((entry=bin_find_entry(table, bin, hash, key, NULL))) {
bin_overflow_remove(bin, entry);
lru_remove(table, entry);
} else {
struct lruhash_bin* bin;
struct lruhash_entry* found, *reclaimlist = NULL;
size_t need_size;
+ size_t collisions;
fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
lock_quick_lock(&bin->lock);
/* see if entry exists already */
- if ((found = bin_find_entry(table, bin, hash, entry->key)) != NULL) {
+ if ((found = bin_find_entry(table, bin, hash, entry->key, &collisions)) != NULL) {
/* if so: keep the existing data - acquire a writelock */
lock_rw_wrlock(&found->lock);
}
bin->overflow_list = entry;
lru_front(table, entry);
table->num++;
+ if (table->max_collisions < collisions)
+ table->max_collisions = collisions;
table->space_used += need_size;
/* return the entry that was presented, and lock it */
found = entry;
size_t space_used;
/** the amount of space the hash table is maximally allowed to use. */
size_t space_max;
+ /** the maximum collisions were detected during the lruhash_insert operations. */
+ size_t max_collisions;
};
/**
* @param bin: hash bin to look into.
* @param hash: hash value to look for.
* @param key: key to look for.
+ * @param collisions: how many collisions were found during the search.
* @return: the entry or NULL if not found.
*/
struct lruhash_entry* bin_find_entry(struct lruhash* table,
- struct lruhash_bin* bin, hashvalue_type hash, void* key);
+ struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions);
/**
* Remove entry from bin overflow chain.
}
return cnt;
}
+
+void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisions)
+{
+ size_t slab, cnt = 0, max_collisions = 0;
+
+ for(slab=0; slab<sh->size; slab++) {
+ lock_quick_lock(&sh->array[slab]->lock);
+ cnt += sh->array[slab]->num;
+ if (max_collisions < sh->array[slab]->max_collisions) {
+ max_collisions = sh->array[slab]->max_collisions;
+ }
+ lock_quick_unlock(&sh->array[slab]->lock);
+ }
+ if (num != NULL)
+ *num = cnt;
+ if (collisions != NULL)
+ *collisions = max_collisions;
+}
*/
size_t count_slabhash_entries(struct slabhash* table);
+/**
+ * Retrieves number of items in slabhash and the current max collision level
+ * @param table: slabbed hash table.
+ * @param entries_count: where to save the current number of elements.
+ * @param max_collisions: where to save the current max collisions level.
+ */
+void get_slabhash_stats(struct slabhash* table,
+ long long* entries_count, long long* max_collisions);
+
/* --- test representation --- */
/** test structure contains test key */
struct slabhash_testkey {