kasp-db-max-size: SIZE
timer-db: STR
timer-db-max-size: SIZE
+ timer-db-sync: never | shutdown | immediate | TIME
catalog-db: str
catalog-db-max-size: SIZE
zone-db-listen: ADDR[@INT] | STR[@INT] ...
*Default:* ``100M`` (100 MiB)
+.. _database_timer-db-sync:
+
+timer-db-sync
+-------------
+
+Specifies when zone timers should be written to the persistent timer database.
+
+Possible values:
+
+- ``never`` – Never written.
+- ``shutdown`` – Written once when the server is shut down.
+- ``immediate`` – Each zone writes its timers whenever they are modified.
+ This mode might slow down zones' events if many zones are configured.
+- `INT` – A dedicated thread continuously iterates through the configured zones
+ and writes their timers at the specified non-zero interval (in seconds).
+
+*Default:* ``shutdown``
+
.. _database_catalog-db:
catalog-db
conf->cache.srv_has_version = false;
conf->cache.srv_version = "Knot DNS " PACKAGE_VERSION;
}
+
+ val = conf_get(conf, C_DB, C_TIMER_DB_SYNC);
+ conf->cache.db_timer_db_sync = conf_int(&val);
}
int conf_new(
uint32_t xdp_tcp_idle_close;
uint32_t xdp_tcp_idle_reset;
uint32_t xdp_tcp_idle_resend;
+ int32_t db_timer_db_sync;
size_t srv_quic_max_clients;
size_t srv_quic_obuf_max_size;
const uint8_t *srv_nsid_data;
{ 0, NULL }
};
+static const knot_lookup_t timer_db_sync[] = {
+ { TIMER_DB_SYNC_IMMEDIATE, "immediate" },
+ { TIMER_DB_SYNC_NEVER, "never" },
+ { TIMER_DB_SYNC_SHUTDOWN, "shutdown" },
+ { 0, NULL }
+};
+
static const knot_lookup_t catalog_roles[] = {
{ CATALOG_ROLE_NONE, "none" },
{ CATALOG_ROLE_INTERPRET, "interpret" },
{ C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" } },
{ C_TIMER_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), VIRT_MEM_LIMIT(GIGA(100)),
MEGA(100), YP_SSIZE } },
+ { C_TIMER_DB_SYNC, YP_TOPTINT, YP_VOPTINT = { 1, UINT32_MAX, TIMER_DB_SYNC_SHUTDOWN,
+ YP_STIME, 0, timer_db_sync } },
{ C_CATALOG_DB, YP_TSTR, YP_VSTR = { "catalog" } },
{ C_CATALOG_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(5), VIRT_MEM_LIMIT(GIGA(100)),
VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE } },
#define C_TIMER "\x05""timer"
#define C_TIMER_DB "\x08""timer-db"
#define C_TIMER_DB_MAX_SIZE "\x11""timer-db-max-size"
+#define C_TIMER_DB_SYNC "\x0D""timer-db-sync"
#define C_TLS "\x03""tls"
#define C_TPL "\x08""template"
#define C_UDP "\x03""udp"
JOURNAL_MODE_ASYNC = 1, // Asynchronous journal DB disk synchronization.
};
+enum {
+ TIMER_DB_SYNC_IMMEDIATE = 0,
+ TIMER_DB_SYNC_NEVER = -1,
+ TIMER_DB_SYNC_SHUTDOWN = -2,
+};
+
enum {
ZONEFILE_LOAD_NONE = 0,
ZONEFILE_LOAD_DIFF = 1,
}
#endif // ENABLE_REDIS
+static int timer_db_do_sync(struct dthread *thread)
+{
+ server_t *s = thread->data;
+
+ while (thread->state & ThreadActive) {
+ int ret = zone_timers_write_all(&s->timerdb, s->zone_db);
+ if (ret == KNOT_EOK) {
+ log_info("updated persistent timer DB");
+ } else {
+ log_error("failed to update persistent timer DB (%s)", knot_strerror(ret));
+ }
+
+ if (conf()->cache.db_timer_db_sync <= 0) {
+ break;
+ }
+ /* NOTE the following sleep() may be interrupted by any signal, including
+ SIGALRM during dt_stop() in case of server_stop() or server_reconfigure(). */
+ sleep(conf()->cache.db_timer_db_sync);
+ }
+
+ return KNOT_EOK;
+}
+
int server_init(server_t *server, int bg_workers)
{
if (server == NULL) {
zone_backups_deinit(&server->backup_ctxs);
/* Save zone timers. */
- if (server->zone_db != NULL) {
+ bool should_sync = (conf()->cache.db_timer_db_sync == TIMER_DB_SYNC_SHUTDOWN ||
+ conf()->cache.db_timer_db_sync > 0);
+ if (should_sync && server->zone_db != NULL) {
log_info("updating persistent timer DB");
int ret = zone_timers_write_all(&server->timerdb, server->zone_db);
if (ret != KNOT_EOK) {
- log_warning("failed to update persistent timer DB (%s)",
- knot_strerror(ret));
+ log_error("failed to update persistent timer DB (%s)",
+ knot_strerror(ret));
}
}
/* Free threads and event handlers. */
worker_pool_destroy(server->workers);
+ /* Free eventual timer DB syncing thread. */
+ dt_delete(&server->timerdb_sync);
+
/* Free optional zone DB event thread. */
dt_delete(&server->rdb_events);
/* Start workers. */
worker_pool_start(server->workers);
+ /* Start timer DB syncing thread. NOTE ignoring return code including KNOT_EINVAL when disabled (NULL). */
+ dt_start(server->timerdb_sync);
+
/* Start zone DB event loop. */
dt_start(server->rdb_events);
evsched_stop(&server->sched);
/* Mark the server is shutting down. */
server->state |= ServerShutting;
+ /* Stop timer DB syncing thread */
+ dt_stop(server->timerdb_sync);
/* Interrupt background workers. */
worker_pool_stop(server->workers);
conf_val_t timer_size = conf_db_param(conf, C_TIMER_DB_MAX_SIZE);
int ret = knot_lmdb_reconfigure(&server->timerdb, timer_dir, conf_int(&timer_size), 0);
free(timer_dir);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ bool should_sync = (conf->cache.db_timer_db_sync > 0);
+ bool exists_sync = (server->timerdb_sync != NULL);
+ if (should_sync && !exists_sync) {
+ server->timerdb_sync = dt_create(1, timer_db_do_sync, NULL, server);
+ if (server->timerdb_sync == NULL) {
+ return KNOT_ENOMEM;
+ }
+ } else if (!should_sync && exists_sync) {
+ dt_stop(server->timerdb_sync);
+ dt_delete(&server->timerdb_sync);
+ }
+
return ret;
}
/*! \brief Background jobs. */
worker_pool_t *workers;
+ /*! \brief TimerDB syncing thread. */
+ dt_unit_t *timerdb_sync;
+
/*! \brief Zone DB event loop context. */
dt_unit_t *rdb_events;
struct redisContext *rdb_ctx;
#include "contrib/wire_ctx.h"
#include "knot/zone/zonedb.h"
+#include <urcu.h>
+
/*
* # Timer database
*
static void txn_zone_write(zone_t *z, knot_lmdb_txn_t *txn)
{
- txn_write_timers(txn, z->name, z->timers_static);
+ rcu_read_lock();
+ zone_timers_t *t = z->timers_static;
+ txn_write_timers(txn, z->name, t);
+ rcu_read_unlock();
}
int zone_timers_write_all(knot_lmdb_db_t *db, knot_zonedb_t *zonedb)
cleanup->timers = old_static;
call_rcu((struct rcu_head *)cleanup, timers_cleanup);
}
+
+ if (conf()->cache.db_timer_db_sync == TIMER_DB_SYNC_IMMEDIATE) {
+ int ret = zone_timers_write(&zone->server->timerdb, zone->name, zone->timers);
+ if (ret != KNOT_EOK) {
+ log_zone_error(zone->name, "failed to update persistent timer DB (%s)",
+ knot_strerror(ret));
+ }
+ }
}
static int try_remote(conf_t *conf, zone_t *zone, zone_master_cb callback,
lastline=""
with open(server.fout, "r") as fl:
for line in fl:
- lastline = line
+ if "persistent" not in line:
+ lastline = line
#detect recent (any) activity in the logfile so that servers settle down before equivalence test
if now_hms(0) in lastline:
s.item_str("kasp-db-max-size", self.kasp_db_size)
s.item_str("journal-db-max-size", self.journal_db_size)
s.item_str("timer-db-max-size", self.timer_db_size)
+ s.item_str("timer-db-sync", random.choice(["shutdown", "immediate", "5", "3600"]))
s.item_str("catalog-db-max-size", self.catalog_db_size)
if self.redis is not None:
tls = random.choice([True, False])
ref = "server\n"
"xdp\n"
"control\n"
+ "database\n"
"remote\n"
"template\n"
"zone\n"
{ NULL }
};
+static const yp_item_t desc_database[] = {
+ { C_TIMER_DB_SYNC, YP_TOPTINT, YP_VNONE },
+ { NULL }
+};
+
static const knot_lookup_t opts[] = {
{ 1, "opt1" },
{ 2, "opt2" },
{ C_SRV, YP_TGRP, YP_VGRP = { desc_server } },
{ C_XDP, YP_TGRP, YP_VGRP = { desc_xdp } },
{ C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
+ { C_DB, YP_TGRP, YP_VGRP = { desc_database } },
{ C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } },