Commit 9594ea43 authored by Marek Vavruša's avatar Marek Vavruša

Merge branch 'cache-backends'

parents 0fa3868f ab3adbb0
......@@ -335,7 +335,21 @@ The cache in Knot DNS Resolver is persistent with LMDB backend, this means that
the cached data on restart or crash to avoid cold-starts. The cache may be reused between cache
daemons or manipulated from other processes, making for example synchronised load-balanced recursors possible.
.. function:: cache.open(max_size)
.. function:: cache.backends()
:return: map of backends
The cache supports runtime-changeable backends, using the optional :rfc:`3986` URI, where the scheme
represents backend protocol and the rest of the URI backend-specific configuration. By default, it
is a ``lmdb`` backend in working directory, i.e. ``lmdb://``.
Example output:
.. code-block:: lua
[lmdb://] => true
.. function:: cache.open(max_size[, config_uri])
:param number max_size: Maximum cache size in bytes.
:return: boolean
......@@ -345,6 +359,13 @@ daemons or manipulated from other processes, making for example synchronised loa
.. tip:: Use ``kB, MB, GB`` constants as a multiplier, e.g. ``100*MB``.
The cache supports runtime-changeable backends, see :func:`cache.backends()` for mor information and
default. Refer to specific documentation of specific backends for configuration string syntax.
- ``lmdb://``
As of now it only allows you to change the cache directory, e.g. ``lmdb:///tmp/cachedir``.
.. function:: cache.count()
:return: Number of entries in the cache.
......@@ -355,13 +376,15 @@ 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()`.
Timers and events
^^^^^^^^^^^^^^^^^
The timer represents exactly the thing described in the examples - it allows you to execute closures after specified time,
or event recurrent events. Time is always described in miliseconds, but there are convenient variables that you can use -
``sec, minute, hour``. For example, ``5 * hour`` represents five hours, or 5*60*60*100 milliseconds.
The timer represents exactly the thing described in the examples - it allows you to execute closures
after specified time, or event recurrent events. Time is always described in milliseconds,
but there are convenient variables that you can use - ``sec, minute, hour``.
For example, ``5 * hour`` represents five hours, or 5*60*60*100 milliseconds.
.. function:: event.after(time, function)
......
......@@ -285,6 +285,21 @@ int lib_net(lua_State *L)
return 1;
}
/** Return available cached backends. */
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() == kr_cache_storage());
lua_setfield(L, -2, storage->prefix);
}
return 1;
}
/** Return number of cached records. */
static int cache_count(lua_State *L)
{
......@@ -304,6 +319,26 @@ static int cache_count(lua_State *L)
return 1;
}
static struct storage_api *cache_select_storage(struct engine *engine, const char **conf)
{
/* Return default backend */
storage_registry_t *registry = &engine->storage_registry;
if (!*conf || !strstr(*conf, "://")) {
return &registry->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;
}
}
return NULL;
}
/** Open cache */
static int cache_open(lua_State *L)
{
......@@ -314,16 +349,26 @@ static int cache_open(lua_State *L)
lua_error(L);
}
/* Close if already open */
/* Select cache storage backend */
struct engine *engine = engine_luaget(L);
const char *conf = n > 1 ? lua_tostring(L, 2) : NULL;
struct storage_api *storage = cache_select_storage(engine, &conf);
if (!storage) {
format_error(L, "unsupported cache backend");
lua_error(L);
}
kr_cache_storage_set(storage->api);
/* Close if already open */
if (engine->resolver.cache != NULL) {
kr_cache_close(engine->resolver.cache);
}
/* Open resolution context cache */
engine->resolver.cache = kr_cache_open(".", engine->pool, lua_tointeger(L, 1));
/* Reopen cache */
void *storage_opts = storage->opts_create(conf, lua_tointeger(L, 1));
engine->resolver.cache = kr_cache_open(storage_opts, engine->pool);
free(storage_opts);
if (engine->resolver.cache == NULL) {
format_error(L, "can't open cache in rundir");
format_error(L, "can't open cache");
lua_error(L);
}
......@@ -347,6 +392,7 @@ static int cache_close(lua_State *L)
int lib_cache(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "backends", cache_backends },
{ "count", cache_count },
{ "open", cache_open },
{ "close", cache_close },
......
......@@ -17,6 +17,8 @@
#include <uv.h>
#include <unistd.h>
#include <libknot/internal/mempattern.h>
/* #include <libknot/internal/namedb/namedb_trie.h> @todo Not supported (doesn't keep value copy) */
#include <libknot/internal/namedb/namedb_lmdb.h>
#include "daemon/engine.h"
#include "daemon/bindings.h"
......@@ -97,6 +99,18 @@ static int l_trampoline(lua_State *L)
* Engine API.
*/
/** @internal Make lmdb options. */
void *namedb_lmdb_mkopts(const char *conf, size_t maxsize)
{
struct namedb_lmdb_opts *opts = malloc(sizeof(*opts));
if (opts) {
memset(opts, 0, sizeof(*opts));
opts->path = conf ? conf : ".";
opts->mapsize = maxsize;
}
return opts;
}
static int init_resolver(struct engine *engine)
{
/* Open resolution context */
......@@ -104,9 +118,15 @@ static int init_resolver(struct engine *engine)
/* Load basic modules */
engine_register(engine, "iterate");
engine_register(engine, "itercache");
engine_register(engine, "rrcache");
engine_register(engine, "pktcache");
return kr_ok();
/* Initialize storage backends */
struct storage_api lmdb = {
"lmdb://", namedb_lmdb_api, namedb_lmdb_mkopts
};
return array_push(engine->storage_registry, lmdb);
}
static int init_state(struct engine *engine)
......@@ -168,6 +188,7 @@ void engine_deinit(struct engine *engine)
kr_module_unload(&engine->modules.at[i]);
}
array_clear(engine->modules);
array_clear(engine->storage_registry);
if (engine->L) {
lua_close(engine->L);
......
......@@ -24,10 +24,21 @@ 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 namedb_api_t *(*api)(void); /**< Storage API implementation */
void *(*opts_create)(const char *, size_t); /**< Storage options factory */
};
/** @internal Array of cache backend options. */
typedef array_t(struct storage_api) storage_registry_t;
struct engine {
struct kr_context resolver;
struct network net;
module_array_t modules;
storage_registry_t storage_registry;
mm_ctx_t *pool;
struct lua_State *L;
};
......
......@@ -54,6 +54,7 @@ void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
uv_loop_t *loop = handle->loop;
struct worker_ctx *worker = loop->data;
knot_pkt_t *query = knot_pkt_new(buf->base, nread, worker->mm);
query->max_size = sizeof(worker->bufs.wire);
worker_exec(worker, (uv_handle_t *)handle, query, addr);
knot_pkt_free(&query);
}
......@@ -103,6 +104,7 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
}
knot_pkt_t *query = knot_pkt_new(buf->base + 2, nbytes, worker->mm);
query->max_size = sizeof(worker->bufs.wire);
worker_exec(worker, (uv_handle_t *)handle, query, NULL);
knot_pkt_free(&query);
}
......
......@@ -85,7 +85,7 @@ static struct qr_task *qr_task_create(struct worker_ctx *worker, uv_handle_t *ha
}
/* Create buffers */
knot_pkt_t *next_query = knot_pkt_new(NULL, KNOT_WIRE_MIN_PKTSIZE, &task->req.pool);
knot_pkt_t *next_query = knot_pkt_new(NULL, KNOT_EDNS_MAX_UDP_PAYLOAD, &task->req.pool);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &task->req.pool);
if (!next_query || !answer) {
mp_delete(pool.ctx);
......
......@@ -33,7 +33,7 @@ Writing layers
The resolver :ref:`library <lib_index>` leverages the `processing API`_ from the libknot to separate packet processing code
into layers. In order to keep the core library sane and coverable, there are only two built-in layers:
the :c:func:`iterate_layer`, and the :c:func:`itercache_layer`.
the :c:func:`iterate_layer`, and the :c:func:`rrcache_layer`.
*Note* |---| This is only crash-course in the library internals, see the resolver :ref:`library <lib_index>` documentation for the complete overview of the services.
......
......@@ -28,23 +28,23 @@
#include "lib/cache.h"
#include "lib/defines.h"
/** Used cache storage engine (default LMDB) */
/* Key size */
#define KEY_SIZE (sizeof(uint8_t) + KNOT_DNAME_MAXLEN + sizeof(uint16_t))
/**
* Used cache storage engine (default LMDB)
* @todo namedb_t should change so it always contains a pointer
* to its API, so it can be carried around instead of keeping it in a
* global variable.
*/
const namedb_api_t *(*kr_cache_storage)(void) = namedb_lmdb_api;
#define db_api kr_cache_storage()
namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize)
namedb_t *kr_cache_open(void *opts, mm_ctx_t *mm)
{
if (handle == NULL || maxsize == 0) {
return NULL;
}
struct namedb_lmdb_opts opts = NAMEDB_LMDB_OPTS_INITIALIZER;
opts.mapsize = maxsize;
opts.path = handle;
namedb_t *db = NULL;
int ret = db_api->init(&db, mm, &opts);
if (ret != KNOT_EOK) {
int ret = db_api->init(&db, mm, opts);
if (ret != 0) {
return NULL;
}
......@@ -53,15 +53,15 @@ namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize)
void kr_cache_close(namedb_t *cache)
{
if (cache != NULL) {
if (cache) {
db_api->deinit(cache);
}
}
int kr_cache_txn_begin(namedb_t *cache, namedb_txn_t *txn, unsigned flags)
{
if (cache == NULL || txn == NULL) {
return KNOT_EINVAL;
if (!cache || !txn) {
return kr_error(EINVAL);
}
return db_api->txn_begin(cache, txn, flags);
......@@ -69,12 +69,12 @@ int kr_cache_txn_begin(namedb_t *cache, namedb_txn_t *txn, unsigned flags)
int kr_cache_txn_commit(namedb_txn_t *txn)
{
if (txn == NULL) {
return KNOT_EINVAL;
if (!txn) {
return kr_error(EINVAL);
}
int ret = db_api->txn_commit(txn);
if (ret != KNOT_EOK) {
if (ret != 0) {
kr_cache_txn_abort(txn);
}
return ret;
......@@ -82,22 +82,25 @@ int kr_cache_txn_commit(namedb_txn_t *txn)
void kr_cache_txn_abort(namedb_txn_t *txn)
{
if (txn != NULL) {
if (txn) {
db_api->txn_abort(txn);
}
}
static size_t cache_key(uint8_t *buf, const knot_dname_t *name, uint16_t type)
/** @internal Composed key as { u8 tag, u8[1-255] name, u16 type } */
static size_t cache_key(uint8_t *buf, uint8_t tag, const knot_dname_t *name, uint16_t type)
{
size_t len = knot_dname_to_wire(buf, name, KNOT_DNAME_MAXLEN);
memcpy(buf + len, &type, sizeof(uint16_t));
return len + sizeof(uint16_t);
knot_dname_lf(buf, name, NULL);
size_t len = buf[0] + 1;
memcpy(buf + len, &type, sizeof(type));
buf[0] = tag;
return len + sizeof(type);
}
static struct kr_cache_rrset *cache_rr(namedb_txn_t *txn, const knot_dname_t *name, uint16_t type)
static struct kr_cache_entry *cache_entry(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name, uint16_t type)
{
uint8_t keybuf[KNOT_DNAME_MAXLEN + sizeof(uint16_t)];
size_t key_len = cache_key(keybuf, name, type);
uint8_t keybuf[KEY_SIZE];
size_t key_len = cache_key(keybuf, tag, name, type);
/* Look up and return value */
namedb_val_t key = { keybuf, key_len };
......@@ -107,49 +110,121 @@ static struct kr_cache_rrset *cache_rr(namedb_txn_t *txn, const knot_dname_t *na
return NULL;
}
return (struct kr_cache_rrset *)val.data;
return (struct kr_cache_entry *)val.data;
}
int kr_cache_peek(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp)
struct kr_cache_entry *kr_cache_peek(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name,
uint16_t type, uint32_t *timestamp)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
if (!txn || !tag || !name) {
return NULL;
}
/* Check if the RRSet is in the cache. */
struct kr_cache_rrset *found_rr = cache_rr(txn, rr->owner, rr->type);
if (found_rr != NULL) {
struct kr_cache_entry *entry = cache_entry(txn, tag, name, type);
if (!entry) {
return NULL;
}
/* No time constraint */
if (!timestamp) {
return entry;
} else if (*timestamp <= entry->timestamp) {
/* John Connor record cached in the future. */
*timestamp = 0;
return entry;
} else {
/* Check if the record is still valid. */
uint32_t drift = *timestamp - entry->timestamp;
if (drift < entry->ttl) {
*timestamp = drift;
return entry;
}
}
/* Assign data and return success. */
rr->rrs.rr_count = found_rr->count;
rr->rrs.data = found_rr->data;
return NULL;
}
/* No time constraint */
if (timestamp == NULL) {
return KNOT_EOK;
}
static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *header, namedb_val_t data)
{
assert(dst);
memcpy(dst, header, sizeof(*header));
memcpy(dst->data, data.data, data.len);
}
/* John Connor record cached from the future. */
if (*timestamp < found_rr->timestamp) {
*timestamp = 0;
return KNOT_EOK;
}
int kr_cache_insert(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
struct kr_cache_entry *header, namedb_val_t data)
{
if (!txn || !name || !tag || !header) {
return kr_error(EINVAL);
}
/* Check if at least one RR is still valid. */
uint32_t drift = *timestamp - found_rr->timestamp;
for (unsigned i = 0; i < rr->rrs.rr_count; ++i) {
const knot_rdata_t *rd = knot_rdataset_at(&rr->rrs, i);
if (knot_rdata_ttl(rd) > drift) {
*timestamp = drift;
return KNOT_EOK;
}
/* Insert key */
uint8_t keybuf[KEY_SIZE];
size_t key_len = cache_key(keybuf, tag, name, type);
namedb_val_t key = { keybuf, key_len };
namedb_val_t entry = { NULL, sizeof(*header) + data.len };
/* LMDB can do late write and avoid copy */
if (db_api == namedb_lmdb_api()) {
int ret = db_api->insert(txn, &key, &entry, 0);
if (ret != 0) {
return ret;
}
entry_write(entry.data, header, data);
} else {
/* Other backends must prepare contiguous data first */
entry.data = malloc(entry.len);
if (!entry.data) {
return kr_error(ENOMEM);
}
entry_write(entry.data, header, data);
int ret = db_api->insert(txn, &key, &entry, 0);
free(entry.data);
if (ret != 0) {
return ret;
}
}
return KNOT_ENOENT;
return kr_ok();
}
int kr_cache_remove(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name, uint16_t type)
{
if (!txn || !tag || !name ) {
return kr_error(EINVAL);
}
uint8_t keybuf[KEY_SIZE];
size_t key_len = cache_key(keybuf, tag, name, type);
namedb_val_t key = { keybuf, key_len };
return db_api->del(txn, &key);
}
int kr_cache_clear(namedb_txn_t *txn)
{
if (!txn) {
return kr_error(EINVAL);
}
return db_api->clear(txn);
}
int kr_cache_peek_rr(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp)
{
if (!txn || !rr || !timestamp) {
return kr_error(EINVAL);
}
/* Check if the RRSet is in the cache. */
struct kr_cache_entry *entry = kr_cache_peek(txn, KR_CACHE_RR, rr->owner, rr->type, timestamp);
if (entry) {
rr->rrs.rr_count = entry->count;
rr->rrs.data = entry->data;
return kr_ok();
}
/* Not found. */
return KNOT_ENOENT;
return kr_error(ENOENT);
}
knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm)
......@@ -160,14 +235,14 @@ knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ct
knot_rrset_t copy;
knot_rrset_init(&copy, NULL, src->type, src->rclass);
copy.owner = knot_dname_copy(src->owner, mm);
if (copy.owner == NULL) {
if (!copy.owner) {
return copy;
}
for (uint16_t i = 0; i < src->rrs.rr_count; ++i) {
knot_rdata_t *rd = knot_rdataset_at(&src->rrs, i);
if (knot_rdata_ttl(rd) > drift) {
if (knot_rdataset_add(&copy.rrs, rd, mm) != KNOT_EOK) {
if (knot_rdataset_add(&copy.rrs, rd, mm) != 0) {
knot_rrset_clear(&copy, mm);
return copy;
}
......@@ -183,54 +258,30 @@ knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ct
return copy;
}
int kr_cache_insert(namedb_txn_t *txn, const knot_rrset_t *rr, uint32_t timestamp)
int kr_cache_insert_rr(namedb_txn_t *txn, const knot_rrset_t *rr, uint32_t timestamp)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
if (!txn || !rr) {
return kr_error(EINVAL);
}
/* Ignore empty records. */
/* Ignore empty records */
if (knot_rrset_empty(rr)) {
return KNOT_EOK;
}
uint8_t keybuf[KNOT_DNAME_MAXLEN + sizeof(uint16_t)];
size_t key_len = cache_key(keybuf, rr->owner, rr->type);
namedb_val_t key = { keybuf, key_len };
namedb_val_t val = { NULL, sizeof(struct kr_cache_rrset) + knot_rdataset_size(&rr->rrs) };
int ret = db_api->insert(txn, &key, &val, 0);
if (ret != KNOT_EOK) {
return ret;
}
/* Write cached record. */
struct kr_cache_rrset *cache_rr = val.data;
cache_rr->timestamp = timestamp;
cache_rr->count = rr->rrs.rr_count;
memcpy(cache_rr->data, rr->rrs.data, knot_rdataset_size(&rr->rrs));
return KNOT_EOK;
}
int kr_cache_remove(namedb_txn_t *txn, const knot_rrset_t *rr)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
return kr_ok();
}
uint8_t keybuf[KNOT_DNAME_MAXLEN + sizeof(uint16_t)];
size_t key_len = cache_key(keybuf, rr->owner, rr->type);
namedb_val_t key = { keybuf, key_len };
return db_api->del(txn, &key);
}
int kr_cache_clear(namedb_txn_t *txn)
{
if (txn == NULL) {
return KNOT_EINVAL;
/* Prepare header to write */
struct kr_cache_entry header = {
.timestamp = timestamp,
.ttl = 0,
.count = rr->rrs.rr_count
};
for (uint16_t i = 0; i < rr->rrs.rr_count; ++i) {
knot_rdata_t *rd = knot_rdataset_at(&rr->rrs, i);
if (knot_rdata_ttl(rd) > header.ttl) {
header.ttl = knot_rdata_ttl(rd);
}
}
return db_api->clear(txn);
namedb_val_t data = { rr->rrs.data, knot_rdataset_size(&rr->rrs) };
return kr_cache_insert(txn, KR_CACHE_RR, rr->owner, rr->type, &header, data);
}
......@@ -19,27 +19,41 @@
#include <libknot/rrset.h>
#include <libknot/internal/namedb/namedb.h>
/** Cache entry tag */
enum kr_cache_tag {
KR_CACHE_RR = 'R',
KR_CACHE_PKT = 'P',
KR_CACHE_SEC = 'S',
KR_CACHE_USER = 0x80
};
/**
* Serialized form of the RRSet with inception timestamp.
* Serialized form of the RRSet with inception timestamp and maximum TTL.
*/
struct kr_cache_rrset
struct kr_cache_entry
{
uint32_t timestamp;
uint32_t ttl;
uint16_t count;
uint8_t data[];
};
/** Used storage API for cache (default LMDB) */
/** Used storage backend for cache (default LMDB) */
extern const namedb_api_t *(*kr_cache_storage)(void);
/** Replace used cache storage backend. */
static inline void kr_cache_storage_set(const namedb_api_t *(*api)(void))
{
kr_cache_storage = api;
}
/**
* Open/create persistent cache in given path.
* @param handle Path to existing directory where the DB should be created.
* Open/create cache with provided storage options.
* @param storage_opts Storage-specific options (may be NULL for default)
* @param mm Memory context.
* @param maxsize Maximum database size (bytes)
* @return database instance or NULL
*/
namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize);
namedb_t *kr_cache_open(void *storage_opts, mm_ctx_t *mm);
/**
* Close persistent cache.
......@@ -54,15 +68,14 @@ void kr_cache_close(namedb_t *cache);
* @param cache database instance
* @param txn transaction instance to be initialized (output)
* @param flags transaction flags (see namedb.h in libknot)
* @return KNOT_E*
* @return 0 or an errcode
*/
int kr_cache_txn_begin(namedb_t *cache, namedb_txn_t *txn, unsigned flags);
/**
* Commit existing transaction.
* @param txn transaction instance
* @return KNOT_E*
* @return 0 or an errcode
*/
int kr_cache_txn_commit(namedb_txn_t *txn);
......@@ -73,14 +86,57 @@ int kr_cache_txn_commit(namedb_txn_t *txn);
void kr_cache_txn_abort(namedb_txn_t *txn);
/**
* Peek the cache for given RRSet (name, type, class)
* Peek the cache for asset (name, type, tag)
* @note The 'drift' is the time passed between the inception time and now (in seconds).
* @param txn transaction instance
* @param tag asset tag
* @param name asset name
* @param type asset type
* @param timestamp current time (will be replaced with drift if successful)
* @return cache entry or NULL
*/
struct kr_cache_entry *kr_cache_peek(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name,
uint16_t type, uint32_t *timestamp);
/**
* Insert asset into cache, replacing any existing data.
* @param txn transaction instance
* @param tag asset tag
* @param name asset name
* @param type asset type
* @param header filled entry header (count, ttl and timestamp)
* @param data inserted data
* @return 0 or an errcode
*/
int kr_cache_insert(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
struct kr_cache_entry *header, namedb_val_t data);
/**
* Remove asset from cache.
* @param txn transaction instance
* @param tag asset tag
* @param name asset name
* @param type record type
* @return 0 or an errcode
*/
int kr_cache_remove(namedb_txn_t *txn, uint8_t tag, const knot_dname_t *name, uint16_t type);
/**
* Clear all items from the cache.
* @param txn transaction instance
* @return 0 or an errcode
*/
int kr_cache_clear(namedb_txn_t *txn);
/**
* Peek the cache for given RRSet (name, type)
* @note The 'drift' is the time passed between the cache time of the RRSet and now (in seconds).
* @param txn transaction instance
* @param rr query RRSet (its rdataset may be changed depending on the result)
* @param timestamp current time (will be replaced with drift if successful)
* @return KNOT_E*
* @return 0 or an errcode
*/
int kr_cache_peek(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp);
int kr_cache_peek_rr(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp);
/**
* Clone read-only RRSet and adjust TTLs.
......@@ -96,21 +152,6 @@ knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ct
* @param txn transaction instance
* @param rr inserted RRSet
* @param timestamp current time
* @return KNOT_E*
* @return 0 or an errcode
*/
int kr_cache_insert(namedb_txn_t *txn, const knot_rrset_t *rr, uint32_t timestamp);
/**