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 ...@@ -258,6 +258,10 @@ src/knot/events/handlers/refresh.c
src/knot/events/handlers/update.c src/knot/events/handlers/update.c
src/knot/events/replan.c src/knot/events/replan.c
src/knot/events/replan.h 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.c
src/knot/modules/dnsproxy/dnsproxy.h src/knot/modules/dnsproxy/dnsproxy.h
src/knot/modules/dnstap/dnstap.c src/knot/modules/dnstap/dnstap.c
...@@ -312,10 +316,6 @@ src/knot/query/requestor.c ...@@ -312,10 +316,6 @@ src/knot/query/requestor.c
src/knot/query/requestor.h src/knot/query/requestor.h
src/knot/server/dthreads.c src/knot/server/dthreads.c
src/knot/server/dthreads.h 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.c
src/knot/server/server.h src/knot/server/server.h
src/knot/server/tcp-handler.c src/knot/server/tcp-handler.c
...@@ -562,7 +562,7 @@ tests/contrib/test_wire_ctx.c ...@@ -562,7 +562,7 @@ tests/contrib/test_wire_ctx.c
tests/dthreads.c tests/dthreads.c
tests/fake_server.h tests/fake_server.h
tests/fdset.c tests/fdset.c
tests/journal.c tests/journal_lmdb.c
tests/libknot/test_control.c tests/libknot/test_control.c
tests/libknot/test_cookies-client.c tests/libknot/test_cookies-client.c
tests/libknot/test_cookies-opt.c tests/libknot/test_cookies-opt.c
......
...@@ -871,9 +871,17 @@ This option has no effect with enabled ...@@ -871,9 +871,17 @@ This option has no effect with enabled
\fIDefault:\fP off \fIDefault:\fP off
.SS max\-journal\-size .SS max\-journal\-size
.sp .sp
Maximum size of the zone journal file. Maximum size of the journal DB.
.sp .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 .SS max\-zone\-size
.sp .sp
Maximum size of the zone. The size is measured as size of the zone records 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. ...@@ -1013,9 +1013,13 @@ is a master server for the zone.
max-journal-size 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: .. _zone_max_zone_size:
......
...@@ -335,10 +335,10 @@ libknotd_la_SOURCES = \ ...@@ -335,10 +335,10 @@ libknotd_la_SOURCES = \
knot/common/stats.h \ knot/common/stats.h \
knot/server/dthreads.c \ knot/server/dthreads.c \
knot/server/dthreads.h \ knot/server/dthreads.h \
knot/server/journal.c \ knot/journal/journal.c \
knot/server/journal.h \ knot/journal/journal.h \
knot/server/serialization.c \ knot/journal/serialization.c \
knot/server/serialization.h \ knot/journal/serialization.h \
knot/server/server.c \ knot/server/server.c \
knot/server/server.h \ knot/server/server.h \
knot/server/tcp-handler.c \ knot/server/tcp-handler.c \
......
...@@ -1048,22 +1048,19 @@ char* conf_zonefile_txn( ...@@ -1048,22 +1048,19 @@ char* conf_zonefile_txn(
char* conf_journalfile_txn( char* conf_journalfile_txn(
conf_t *conf, conf_t *conf,
knot_db_txn_t *txn, knot_db_txn_t *txn)
const knot_dname_t *zone)
{ {
if (zone == NULL) { conf_val_t val;
return NULL;
}
conf_val_t val = conf_zone_get_txn(conf, txn, C_JOURNAL, zone); val = conf_default_get_txn(conf, txn, C_STORAGE);
const char *journal = conf_str(&val); char *storage = conf_abs_path(&val, NULL);
// Use default journalfile name pattern if not specified. val = conf_default_get_txn(conf, txn, C_JOURNAL);
if (journal == NULL) {
journal = "%s.db"; char *journaldir = conf_abs_path(&val, storage);
} free(storage);
return get_filename(conf, txn, zone, journal); return journaldir;
} }
size_t conf_udp_threads_txn( size_t conf_udp_threads_txn(
......
...@@ -584,16 +584,12 @@ static inline char* conf_zonefile( ...@@ -584,16 +584,12 @@ static inline char* conf_zonefile(
* *
* \return Absolute journal file path string pointer. * \return Absolute journal file path string pointer.
*/ */
char* conf_journalfile_txn( char* conf_journalfile_txn(conf_t *conf,
conf_t *conf, knot_db_txn_t *txn);
knot_db_txn_t *txn,
const knot_dname_t *zone
);
static inline char* conf_journalfile( static inline char* conf_journalfile(
conf_t *conf, conf_t *conf)
const knot_dname_t *zone)
{ {
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[] = { ...@@ -225,10 +225,14 @@ static const yp_item_t desc_remote[] = {
{ NULL } { 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) \ #define ZONE_ITEMS(FLAGS) \
{ C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \ { C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \
{ C_FILE, YP_TSTR, YP_VNONE, 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_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_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { 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[] = { ...@@ -237,8 +241,6 @@ static const yp_item_t desc_remote[] = {
{ C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \ { C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \
{ C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \ { C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \
{ C_IXFR_DIFF, YP_TBOOL, YP_VNONE }, \ { 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 }, \ { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, \
FLAGS }, \ FLAGS }, \
{ C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, FLAGS }, \ { C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, FLAGS }, \
...@@ -256,6 +258,9 @@ static const yp_item_t desc_template[] = { ...@@ -256,6 +258,9 @@ static const yp_item_t desc_template[] = {
{ C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" }, CONF_IO_FRLD_ZONES }, \ { 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 }, \ { 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 } }, \ 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 } { NULL }
}; };
......
...@@ -514,6 +514,24 @@ int check_template( ...@@ -514,6 +514,24 @@ int check_template(
return KNOT_EINVAL; 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; return KNOT_EOK;
} }
......
...@@ -930,9 +930,9 @@ static int zone_purge(zone_t *zone, ctl_args_t *args) ...@@ -930,9 +930,9 @@ static int zone_purge(zone_t *zone, ctl_args_t *args)
free(zonefile); free(zonefile);
// Purge the zone journal. // Purge the zone journal.
char *journalfile = conf_journalfile(conf(), zone->name); if (journal_open(zone->journal, zone->journal_db, zone->name) == KNOT_EOK) {
(void)unlink(journalfile); (void)scrape_journal(zone->journal);
free(journalfile); }
// Purge the zone timers. // Purge the zone timers.
(void)remove_timer_db(args->server->timers_db, args->server->zone_db, (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 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 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); ...@@ -59,6 +59,63 @@ int rrset_serialize(const knot_rrset_t *rrset, uint8_t *stream, size_t *size);
* \return KNOT_E* * \return KNOT_E*
*/ */
int rrset_deserialize(const uint8_t *stream, size_t *stream_size, 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, ...@@ -161,7 +161,7 @@ static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item,
#undef IXFR_SAFE_PUT #undef IXFR_SAFE_PUT
/*! \brief Loads IXFRs from journal. */ /*! \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) const knot_rrset_t *their_soa)
{ {
assert(chgsets); assert(chgsets);
...@@ -175,12 +175,7 @@ static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone, ...@@ -175,12 +175,7 @@ static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
return KNOT_EUPTODATE; return KNOT_EUPTODATE;
} }
char *path = conf_journalfile(conf(), zone->name); ret = zone_changes_load(conf(), zone, chgsets, serial_from);
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);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
changesets_free(chgsets); changesets_free(chgsets);
} }
...@@ -582,12 +577,6 @@ static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc) ...@@ -582,12 +577,6 @@ static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
return ret; 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. */ /*! \brief Checks whether RR belongs into zone. */
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc) 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) ...@@ -617,11 +606,6 @@ static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
// Process RRs in the message. // Process RRs in the message.
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
for (uint16_t i = 0; i < answer->count; ++i) { 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); const knot_rrset_t *rr = knot_pkt_rr(answer, i);
if (out_of_zone(rr, ixfr)) { if (out_of_zone(rr, ixfr)) {
continue; 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)