Commit 9775d0ea authored by Libor Peltan's avatar Libor Peltan

Journal: re-implemented completely, LMDB, all zones in one db

parent 0f8d449e
......@@ -258,6 +258,10 @@ src/knot/events/handlers/refresh.c
src/knot/events/handlers/update.c
src/knot/events/replan.c
src/knot/events/replan.h
src/knot/journal/serialization.c
src/knot/journal/serialization.h
src/knot/journal/journal.c
src/knot/journal/journal.h
src/knot/modules/dnsproxy/dnsproxy.c
src/knot/modules/dnsproxy/dnsproxy.h
src/knot/modules/dnstap/dnstap.c
......@@ -312,10 +316,6 @@ src/knot/query/requestor.c
src/knot/query/requestor.h
src/knot/server/dthreads.c
src/knot/server/dthreads.h
src/knot/server/journal.c
src/knot/server/journal.h
src/knot/server/serialization.c
src/knot/server/serialization.h
src/knot/server/server.c
src/knot/server/server.h
src/knot/server/tcp-handler.c
......@@ -562,7 +562,7 @@ tests/contrib/test_wire_ctx.c
tests/dthreads.c
tests/fake_server.h
tests/fdset.c
tests/journal.c
tests/journal_lmdb.c
tests/libknot/test_control.c
tests/libknot/test_cookies-client.c
tests/libknot/test_cookies-opt.c
......
......@@ -871,9 +871,17 @@ This option has no effect with enabled
\fIDefault:\fP off
.SS max\-journal\-size
.sp
Maximum size of the zone journal file.
Maximum size of the journal DB.
.sp
\fIDefault:\fP 2^64
\fIDefault:\fP 1 GiB
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Decreasing this value will lead to discarding
whole journal history of all zones.
.UNINDENT
.UNINDENT
.SS max\-zone\-size
.sp
Maximum size of the zone. The size is measured as size of the zone records
......
......@@ -1013,9 +1013,13 @@ is a master server for the zone.
max-journal-size
----------------
Maximum size of the zone journal file.
Maximum size of the journal DB.
*Default:* 2^64
*Default:* 1 GiB
.. NOTE::
Decreasing this value will lead to discarding
whole journal history of all zones.
.. _zone_max_zone_size:
......
......@@ -335,10 +335,10 @@ libknotd_la_SOURCES = \
knot/common/stats.h \
knot/server/dthreads.c \
knot/server/dthreads.h \
knot/server/journal.c \
knot/server/journal.h \
knot/server/serialization.c \
knot/server/serialization.h \
knot/journal/journal.c \
knot/journal/journal.h \
knot/journal/serialization.c \
knot/journal/serialization.h \
knot/server/server.c \
knot/server/server.h \
knot/server/tcp-handler.c \
......
......@@ -1048,22 +1048,19 @@ char* conf_zonefile_txn(
char* conf_journalfile_txn(
conf_t *conf,
knot_db_txn_t *txn,
const knot_dname_t *zone)
knot_db_txn_t *txn)
{
if (zone == NULL) {
return NULL;
}
conf_val_t val;
conf_val_t val = conf_zone_get_txn(conf, txn, C_JOURNAL, zone);
const char *journal = conf_str(&val);
val = conf_default_get_txn(conf, txn, C_STORAGE);
char *storage = conf_abs_path(&val, NULL);
// Use default journalfile name pattern if not specified.
if (journal == NULL) {
journal = "%s.db";
}
val = conf_default_get_txn(conf, txn, C_JOURNAL);
char *journaldir = conf_abs_path(&val, storage);
free(storage);
return get_filename(conf, txn, zone, journal);
return journaldir;
}
size_t conf_udp_threads_txn(
......
......@@ -584,16 +584,12 @@ static inline char* conf_zonefile(
*
* \return Absolute journal file path string pointer.
*/
char* conf_journalfile_txn(
conf_t *conf,
knot_db_txn_t *txn,
const knot_dname_t *zone
);
char* conf_journalfile_txn(conf_t *conf,
knot_db_txn_t *txn);
static inline char* conf_journalfile(
conf_t *conf,
const knot_dname_t *zone)
conf_t *conf)
{
return conf_journalfile_txn(conf, &conf->read_txn, zone);
return conf_journalfile_txn(conf, &conf->read_txn);
}
/*!
......
......@@ -225,10 +225,14 @@ static const yp_item_t desc_remote[] = {
{ NULL }
};
#define VIRT_MEM_TOP (2LLU * 1024 * 1204 * 1204)
#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP)) ? VIRT_MEM_TOP : (x))
#define ZONE_ITEMS(FLAGS) \
{ C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \
{ C_FILE, YP_TSTR, YP_VNONE, FLAGS }, \
{ C_JOURNAL, YP_TSTR, YP_VNONE, FLAGS }, \
{ C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
{ C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
......@@ -237,8 +241,6 @@ static const yp_item_t desc_remote[] = {
{ C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \
{ C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \
{ C_IXFR_DIFF, YP_TBOOL, YP_VNONE }, \
{ C_MAX_JOURNAL_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, \
FLAGS }, \
{ C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, \
FLAGS }, \
{ C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, FLAGS }, \
......@@ -256,6 +258,9 @@ static const yp_item_t desc_template[] = {
{ C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" }, CONF_IO_FRLD_ZONES }, \
{ C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \
YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } }, \
{ C_JOURNAL, YP_TSTR, YP_VSTR = { "journal.db" }, CONF_IO_FRLD_ZONES }, \
{ C_MAX_JOURNAL_SIZE, YP_TINT, YP_VINT = { 1024 * 1024, VIRT_MEM_LIMIT(100LLU * 1024 * 1024 * 1024 * 1024), \
VIRT_MEM_LIMIT(20LLU * 1024 * 1024 * 1024), YP_SSIZE } }, \
{ NULL }
};
......
......@@ -514,6 +514,24 @@ int check_template(
return KNOT_EINVAL;
}
// Check journal.
conf_val_t journal = conf_rawid_get_txn(args->conf, args->txn, C_TPL,
C_JOURNAL, args->id, args->id_len);
if (journal.code == KNOT_EOK) {
args->err_str = "journal location in non-default template";
return KNOT_EINVAL;
}
// Check max-journal-size.
conf_val_t max_journal_size = conf_rawid_get_txn(args->conf, args->txn, C_TPL,
C_MAX_JOURNAL_SIZE, args->id, args->id_len);
if (max_journal_size.code == KNOT_EOK) {
args->err_str = "journal size in non-default template";
return KNOT_EINVAL;
}
return KNOT_EOK;
}
......
......@@ -930,9 +930,9 @@ static int zone_purge(zone_t *zone, ctl_args_t *args)
free(zonefile);
// Purge the zone journal.
char *journalfile = conf_journalfile(conf(), zone->name);
(void)unlink(journalfile);
free(journalfile);
if (journal_open(zone->journal, zone->journal_db, zone->name) == KNOT_EOK) {
(void)scrape_journal(zone->journal);
}
// Purge the zone timers.
(void)remove_timer_db(args->server->timers_db, args->server->zone_db,
......
This diff is collapsed.
/* Copyright (C) 2016 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/>.
*/
#pragma once
#include <pthread.h>
#include "libknot/libknot.h"
#include "contrib/ucw/lists.h"
#include "knot/updates/changesets.h"
#include "knot/journal/serialization.h"
/*!
* \brief j->fslimit special value to open with minimal possible mapsize
*
* ...it is equal to the actual DB file size.
* Beware of using this value for the first time initialized DB !
* It is mostly useful for read only access
*/
#define KNOT_JOURNAL_FSLIMIT_SAMEASLAST (400 * 1024)
typedef struct {
knot_db_t *db;
const knot_db_api_t *db_api;
char *path;
size_t fslimit;
pthread_mutex_t db_mutex; // please delete this once you move DB opening from journal_open to db_init
// common metadata: last_inserter_zone, last_total_occupied, journal_count
} journal_db_t;
typedef struct {
uint32_t first_serial; // serial_from of the first changeset
uint32_t last_serial; // serial_from of the last changeset
uint32_t last_serial_to; // serial_to of the last changeset
uint32_t last_flushed; // serial_from of the last flushed (or merged) chengeset
uint32_t merged_serial; // "serial_from" of merged changeset
uint32_t dirty_serial; // serial_from of an incompletely inserted changeset which shall be deleted (see DB_MAX_INSERT_TXN)
uint32_t flags; // LAST_FLUSHED_VALID, SERIAL_TO_VALID, MERGED_SERIAL_VALID
// specific metadata: occupied
} journal_metadata_t;
typedef struct {
journal_db_t *db;
knot_dname_t *zone;
//journal_metadata_t md;
} journal_t;
enum {
KNOT_JOURNAL_CHECK_SILENT = 0,
KNOT_JOURNAL_CHECK_WARN = 1,
KNOT_JOURNAL_CHECK_INFO = 2
};
/*!
* \brief Allocate a new journal structure.
*
* \retval new journal instance if successful.
* \retval NULL on error.
*/
journal_t *journal_new(void);
/*!
* \brief Free a journal structure.
*
* \param journal A journal structure to free.
*/
void journal_free(journal_t **journal);
/*!
* \brief Open journal.
*
* \param j Journal struct to use.
* \param db Shared journal database
* \param zone_name Name of the zone this journal belongs to.
*
* \retval KNOT_EOK on success.
* \return < KNOT_EOK on other errors.
*/
int journal_open(journal_t *j, journal_db_t **db, const knot_dname_t *zone_name);
/*!
* \brief Close journal.
*
* \param journal Journal to close.
*/
void journal_close(journal_t *journal);
/*!
* \brief Initialize shared journal DB file. The DB will be open on first use.
*
* \param db Database to be initialized. Must be (*db == NULL) before!
* \param lmdb_dir_path Path to the directory with DB
* \param lmdb_fslimit Maximum size of DB data file
*
* \return KNOT_E*
*/
int init_journal_db(journal_db_t **db, const char *lmdb_dir_path, size_t lmdb_fslimit);
/*!
* \brief Close shared journal DB file.
*
* \param db DB to close.
*/
void close_journal_db(journal_db_t **db);
/*!
* \brief Load changesets from journal.
*
* \param journal Journal to load from.
* \param dst Store changesets here.
* \param from Start serial.
*
* \retval KNOT_EOK on success.
* \retval KNOT_ENOENT when the lookup of the first entry fails.
* \return < KNOT_EOK on other error.
*/
int journal_load_changesets(journal_t *journal, list_t *dst, uint32_t from);
/*!
* \brief Store changesets in journal.
*
* \param journal Journal to store in.
* \param src Changesets to store.
*
* \retval KNOT_EOK on success.
* \retval KNOT_EBUSY when full, asking zone to flush itself to zonefile
* to allow cleaning up history and freeing up space
* \retval KNOT_ESPACE when full and not able to free up any space
* \return < KNOT_EOK on other errors.
*/
int journal_store_changesets(journal_t *journal, list_t *src);
/*!
* \brief Store changesets in journal.
*
* \param journal Journal to store in.
* \param change Changeset to store.
*
* \retval (same as for journal_store_changesets())
*/
int journal_store_changeset(journal_t *journal, changeset_t *change);
/*!
* \brief Check if this (zone's) journal is present in shared journal DB.
*
* \param db Shared journal DB
* \param zone_name Name of the zone of the journal in question
*
* \return true or false
*/
bool journal_exists(journal_db_t **db, knot_dname_t *zone_name);
/*! \brief Tell the journal that zone has been flushed.
*
* \param journal Journal to flush.
*
* \return KNOT_E*
*/
int journal_flush(journal_t *journal);
/*! \brief Remove completely this (zone's) journal from shared journal DB.
*
* This must be called with opened journal.
*
* \param j Journal to be deleted
*
* \return KNOT_E*
*/
int scrape_journal(journal_t *j);
/*! \brief Obtain public information from journal metadata
*
* \param[in] j Journal
* \param[out] is_empty 1 if j contains no changesets
* \param[out] serial_from [if !is_empty] starting serial of changesets history
* \param[out] serial_to [if !is_empty] ending serial of changesets history
*/
void journal_metadata_info(journal_t *j, int *is_empty, uint32_t *serial_from, uint32_t *serial_to);
/*!
* \brief List the zones contained in journal DB.
*
* \param db[in] Shared journal DB
* \param zones[out] List of strings (char *) of zone names
*
* \return KNOT_EOK ok
* \retval KNOT_ENOMEM no zones found
* \retval KNOT_EMALF different # of zones found than expected
* \retval KNOT_E* other error
*/
int journal_db_list_zones(journal_db_t **db, list_t *zones);
/*! \brief Check the journal consistency, errors to stderr.
*
* \param journal Journal to check.
* \param warn_level SILENT: no logging, just curious for return value; WARN: log journal inconsistencies; INFO: log journal state
*
* \return KNOT_E*
*/
int journal_check(journal_t *j, int warn_level);
/*! @} */
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2016 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
......@@ -59,6 +59,63 @@ int rrset_serialize(const knot_rrset_t *rrset, uint8_t *stream, size_t *size);
* \return KNOT_E*
*/
int rrset_deserialize(const uint8_t *stream, size_t *stream_size,
knot_rrset_t *rrset);
knot_rrset_t *rrset);
/*!
* \brief Returns size of changeset in serialized form.
*
* \param[in] ch Changeset whose size we want to compute.
*
* \return Size of the changeset.
*/
size_t changeset_serialized_size(const changeset_t *ch);
/*!
* \brief Serializes one changeset into byte stream.
*
* \param[in] ch Changeset to serialize.
* \param[out] dst Output stream.
* \param[in] size Output stream size.
*
* \return KNOT_E*
*/
int changeset_serialize(const changeset_t *ch, uint8_t *dst, size_t size);
/*!
* \brief Deserializes one changeset from byte stream.
*
* \param[out] ch Changeset to deserialize.
* \param[in] src Input stream.
* \param[in] size Input stream size.
*
* \return KNOT_E*
*/
int changeset_deserialize(changeset_t *ch, const uint8_t *src, size_t size);
/*!
* \brief Serializes given changeset into chunked area.
*
* \param[in] ch The changeset
* \param[in] dst_chunks The chunks to serialize into
* \param[in] chunk_size Maximum size of each chunk
* \param[in] chunks_count Maximum number of used chunks
* \param[out] chunks_real_sizes real size of each chunk after serialization, or zeros for unused chunks
* \param[out] chunks_real_count real # of chunks after serialization. Can be wrong if error returned!
*
* \retval KNOT_E*
*/
int changeset_serialize_chunks(const changeset_t *ch, uint8_t *dst_chunks[], size_t chunk_size, int chunks_count, size_t *chunks_real_sizes, int *chunks_real_count);
/*!
* \brief Deserializes chunked area into ch
*
* \param[out] ch The changeset
* \param[in] src_chunks The chunks to deserialize
* \param[in] chunk_sizes The size of each chunk
* \param[in] chunks_count The number of chunks
*
* \retval KNOT_E*
*/
int changeset_deserialize_chunks(changeset_t *ch, uint8_t *src_chunks[], const size_t *chunks_sizes, int chunks_count);
/*! @} */
......@@ -161,7 +161,7 @@ static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item,
#undef IXFR_SAFE_PUT
/*! \brief Loads IXFRs from journal. */
static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
static int ixfr_load_chsets(list_t *chgsets, zone_t *zone,
const knot_rrset_t *their_soa)
{
assert(chgsets);
......@@ -175,12 +175,7 @@ static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
return KNOT_EUPTODATE;
}
char *path = conf_journalfile(conf(), zone->name);
pthread_mutex_lock((pthread_mutex_t *)&zone->journal_lock);
ret = journal_load_changesets(path, zone->name, chgsets, serial_from, serial_to);
pthread_mutex_unlock((pthread_mutex_t *)&zone->journal_lock);
free(path);
ret = zone_changes_load(conf(), zone, chgsets, serial_from);
if (ret != KNOT_EOK) {
changesets_free(chgsets);
}
......@@ -582,12 +577,6 @@ static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
return ret;
}
/*! \brief Checks whether journal node limit has not been exceeded. */
static bool journal_limit_exceeded(struct ixfr_proc *proc)
{
return proc->change_count > JOURNAL_NCOUNT;
}
/*! \brief Checks whether RR belongs into zone. */
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc)
{
......@@ -617,11 +606,6 @@ static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
// Process RRs in the message.
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
for (uint16_t i = 0; i < answer->count; ++i) {
if (journal_limit_exceeded(ixfr)) {
IXFRIN_LOG(LOG_WARNING, "journal is full");
return KNOT_STATE_FAIL;
}
const knot_rrset_t *rr = knot_pkt_rr(answer, i);
if (out_of_zone(rr, ixfr)) {
continue;
......
This diff is collapsed.
/* 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/>.
*/
/*!
* \file journal.h
*
* \author Marek Vavrusa <marek.vavrusa@nic.cz>
*
* \brief Journal for storing transactions on permanent storage.
*
* Journal stores entries on a permanent storage.
* Each written entry is guaranteed to persist until
* the maximum file size or node count is reached.
* Entries are removed from the least recent.
*
* Journal file structure
* <pre>
* uint16_t node_count
* uint16_t node_queue_head
* uint16_t node_queue_tail
* journal_entry_t free_segment
* node_count *journal_entry_t
* ...data...
* </pre>
* \addtogroup utils
* @{
*/
#pragma once
#include <stdint.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include "knot/updates/changesets.h"
struct zone;
/*!
* \brief Journal entry flags.
*/
typedef enum journal_flag_t {
JOURNAL_NULL = 0 << 0, /*!< Invalid journal entry. */
JOURNAL_FREE = 1 << 0, /*!< Free journal entry. */
JOURNAL_VALID = 1 << 1, /*!< Valid journal entry. */
JOURNAL_DIRTY = 1 << 2 /*!< Journal entry cannot be evicted. */
} journal_flag_t;
/*!
* \brief Journal node structure.
*
* Each node represents journal entry and points
* to position of the data in the permanent storage.
*/
typedef struct journal_node
{
uint64_t id; /*!< Node ID. */
uint16_t flags; /*!< Node flags. */
uint16_t next; /*!< UNUSED */
uint32_t pos; /*!< Position in journal file. */
uint32_t len; /*!< Entry data length. */
} journal_node_t;
/*!
* \brief Journal structure.
*
* Journal organizes entries as nodes.
* Nodes are stored in-memory for fast lookup and also
* backed by a permanent storage.
* Each journal has a fixed number of nodes.
*
*/
typedef struct journal
{
int fd;
char *path; /*!< Path to journal file. */
uint16_t tmark; /*!< Transaction start mark. */
uint16_t max_nodes; /*!< Number of nodes. */
uint16_t qhead; /*!< Node queue head. */
uint16_t qtail; /*!< Node queue tail. */
uint16_t bflags; /*!< Initial flags for each written node. */
size_t fsize; /*!< Journal file size. */
size_t fslimit; /*!< File size limit. */
journal_node_t free; /*!< Free segment. */
journal_node_t *nodes; /*!< Array of nodes. */
} journal_t;
/*
* Journal defaults and constants.
*/
#define JOURNAL_NCOUNT 1024 /*!< Default node count. */
#define JOURNAL_MAGIC {'k', 'n', 'o', 't', '1', '5', '2'}
#define MAGIC_LENGTH 7
/* HEADER = magic, crc, max_entries, qhead, qtail */
#define JOURNAL_HSIZE (MAGIC_LENGTH + sizeof(uint32_t) + sizeof(uint16_t) * 3)
/*!
* \brief Open journal.
*
* \param journal Returned journal.
* \param path Journal file name.
* \param fslimit File size limit (0 for no limit).
*
* \retval new journal instance if successful.
* \retval NULL on error.
*/
int journal_open(journal_t **journal, const char *path, size_t fslimit);
/*!
* \brief Map journal entry for read/write.
*
* \warning New nodes shouldn't be created until the entry is unmapped.
*
* \param journal Associated journal.
* \param id Entry identifier.
* \param dst Will contain mapped memory.
* \param rdonly If read only.
*
* \retval KNOT_EOK if successful.
* \retval KNOT_ESPACE if entry too big.
* \retval KNOT_ERROR on I/O error.
*/
int journal_map(journal_t *journal, uint64_t id, char **dst, size_t size, bool rdonly);
/*!
* \brief Finalize mapped journal entry.
*
* \param journal Associated journal.
* \param id Entry identifier.
* \param ptr Mapped memory.
* \param finalize Set to true to finalize node or False to discard it.
*
* \retval KNOT_EOK if successful.
* \retval KNOT_ENOENT if the entry cannot be found.
* \retval KNOT_ERROR on I/O error.
*/
int journal_unmap(journal_t *journal, uint64_t id, void *ptr, int finalize);
/*!
* \brief Close journal file.
*
* \param journal Associated journal.
*
* \retval KNOT_EOK on success.
* \retval KNOT_EINVAL on invalid parameter.
*/
int journal_close(journal_t *journal);
/*!
* \brief Check if the journal file is used or not.
*
* \param path Journal file.
*
* \return true or false
*/
bool journal_exists(const char *path);
/*!
* \brief Load changesets from journal.
*
* \param path Path to journal file.
* \param zone Corresponding zone.
* \param dst Store changesets here.
* \param from Start serial.
* \param to End serial.
*
* \retval KNOT_EOK on success.
* \retval KNOT_ERANGE if given entry was not found.
* \return < KNOT_EOK on error.
*/
int journal_load_changesets(const char *path, const knot_dname_t *zone, list_t *dst,
uint32_t from, uint32_t to);
// TODO: :-/
int load_changeset(journal_t *journal, journal_node_t *n, const knot_dname_t *zone, list_t *chgs);
int changesets_unpack(changeset_t *chs);
/*!
* \brief Store changesets in journal.
*