diff --git a/src/knot/events/handlers/dnssec.c b/src/knot/events/handlers/dnssec.c index 67aecf6ad41c77cf5c4a6468bc4cc6bc42cf1ff8..4b2d9e3fc2806417629fff96d2e0e463271c3040 100644 --- a/src/knot/events/handlers/dnssec.c +++ b/src/knot/events/handlers/dnssec.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -92,7 +92,7 @@ int event_dnssec(conf_t *conf, zone_t *zone) } zone_update_t up; - int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL); + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { return ret; } diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c index 063621ada281224bfcdabe3be90c0db5f0fdfcbe..0855b5c506b5cc2adb42e4bb2b83fe950cbf8e29 100644 --- a/src/knot/events/handlers/refresh.c +++ b/src/knot/events/handlers/refresh.c @@ -502,7 +502,7 @@ static int ixfr_finalize(struct refresh_data *data) } zone_update_t up = { 0 }; - int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT); + int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { data->fallback_axfr = false; data->fallback->remote = false; diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c index 2b17375b0be04887c7187d6e4bcbebdc2bb84ed7..51d7940e01195bb8c54b7c50e519cc0fd08d5a38 100644 --- a/src/knot/events/handlers/update.c +++ b/src/knot/events/handlers/update.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -138,7 +138,7 @@ static int process_normal(conf_t *conf, zone_t *zone, list_t *requests) // Init zone update structure zone_update_t up; - int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL); + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; diff --git a/src/knot/journal/journal_basic.c b/src/knot/journal/journal_basic.c index 0285630ed34a7c59db55e1f2148e94dc61274bec..825130a243fff2f6074e971aa076d2f22eea968e 100644 --- a/src/knot/journal/journal_basic.c +++ b/src/knot/journal/journal_basic.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -27,12 +27,12 @@ MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const } } -MDB_val journal_changeset_to_chunk_key(const changeset_t *ch, uint32_t chunk_id) +MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id) { - if (ch->soa_from == NULL) { - return knot_lmdb_make_key("NISI", ch->add->apex->owner, (uint32_t)0, "bootstrap", chunk_id); + if (zij) { + return knot_lmdb_make_key("NISI", apex, (uint32_t)0, "bootstrap", chunk_id); } else { - return knot_lmdb_make_key("NIII", ch->add->apex->owner, (uint32_t)0, changeset_from(ch), chunk_id); + return knot_lmdb_make_key("NIII", apex, (uint32_t)0, ch_from, chunk_id); } } diff --git a/src/knot/journal/journal_basic.h b/src/knot/journal/journal_basic.h index 9234de68079d64784b26574f1e269f425bdce655..8804d7bdf5cc61147a88c8778e3c9ace3d14fcf4 100644 --- a/src/knot/journal/journal_basic.h +++ b/src/knot/journal/journal_basic.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -52,12 +52,14 @@ MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const /*! * \brief Create a database key for changeset chunk. * - * \param ch Corresponding changeset (perhaps to be stored). + * \param apex Zone apex owner name. + * \param ch_from Serial "from" of the stored changeset. + * \param zij Zone-in-journal is stored. * \param chunk_id Ordinal number of this changeset's chunk. * * \return DB key. 'mv_data' shall be freed later. 'mv_data' is NULL on failure. */ -MDB_val journal_changeset_to_chunk_key(const changeset_t *ch, uint32_t chunk_id); +MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id); /*! * \brief Return a key prefix to operate with all zone-related records. diff --git a/src/knot/journal/journal_write.c b/src/knot/journal/journal_write.c index af1e540981e0107d9942b0839d69f1694fdadb5c..ad1247bd7c84355c9cb74ff7dc4d85f9061e5860 100644 --- a/src/knot/journal/journal_write.c +++ b/src/knot/journal/journal_write.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -22,7 +22,8 @@ #include "knot/journal/serialization.h" #include "libknot/error.h" -static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, const changeset_t *ch, uint32_t ch_serial_to) +static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, + const knot_dname_t *apex, bool zij, uint32_t ch_from, uint32_t ch_to) { MDB_val chunk; uint32_t i = 0; @@ -34,16 +35,18 @@ static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, } chunk.mv_size += JOURNAL_HEADER_SIZE; chunk.mv_data = NULL; - MDB_val key = journal_changeset_to_chunk_key(ch, i); + MDB_val key = journal_make_chunk_key(apex, ch_from, zij, i); if (knot_lmdb_insert(txn, &key, &chunk)) { - journal_make_header(chunk.mv_data, ch_serial_to); + journal_make_header(chunk.mv_data, ch_to); serialize_chunk(ser, chunk.mv_data + JOURNAL_HEADER_SIZE, chunk.mv_size - JOURNAL_HEADER_SIZE); } free(key.mv_data); i++; } - serialize_deinit(ser); - // return value is in the txn + int ret = serialize_deinit(ser); + if (txn->ret == KNOT_EOK) { + txn->ret = ret; + } } void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch) @@ -53,7 +56,11 @@ void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch) txn->ret = KNOT_ENOMEM; return; } - journal_write_serialize(txn, ser, ch, changeset_to(ch)); + if (ch->remove == NULL) { + journal_write_serialize(txn, ser, ch->soa_to->owner, true, 0, changeset_to(ch)); + } else { + journal_write_serialize(txn, ser, ch->soa_to->owner, false, changeset_from(ch), changeset_to(ch)); + } } void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z) @@ -63,10 +70,17 @@ void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z) txn->ret = KNOT_ENOMEM; return; } - changeset_t fake_ch; - fake_ch.soa_from = NULL; - fake_ch.add = (zone_contents_t *)z; - journal_write_serialize(txn, ser, &fake_ch, zone_contents_serial(z)); + journal_write_serialize(txn, ser, z->apex->owner, true, 0, zone_contents_serial(z)); +} + +void journal_write_zone_diff(knot_lmdb_txn_t *txn, const zone_diff_t *z) +{ + serialize_ctx_t *ser = serialize_zone_diff_init(z); + if (ser == NULL) { + txn->ret = KNOT_ENOMEM; + return; + } + journal_write_serialize(txn, ser, z->apex->owner, false, zone_diff_from(z), zone_diff_to(z)); } static bool delete_one(knot_lmdb_txn_t *txn, bool del_zij, uint32_t del_serial, @@ -242,15 +256,22 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z) return txn.ret; } -int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra) +int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra, + const zone_diff_t *zdiff) { - size_t ch_size = changeset_serialized_size(ch); + assert(zdiff == NULL || (ch == NULL && extra == NULL)); + + size_t ch_size = zdiff == NULL ? changeset_serialized_size(ch) : + zone_diff_serialized_size(*zdiff); size_t max_usage = journal_conf_max_usage(j); if (ch_size >= max_usage) { return KNOT_ESPACE; } - if (extra != NULL && (changeset_to(extra) != changeset_to(ch) || - changeset_from(extra) == changeset_from(ch))) { + + uint32_t ch_from = zdiff == NULL ? changeset_from(ch) : zone_diff_from(zdiff); + uint32_t ch_to = zdiff == NULL ? changeset_to(ch) : zone_diff_to(zdiff); + if (extra != NULL && (changeset_to(extra) != ch_to || + changeset_from(extra) == ch_from)) { return KNOT_EINVAL; } int ret = knot_lmdb_open(j.db); @@ -280,7 +301,7 @@ int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *e journal_fix_occupation(j, &txn, &md, max_usage - ch_size, chs_limit - 1); // avoid discontinuity - if ((md.flags & JOURNAL_SERIAL_TO_VALID) && md.serial_to != changeset_from(ch)) { + if ((md.flags & JOURNAL_SERIAL_TO_VALID) && md.serial_to != ch_from) { if (journal_contains(&txn, true, 0, j.zone)) { txn.ret = KNOT_ESEMCHECK; } else { @@ -290,12 +311,16 @@ int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *e } // avoid cycle - if (journal_contains(&txn, false, changeset_to(ch), j.zone)) { + if (journal_contains(&txn, false, ch_to, j.zone)) { journal_fix_occupation(j, &txn, &md, INT64_MAX, 1); } - journal_write_changeset(&txn, ch); - journal_metadata_after_insert(&md, changeset_from(ch), changeset_to(ch)); + if (zdiff == NULL) { + journal_write_changeset(&txn, ch); + } else { + journal_write_zone_diff(&txn, zdiff); + } + journal_metadata_after_insert(&md, ch_from, ch_to); if (extra != NULL) { journal_write_changeset(&txn, extra); diff --git a/src/knot/journal/journal_write.h b/src/knot/journal/journal_write.h index ad280e46db2a54fdf97452473971cf5305e45e2a..a55fd341bdfe06efaa7340a145e73ae342ee9c0f 100644 --- a/src/knot/journal/journal_write.h +++ b/src/knot/journal/journal_write.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -18,6 +18,7 @@ #include "knot/journal/journal_basic.h" #include "knot/journal/journal_metadata.h" +#include "knot/journal/serialization.h" /*! * \brief Serialize a changeset into chunks and write it into DB with no checks and metadata update. @@ -107,6 +108,7 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z); * \param j Zone journal. * \param ch Changeset to be stored. * \param extra Extra changeset to be stored in the role of merged changeset. + * \param zdiff Zone diff to be stored instead of changeset. * * \note The extra changesetis being stored on zone load, it is basically the diff * between zonefile and loaded zone contents. Afterwards, it will be treated @@ -115,4 +117,5 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z); * * \return KNOT_E* */ -int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra); +int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra, + const zone_diff_t *zdiff); diff --git a/src/knot/journal/serialization.c b/src/knot/journal/serialization.c index 5946152defd7a279943b3bff27c81b5e9fb6683d..575848116ecdb819a2d75fdac50a5beefce55f21 100644 --- a/src/knot/journal/serialization.c +++ b/src/knot/journal/serialization.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -18,7 +18,6 @@ #include "knot/journal/serialization.h" #include "knot/zone/zone-tree.h" -#include "libknot/libknot.h" #define SERIALIZE_RRSET_INIT (-1) #define SERIALIZE_RRSET_DONE ((1L<<16)+1) @@ -37,10 +36,13 @@ typedef enum { #define RRSET_BUF_MAXSIZE 256 struct serialize_ctx { - const zone_contents_t *z; + zone_diff_t zdiff; zone_tree_it_t zit; zone_node_t *n; uint16_t node_pos; + bool zone_diff; + bool zone_diff_add; + int ret; const changeset_t *ch; changeset_iter_t it; @@ -48,6 +50,7 @@ struct serialize_ctx { long rrset_phase; knot_rrset_t rrset_buf[RRSET_BUF_MAXSIZE]; size_t rrset_buf_size; + list_t free_rdatasets; }; serialize_ctx_t *serialize_init(const changeset_t *ch) @@ -61,6 +64,7 @@ serialize_ctx_t *serialize_init(const changeset_t *ch) ctx->changeset_phase = ch->soa_from != NULL ? PHASE_SOA_1 : PHASE_SOA_2; ctx->rrset_phase = SERIALIZE_RRSET_INIT; ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); return ctx; } @@ -72,10 +76,30 @@ serialize_ctx_t *serialize_zone_init(const zone_contents_t *z) return NULL; } - ctx->z = z; + zone_diff_from_zone(&ctx->zdiff, z); ctx->changeset_phase = PHASE_ZONE_SOA; ctx->rrset_phase = SERIALIZE_RRSET_INIT; ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); + + return ctx; +} + +serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z) +{ + serialize_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->zone_diff = true; + ctx->zdiff = *z; + zone_diff_reverse(&ctx->zdiff); // start with removals of counterparts + + ctx->changeset_phase = PHASE_ZONE_SOA; + ctx->rrset_phase = SERIALIZE_RRSET_INIT; + ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); return ctx; } @@ -86,19 +110,28 @@ static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx) knot_rrset_init_empty(&res); switch (ctx->changeset_phase) { case PHASE_ZONE_SOA: - zone_tree_it_begin(ctx->z->nodes, &ctx->zit); + zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit); ctx->changeset_phase = PHASE_ZONE_NODES; - return node_rrset(ctx->z->apex, KNOT_RRTYPE_SOA); + return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA); case PHASE_ZONE_NODES: case PHASE_ZONE_NSEC3: while (ctx->n == NULL || ctx->node_pos >= ctx->n->rrset_count) { if (zone_tree_it_finished(&ctx->zit)) { zone_tree_it_free(&ctx->zit); - if (ctx->changeset_phase == PHASE_ZONE_NSEC3 || zone_tree_is_empty(ctx->z->nsec3_nodes)) { - ctx->changeset_phase = PHASE_END; - return res; + if (ctx->changeset_phase == PHASE_ZONE_NSEC3 || + zone_tree_is_empty(&ctx->zdiff.nsec3s)) { + if (ctx->zone_diff && !ctx->zone_diff_add) { + ctx->zone_diff_add = true; + zone_diff_reverse(&ctx->zdiff); + zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit); + ctx->changeset_phase = PHASE_ZONE_NODES; + return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA); + } else { + ctx->changeset_phase = PHASE_END; + return res; + } } else { - zone_tree_it_begin(ctx->z->nsec3_nodes, &ctx->zit); + zone_tree_it_begin(&ctx->zdiff.nsec3s, &ctx->zit); ctx->changeset_phase = PHASE_ZONE_NSEC3; } } @@ -107,9 +140,27 @@ static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx) ctx->node_pos = 0; } res = node_rrset_at(ctx->n, ctx->node_pos++); - if (ctx->n == ctx->z->apex && res.type == KNOT_RRTYPE_SOA) { + if (ctx->n == ctx->zdiff.apex && res.type == KNOT_RRTYPE_SOA) { return get_next_rrset(ctx); } + if (ctx->zone_diff) { + knot_rrset_t counter_rr = node_rrset(binode_counterpart(ctx->n), res.type); + if (counter_rr.ttl == res.ttl && !knot_rrset_empty(&counter_rr)) { + if (knot_rdataset_subset(&res.rrs, &counter_rr.rrs)) { + return get_next_rrset(ctx); + } + knot_rdataset_t rd_copy; + ctx->ret = knot_rdataset_copy(&rd_copy, &res.rrs, NULL); + if (ctx->ret == KNOT_EOK) { + knot_rdataset_subtract(&rd_copy, &counter_rr.rrs, NULL); + ptrlist_add(&ctx->free_rdatasets, rd_copy.rdata, NULL); + res.rrs = rd_copy; + assert(!knot_rrset_empty(&res)); + } else { + ctx->changeset_phase = PHASE_END; + } + } + } return res; case PHASE_SOA_1: changeset_iter_rem(&ctx->it, ctx->ch); @@ -152,6 +203,16 @@ void serialize_prepare(serialize_ctx_t *ctx, size_t thresh_size, if (ctx->rrset_buf_size > 0) { ctx->rrset_buf[0] = ctx->rrset_buf[ctx->rrset_buf_size - 1]; ctx->rrset_buf_size = 1; + + // memory optimization: free all buffered rrsets except last one + ptrnode_t *n, *next; + WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) { + if (n != TAIL(ctx->free_rdatasets)) { + free(n->d); + rem_node(&n->n); + free(n); + } + } } else { ctx->rrset_buf[0] = get_next_rrset(ctx); if (ctx->changeset_phase == PHASE_END) { @@ -237,7 +298,7 @@ bool serialize_unfinished(serialize_ctx_t *ctx) return ctx->changeset_phase < PHASE_END; } -void serialize_deinit(serialize_ctx_t *ctx) +int serialize_deinit(serialize_ctx_t *ctx) { if (ctx->it.node != NULL) { changeset_iter_clear(&ctx->it); @@ -245,7 +306,15 @@ void serialize_deinit(serialize_ctx_t *ctx) if (ctx->zit.tree != NULL) { zone_tree_it_free(&ctx->zit); } + ptrnode_t *n, *next; + WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) { + free(n->d); + rem_node(&n->n); + free(n); + } + int ret = ctx->ret; free(ctx); + return ret; } static uint64_t rrset_binary_size(const knot_rrset_t *rrset) @@ -268,6 +337,40 @@ static uint64_t rrset_binary_size(const knot_rrset_t *rrset) return size; } +static size_t node_diff_size(zone_node_t *node) +{ + size_t res = 0; + knot_rrset_t rr, counter_rr; + for (int i = 0; i < node->rrset_count; i++) { + rr = node_rrset_at(node, i); + counter_rr = node_rrset(binode_counterpart(node), rr.type); + if (!knot_rrset_equal(&rr, &counter_rr, true)) { + res += rrset_binary_size(&rr); + } + } + return res; +} + +size_t zone_diff_serialized_size(zone_diff_t diff) +{ + size_t res = 0; + for (int i = 0; i < 2; i++) { + zone_diff_reverse(&diff); + zone_tree_it_t it = { 0 }; + int ret = zone_tree_it_double_begin(&diff.nodes, diff.nsec3s.trie != NULL ? + &diff.nsec3s : NULL, &it); + if (ret != KNOT_EOK) { + return 0; + } + while (!zone_tree_it_finished(&it)) { + res += node_diff_size(zone_tree_it_val(&it)); + zone_tree_it_next(&it); + } + zone_tree_it_free(&it); + } + return res; +} + size_t changeset_serialized_size(const changeset_t *ch) { if (ch == NULL) { diff --git a/src/knot/journal/serialization.h b/src/knot/journal/serialization.h index b4f80a364ff8b64bf944cbee1f12ff91e5454e00..6f461b7a34ce5078cad0f8a2affaa210898e56cd 100644 --- a/src/knot/journal/serialization.h +++ b/src/knot/journal/serialization.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -17,10 +17,46 @@ #pragma once #include <stdint.h> + #include "libknot/rrset.h" +#include "libknot/rrtype/soa.h" #include "knot/updates/changesets.h" #include "contrib/wire_ctx.h" +typedef struct { + zone_tree_t nodes; + zone_tree_t nsec3s; + zone_node_t *apex; +} zone_diff_t; + +inline static void zone_diff_reverse(zone_diff_t *diff) +{ + diff->nodes.flags ^= ZONE_TREE_BINO_SECOND; + diff->nsec3s.flags ^= ZONE_TREE_BINO_SECOND; + diff->apex = binode_counterpart(diff->apex); +} + +inline static void zone_diff_from_zone(zone_diff_t *diff, const zone_contents_t *z) +{ + diff->nodes = *z->nodes; + if (z->nsec3_nodes != NULL) { + diff->nsec3s = *z->nsec3_nodes; + } else { + memset(&diff->nsec3s, 0, sizeof(diff->nsec3s)); + } + diff->apex = z->apex; +} + +inline static uint32_t zone_diff_to(const zone_diff_t *diff) +{ + return knot_soa_serial(node_rdataset(diff->apex, KNOT_RRTYPE_SOA)->rdata); +} + +inline static uint32_t zone_diff_from(const zone_diff_t *diff) +{ + return knot_soa_serial(node_rdataset(binode_counterpart(diff->apex), KNOT_RRTYPE_SOA)->rdata); +} + typedef struct serialize_ctx serialize_ctx_t; /*! @@ -41,6 +77,15 @@ serialize_ctx_t *serialize_init(const changeset_t *ch); */ serialize_ctx_t *serialize_zone_init(const zone_contents_t *z); +/*! + * \brief Init serialization context. + * + * \param z Zone with binodes being updated. + * + * \return Context. + */ +serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z); + /*! * \brief Pre-check and space computation before serializing a chunk. * @@ -67,8 +112,23 @@ void serialize_chunk(serialize_ctx_t *ctx, uint8_t *chunk, size_t chunk_size); * to be serialized into next chunk(s) yet. */ bool serialize_unfinished(serialize_ctx_t *ctx); -/*! \brief Free serialization context. */ -void serialize_deinit(serialize_ctx_t *ctx); +/*! + * \brief Free serialization context. + * + * \return KNOT_E* if there were errors during serialization. + */ +int serialize_deinit(serialize_ctx_t *ctx); + +/*! + * \brief Returns size of serialized changeset from zone diff. + * + * \warning Not accurate! This is an upper bound, suitable for policy enforcement etc. + * + * \param[in] diff Zone diff structure to create changeset from. + * + * \return Size of the resulting changeset. + */ +size_t zone_diff_serialized_size(zone_diff_t diff); /*! * \brief Returns size of changeset in serialized form. diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c index 944a6f9ceaf232e34895813d6b1d506f1e455757..e4a96d7eab1beaacb0ceecff6831f9e4375a777d 100644 --- a/src/knot/updates/zone-update.c +++ b/src/knot/updates/zone-update.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -361,7 +361,11 @@ const knot_rdataset_t *zone_update_to(zone_update_t *update) return NULL; } - if (update->flags & UPDATE_FULL) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff = { .apex = update->new_cont->apex }; + return zone_diff_to(&diff) == zone_diff_from(&diff) ? + NULL : node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA); + } else if (update->flags & UPDATE_FULL) { const zone_node_t *apex = update->new_cont->apex; return node_rdataset(apex, KNOT_RRTYPE_SOA); } else { @@ -672,10 +676,16 @@ static int commit_journal(conf_t *conf, zone_update_t *update) conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name); unsigned content = conf_opt(&val); int ret = KNOT_EOK; - if ((update->flags & UPDATE_INCREMENTAL) || - (update->flags & UPDATE_HYBRID)) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff; + diff.nodes = *update->a_ctx->node_ptrs; + diff.nsec3s = *update->a_ctx->nsec3_ptrs; + diff.apex = update->new_cont->apex; + return zone_diff_store(conf, update->zone, &diff); + } else if ((update->flags & UPDATE_INCREMENTAL) || + (update->flags & UPDATE_HYBRID)) { changeset_t *extra = (update->flags & UPDATE_EXTRA_CHSET) ? &update->extra_ch : NULL; - if (content != JOURNAL_CONTENT_NONE && !changeset_empty(&update->change)) { + if (content != JOURNAL_CONTENT_NONE && !zone_update_no_change(update)) { ret = zone_change_store(conf, update->zone, &update->change, extra); } } else { @@ -692,7 +702,7 @@ static int commit_incremental(conf_t *conf, zone_update_t *update) { assert(update); - if (zone_update_to(update) == NULL && !changeset_empty(&update->change)) { + if (zone_update_to(update) == NULL && !zone_update_no_change(update)) { /* No SOA in the update, create one according to the current policy */ int ret = zone_update_increment_soa(update, conf); if (ret != KNOT_EOK) { @@ -879,7 +889,7 @@ int zone_update_commit(conf_t *conf, zone_update_t *update) int ret = KNOT_EOK; - if ((update->flags & UPDATE_INCREMENTAL) && changeset_empty(&update->change)) { + if ((update->flags & UPDATE_INCREMENTAL) && zone_update_no_change(update)) { zone_update_clear(update); return KNOT_EOK; } @@ -1003,7 +1013,13 @@ bool zone_update_no_change(zone_update_t *update) return true; } - if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff; + diff.nodes = *update->a_ctx->node_ptrs; + diff.nsec3s = *update->a_ctx->nsec3_ptrs; + diff.apex = update->new_cont->apex; + return (zone_diff_serialized_size(diff) == 0); + } else if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) { return changeset_empty(&update->change); } else { /* This branch does not make much sense and FULL update will most likely @@ -1012,6 +1028,17 @@ bool zone_update_no_change(zone_update_t *update) } } +static bool zone_diff_rdataset(const zone_contents_t *c, uint16_t rrtype) +{ + const knot_rdataset_t *a = node_rdataset(binode_counterpart(c->apex), rrtype); + const knot_rdataset_t *b = node_rdataset(c->apex, rrtype); + if (a == NULL && b == NULL) { + return false; + } else { + return !knot_rdataset_eq(a, b); + } +} + static bool contents_have_dnskey(const zone_contents_t *contents) { if (contents == NULL) { @@ -1025,7 +1052,11 @@ static bool contents_have_dnskey(const zone_contents_t *contents) bool zone_update_changes_dnskey(zone_update_t *update) { - if (update->flags & UPDATE_FULL) { + if (update->flags & UPDATE_NO_CHSET) { + return (zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_DNSKEY) || + zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDNSKEY) || + zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDS)); + } else if (update->flags & UPDATE_FULL) { return contents_have_dnskey(update->new_cont); } else { return (contents_have_dnskey(update->change.remove) || diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h index 8a2d7803b5269920afb4d81ce63b36873fbf0a74..1cda54b262b798a4135a8ad09e9913d087a9fe5b 100644 --- a/src/knot/updates/zone-update.h +++ b/src/knot/updates/zone-update.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -56,6 +56,7 @@ typedef enum { UPDATE_STRICT = 1 << 4, /*!< Apply changes strictly, i.e. fail when removing nonexistent RR. */ UPDATE_EXTRA_CHSET = 1 << 6, /*!< Extra changeset in use, to store diff btwn zonefile and final contents. */ UPDATE_CHANGED_NSEC = 1 << 7, /*!< This incremental update affects NSEC or NSEC3 nodes in zone. */ + UPDATE_NO_CHSET = 1 << 8, /*!< Avoid using changeset and serialize to journal from diff of bi-nodes. */ } zone_update_flags_t; /*! @@ -153,6 +154,8 @@ const knot_rdataset_t *zone_update_from(zone_update_t *update); * \param update Zone update. * * \return NULL if no new SOA has been added, new SOA otherwise. + * + * \todo Refactor this function according to its use. */ const knot_rdataset_t *zone_update_to(zone_update_t *update); diff --git a/src/knot/zone/zone-tree.h b/src/knot/zone/zone-tree.h index bf8a6600d48dffef7bd99d20f8d2a491018a5695..384e87efdfc7cd8a463d15c7a3684d7d8dbaf6d2 100644 --- a/src/knot/zone/zone-tree.h +++ b/src/knot/zone/zone-tree.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -86,7 +86,7 @@ zone_tree_t *zone_tree_shallow_copy(zone_tree_t *from); */ inline static size_t zone_tree_count(const zone_tree_t *tree) { - if (tree == NULL) { + if (tree == NULL || tree->trie == NULL) { return 0; } diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 321f3f5e66661a8b48ddd325e295a61482e133a0..8d412613012ce3c6053c04f896107f188b486844 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -280,14 +280,36 @@ int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset zone_journal_t j = { zone_journaldb(zone), zone->name, conf }; - int ret = journal_insert(j, change, extra); + int ret = journal_insert(j, change, extra, NULL); if (ret == KNOT_EBUSY) { log_zone_notice(zone->name, "journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = flush_journal(conf, zone, true, false); if (ret == KNOT_EOK) { - ret = journal_insert(j, change, extra); + ret = journal_insert(j, change, extra, NULL); + } + } + + return ret; +} + +int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff) +{ + if (conf == NULL || zone == NULL || diff == NULL) { + return KNOT_EINVAL; + } + + zone_journal_t j = { zone_journaldb(zone), zone->name, conf }; + + int ret = journal_insert(j, NULL, NULL, diff); + if (ret == KNOT_EBUSY) { + log_zone_notice(zone->name, "journal is full, flushing"); + + /* Transaction rolled back, journal released, we may flush. */ + ret = flush_journal(conf, zone, true, false); + if (ret == KNOT_EOK) { + ret = journal_insert(j, NULL, NULL, diff); } } diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 7a8c2ac0a567eb67b190b08373c2311855c991cd..6623c43848456f2c290cb8f291515bb9d3dbc9a5 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -21,6 +21,7 @@ #include "knot/conf/conf.h" #include "knot/conf/confio.h" #include "knot/journal/journal_basic.h" +#include "knot/journal/serialization.h" #include "knot/events/events.h" #include "knot/updates/changesets.h" #include "knot/zone/contents.h" @@ -153,6 +154,7 @@ inline static zone_journal_t zone_journal(zone_t *zone) } int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset_t *extra); +int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff); int zone_changes_clear(conf_t *conf, zone_t *zone); int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents); diff --git a/src/libknot/rdataset.c b/src/libknot/rdataset.c index 6e5c81c8f06408e4b80c9bd8e8489a96aa3b8707..fe3e0e54462d0dfeb0add9b9b98f3b89387c4224 100644 --- a/src/libknot/rdataset.c +++ b/src/libknot/rdataset.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -246,6 +246,24 @@ bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr) return false; } +_public_ +bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of) +{ + if (subset == NULL) { + return true; + } + + knot_rdata_t *rd = subset->rdata; + for (uint16_t i = 0; i < subset->count; ++i) { + if (!knot_rdataset_member(of, rd)) { + return false; + } + rd = knot_rdataset_next(rd); + } + + return true; +} + _public_ int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, knot_mm_t *mm) diff --git a/src/libknot/rdataset.h b/src/libknot/rdataset.h index 5c49847fdd69374990eb4047c626eb0697f6bf1e..f3e65a86e63b49277c1af3a6ddd1d4e9d49eeb0b 100644 --- a/src/libknot/rdataset.h +++ b/src/libknot/rdataset.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -134,6 +134,17 @@ bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2); */ bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr); +/*! + * \brief Returns true if \a subset is a sub-set of \a of, false otherwise. + * + * \param subset RRS to check. + * \param of RRS to search in. + * + * \retval true if \a subset is a sub-set of \a of. + * \retval false if \a subset is not a sub-set of \a of. + */ +bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of); + /*! * \brief Merges two RRS into the first one. Second RRS is left intact. * Canonical order is preserved. diff --git a/tests/knot/test_journal.c b/tests/knot/test_journal.c index afc824422aa4e0f868cef6e215e7b980f63175a8..748ab02d02622269801a1596fd51e87c2621a612 100644 --- a/tests/knot/test_journal.c +++ b/tests/knot/test_journal.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -315,7 +315,7 @@ static void test_store_load(const knot_dname_t *apex) changeset_t *m_ch = changeset_new(apex), r_ch, e_ch; init_random_changeset(m_ch, 0, 1, 128, apex, false); - int ret = journal_insert(jj, m_ch, NULL); + int ret = journal_insert(jj, m_ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: store changeset (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal: check after store changeset (%s)", knot_strerror(ret)); @@ -336,7 +336,7 @@ static void test_store_load(const knot_dname_t *apex) for (; ret == KNOT_EOK && serial < 40000; ++serial) { changeset_t *m_ch2 = changeset_new(apex); init_random_changeset(m_ch2, serial, serial + 1, 128, apex, false); - ret = journal_insert(jj, m_ch2, NULL); + ret = journal_insert(jj, m_ch2, NULL, NULL); if (ret != KNOT_EOK) { changeset_free(m_ch2); break; @@ -378,7 +378,7 @@ static void test_store_load(const knot_dname_t *apex) ret = changeset_init(&ch, apex); ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret); init_random_changeset(&ch, serial, serial + 1, 555, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: store after flush (%d)", ret); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -393,7 +393,7 @@ static void test_store_load(const knot_dname_t *apex) changeset_init(&ch, apex); init_random_changeset(&ch, 2, 3, 100, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert discontinuous changeset (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -412,14 +412,14 @@ static void test_store_load(const knot_dname_t *apex) changeset_clear(&ch); changeset_init(&ch, apex); init_random_changeset(&ch, serials[i], serials[i + 1], 100, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(i == 4 ? KNOT_EBUSY : KNOT_EOK, ret, "journal: inserting cycle (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); } ret = journal_set_flushed(jj); is_int(KNOT_EOK, ret, "journal: flush in cycle (%s)", knot_strerror(ret)); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: inserted cycle (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -445,7 +445,7 @@ static void test_store_load(const knot_dname_t *apex) is_int(KNOT_EOK, ret, "journal: insert zone-in-journal (%s)", knot_strerror(ret)); changeset_init(&r_ch, apex); init_random_changeset(&r_ch, 1, 2, 200, apex, false); - ret = journal_insert(jj, &r_ch, NULL); + ret = journal_insert(jj, &r_ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert after zone-in-journal (%s)", knot_strerror(ret)); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -470,7 +470,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon zone_journal_t jj2 = { &jdb, zone2, conf() }; changeset_t *small_ch2 = changeset_new(zone2); init_random_changeset(small_ch2, 1, 2, 100, zone2, false); - int ret = journal_insert(jj2, small_ch2, NULL); + int ret = journal_insert(jj2, small_ch2, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok"); changeset_t *big_zij = changeset_new(zone1); @@ -482,7 +482,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *big_ch2 = changeset_new(zone2); init_random_changeset(big_ch2, 2, 3, 750, zone2, false); - ret = journal_insert(jj2, big_ch2, NULL); + ret = journal_insert(jj2, big_ch2, NULL, NULL); is_int(KNOT_EOK, ret, "journal: second zone is not affected by storing big zij of other zone"); journal_read_t *read = NULL; @@ -490,7 +490,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon init_list(&l); changeset_t *medium_ch1 = changeset_new(zone1); init_random_changeset(medium_ch1, 1, 2, 300, zone1, false); - ret = journal_insert(jj, medium_ch1, NULL); + ret = journal_insert(jj, medium_ch1, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing medium changeset must be ok"); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -500,7 +500,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *small_ch1 = changeset_new(zone1); init_random_changeset(small_ch1, 2, 3, 100, zone1, false); - ret = journal_insert(jj, small_ch1, NULL); + ret = journal_insert(jj, small_ch1, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok"); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -510,7 +510,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *medium_ch1b = changeset_new(zone1); init_random_changeset(medium_ch1b, 3, 4, 300, zone1, false); - ret = journal_insert(jj, medium_ch1b, NULL); + ret = journal_insert(jj, medium_ch1b, NULL, NULL); is_int(KNOT_ESPACE, ret, "journal: not able to free space for changeset by merging"); changeset_t *too_big_zij = changeset_new(zone1); @@ -726,7 +726,7 @@ static void test_merge(const knot_dname_t *apex) // insert stuff and check the merge for (i = 0; !merged_present() && i < 40000; i++) { - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); assert(ret == KNOT_EOK); } ret = journal_sem_check(jj); @@ -743,7 +743,7 @@ static void test_merge(const knot_dname_t *apex) journal_read_end(read); // insert one more and check the #s of results - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert one more (%s)", knot_strerror(ret)); ret = load_j_list(&jj, false, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: journal_load_changesets2 must be ok"); @@ -764,7 +764,7 @@ static void test_merge(const knot_dname_t *apex) // insert changeset that will cancel it mostly out changeset_t *bigz_cancelout = tm2_chs_unzone(apex); - ret = journal_insert(jj, bigz_cancelout, NULL); + ret = journal_insert(jj, bigz_cancelout, NULL, NULL); changeset_free(bigz_cancelout); is_int(KNOT_EOK, ret, "journal: insert cancel-out changeset"); @@ -772,7 +772,7 @@ static void test_merge(const knot_dname_t *apex) tm_chs(apex, -1); while (changeset_to(tm_chs(apex, 0)) != 2) { } for (i = 0; i < 1600; i++) { - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); assert(ret == KNOT_EOK); } @@ -817,7 +817,7 @@ static void test_stress_base(const knot_dname_t *apex, serial = 0; while (true) { changeset_set_soa_serials(&ch, serial, serial + 1, apex); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); if (ret == KNOT_EOK) { serial++; } else {