diff --git a/Knot.files b/Knot.files index 6ab1afe9e3e2dbedad172069858edd067ff0a025..4d4316b3ff6eaecec5560c209ef6b88dba7c04dd 100644 --- a/Knot.files +++ b/Knot.files @@ -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 diff --git a/src/common/namedb/namedb.h b/src/common/namedb/namedb.h index c83f6f843e9488a8996fa91921d43087f9aaf47b..1295691aff6914087d71d7bf4bb8601f3096262d 100644 --- a/src/common/namedb/namedb.h +++ b/src/common/namedb/namedb.h @@ -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); }; diff --git a/src/common/namedb/namedb_lmdb.c b/src/common/namedb/namedb_lmdb.c index 2dd877551b246ee3d78d7532edbc4a7203b69f3d..1eb2b478d43e06e0e66f504db19ca8cc8198adf9 100644 --- a/src/common/namedb/namedb_lmdb.c +++ b/src/common/namedb/namedb_lmdb.c @@ -1,10 +1,10 @@ +#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 diff --git a/src/common/namedb/namedb_trie.c b/src/common/namedb/namedb_trie.c index cbc123f6b01b74f825e67f73ff0e0b9c31be2ae1..941057345c204dcdbe997249bbc2fbfb8b936479 100644 --- a/src/common/namedb/namedb_trie.c +++ b/src/common/namedb/namedb_trie.c @@ -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; } + diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index ba92f4d89c7744d271a9b42a95f7e493bc7808ab..a0268db0a4b40c82aa1c1c909a3e0445ba92d24c 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -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; diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c index 68fd8336bb8bcf86ece5675dea8a541df3a9eecb..a85f6a9d05fdc9fca4f01943abeb14851b24ba16 100644 --- a/src/libknot/errcode.c +++ b/src/libknot/errcode.c @@ -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. */ diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h index c91fa46d91edb30fc4c631d73694e22f05bf5248..88bec58fc42a2ead7846a24488117f5f516d1e11 100644 --- a/src/libknot/errcode.h +++ b/src/libknot/errcode.h @@ -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. */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 74fff3bd17da996c70e0685e3309315c06bc7191..53c18100ab60918b8d2043c7531b5bfac143a220 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -45,7 +45,8 @@ check_PROGRAMS = \ worker_pool \ worker_queue \ zone_events \ - zone_update + zone_update \ + namedb check-compile-only: $(check_PROGRAMS) diff --git a/tests/namedb.c b/tests/namedb.c new file mode 100644 index 0000000000000000000000000000000000000000..0984e7491ebe11b4702c265f793994727f5f1078 --- /dev/null +++ b/tests/namedb.c @@ -0,0 +1,173 @@ +/* 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; +}