ddns-master: remote_id
notify: remote_id | remotes_id ...
notify-delay: TIME
+ update-delay: TIME
acl: acl_id ...
master-pin-tolerance: TIME
provide-ixfr: BOOL
*Default:* ``0``
+.. _zone_update-delay:
+
+update-delay
+------------
+
+A time delay in seconds before a change to zone contents is made after an external
+trigger such as incoming NOTIFY or DDNS, or an internal trigger from different zone
+such as change to zone to be :ref:`reversed<zone_reverse-generate>`,
+:ref:`included from<zone_include-from>` or a member of generated catalog zone.
+
+Exception: zone changing events triggered by control socket (knotc zone-*
+commands) or by interpreted catalog are performed immediately, without configured
+delay.
+
+*Default:* ``0``
+
.. _zone_acl:
acl
return out;
}
-void catalog_generate_rem(zone_t *zone, knot_zonedb_t *db_new)
+void catalog_generate_rem(conf_t *conf, zone_t *zone, knot_zonedb_t *db_new)
{
if (zone == NULL) {
return;
if (ret != KNOT_EOK) {
catz->cat_members->error = ret;
} else {
- zone_events_schedule_now(catz, ZONE_EVENT_LOAD);
+ zone_schedule_update(conf, catz, ZONE_EVENT_LOAD);
}
free(owner);
}
-void catalog_generate_add(zone_t *zone, knot_zonedb_t *db_new, bool property)
+void catalog_generate_add(conf_t *conf, zone_t *zone, knot_zonedb_t *db_new, bool property)
{
if (zone == NULL) {
return;
if (ret != KNOT_EOK) {
catz->cat_members->error = ret;
} else {
- zone_events_schedule_now(catz, ZONE_EVENT_LOAD);
+ zone_schedule_update(conf, catz, ZONE_EVENT_LOAD);
}
free(owner);
}
/*!
* \brief Add a member removal to corresponding catalog update.
*
+ * \param conf Configuration.
* \param zone Member zone.
* \param db_new New zone database.
*/
-void catalog_generate_rem(zone_t *zone, knot_zonedb_t *db_new);
+void catalog_generate_rem(conf_t *conf, zone_t *zone, knot_zonedb_t *db_new);
/*!
* \brief Add a member addition/prop to corresponding catalog update.
*
+ * \param conf Configuration.
* \param zone Member zone.
* \param db_new New zone database.
* \param property Property or addition indicator.
*/
-void catalog_generate_add(zone_t *zone, knot_zonedb_t *db_new, bool property);
+void catalog_generate_add(conf_t *conf, zone_t *zone, knot_zonedb_t *db_new, bool property);
/*!
* \brief Generate catalog zone contents from (full) catalog update.
{ C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref_empty } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | CONF_REF_EMPTY, \
{ check_ref } }, \
- { C_NOTIFY_DELAY, YP_TINT, YP_VINT = { -1, UINT32_MAX, 0, YP_STIME } }, \
+ { C_NOTIFY_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } }, \
+ { C_UPDATE_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } }, \
{ C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
{ C_MASTER_PIN_TOL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } }, \
{ C_PROVIDE_IXFR, YP_TBOOL, YP_VBOOL = { true } }, \
#define C_UDP_MAX_PAYLOAD_IPV6 "\x14""udp-max-payload-ipv6"
#define C_UDP_WORKERS "\x0B""udp-workers"
#define C_UNSAFE_OPERATION "\x10""unsafe-operation"
+#define C_UPDATE_DELAY "\x0C""update-delay"
#define C_UPDATE_OWNER "\x0C""update-owner"
#define C_UPDATE_OWNER_MATCH "\x12""update-owner-match"
#define C_UPDATE_OWNER_NAME "\x11""update-owner-name"
case KNOT_NO_READY_KEY:
break;
case KNOT_EOK:
- zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
+ zone_schedule_update(conf, zone, ZONE_EVENT_DNSSEC);
break;
default:
if (ctx.policy->ksk_sbm_check_interval > 0) {
zone_schedule_notify(conf, zone, 1);
}
if (trctx.more_xfr && ret == KNOT_EOK) {
- zone_events_schedule_now(zone, ZONE_EVENT_REFRESH);
+ zone_schedule_update(conf, zone, ZONE_EVENT_REFRESH);
}
return ret;
/* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */
zone_set_preferred_master(zone, knotd_qdata_remote_addr(qdata));
- zone_events_schedule_now(zone, ZONE_EVENT_REFRESH);
+ zone_schedule_update(conf(), zone, ZONE_EVENT_REFRESH);
return KNOT_STATE_DONE;
}
pthread_mutex_unlock(&zone->ddns_lock);
/* Schedule UPDATE event. */
- zone_events_schedule_now(zone, ZONE_EVENT_UPDATE);
+ zone_schedule_update(conf(), zone, ZONE_EVENT_UPDATE);
return KNOT_EOK;
}
#include "knot/updates/acl.h"
#include "knot/zone/redis.h"
#include "knot/zone/timers.h"
+#include "knot/zone/zone.h"
#include "knot/zone/zonedb-load.h"
#include "knot/worker/pool.h"
#include "contrib/base64.h"
switch (type) {
case RDB_EVENT_ZONE:
case RDB_EVENT_UPD:
- zone_events_schedule_now(zone, ZONE_EVENT_LOAD);
+ zone_schedule_update(conf(), zone, ZONE_EVENT_LOAD);
break;
default:
break;
zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + conf_delay + delay);
}
+void zone_schedule_update(conf_t *conf, zone_t *zone, zone_event_type_t type)
+{
+ int64_t conf_delay = 0;
+ if (zone->contents != NULL) {
+ conf_val_t val = conf_zone_get(conf, C_UPDATE_DELAY, zone->name);
+ conf_delay = conf_int(&val);
+ }
+ zone_events_schedule_at(zone, type, time(NULL) + conf_delay);
+}
+
zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents)
{
if (zone == NULL) {
ptrnode_t *n;
WALK_LIST(n, zone->internal_notify) {
zone_t *to_notify = n->d;
- zone_events_schedule_now(to_notify, zone_is_slave(conf, to_notify) ? ZONE_EVENT_REFRESH : ZONE_EVENT_LOAD);
+ zone_schedule_update(conf, to_notify, zone_is_slave(conf, to_notify) ? ZONE_EVENT_REFRESH : ZONE_EVENT_LOAD);
}
}
void zone_notifailed_clear(zone_t *zone);
void zone_schedule_notify(conf_t *conf, zone_t *zone, time_t delay);
+void zone_schedule_update(conf_t *conf, zone_t *zone, zone_event_type_t type);
+
/*!
* \brief Atomically switch the content of the zone.
*/
}
}
knot_zonedb_insert(db_new, zone);
- catalog_generate_add(zone, db_new, false);
+ catalog_generate_add(conf, zone, db_new, false);
reg_reverse(conf, db_new, zone);
} else if (type & CONF_IO_TUNSET) {
zone_t *zone = knot_zonedb_find(db_new, name);
unreg_reverse(zone);
knot_zonedb_del(db_new, name);
- catalog_generate_rem(zone, db_new);
+ catalog_generate_rem(conf, zone, db_new);
} else {
zone_t *zone = knot_zonedb_find(db_new, name);
zone_t *old = knot_zonedb_find(db_old, name);
if (!same_group(old, zone)) {
- catalog_generate_add(zone, db_new, true);
+ catalog_generate_add(conf, zone, db_new, true);
}
reg_reverse(conf, db_new, zone);
}
case CAT_UPD_ADD:
zone = add_member_zone(upd, db_new, server, conf);
knot_zonedb_insert(db_new, zone);
- catalog_generate_add(zone, db_new, false);
+ catalog_generate_add(conf, zone, db_new, false);
reg_reverse(conf, db_new, zone);
break;
case CAT_UPD_REM:
zone = knot_zonedb_find(db_new, upd->member);
unreg_reverse(zone);
knot_zonedb_del(db_new, upd->member);
- catalog_generate_rem(zone, db_new);
+ catalog_generate_rem(conf, zone, db_new);
break;
case CAT_UPD_UNIQ:
case CAT_UPD_PROP:
}
zone_t *old = knot_zonedb_find(db_old, upd->member);
if (!same_group(old, zone)) {
- catalog_generate_add(zone, db_new, true);
+ catalog_generate_add(conf, zone, db_new, true);
}
reg_reverse(conf, db_new, zone);
break;
for (; !knot_zonedb_iter_finished(db_it); knot_zonedb_iter_next(db_it)) {
zone_t *zone = knot_zonedb_iter_val(db_it);
if (knot_zonedb_find(db_new, zone->name) == NULL) {
- catalog_generate_rem(zone, db_new);
+ catalog_generate_rem(conf, zone, db_new);
}
}
knot_zonedb_iter_free(db_it);
zone_t *zone = knot_zonedb_iter_val(db_it);
zone_t *old = knot_zonedb_find(db_old, zone->name);
if (old == NULL) {
- catalog_generate_add(zone, db_new, false);
+ catalog_generate_add(conf, zone, db_new, false);
} else if (!same_group(old, zone)) {
- catalog_generate_add(zone, db_new, true);
+ catalog_generate_add(conf, zone, db_new, true);
}
reg_reverse(conf, db_new, zone);
}
--- /dev/null
+#!/usr/bin/env python3
+
+"""
+Test of update delay.
+"""
+
+from dnstest.utils import *
+from dnstest.test import Test
+import random
+import threading
+import time
+
+t = Test()
+
+master = t.server("knot")
+slave = t.server("knot")
+zones = t.zone_rnd(2, records=10)
+
+t.link(zones, master, slave)
+
+master.update_delay = 6
+slave.update_delay = 6
+
+master.serial_policy = "unixtime"
+slave.serial_policy = "unixtime"
+
+for z in zones:
+ master.zones[z.name].zfile.update_soa(serial=int(time.time()))
+ slave.dnssec(z).enable = True # so that slave has own SOA serial management
+
+def increment_serials(server, zones, serials):
+ res = serials
+ for z in zones:
+ res[z.name] += server.update_delay
+ return res
+
+def zones_wait_eq(server, zones, serials):
+ return server.zones_wait(zones, serials, greater=True, equal=True)
+
+def send_update(up):
+ try:
+ up.try_send()
+ except:
+ pass
+
+def send_up_bg(up):
+ threading.Thread(target=send_update, args=[up]).start()
+
+t.start()
+serials = master.zones_wait(zones)
+serials = zones_wait_eq(slave, zones, serials) # initial AXFR: without delay
+
+for z in zones:
+ up = master.update(z)
+ up.add("test-update-delay-add", 3600, "A", "1.2.3.4")
+ send_up_bg(up)
+
+increment_serials(master, zones, serials)
+serials = zones_wait_eq(master, zones, serials) # DDNS processing with delay
+
+increment_serials(slave, zones, serials)
+serials = zones_wait_eq(slave, zones, serials) # slave XFRs the zone swith another delay
+
+t.end()
self.semantic_check = True
self.zonefile_sync = "1d"
self.notify_delay = None
+ self.update_delay = None
self.zonefile_load = None
self.zonefile_skip = None
self.zonemd_verify = None
if self.notify_delay is None:
self.notify_delay = random.randint(0, 1)
s.item_str("notify-delay", self.notify_delay)
+ self._str(s, "update-delay", self.update_delay)
if self.zonemd_verify:
s.item_str("zonemd-verify", "on")
if self.zonemd_generate is not None: