Commit 678e9361 authored by Marek Vavruša's avatar Marek Vavruša Committed by Jan Kadlec

namedb: fixed leak, lmdb iteration

Conflicts:
	src/common/namedb/namedb_lmdb.c
	src/common/namedb/namedb_trie.c
parent 56b72ec7
......@@ -329,6 +329,7 @@ tests/fdset.c
tests/hattrie.c
tests/hhash.c
tests/journal.c
tests/namedb.c
tests/node.c
tests/pkt.c
tests/process_answer.c
......
......@@ -19,7 +19,8 @@
#include "libknot/dname.h"
enum {
NAMEDB_RDONLY = 1 << 0
NAMEDB_RDONLY = 1 << 0,
NAMEDB_SORTED = 1 << 1
};
typedef void knot_namedb_t;
......@@ -37,6 +38,8 @@ typedef struct knot_txn {
struct namedb_api {
const char *name;
/* Context operations */
knot_namedb_t* (*init)(const char *handle, mm_ctx_t *mm);
......@@ -51,17 +54,17 @@ struct namedb_api {
/* Data access */
int (*count)(knot_txn_t *txn);
int (*find)(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val, unsigned op);
int (*insert)(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val);
int (*del)(knot_txn_t *txn, const knot_dname_t *key);
int (*find)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags);
int (*insert)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags);
int (*del)(knot_txn_t *txn,knot_val_t *key);
/* Iteration */
knot_iter_t *(*iter_begin)(knot_txn_t *txn, unsigned flags);
int (*iter_next)(knot_iter_t *iter);
const knot_dname_t *(*iter_key)(knot_iter_t *iter);
knot_iter_t *(*iter_next)(knot_iter_t *iter);
int (*iter_key)(knot_iter_t *iter, knot_val_t *key);
int (*iter_val)(knot_iter_t *iter, knot_val_t *val);
int (*iter_finish)(knot_iter_t *iter);
void (*iter_finish)(knot_iter_t *iter);
};
#ifdef HAVE_LMDB
#include <lmdb.h>
#include "common/namedb/namedb_lmdb.h"
#include "libknot/errcode.h"
#define NAME_TO_KEY(key) { knot_dname_size(key), (void *)(key) }
struct lmdb_env
{
MDB_dbi dbi;
......@@ -54,7 +54,7 @@ static void dbase_close(struct lmdb_env *env)
mdb_env_close(env->env);
}
knot_namedb_t* init(const char *handle, mm_ctx_t *mm)
static knot_namedb_t* init(const char *handle, mm_ctx_t *mm)
{
struct lmdb_env *env = mm_alloc(mm, sizeof(struct lmdb_env));
if (env == NULL) {
......@@ -72,7 +72,7 @@ knot_namedb_t* init(const char *handle, mm_ctx_t *mm)
return env;
}
void deinit(knot_namedb_t *db)
static void deinit(knot_namedb_t *db)
{
struct lmdb_env *env = db;
......@@ -80,7 +80,7 @@ void deinit(knot_namedb_t *db)
mm_free(env->pool, env);
}
int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
{
txn->db = db;
txn->txn = NULL;
......@@ -99,7 +99,7 @@ int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
return KNOT_EOK;
}
int txn_commit(knot_txn_t *txn)
static int txn_commit(knot_txn_t *txn)
{
int ret = mdb_txn_commit((MDB_txn *)txn->txn);
if (ret != 0) {
......@@ -109,12 +109,12 @@ int txn_commit(knot_txn_t *txn)
return KNOT_EOK;
}
void txn_abort(knot_txn_t *txn)
static void txn_abort(knot_txn_t *txn)
{
mdb_txn_abort((MDB_txn *)txn->txn);
}
int count(knot_txn_t *txn)
static int count(knot_txn_t *txn)
{
struct lmdb_env *env = txn->db;
......@@ -127,20 +127,16 @@ int count(knot_txn_t *txn)
return stat.ms_entries;
}
int find(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val, unsigned op)
static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = NAME_TO_KEY(key);
MDB_val db_key = { key->len, key->data };
MDB_val data = { 0, NULL };
int ret = mdb_get(txn->txn, env->dbi, &db_key, &data);
if (ret != 0) {
if (ret == MDB_NOTFOUND) {
return KNOT_ENOENT;
} else {
return KNOT_ERROR;
}
return KNOT_ERROR;
}
val->data = data.mv_data;
......@@ -148,24 +144,27 @@ int find(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val, unsigned op)
return KNOT_EOK;
}
int insert(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val)
static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = NAME_TO_KEY(key);
MDB_val db_key = { key->len, key->data };
MDB_val data = { val->len, val->data };
int ret = mdb_put(txn->txn, env->dbi, &db_key, &data, 0);
if (ret != 0) {
if (ret == MDB_KEYEXIST) {
return KNOT_EEXIST;
}
return KNOT_ERROR;
}
return KNOT_EOK;
}
int del(knot_txn_t *txn, const knot_dname_t *key)
static int del(knot_txn_t *txn, knot_val_t *key)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = NAME_TO_KEY(key);
MDB_val db_key = { key->len, key->data };
MDB_val data = { 0, NULL };
int ret = mdb_del(txn->txn, env->dbi, &db_key, &data);
......@@ -176,34 +175,82 @@ int del(knot_txn_t *txn, const knot_dname_t *key)
return KNOT_EOK;
}
knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
{
return NULL; /* NOTIMPL */
struct lmdb_env *env = txn->db;
MDB_cursor *cursor = NULL;
int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor);
if (ret != 0) {
return NULL;
}
ret = mdb_cursor_get(cursor, NULL, NULL, MDB_FIRST);
if (ret != 0) {
mdb_cursor_close(cursor);
return NULL;
}
return cursor;
}
int iter_next(knot_iter_t *iter)
static knot_iter_t *iter_next(knot_iter_t *iter)
{
return KNOT_ENOTSUP;
MDB_cursor *cursor = iter;
int ret = mdb_cursor_get(cursor, NULL, NULL, MDB_NEXT);
if (ret != 0) {
mdb_cursor_close(cursor);
return NULL;
}
return cursor;
}
const knot_dname_t *iter_key(knot_iter_t *iter)
static int iter_key(knot_iter_t *iter, knot_val_t *key)
{
return NULL; /* NOTIMPL */
MDB_cursor *cursor = iter;
MDB_val mdb_key, mdb_val;
int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
if (ret != 0) {
return KNOT_ERROR;
}
key->data = mdb_key.mv_data;
key->len = mdb_key.mv_size;
return KNOT_EOK;
}
int iter_val(knot_iter_t *iter, knot_val_t *val)
static int iter_val(knot_iter_t *iter, knot_val_t *val)
{
return KNOT_ENOTSUP;
MDB_cursor *cursor = iter;
MDB_val mdb_key, mdb_val;
int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
if (ret != 0) {
return KNOT_ERROR;
}
val->data = mdb_val.mv_data;
val->len = mdb_val.mv_size;
return KNOT_EOK;
}
int iter_finish(knot_iter_t *iter)
static void iter_finish(knot_iter_t *iter)
{
return KNOT_ENOTSUP;
if (iter == NULL) {
return;
}
MDB_cursor *cursor = iter;
mdb_cursor_close(cursor);
}
struct namedb_api *namedb_lmdb_api(void)
{
static struct namedb_api api = {
"lmdb",
init, deinit,
txn_begin, txn_commit, txn_abort,
count, find, insert, del,
......@@ -213,3 +260,11 @@ struct namedb_api *namedb_lmdb_api(void)
return &api;
}
#else
struct namedb_api *namedb_lmdb_api(void)
{
return NULL;
}
#endif
\ No newline at end of file
......@@ -2,77 +2,118 @@
#include "common-knot/hattrie/hat-trie.h"
#include "libknot/errcode.h"
knot_namedb_t* init(const char *handle, mm_ctx_t *mm)
static knot_namedb_t* init(const char *handle, mm_ctx_t *mm)
{
return NULL; /* NOTIMPL */
return hattrie_create_n(TRIE_BUCKET_SIZE, mm);
}
void deinit(knot_namedb_t *db)
static void deinit(knot_namedb_t *db)
{
hattrie_free((hattrie_t *)db);
}
int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
{
return KNOT_ENOTSUP;
txn->txn = (void *)(size_t)flags;
txn->db = db;
return KNOT_EOK; /* N/A */
}
int txn_commit(knot_txn_t *txn)
static int txn_commit(knot_txn_t *txn)
{
return KNOT_ENOTSUP;
/* Rebuild order index only for WR transactions. */
if ((size_t)txn->txn & NAMEDB_RDONLY) {
return KNOT_EOK;
}
hattrie_build_index((hattrie_t *)txn->db);
return KNOT_EOK;
}
void txn_abort(knot_txn_t *txn)
static void txn_abort(knot_txn_t *txn)
{
}
int count(knot_txn_t *txn)
static int count(knot_txn_t *txn)
{
return KNOT_ENOTSUP;
return hattrie_weight((hattrie_t *)txn->db);
}
int find(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val, unsigned op)
static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
return KNOT_ENOTSUP;
value_t *ret = hattrie_tryget((hattrie_t *)txn->db, key->data, key->len);
if (ret == NULL) {
return KNOT_ENOENT;
}
val->data = *ret;
val->len = sizeof(value_t); /* Trie doesn't support storing length. */
return KNOT_EOK;
}
int insert(knot_txn_t *txn, const knot_dname_t *key, knot_val_t *val)
static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
return KNOT_ENOTSUP;
value_t *ret = hattrie_get((hattrie_t *)txn->db, key->data, key->len);
if (ret == NULL) {
return KNOT_ENOMEM;
}
*ret = val->data;
return KNOT_EOK;
}
int del(knot_txn_t *txn, const knot_dname_t *key)
static int del(knot_txn_t *txn, knot_val_t *key)
{
return KNOT_ENOTSUP;
return hattrie_del((hattrie_t *)txn->db, key->data, key->len);
}
knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
{
return NULL; /* NOTIMPL */
return hattrie_iter_begin((hattrie_t *)txn->db, (flags & NAMEDB_SORTED));
}
int iter_next(knot_iter_t *iter)
static knot_iter_t *iter_next(knot_iter_t *iter)
{
return KNOT_ENOTSUP;
hattrie_iter_next((hattrie_iter_t *)iter);
if (hattrie_iter_finished((hattrie_iter_t *)iter)) {
hattrie_iter_free((hattrie_iter_t *)iter);
return NULL;
}
return iter;
}
const knot_dname_t *iter_key(knot_iter_t *iter)
static int iter_key(knot_iter_t *iter, knot_val_t *val)
{
return NULL; /* NOTIMPL */
val->data = (void *)hattrie_iter_key((hattrie_iter_t *)iter, &val->len);
if (val->data == NULL) {
return KNOT_ENOENT;
}
return KNOT_EOK;
}
int iter_val(knot_iter_t *iter, knot_val_t *val)
static int iter_val(knot_iter_t *iter, knot_val_t *val)
{
return KNOT_ENOTSUP;
value_t *ret = hattrie_iter_val((hattrie_iter_t *)iter);
if (ret == NULL) {
return KNOT_ENOENT;
}
val->data = *ret;
val->len = sizeof(value_t);
return KNOT_EOK;
}
int iter_finish(knot_iter_t *iter)
static void iter_finish(knot_iter_t *iter)
{
return KNOT_ENOTSUP;
hattrie_iter_free((hattrie_iter_t *)iter);
}
struct namedb_api *namedb_trie_api(void)
{
static struct namedb_api api = {
"hattrie",
init, deinit,
txn_begin, txn_commit, txn_abort,
count, find, insert, del,
......@@ -81,3 +122,4 @@ struct namedb_api *namedb_trie_api(void)
return &api;
}
......@@ -113,26 +113,13 @@ static void log_zone_load_info(const zone_t *zone, const char *zone_name,
log_zone_info(zone->name, "zone %s, serial %u", action, serial);
}
static bool timer_set(const struct timer_storage *timers, const int event_id)
static int create_zone_reload(zone_t *zone, conf_zone_t *zone_conf, server_t *server,
zone_t *old_zone)
{
for (size_t i = 0; i < PERSISTENT_EVENT_COUNT; ++i) {
if (timers[i].id == event_id) {
return true;
}
}
return false;
}
static time_t get_timer(const struct timer_storage *timers, const int event_id)
static int create_zone_restart(zone_t *zone, conf_zone_t *zone_conf, server_t *server)
{
for (size_t i = 0; i < PERSISTENT_EVENT_COUNT; ++i) {
if (timers[i].id == event_id) {
return timers[i].timer;
}
}
return 0;
}
/*!
......@@ -149,7 +136,7 @@ static zone_t *create_zone(conf_zone_t *zone_conf, server_t *server, zone_t *old
assert(zone_conf);
assert(server);
struct timer_storage timers[PERSISTENT_EVENT_COUNT];
time_t timers[ZONE_EVENT_COUNT];
memset(timers, 0, sizeof(timers));
zone_t *zone = zone_new(zone_conf);
......@@ -185,20 +172,10 @@ static zone_t *create_zone(conf_zone_t *zone_conf, server_t *server, zone_t *old
zone_events_enqueue(zone, ZONE_EVENT_RELOAD);
/* Replan DDNS processing if there are pending updates. */
zone_events_replan_ddns(zone, old_zone);
if (!old_zone) {
/* Reuse timers. */
}
break;
case ZONE_STATUS_BOOSTRAP: // TIMERS: sort bootstrap
if (!old_zone && timer_set(timers, ZONE_EVENT_REFRESH)) {
zone_events_schedule(zone, ZONE_EVENT_REFRESH,
get_timer(timers, ZONE_EVENT_REFRESH));
} else {
zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW);
}
#warning should not call this if we have old zone!
zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW);
break;
case ZONE_STATUS_NOT_FOUND:
break;
......
......@@ -39,6 +39,7 @@ const error_table_t error_messages[] = {
{ KNOT_EISCONN, "already connected" },
{ KNOT_EADDRINUSE, "address already in use" },
{ KNOT_ENOENT, "not exists" },
{ KNOT_EEXIST, "already exists" },
{ KNOT_ERANGE, "value is out of range" },
/* General errors. */
......
......@@ -55,6 +55,7 @@ enum knot_error {
KNOT_EISCONN = knot_errno_to_error(EISCONN),
KNOT_EADDRINUSE = knot_errno_to_error(EADDRINUSE),
KNOT_ENOENT = knot_errno_to_error(ENOENT),
KNOT_EEXIST = knot_errno_to_error(EEXIST),
KNOT_ERANGE = knot_errno_to_error(ERANGE),
/* General errors. */
......
......@@ -45,7 +45,8 @@ check_PROGRAMS = \
worker_pool \
worker_queue \
zone_events \
zone_update
zone_update \
namedb
check-compile-only: $(check_PROGRAMS)
......
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <time.h>
#include <tap/basic.h>
#include "libknot/common.h"
#include "common/mempool.h"
#include "common/mem.h"
#include "common/namedb/namedb_lmdb.h"
#include "common/namedb/namedb_trie.h"
/* Constants. */
#define KEY_MAXLEN 64
#define KEY_SET(key, str) key.data = (str); key.len = strlen(str) + 1
/*! \brief Generate random key. */
static const char *alphabet = "abcdefghijklmn0123456789";
static char *str_key_rand(size_t len, mm_ctx_t *pool)
{
char *s = mm_alloc(pool, len);
memset(s, 0, len);
for (unsigned i = 0; i < len - 1; ++i) {
s[i] = alphabet[rand() % strlen(alphabet)];
}
return s;
}
/* UCW array sorting defines. */
#define ASORT_PREFIX(X) str_key_##X
#define ASORT_KEY_TYPE char*
#define ASORT_LT(x, y) (strcmp((x), (y)) < 0)
#include "common/array-sort.h"
static int namedb_test_set(unsigned nkeys, char **keys, char *dbid,
struct namedb_api *api, mm_ctx_t *pool)
{
/* Create database */
knot_namedb_t *db = api->init(dbid, pool);
ok(db != NULL, "%s: create", api->name);
/* Start WR transaction. */
knot_txn_t txn;
int ret = api->txn_begin(db, &txn, 0);
ok(ret == KNOT_EOK, "%s: txn_begin(WR)", api->name);
/* Insert keys */
knot_val_t key, val;
bool passed = true;
for (unsigned i = 0; i < nkeys; ++i) {
KEY_SET(key, keys[i]);
val.len = sizeof(void*);
val.data = &key.data;
ret = api->insert(&txn, &key, &val, 0);
if (ret != KNOT_EOK && ret != KNOT_EEXIST) {
passed = false;
break;
}
}
ok(passed, "%s: insert", api->name);
/* Commit WR transaction. */
ret = api->txn_commit(&txn);
ok(ret == KNOT_EOK, "%s: txn_commit(WR)", api->name);
/* Start RD transaction. */
ret = api->txn_begin(db, &txn, NAMEDB_RDONLY);
ok(ret == KNOT_EOK, "%s: txn_begin(RD)", api->name);
/* Lookup all keys */
passed = true;
for (unsigned i = 0; i < nkeys; ++i) {
KEY_SET(key, keys[i]);
ret = api->find(&txn, &key, &val, 0);
if (ret != KNOT_EOK) {
passed = false;
break;
}
const char **stored_key = val.data;
if (strcmp(*stored_key, keys[i]) != 0) {
diag("%s: mismatch on element '%u'", api->name, i);
passed = false;
break;
}
}
ok(passed, "%s: lookup all keys", api->name);
/* Fetch dataset size. */
int db_size = api->count(&txn);
ok(db_size > 0 && db_size <= nkeys, "%s: count %d", api->name, db_size);
/* Unsorted iteration */
int iterated = 0;
knot_iter_t *it = api->iter_begin(&txn, 0);
while (it != NULL) {
++iterated;
it = api->iter_next(it);
}
api->iter_finish(it);
is_int(db_size, iterated, "%s: unsorted iteration", api->name);
/* Sorted iteration. */
char key_buf[KEY_MAXLEN] = {'\0'};
iterated = 0;
it = api->iter_begin(&txn, NAMEDB_SORTED);
while (it != NULL) {
ret = api->iter_key(it, &key);
if (iterated > 0) { /* Only if previous exists. */
if (strcmp(key_buf, key.data) > 0) {
diag("%s: iter_sort '%s' <= '%s' FAIL\n",
api->name, key_buf, (const char *)key.data);
break;
}
}
++iterated;
memcpy(key_buf, key.data, key.len);
it = api->iter_next(it);
}
is_int(db_size, iterated, "hattrie: sorted iteration");
api->iter_finish(it);
api->txn_abort(&txn);
api->deinit(db);
return 0;
}
int main(int argc, char *argv[])
{
plan(9 * 2);
mm_ctx_t pool;
mm_ctx_mempool(&pool, 4096);
/* Temporary DB identifier. */
char dbid_buf[] = "/tmp/namedb.XXXXXX";
char *dbid = mkdtemp(dbid_buf);
/* Random keys. */
unsigned nkeys = 10000;
char **keys = mm_alloc(&pool, sizeof(char*) * nkeys);
for (unsigned i = 0; i < nkeys; ++i) {
keys[i] = str_key_rand(KEY_MAXLEN, &pool);
}
/* Sort random keys. */
str_key_sort(keys, nkeys);
/* Execute test set for all backends. */
namedb_test_set(nkeys, keys, dbid, namedb_lmdb_api(), &pool);
namedb_test_set(nkeys, keys, dbid, namedb_trie_api(), &pool);
/* Cleanup */
mp_delete(pool.ctx);
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment