Commit e68c3a0a authored by Marek Vavrusa's avatar Marek Vavrusa

lib: cache api v2, removed dep on libknot db.h

this change introduces new API for cache backends,
that is a subset of knot_db_api_t from libknot
with several cache-specific operations

major changes are:
* merged 'cachectl' module into 'cache' as it is
  99% default-on and it simplifies things
* not transaction oriented, transactions may be
  reused and cached for higher performance
* scatter/gather API, this is important for
  latency and performance of non-local backends
  like Redis
* faster and reliable cache clearing
* cache-specific operations (prefix scan, ...) in
  the API not hacked in
* simpler code for both backends and caller
parent 96bbc909
......@@ -24,7 +24,7 @@ env:
global:
- PKG_CONFIG_PATH="${HOME}/.local/lib/pkgconfig"
- PATH="${HOME}/.local/bin:/usr/local/bin:${PATH}"
- CFLAGS="-O2 -g -fno-omit-frame-pointer -DMP_FREELIST_SIZE=0"
- CFLAGS="-O2 -g -fno-omit-frame-pointer -DDEBUG"
- LD_LIBRARY_PATH="${HOME}/.local/lib"
- DYLD_LIBRARY_PATH="${HOME}/.local/lib"
- MALLOC_CHECK_=3
......
......@@ -23,7 +23,7 @@ INSTALL := install
# Flags
BUILD_LDFLAGS += $(LDFLAGS)
BUILD_CFLAGS := $(CFLAGS) -std=c99 -D_GNU_SOURCE -Wno-unused -Wtype-limits -Wformat -Wformat-security -Wall -I$(abspath .) -I$(abspath lib/generic) -I$(abspath contrib)
BUILD_CFLAGS := $(CFLAGS) -std=c99 -D_GNU_SOURCE -Wno-unused -Wtype-limits -Wformat -Wformat-security -Wall -I$(abspath .) -I$(abspath lib/generic) -I$(abspath contrib) -I$(abspath contrib/lmdb)
BUILD_CFLAGS += -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR).$(PATCH)\"" -DPREFIX="\"$(PREFIX)\"" -DMODULEDIR="\"$(MODULEDIR)\"" -DETCDIR="\"$(ETCDIR)\""
ifeq (,$(findstring -O,$(CFLAGS)))
BUILD_CFLAGS += -O2
......
......@@ -8,4 +8,13 @@ contrib_SOURCES := \
contrib/base32hex.c
contrib_CFLAGS := -fPIC
contrib_TARGET := $(abspath contrib)/contrib$(AREXT)
$(eval $(call make_static,contrib,contrib))
# Use built-in LMDB if not found
ifneq ($(HAS_lmdb), yes)
contrib_SOURCES += contrib/lmdb/mdb.c \
contrib/lmdb/midl.c
contrib_CFLAGS += -pthread
contrib_LIBS += -pthread
endif
$(eval $(call make_static,contrib,contrib))
\ No newline at end of file
......@@ -169,7 +169,7 @@ Configuration example
-- interfaces
net = { '127.0.0.1', '::1' }
-- load some modules
modules = { 'policy', 'cachectl' }
modules = { 'policy' }
-- 10MB cache
cache.size = 10*MB
......@@ -198,7 +198,6 @@ the modules use as the :ref:`input configuration <mod-properties>`.
.. code-block:: lua
modules = {
cachectl = true,
hints = '/etc/hosts'
}
......@@ -274,7 +273,7 @@ Here's an example of an anonymous function with :func:`event.recurrent()`:
-- every 5 minutes
event.recurrent(5 * minute, function()
cachectl.prune()
cache.prune()
end)
Note that each scheduled event is identified by a number valid for the duration of the event,
......@@ -288,7 +287,7 @@ as a parameter, but it's not very useful as you don't have any *non-global* way
local i = 0
-- pruning function
return function(e)
cachectl.prune()
cache.prune()
-- cancel event on 5th attempt
i = i + 1
if i == 5 then
......@@ -609,7 +608,6 @@ The daemon provides an interface for dynamic loading of :ref:`daemon modules <mo
.. code-block:: lua
modules = { 'cachectl' }
modules = {
hints = {file = '/etc/hosts'}
}
......@@ -618,7 +616,6 @@ The daemon provides an interface for dynamic loading of :ref:`daemon modules <mo
.. code-block:: lua
modules.load('cachectl')
modules.load('hints')
hints.config({file = '/etc/hosts'})
......@@ -716,7 +713,7 @@ daemons or manipulated from other processes, making for example synchronised loa
Close the cache.
.. note:: This may or may not clear the cache, depending on the used backend. See :func:`cachectl.clear()`.
.. note:: This may or may not clear the cache, depending on the used backend. See :func:`cache.clear()`.
.. function:: cache.stats()
......@@ -729,6 +726,56 @@ daemons or manipulated from other processes, making for example synchronised loa
print('Insertions:', cache.stats().insert)
.. function:: cache.prune([max_count])
:param number max_count: maximum number of items to be pruned at once (default: 65536)
:return: ``{ pruned: int }``
Prune expired/invalid records.
.. function:: cache.get([domain])
:return: list of matching records in cache
Fetches matching records from cache. The **domain** can either be:
- a domain name (e.g. ``"domain.cz"``)
- a wildcard (e.g. ``"*.domain.cz"``)
The domain name fetches all records matching this name, while the wildcard matches all records at or below that name.
You can also use a special namespace ``"P"`` to purge NODATA/NXDOMAIN matching this name (e.g. ``"domain.cz P"``).
.. note:: This is equivalent to ``cache['domain']`` getter.
Examples:
.. code-block:: lua
-- Query cache for 'domain.cz'
cache['domain.cz']
-- Query cache for all records at/below 'insecure.net'
cache['*.insecure.net']
.. function:: cache.clear([domain])
:return: ``bool``
Purge cache records. If the domain isn't provided, whole cache is purged. See *cache.get()* documentation for subtree matching policy.
Examples:
.. code-block:: lua
-- Clear records at/below 'bad.cz'
cache.clear('*.bad.cz')
-- Clear packet cache
cache.clear('*. P')
-- Clear whole cache
cache.clear()
Timers and events
^^^^^^^^^^^^^^^^^
......
......@@ -20,6 +20,7 @@
#include <libknot/descriptor.h>
#include "lib/cache.h"
#include "lib/cdb.h"
#include "daemon/bindings.h"
#include "daemon/worker.h"
......@@ -349,13 +350,12 @@ int lib_net(lua_State *L)
static int cache_backends(lua_State *L)
{
struct engine *engine = engine_luaget(L);
storage_registry_t *registry = &engine->storage_registry;
lua_newtable(L);
for (unsigned i = 0; i < registry->len; ++i) {
struct storage_api *storage = &registry->at[i];
lua_pushboolean(L, storage->api() == engine->resolver.cache.api);
lua_setfield(L, -2, storage->prefix);
for (unsigned i = 0; i < engine->backends.len; ++i) {
const struct kr_cdb_api *api = engine->backends.at[i];
lua_pushboolean(L, api == engine->resolver.cache.api);
lua_setfield(L, -2, api->name);
}
return 1;
}
......@@ -364,20 +364,15 @@ static int cache_backends(lua_State *L)
static int cache_count(lua_State *L)
{
struct engine *engine = engine_luaget(L);
const knot_db_api_t *storage = engine->resolver.cache.api;
/* Fetch item count */
struct kr_cache_txn txn;
int ret = kr_cache_txn_begin(&engine->resolver.cache, &txn, KNOT_DB_RDONLY);
if (ret != 0) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
const struct kr_cdb_api *api = engine->resolver.cache.api;
/* First key is a version counter, omit it. */
lua_pushinteger(L, storage->count(&txn.t) - 1);
kr_cache_txn_abort(&txn);
return 1;
struct kr_cache *cache = &engine->resolver.cache;
if (kr_cache_is_open(cache)) {
lua_pushinteger(L, api->count(cache->db) - 1);
return 1;
}
return 0;
}
/** Return cache statistics. */
......@@ -394,27 +389,22 @@ static int cache_stats(lua_State *L)
lua_setfield(L, -2, "insert");
lua_pushnumber(L, cache->stats.delete);
lua_setfield(L, -2, "delete");
lua_pushnumber(L, cache->stats.txn_read);
lua_setfield(L, -2, "txn_read");
lua_pushnumber(L, cache->stats.txn_write);
lua_setfield(L, -2, "txn_write");
return 1;
}
static struct storage_api *cache_select_storage(struct engine *engine, const char **conf)
static const struct kr_cdb_api *cache_select(struct engine *engine, const char **conf)
{
/* Return default backend */
storage_registry_t *registry = &engine->storage_registry;
if (!*conf || !strstr(*conf, "://")) {
return &registry->at[0];
if (*conf == NULL || !strstr(*conf, "://")) {
return engine->backends.at[0];
}
/* Find storage backend from config prefix */
for (unsigned i = 0; i < registry->len; ++i) {
struct storage_api *storage = &registry->at[i];
if (strncmp(*conf, storage->prefix, strlen(storage->prefix)) == 0) {
*conf += strlen(storage->prefix);
return storage;
for (unsigned i = 0; i < engine->backends.len; ++i) {
const struct kr_cdb_api *api = engine->backends.at[i];
if (strncmp(*conf, api->name, strlen(api->name)) == 0) {
*conf += strlen(api->name) + strlen("://");
return api;
}
}
......@@ -436,8 +426,8 @@ static int cache_open(lua_State *L)
unsigned cache_size = lua_tonumber(L, 1);
const char *conf = n > 1 ? lua_tostring(L, 2) : NULL;
const char *uri = conf;
struct storage_api *storage = cache_select_storage(engine, &conf);
if (!storage) {
const struct kr_cdb_api *api = cache_select(engine, &conf);
if (!api) {
format_error(L, "unsupported cache backend");
lua_error(L);
}
......@@ -446,9 +436,11 @@ static int cache_open(lua_State *L)
kr_cache_close(&engine->resolver.cache);
/* Reopen cache */
void *storage_opts = storage->opts_create(conf, cache_size);
int ret = kr_cache_open(&engine->resolver.cache, storage->api(), storage_opts, engine->pool);
free(storage_opts);
struct kr_cdb_opts opts = {
(conf && strlen(conf)) ? conf : ".",
cache_size
};
int ret = kr_cache_open(&engine->resolver.cache, api, &opts, engine->pool);
if (ret != 0) {
format_error(L, "can't open cache");
lua_error(L);
......@@ -481,6 +473,190 @@ static int cache_close(lua_State *L)
return 1;
}
/** @internal Prefix walk. */
static int cache_prefixed(struct kr_cache *cache, const char *args, knot_db_val_t *results, int maxresults)
{
/* Decode parameters */
uint8_t namespace = 'R';
char *extra = (char *)strchr(args, ' ');
if (extra != NULL) {
extra[0] = '\0';
namespace = extra[1];
}
/* Convert to domain name */
uint8_t buf[KNOT_DNAME_MAXLEN];
if (!knot_dname_from_str(buf, args, sizeof(buf))) {
return kr_error(EINVAL);
}
/* Start prefix search */
return kr_cache_match(cache, namespace, buf, results, maxresults);
}
/** @internal Delete iterated key. */
static int cache_remove_prefix(struct kr_cache *cache, const char *args)
{
/* Check if we can remove */
if (!cache || !cache->api || !cache->api->remove) {
return kr_error(ENOSYS);
}
static knot_db_val_t result_set[1000];
int ret = cache_prefixed(cache, args, result_set, 1000);
if (ret < 0) {
return ret;
}
/* Duplicate result set as we're going to remove it
* which will invalidate result set. */
for (int i = 0; i < ret; ++i) {
void *dst = malloc(result_set[i].len);
if (!dst) {
return kr_error(ENOMEM);
}
memcpy(dst, result_set[i].data, result_set[i].len);
result_set[i].data = dst;
}
cache->api->remove(cache->db, result_set, ret);
/* Free keys */
for (int i = 0; i < ret; ++i) {
free(result_set[i].data);
}
return ret;
}
/** Prune expired/invalid records. */
static int cache_prune(lua_State *L)
{
/* Check parameters */
int prune_max = UINT16_MAX;
int n = lua_gettop(L);
if (n >= 1 && lua_isnumber(L, 1)) {
prune_max = lua_tointeger(L, 1);
}
struct engine *engine = engine_luaget(L);
struct kr_cache *cache = &engine->resolver.cache;
/* Check if API supports pruning. */
int ret = kr_error(ENOSYS);
if (cache->api->prune) {
ret = cache->api->prune(cache->db, prune_max);
}
/* Commit and format result. */
if (ret < 0) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
lua_pushinteger(L, ret);
return 1;
}
/** Clear all records. */
static int cache_clear(lua_State *L)
{
/* Check parameters */
const char *args = NULL;
int n = lua_gettop(L);
if (n >= 1 && lua_isstring(L, 1)) {
args = lua_tostring(L, 1);
}
/* Clear a sub-tree in cache. */
struct engine *engine = engine_luaget(L);
struct kr_cache *cache = &engine->resolver.cache;
if (args && strlen(args) > 0) {
int ret = cache_remove_prefix(cache, args);
if (ret < 0) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
lua_pushinteger(L, ret);
return 1;
}
/* Clear cache. */
int ret = kr_cache_clear(cache);
if (ret < 0) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
/* Clear reputation tables */
lru_deinit(engine->resolver.cache_rtt);
lru_deinit(engine->resolver.cache_rep);
lru_init(engine->resolver.cache_rtt, LRU_RTT_SIZE);
lru_init(engine->resolver.cache_rep, LRU_REP_SIZE);
lua_pushboolean(L, true);
return 1;
}
/** @internal Dump cache key into table on Lua stack. */
static void cache_dump_key(lua_State *L, knot_db_val_t *key)
{
char buf[KNOT_DNAME_MAXLEN];
/* Extract type */
uint16_t type = 0;
const char *endp = (const char *)key->data + key->len - sizeof(uint16_t);
memcpy(&type, endp, sizeof(uint16_t));
endp -= 1;
/* Extract domain name */
char *dst = buf;
const char *scan = endp - 1;
while (scan > (const char *)key->data) {
if (*scan == '\0') {
const size_t lblen = endp - scan - 1;
memcpy(dst, scan + 1, lblen);
dst += lblen;
*dst++ = '.';
endp = scan;
}
--scan;
}
memcpy(dst, scan + 1, endp - scan);
/* If name typemap doesn't exist yet, create it */
lua_getfield(L, -1, buf);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
}
/* Append to typemap */
char type_buf[16] = { '\0' };
knot_rrtype_to_string(type, type_buf, sizeof(type_buf));
lua_pushboolean(L, true);
lua_setfield(L, -2, type_buf);
/* Set name typemap */
lua_setfield(L, -2, buf);
}
/** Query cached records. */
static int cache_get(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 1 || !lua_isstring(L, 1)) {
format_error(L, "expected 'cache.get(string key)'");
lua_error(L);
}
/* Clear a sub-tree in cache. */
struct engine *engine = engine_luaget(L);
struct kr_cache *cache = &engine->resolver.cache;
const char *args = lua_tostring(L, 1);
/* Retrieve set of keys */
static knot_db_val_t result_set[100];
int ret = cache_prefixed(cache, args, result_set, 100);
if (ret < 0) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
/* Format output */
lua_newtable(L);
for (int i = 0; i < ret; ++i) {
cache_dump_key(L, &result_set[i]);
}
return 1;
}
int lib_cache(lua_State *L)
{
static const luaL_Reg lib[] = {
......@@ -489,6 +665,9 @@ int lib_cache(lua_State *L)
{ "stats", cache_stats },
{ "open", cache_open },
{ "close", cache_close },
{ "prune", cache_prune },
{ "clear", cache_clear },
{ "get", cache_get },
{ NULL, NULL }
};
......
......@@ -21,8 +21,6 @@
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
/* #include <libknot/internal/namedb/knot_db_trie.h> @todo Not supported (doesn't keep value copy) */
#include <libknot/db/db_lmdb.h>
#include <zscanner/scanner.h>
#include "daemon/engine.h"
......@@ -31,6 +29,7 @@
#include "lib/nsrep.h"
#include "lib/cache.h"
#include "lib/defines.h"
#include "lib/cdb_lmdb.h"
#include "lib/dnssec/ta.h"
/** @internal Compatibility wrapper for Lua < 5.2 */
......@@ -370,19 +369,6 @@ static int l_trampoline(lua_State *L)
* Engine API.
*/
/** @internal Make lmdb options. */
void *knot_db_lmdb_mkopts(const char *conf, size_t maxsize)
{
struct knot_db_lmdb_opts *opts = malloc(sizeof(*opts));
if (opts) {
memset(opts, 0, sizeof(*opts));
opts->path = (conf && strlen(conf)) ? conf : ".";
opts->mapsize = maxsize;
opts->flags.env = 0x80000 | 0x100000; /* MDB_WRITEMAP|MDB_MAPASYNC */
}
return opts;
}
static int init_resolver(struct engine *engine)
{
/* Open resolution context */
......@@ -415,12 +401,7 @@ static int init_resolver(struct engine *engine)
engine_register(engine, "rrcache", NULL, NULL);
engine_register(engine, "pktcache", NULL, NULL);
/* Initialize storage backends */
struct storage_api lmdb = {
"lmdb://", knot_db_lmdb_api, knot_db_lmdb_mkopts
};
return array_push(engine->storage_registry, lmdb);
return array_push(engine->backends, kr_cdb_lmdb());
}
static int init_state(struct engine *engine)
......@@ -538,7 +519,7 @@ void engine_deinit(struct engine *engine)
/* Free data structures */
array_clear(engine->modules);
array_clear(engine->storage_registry);
array_clear(engine->backends);
kr_ta_clear(&engine->resolver.trust_anchors);
kr_ta_clear(&engine->resolver.negative_anchors);
}
......
......@@ -45,22 +45,11 @@ struct lua_State;
#include "lib/resolve.h"
#include "daemon/network.h"
/** Cache storage backend. */
struct storage_api {
const char *prefix; /**< Storage prefix, e.g. 'lmdb://' */
const knot_db_api_t *(*api)(void); /**< Storage API implementation */
void *(*opts_create)(const char *, size_t); /**< Storage options factory */
};
/** @cond internal Array of cache backend options. */
typedef array_t(struct storage_api) storage_registry_t;
/* @endcond */
struct engine {
struct kr_context resolver;
struct network net;
module_array_t modules;
storage_registry_t storage_registry;
array_t(const struct kr_cdb_api *) backends;
knot_mm_t *pool;
uv_timer_t *updater;
struct lua_State *L;
......
......@@ -72,7 +72,7 @@ setmetatable(modules, {
modules.load(k)
k = string.match(k, '%w+')
local mod = _G[k]
local config = rawget(mod, 'config')
local config = mod and rawget(mod, 'config')
if mod ~= nil and config ~= nil then
if k ~= v then config(v)
else config()
......@@ -83,8 +83,16 @@ setmetatable(modules, {
})
-- Syntactic sugar for cache
-- `#cache -> cache.count()`
-- `cache[x] -> cache.get(x)`
-- `cache.{size|storage} = value`
setmetatable(cache, {
__len = function (t)
return t.count()
end,
__index = function (t, k)
return rawget(t, k) or (rawget(t, 'current_size') and t.get(k))
end,
__newindex = function (t,k,v)
-- Defaults
local storage = rawget(t, 'current_storage')
......
......@@ -17,7 +17,6 @@ Knot DNS Resolver modules
.. include:: ../modules/kmemcached/README.rst
.. include:: ../modules/redis/README.rst
.. include:: ../modules/ketcd/README.rst
.. include:: ../modules/cachectl/README.rst
.. include:: ../modules/tinyweb/README.rst
.. include:: ../modules/dns64/README.rst
.. include:: ../modules/renumber/README.rst
......@@ -21,7 +21,6 @@ cache.size = 100 * GB
-- Load Useful modules
modules = {
'policy', -- Block queries to local zones/bad sites
'cachectl', -- Cache control interface
'hints', -- Load /etc/hosts and allow custom root hints
'stats', -- Track internal statistics
graphite = { -- Send statistics to local InfluxDB
......
......@@ -19,7 +19,6 @@ cache.size = 4 * GB
modules = {
'policy', -- Block queries to local zones/bad sites
'view', -- Views for certain clients
'cachectl', -- Cache control interface
'hints', -- Load /etc/hosts and allow custom root hints
'stats', -- Track internal statistics
graphite = { -- Send statistics to local InfluxDB
......
......@@ -15,7 +15,6 @@ trust_anchors.file = 'root.keys'
-- Load Useful modules
modules = {
'policy', -- Block queries to local zones/bad sites
'cachectl', -- Cache control interface
'hints', -- Load /etc/hosts and allow custom root hints
'stats', -- Track internal statistics
'predict', -- Prefetch expiring/frequent records
......
......@@ -13,7 +13,6 @@ trust_anchors.file = 'root.keys'
-- Load Useful modules
modules = {
'policy', -- Block queries to local zones/bad sites
'cachectl', -- Cache control interface
'hints', -- Load /etc/hosts and allow custom root hints
'stats', -- Track internal statistics
graphite = { -- Send statistics to local InfluxDB
......
......@@ -20,13 +20,14 @@
#include <unistd.h>
#include <errno.h>
#include <libknot/db/db_lmdb.h>
#include <libknot/errcode.h>
#include <libknot/descriptor.h>
#include <libknot/dname.h>
#include <libknot/rrtype/rrsig.h>
#include "contrib/cleanup.h"
#include "lib/cache.h"
#include "lib/cdb_lmdb.h"
#include "lib/defines.h"
#include "lib/utils.h"
......@@ -35,32 +36,25 @@
/* Key size */
#define KEY_HSIZE (sizeof(uint8_t) + sizeof(uint16_t))
#define KEY_SIZE (KEY_HSIZE + KNOT_DNAME_MAXLEN)
#define txn_api(txn) ((txn)->owner->api)
#define txn_is_valid(txn) ((txn) && (txn)->owner && txn_api(txn))
/* Shorthand for operations on cache backend */
#define cache_isvalid(cache) ((cache) && (cache)->api && (cache)->db)
#define cache_op(cache, op, ...) (cache)->api->op((cache)->db, ## __VA_ARGS__)
/** @internal Removes all records from cache. */
static int cache_purge(struct kr_cache_txn *txn)
static inline int cache_purge(struct kr_cache *cache)
{
int ret = kr_error(EINVAL);
if (txn_is_valid(txn)) {
txn->owner->stats.delete += 1;
ret = txn_api(txn)->clear(&txn->t);
}
return ret;
cache->stats.delete += 1;
return cache_op(cache, clear);
}
/** @internal Check cache internal data version. Clear if it doesn't match.
* returns : EEXIST - cache data version matched.
* 0 - cache recreated, txn has to be committed.
* Otherwise - cache recreation fails.
*/
static int assert_right_version_txn(struct kr_cache_txn *txn)
/** @internal Open cache db transaction and check internal data version. */
static int assert_right_version(struct kr_cache *cache)
{
/* Check cache ABI version */
knot_db_val_t key = { KEY_VERSION, 2 };
knot_db_val_t val = { NULL, 0 };
int ret = txn_api(txn)->find(&txn->t, &key, &val, 0);
int ret = cache_op(cache, read, &key, &val, 1);
if (ret == 0) {
ret = kr_error(EEXIST);
} else {
......@@ -68,101 +62,51 @@ static int assert_right_version_txn(struct kr_cache_txn *txn)
* Version doesn't match.
* Recreate cache and write version key.
*/
ret = txn_api(txn)->count(&txn->t);
ret = cache_op(cache, count);
if (ret != 0) { /* Non-empty cache, purge it. */
kr_log_info("[cache] purging cache\n");
ret = cache_purge(txn);
ret = cache_purge(cache);
}
/* Either purged or empty. */
if (ret == 0) {
ret = txn_api(txn)->insert(&txn->t, &key, &val, 0);
ret = cache_op(cache, write, &key, &val, 1);
}
}
return ret;
}
/** @internal Open cache db transaction and check internal data version. */
static void assert_right_version(struct kr_cache *cache)
{
/* Check cache ABI version */
struct kr_cache_txn txn;
int ret = kr_cache_txn_begin(cache, &txn, 0);
if (ret != 0) {
return; /* N/A, doesn't work. */
}
ret = assert_right_version_txn(&txn);
if (ret == 0) { /* Cache recreated, commit. */
kr_cache_txn_commit(&txn);
} else {
kr_cache_txn_abort(&txn);
}
}
int kr_cache_open(struct kr_cache *cache, const knot_db_api_t *api, void *opts, knot_mm_t *mm)
int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct kr_cdb_opts *opts, knot_mm_t *mm)
{
if (!cache) {
return kr_error(EINVAL);
}
/* Open cache */
cache->api = (api == NULL) ? knot_db_lmdb_api() : api;
int ret = cache->api->init(&cache->db, mm, opts);
if (!api) {
api = kr_cdb_lmdb();
}
cache->api = api;
int ret = cache->api->open(&cache->db, opts, mm);
if (ret != 0) {
return ret;
}
memset(&cache->stats, 0, sizeof(cache->stats));
/* Check cache ABI version */
assert_right_version(cache);
return kr_ok();
(void) assert_right_version(cache);
return 0;