Commit 0acc6a71 authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

journal: add new implementation of LMDB layer and Journal

parent 7ae5866a
......@@ -142,6 +142,16 @@ src/knot/journal/chgset_ctx.c
src/knot/journal/chgset_ctx.h
src/knot/journal/journal.c
src/knot/journal/journal.h
src/knot/journal/journal_basic.c
src/knot/journal/journal_basic.h
src/knot/journal/journal_metadata.c
src/knot/journal/journal_metadata.h
src/knot/journal/journal_read.c
src/knot/journal/journal_read.h
src/knot/journal/journal_write.c
src/knot/journal/journal_write.h
src/knot/journal/knot_lmdb.c
src/knot/journal/knot_lmdb.h
src/knot/journal/serialization.c
src/knot/journal/serialization.h
src/knot/modules/cookies/cookies.c
......
......@@ -125,6 +125,16 @@ libknotd_la_SOURCES = \
knot/journal/chgset_ctx.h \
knot/journal/journal.c \
knot/journal/journal.h \
knot/journal/journal_basic.c \
knot/journal/journal_basic.h \
knot/journal/journal_metadata.c \
knot/journal/journal_metadata.h \
knot/journal/journal_read.c \
knot/journal/journal_read.h \
knot/journal/journal_write.c \
knot/journal/journal_write.h \
knot/journal/knot_lmdb.c \
knot/journal/knot_lmdb.h \
knot/journal/serialization.c \
knot/journal/serialization.h \
knot/server/server.c \
......
......@@ -23,7 +23,6 @@
#include "knot/conf/confio.h"
#include "knot/conf/tools.h"
#include "knot/common/log.h"
#include "knot/journal/journal.h"
#include "knot/updates/acl.h"
#include "libknot/rrtype/opt.h"
#include "libdnssec/tsig.h"
......@@ -332,7 +331,7 @@ static const yp_item_t desc_template[] = {
{ C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "journal" }, CONF_IO_FRLD_SRV },
{ C_JOURNAL_DB_MODE, YP_TOPT, YP_VOPT = { journal_modes, JOURNAL_MODE_ROBUST },
CONF_IO_FRLD_SRV },
{ C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { JOURNAL_MIN_FSLIMIT, VIRT_MEM_LIMIT(TERA(100)),
{ C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { MEGA(1), VIRT_MEM_LIMIT(TERA(100)),
VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE },
CONF_IO_FRLD_SRV },
{ C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, CONF_IO_FRLD_SRV },
......
......@@ -150,6 +150,11 @@ enum {
JOURNAL_CONTENT_ALL = 2,
};
enum {
JOURNAL_MODE_ROBUST = 0, // Robust journal DB disk synchronization.
JOURNAL_MODE_ASYNC = 1, // Asynchronous journal DB disk synchronization.
};
enum {
ZONEFILE_LOAD_NONE = 0,
ZONEFILE_LOAD_DIFF = 1,
......
......@@ -1708,7 +1708,7 @@ void journal_close(journal_t *journal)
}
int journal_db_init(journal_db_t **db, const char *lmdb_dir_path, size_t lmdb_fslimit,
journal_mode_t mode)
int mode)
{
if (*db != NULL) {
return KNOT_EOK;
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2019 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
......@@ -28,17 +28,12 @@
/*! \brief Minimum journal size. */
#define JOURNAL_MIN_FSLIMIT (1 * 1024 * 1024)
typedef enum {
JOURNAL_MODE_ROBUST = 0, // Robust journal DB disk synchronization.
JOURNAL_MODE_ASYNC = 1, // Asynchronous journal DB disk synchronization.
} journal_mode_t;
typedef struct {
knot_db_t *db;
const knot_db_api_t *db_api;
char *path;
size_t fslimit;
journal_mode_t mode;
int mode;
pthread_mutex_t db_mutex; // please delete this once you move DB opening from journal_open to db_init
} journal_db_t;
......@@ -67,7 +62,7 @@ struct journal_txn;
* \return KNOT_E*
*/
int journal_db_init(journal_db_t **db, const char *lmdb_dir_path, size_t lmdb_fslimit,
journal_mode_t mode);
int mode);
/*!
* \brief Close shared journal DB file.
......
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#include "knot/journal/journal_basic.h"
#include "knot/conf/conf.h"
#include "knot/journal/journal_metadata.h"
#include "libknot/error.h"
MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const knot_dname_t *zone)
{
if (zone_in_journal) {
return knot_lmdb_make_key("NIS", zone, (uint32_t)0, "bootstrap");
} else {
return knot_lmdb_make_key("NII", zone, (uint32_t)0, serial);
}
}
MDB_val journal_changeset_to_chunk_key(const changeset_t *ch, 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);
} else {
return knot_lmdb_make_key("NIII", ch->add->apex->owner, (uint32_t)0, changeset_from(ch), chunk_id);
}
}
void journal_make_header(void *chunk, const changeset_t *ch)
{
knot_lmdb_make_key_part(chunk, JOURNAL_HEADER_SIZE, "IILLL", changeset_to(ch),
(uint32_t)0 /* we no longer care for # of chunks */,
(uint64_t)0, (uint64_t)0, (uint64_t)0);
}
uint32_t journal_next_serial(const MDB_val *chunk)
{
return be32toh(*(uint32_t *)chunk->mv_data);
}
bool journal_serial_to(knot_lmdb_txn_t *txn, bool zij, uint32_t serial,
const knot_dname_t *zone, uint32_t *serial_to)
{
MDB_val key = journal_changeset_id_to_key(zij, serial, zone);
bool found = knot_lmdb_find_prefix(txn, &key);
if (found && serial_to != NULL) {
*serial_to = journal_next_serial(&txn->cur_val);
}
free(key.mv_data);
return found;
}
bool journal_allow_flush(zone_journal_t j)
{
conf_val_t val = conf_zone_get(conf(), C_ZONEFILE_SYNC, j.zone);
return conf_int(&val) >= 0;
}
size_t journal_conf_max_usage(zone_journal_t j)
{
conf_val_t val = conf_zone_get(conf(), C_MAX_JOURNAL_USAGE, j.zone);
return conf_int(&val);
}
size_t journal_conf_max_changesets(zone_journal_t j)
{
conf_val_t val = conf_zone_get(conf(), C_MAX_JOURNAL_DEPTH, j.zone);
return conf_int(&val);
}
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "knot/conf/schema.h"
#include "knot/journal/knot_lmdb.h"
#include "knot/updates/changesets.h"
#include "libknot/dname.h"
typedef struct {
knot_lmdb_db_t *db;
const knot_dname_t *zone;
} zone_journal_t;
#define JOURNAL_CHUNK_MAX (70 * 1024)
#define JOURNAL_HEADER_SIZE (32)
/*! \brief Convert journal_mode to LMDB environment flags. */
inline static unsigned journal_env_flags(int journal_mode)
{
return journal_mode == JOURNAL_MODE_ASYNC ? (MDB_WRITEMAP | MDB_MAPASYNC) : 0;
}
/*!
* \brief Create a database key prefix to search for a changeset.
*
* \param zone_in_journal True if searching for zone-in-journal special changeset.
* \param serial Serial-from of the changeset to be searched for. Ignored if 'zone_in_journal'.
* \param zone Name of the zone.
*
* \return DB key. 'mv_data' shall be freed later. 'mv_data' is NULL on failure.
*/
MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const knot_dname_t *zone);
/*!
* \brief Create a database key for changeset chunk.
*
* \param ch Corresponding changeset (perhaps to be 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);
/*!
* \brief Initialise chunk header.
*
* \param chunk Pointer to the changeset chunk. It must be at least JOURNAL_HEADER_SIZE, perhaps more.
* \param ch Changeset to be serialized into the chunk.
*/
void journal_make_header(void *chunk, const changeset_t *ch);
/*!
* \brief Obtain serial-to of the serialized changeset.
*
* \param chunk Any chunk of a serialized changeset.
*
* \return The changeset's serial-to.
*/
uint32_t journal_next_serial(const MDB_val *chunk);
/*!
* \brief Obtain serial-to of a changeset stored in journal.
*
* \param txn Journal DB transaction.
* \param zij True if changeset in question is zone-in-journal.
* \param serial Serial-from of the changeset in question.
* \param zone Zone name.
* \param serial_to Output: serial-to of the changeset in question.
*
* \return True if the changeset exists in the journal.
*/
bool journal_serial_to(knot_lmdb_txn_t *txn, bool zij, uint32_t serial,
const knot_dname_t *zone, uint32_t *serial_to);
/*! \brief Return true if the changeset in question exists in the journal. */
inline static bool journal_contains(knot_lmdb_txn_t *txn, bool zone, uint32_t serial, const knot_dname_t *zone_name)
{
return journal_serial_to(txn, zone, serial, zone_name, NULL);
}
/*! \brief Return true if the journal may be flushed according to conf. */
bool journal_allow_flush(zone_journal_t j);
/*! \brief Return configured maximal per-zone usage of journal DB. */
size_t journal_conf_max_usage(zone_journal_t j);
/*! \brief Return configured maximal depth of journal. */
size_t journal_conf_max_changesets(zone_journal_t j);
This diff is collapsed.
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "knot/journal/journal_basic.h"
typedef struct {
uint32_t first_serial;
uint32_t serial_to;
uint32_t flushed_upto;
uint32_t merged_serial;
uint32_t changeset_count;
uint32_t flags; // a bitmap of flags, see enum below
bool _new_zone; // private: if there were no metadata at all previously
} journal_metadata_t;
enum journal_metadata_flags {
JOURNAL_LAST_FLUSHED_VALID = (1 << 0), // deprecated
JOURNAL_SERIAL_TO_VALID = (1 << 1),
JOURNAL_MERGED_SERIAL_VALID = (1 << 2),
};
typedef int (*journals_walk_cb_t)(const knot_dname_t *zone, void *ctx);
/*!
* \brief Update the computation of DB resources used by each zone.
*
* Because the amount of used space is bigger than sum of changesets' serialized_sizes,
* journal uses a complicated way to compute each zone's used space: there is a metadata
* showing always the previously-inserting zone. Before the next insert, it is computed
* how the total usage of the DB changed during the previous insert (or delete), and the
* usage increase (or decrease) is accounted on the bill of the previous inserter.
*
* \param txn Journal DB transaction.
* \param new_inserter Name of the zone that is going to insert now. Might be NULL if no insert nor delete will be done.
*/
void update_last_inserter(knot_lmdb_txn_t *txn, const knot_dname_t *new_inserter);
/* \brief Return the journal database usage by given zone. */
uint64_t journal_get_occupied(knot_lmdb_txn_t *txn, const knot_dname_t *zone);
/*!
* \brief Load the metadata from DB into structure.
*
* \param txn Journal DB transaction.
* \param zone Zone name.
* \param md Output: metadata structure.
*/
void journal_load_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, journal_metadata_t *md);
/*!
* \brief Store the metadata from structure into DB.
*
* \param txn Journal DB transaction.
* \param zone Zone name.
* \param md Metadata structure.
*/
void journal_store_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const journal_metadata_t *md);
/*!
* \brief Update metadata according to what was deleted.
*
* \param md Metadata structure to be updated.
* \param deleted_upto Serial-to of the last deleted changeset.
* \param deleted_count Number of deleted changesets.
*/
void journal_metadata_after_delete(journal_metadata_t *md, uint32_t deleted_upto,
size_t deleted_count);
/*!
* \brief Update metadata according to what was merged.
*
* \param md Metadata structure to be updated.
* \param merged_zij True if it was a merge into zone-in-journal.
* \param merged_serial Serial-from of the merged changeset (ignored if 'merged_zij').
* \param merged_serial_to Serial-to of the merged changeset.
* \param original_serial_to Previous serial-to of the merged changeset before the merge.
*/
void journal_metadata_after_merge(journal_metadata_t *md, bool merged_zij, uint32_t merged_serial,
uint32_t merged_serial_to, uint32_t original_serial_to);
/*!
* \brief Update metadata according to what was inserted.
*
* \param md Metadata structure to be updated.
* \param serial Serial-from of the inserted changeset.
* \param serial_to Serial-to of the inserted changeset.
*/
void journal_metadata_after_insert(journal_metadata_t *md, uint32_t serial, uint32_t serial_to);
/*!
* \brief Completely delete all journal records belonging to this zone, including metadata.
*
* \param j Journal to be scraped.
*
* \return KNOT_E*
*/
int journal_scrape_with_md(zone_journal_t j);
/*!
* \brief Update the metadata stored in journal DB after a zone flush.
*
* \param j Journal to be notified about flush.
*
* \return KNOT_E*
*/
int journal_set_flushed(zone_journal_t j);
/*!
* \brief Obtain information about the zone's journal from the DB (mostly metadata).
*
* \param j Zone journal.
* \param exists Output: bool if the zone exists in the journal.
* \param first_serial Optional output: serial-from of the first changeset in journal.
* \param serial_to Optional output: serial.to of the last changeset in journal.
* \param has_merged Optional output: bool if there is a special (non zone-in-journal) merged changeset.
* \param merged_serial Optional output: serial-from of the merged changeset.
* \param occupied Optional output: DB space occupied by this zones.
* \param occupied_total Optional output: DB space occupied in total by all zones.
*
* \return KNOT_E*
*/
int journal_info(zone_journal_t j, bool *exists, uint32_t *first_serial,
uint32_t *serial_to, bool *has_merged, uint32_t *merged_serial,
uint64_t *occupied, uint64_t *occupied_total);
/*! \brief Return true if this zone exists in journal DB. */
inline static bool journal_is_existing(zone_journal_t j) {
bool ex = false;
journal_info(j, &ex, NULL, NULL, NULL, NULL, NULL, NULL);
return ex;
}
/*!
* \brief Call a function for each zone being in the journal DB.
*
* \param db Journal database.
* \param cb Callback to be called for each zone-name found.
* \param ctx Arbitrary context to be passed to the callback.
*
* \return An error code from either journal operations or from the callback.
*/
int journals_walk(knot_lmdb_db_t *db, journals_walk_cb_t cb, void *ctx);
This diff is collapsed.
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "knot/journal/journal_basic.h"
typedef struct journal_read journal_read_t;
typedef int (*journal_read_cb_t)(bool in_remove_section, const knot_rrset_t *rr, void *ctx);
typedef int (*journal_walk_cb_t)(bool special, const changeset_t *ch, void *ctx);
/*!
* \brief Start reading journal from specified changeset.
*
* \param j Journal to be read.
* \param read_zone True if reading shall start with zone-in-journal.
* \param serial_from Serial-from of the changeset to be started at (ignored if 'read_zone').
* \param ctx Output: journal reading context initialised.
*
* \return KNOT_E*
*/
int journal_read_begin(zone_journal_t j, bool read_zone, uint32_t serial_from, journal_read_t **ctx);
/*!
* \brief Read a single RRSet from a journal changeset.
*
* \param ctx Journal reading context.
* \param rr Output: RRSet to be filled with serialized data.
* \param allow_next_changeset True to allow jumping to next changeset.
*
* \return False if no more RRSet in this changeset/journal, or failure.
*/
bool journal_read_rrset(journal_read_t *ctx, knot_rrset_t *rr, bool allow_next_changeset);
/*!
* \brief Free up heap allocations by journal_read_rrset().
*
* \param rr RRSet initialised by journal_read_rrset().
*/
void journal_read_clear_rrset(knot_rrset_t *rr);
// TODO move somewhere. Libknot?
inline static bool rr_is_apex_soa(const knot_rrset_t *rr, const knot_dname_t *apex)
{
return (rr->type == KNOT_RRTYPE_SOA && knot_dname_is_equal(rr->owner, apex));
}
/*!
* \brief Read all RRSets up to the end of journal, calling a function for each.
*
* \note Closes reading context at the end.
*
* \param read Journal reading context.
* \param cb Callback to be called on each read.
* \param ctx Arbitrary context to be passed to the callback.
*
* \return An error code from either journal operations or from the callback.
*/
int journal_read_rrsets(journal_read_t *read, journal_read_cb_t cb, void *ctx);
/*!
* \brief Read a single changeset from journal.
*
* \param ctx Journal reading context.
* \param ch Output: changeset to be filled with serialized data.
*
* \return False if no more changesets in the journal, or failure.
*/
bool journal_read_changeset(journal_read_t *ctx, changeset_t *ch);
/*!
* \brief Free up heap allocations by journal_read_changeset().
*
* \param ch Changeset initialised by journal_read_changeset().
*/
void journal_read_clear_changeset(changeset_t *ch);
/*!
* \brief Obtain error code from the journal_read operations previously performed.
*
* \param ctx Journal reading context.
* \param another_error An error code from outside the reading operations to be combined.
*
* \return KNOT_EOK if completely every operation succeeded, KNOT_E*
*/
int journal_read_get_error(const journal_read_t *ctx, int another_error);
/*!
* \brief Finalise journal reading.
*
* \param ctx Journal reading context (will be freed).
*/
void journal_read_end(journal_read_t *ctx);
/*!
* \brief Call a function for each changeset stored in journal.
*
* First, the callback will be called for the special changeset -
* either zone-in-journal or merged changeset, with special=true.
* If there is no such, it will be called anyway with ch=NULL.
*
* Than, the callback will be called for each regular changeset
* with special=false. If there is none, it will be called once
* with ch=NULL.
*
* \param j Zone journal to be read.
* \param cb Callback to be called for each changeset (or its non-existence).
* \param ctx Arbitrary context to be passed to the callback.
*
* \return An error code from either journal operations or from the callback.
*/
int journal_walk(zone_journal_t j, journal_walk_cb_t cb, void *ctx);
/*!
* \brief Perform semantic check of the zone journal (consistency, metadata...).
*
* \param j Zone journal to be checked.
*
* \retval KNOT_E* ( < 0 ) if an error during journal operation.
* \retval > 100 if some inconsistency found.
* \return KNOT_EOK of all ok.
*/
int journal_sem_check(zone_journal_t j);
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#include "knot/journal/journal_write.h"
#include "contrib/macros.h"
#include "knot/journal/journal_metadata.h"
#include "knot/journal/journal_read.h"
#include "knot/journal/serialization.h"
#include "libknot/error.h"
void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch)
{
MDB_val chunk;
serialize_ctx_t *ser = serialize_init(ch);
if (ser == NULL) {
txn->ret = KNOT_ENOMEM;
}
uint32_t i = 0;
while (serialize_unfinished(ser) && txn->ret == KNOT_EOK) {
serialize_prepare(ser, JOURNAL_CHUNK_MAX - JOURNAL_HEADER_SIZE, &chunk.mv_size);
if (chunk.mv_size == 0) {
break; // beware! If this is ommited, it creates empty chunk => EMALF when reading.
}
chunk.mv_size += JOURNAL_HEADER_SIZE;
chunk.mv_data = NULL;
MDB_val key = journal_changeset_to_chunk_key(ch, i);
if (knot_lmdb_insert(txn, &key, &chunk)) {
journal_make_header(chunk.mv_data, ch);
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
}
static int merge_cb(bool remove, const knot_rrset_t *rr, void *ctx)
{
changeset_t *ch = ctx;
return remove ? (rr_is_apex_soa(rr, ch->soa_to->owner) ?
KNOT_EOK : changeset_add_removal(ch, rr, CHANGESET_CHECK))
: changeset_add_addition(ch, rr, CHANGESET_CHECK);
}
void journal_merge(zone_journal_t j, knot_lmdb_txn_t *txn, bool merge_zij,
uint32_t merge_serial, uint32_t *original_serial_to)
{
changeset_t merge;
memset(&merge, 0, sizeof(merge));
journal_read_t *read = NULL;
txn->ret = journal_read_begin(j, merge_zij, merge_serial, &read);
if (txn->ret != KNOT_EOK) {
return;
}
if (journal_read_changeset(read, &merge)) {
*original_serial_to = changeset_to(&merge);
}
txn->ret = journal_read_rrsets(read, merge_cb, &merge);
journal_write_changeset(txn, &merge);
//knot_rrset_clear(&rr, NULL);
journal_read_clear_changeset(&merge);
}
static bool delete_one(knot_lmdb_txn_t *txn, bool del_zij, uint32_t del_serial,
const knot_dname_t *zone, size_t *freed, uint32_t *next_serial)
{
*freed = 0;
MDB_val prefix = journal_changeset_id_to_key(del_zij, del_serial, zone);
knot_lmdb_foreach(txn, &prefix) {
*freed += txn->cur_val.mv_size;
*next_serial = journal_next_serial(&txn->cur_val);
knot_lmdb_del_cur(txn);
}
free(prefix.mv_data);
return (*freed > 0);
}
bool journal_delete(knot_lmdb_txn_t *txn, uint32_t from, const knot_dname_t *zone,
size_t tofree_size, size_t tofree_count, uint32_t stop_at_serial,
size_t *freed_size, size_t *freed_count, uint32_t *stopped_at)
{
*freed_size = 0;
*freed_count = 0;
size_t freed_now;
while (from != stop_at_serial &&
(*freed_size < tofree_size || *freed_count < tofree_count) &&
delete_one(txn, false, from, zone, &freed_now, stopped_at)) {
*freed_size += freed_now;
++(*freed_count);
from = *stopped_at;
}
return (*freed_count > 0);
}
void journal_try_flush(zone_journal_t j, knot_lmdb_txn_t *txn, journal_metadata_t *md)
{
bool flush = journal_allow_flush(j);
uint32_t merge_orig;
if (journal_contains(txn, true, 0, j.zone)) {
journal_merge(j, txn, true, 0, &merge_orig);
if (!flush) {
journal_metadata_after_merge(md, true, 0, md->serial_to, merge_orig);
}
} else if (!flush) {
uint32_t merge_serial = ((md->flags & JOURNAL_MERGED_SERIAL_VALID) ? md->merged_serial : md->first_serial);
journal_merge(j, txn, false, merge_serial, &merge_orig);
journal_metadata_after_merge(md, false, merge_serial, md->serial_to, merge_orig);
}
if (flush) {
// delete merged serial if (very unlikely) exists
if ((md->flags & JOURNAL_MERGED_SERIAL_VALID)) {
size_t unused;
(void)delete_one(txn, false, md->merged_serial, j.zone, &unused, (uint32_t *)&unused);
md->flags &= ~JOURNAL_MERGED_SERIAL_VALID;
}
// commit partial job and ask zone to flush itself