Skip to content

Commit

Permalink
hash: bound the hashtable size
Browse files Browse the repository at this point in the history
The hashtable is being used as a cache for kubernetes metadata and
environment variables. The drawback is the hashtable is unbounded and
doesn't resize itself. Thus over time it uses more memory and
performance becomes worse as the chains get longer.

For Kubernetes this can be particularly bad in busy environments as the
cache key uses podname, which will change every time a deployment
occurs.

By bounding the hashtable we ensure memory doesn't become unbounded and
performance remains constant. For a cache it should be okay to evict
old entries.

This implementation tries to keep things simple by performing eviction
on adding a key to the table. As long as the hashtable size isn't
exceeded, the behaviour remains the same as before - the table is looked
up via hash function, and the table chain is extended.

When the hashtable size is exceeded, the housekeeping will remove all
prior keys in a random chain to bring down the hashtable size.  The
advantage of a random eviction is it's simple and fast versus
constructing an LRU list (or something else).
  • Loading branch information
James Ravn authored and edsiper committed Nov 22, 2017
1 parent b5e9a56 commit fad0707
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 20 deletions.
2 changes: 2 additions & 0 deletions include/fluent-bit/flb_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#define FLB_HASH_EVICT_NONE 0
#define FLB_HASH_EVICT_OLDER 1
#define FLB_HASH_EVICT_LESS_USED 2
#define FLB_HASH_EVICT_RANDOM 3

struct flb_hash_entry {
time_t created;
Expand All @@ -38,6 +39,7 @@ struct flb_hash_entry {
size_t key_len;
char *val;
size_t val_size;
struct flb_hash_table *table; /* link to parent flb_hash_table */
struct mk_list _head; /* link to flb_hash_table->chains */
struct mk_list _head_parent; /* link to flb_hash->entries */
};
Expand Down
5 changes: 3 additions & 2 deletions plugins/filter_kubernetes/kube_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,9 @@ struct flb_kube *flb_kube_conf_create(struct flb_filter_instance *i,
ctx->api_https ? "https" : "http",
ctx->api_host, ctx->api_port);

ctx->hash_table = flb_hash_create(FLB_HASH_TABLE_SIZE,
FLB_HASH_EVICT_NONE, -1);
ctx->hash_table = flb_hash_create(FLB_HASH_EVICT_RANDOM,
FLB_HASH_TABLE_SIZE,
FLB_HASH_TABLE_SIZE);
if (!ctx->hash_table) {
flb_kube_conf_destroy(ctx);
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/flb_env.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ struct flb_env *flb_env_create()
}

/* Create the hash-table */
ht = flb_hash_create(FLB_ENV_SIZE, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, FLB_ENV_SIZE, -1);
if (!ht) {
flb_free(env);
return NULL;
Expand Down
38 changes: 31 additions & 7 deletions src/flb_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,12 @@ static unsigned int gen_hash(const void *key, int len)
return (unsigned int) h;
}

static inline void flb_hash_entry_free(struct flb_hash_entry *entry)
static inline void flb_hash_entry_free(struct flb_hash *ht, struct flb_hash_entry *entry)
{
mk_list_del(&entry->_head);
mk_list_del(&entry->_head_parent);
entry->table->count--;
ht->total_count--;
flb_free(entry->key);
flb_free(entry->val);
flb_free(entry);
Expand All @@ -114,6 +116,7 @@ struct flb_hash *flb_hash_create(int evict_mode, size_t size, int max_entries)
ht->max_entries = max_entries;
ht->total_count = 0;
ht->size = size;
ht->total_count = 0;
ht->table = flb_calloc(1, sizeof(struct flb_hash_table) * size);
if (!ht->table) {
flb_errno();
Expand Down Expand Up @@ -143,15 +146,33 @@ void flb_hash_destroy(struct flb_hash *ht)
table = &ht->table[i];
mk_list_foreach_safe(head, tmp, &table->chains) {
entry = mk_list_entry(head, struct flb_hash_entry, _head);
flb_hash_entry_free(entry);
table->count--;
flb_hash_entry_free(ht, entry);
}
}

flb_free(ht->table);
flb_free(ht);
}

static void flb_hash_evict_random(struct flb_hash *ht)
{
int id;
int count = 0;
struct mk_list *tmp;
struct mk_list *head;
struct flb_hash_entry *entry;

id = random() % ht->total_count;
mk_list_foreach_safe(head, tmp, &ht->entries) {
if (id == count) {
entry = mk_list_entry(head, struct flb_hash_entry, _head_parent);
flb_hash_entry_free(ht, entry);
break;
}
count++;
}
}

int flb_hash_add(struct flb_hash *ht, char *key, int key_len,
char *val, size_t val_size)
{
Expand Down Expand Up @@ -179,6 +200,9 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len,
else if (ht->evict_mode == FLB_HASH_EVICT_LESS_USED) {

}
else if (ht->evict_mode == FLB_HASH_EVICT_RANDOM) {
flb_hash_evict_random(ht);
}
}

/* Generate hash number */
Expand Down Expand Up @@ -215,6 +239,7 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len,

/* Link the new entry in our table at the end of the list */
table = &ht->table[id];
entry->table = table;

/* Check if the new key already exists */
if (table->count == 0) {
Expand All @@ -225,8 +250,7 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len,
mk_list_foreach_safe(head, tmp, &table->chains) {
old = mk_list_entry(head, struct flb_hash_entry, _head);
if (strcmp(old->key, entry->key) == 0) {
flb_hash_entry_free(old);
table->count--;
flb_hash_entry_free(ht, old);
break;
}
}
Expand All @@ -235,6 +259,7 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len,
}

table->count++;
ht->total_count++;

return id;
}
Expand Down Expand Up @@ -382,8 +407,7 @@ int flb_hash_del(struct flb_hash *ht, char *key)
return -1;
}

flb_hash_entry_free(entry);
table->count--;
flb_hash_entry_free(ht, entry);

return 0;
}
48 changes: 38 additions & 10 deletions tests/internal/hashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void test_create_zero()
{
struct flb_hash *ht;

ht = flb_hash_create(0, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 0, -1);
TEST_CHECK(ht == NULL);
}

Expand All @@ -91,7 +91,7 @@ void test_single()
size_t out_size;
struct flb_hash *ht;

ht = flb_hash_create(1, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 1, -1);
TEST_CHECK(ht != NULL);

ret = ht_add(ht, "key", "value");
Expand All @@ -112,7 +112,7 @@ void test_small_table()
struct map *m;
struct flb_hash *ht;

ht = flb_hash_create(1, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 8, -1);
TEST_CHECK(ht != NULL);

for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
Expand All @@ -129,7 +129,7 @@ void test_medium_table()
struct map *m;
struct flb_hash *ht;

ht = flb_hash_create(8, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 8, -1);
TEST_CHECK(ht != NULL);

for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
Expand All @@ -145,16 +145,16 @@ void test_chaining()
int i;
int inserts = 0;
int count;
int total = 0;
int chains = 0;
struct map *m;
struct mk_list *head;
struct flb_hash_table *table;
struct flb_hash *ht;

ht = flb_hash_create(8, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 8, -1);
TEST_CHECK(ht != NULL);

for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
for (i = 0; i < 8; i++) {
m = &entries[i];
ht_add(ht, m->key, m->val);
inserts++;
Expand All @@ -168,11 +168,13 @@ void test_chaining()
}
TEST_CHECK(count == table->count);

total += count;
if (count > 0) {
chains++;
}
}

/* Tests diff between total, new minus 3 overrides */
TEST_CHECK(total == inserts - 3);
TEST_CHECK(chains == inserts - 3);
flb_hash_destroy(ht);
}

Expand All @@ -187,7 +189,7 @@ void test_delete_all()
struct flb_hash_table *table;
struct flb_hash *ht;

ht = flb_hash_create(8, FLB_HASH_EVICT_NONE, -1);
ht = flb_hash_create(FLB_HASH_EVICT_NONE, 8, -1);
TEST_CHECK(ht != NULL);

total = sizeof(entries) / sizeof(struct map);
Expand All @@ -214,12 +216,38 @@ void test_delete_all()
flb_hash_destroy(ht);
}

void test_random_eviction()
{
int ret;
char *out_buf;
size_t out_size;
struct flb_hash *ht;

ht = flb_hash_create(FLB_HASH_EVICT_RANDOM, 8, 1);
TEST_CHECK(ht != NULL);

ret = ht_add(ht, "key1", "value1");
TEST_CHECK(ret != -1);

ret = ht_add(ht, "key2", "value2");
TEST_CHECK(ret != -1);

ret = flb_hash_get(ht, "key1", 4, &out_buf, &out_size);
TEST_CHECK(ret == -1);

ret = flb_hash_get(ht, "key2", 4, &out_buf, &out_size);
TEST_CHECK(ret >= 0);

flb_hash_destroy(ht);
}

TEST_LIST = {
{ "zero_size", test_create_zero },
{ "single", test_single },
{ "small_table", test_small_table },
{ "medium_table", test_medium_table },
{ "chaining_count", test_chaining },
{ "delete_all", test_delete_all },
{ "random_eviction", test_random_eviction },
{ 0 }
};

0 comments on commit fad0707

Please sign in to comment.