]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
server: force zone reload from database if RDB_EVENT_ZONE
authorDaniel Salzman <daniel.salzman@nic.cz>
Mon, 12 Jan 2026 14:01:49 +0000 (15:01 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Tue, 13 Jan 2026 16:25:05 +0000 (17:25 +0100)
src/knot/events/handlers/load.c
src/knot/server/server.c
src/knot/zone/zone.h
tests-extra/tests/redis/retransfer/data/example.com.zone [new file with mode: 0644]
tests-extra/tests/redis/retransfer/data/example.com.zone.1 [new file with mode: 0644]
tests-extra/tests/redis/retransfer/test.py [new file with mode: 0644]

index 6f6ab559103c7e19cb89a7d9adc74c8465a5698a..3c9112dc50b968bf80e1f06b2c95011805f653e2 100644 (file)
@@ -59,6 +59,7 @@ int event_load(conf_t *conf, zone_t *zone)
        bool old_contents_exist = (zone->contents != NULL), zone_in_journal_exists = false;
        const char *zone_src = "zone file";
        struct redisContext *db_ctx = NULL;
+       bool rdb_reload = false;
 
        conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, zone->name);
        unsigned load_from = conf_opt(&val);
@@ -111,11 +112,12 @@ int event_load(conf_t *conf, zone_t *zone)
        bool db_enabled = conf_zone_rdb_enabled(conf, zone->name, true, &db_instance);
        if (db_enabled) {
                zone_src = "database";
+               rdb_reload = zone_get_flag(zone, ZONE_RDB_RELOAD, false);
                db_ctx = zone_redis_connect(conf, false);
        }
 
        // Attempt to load changes from database. If fails, load full zone from there later.
-       if (db_enabled && (old_contents_exist || journal_conts != NULL) &&
+       if (db_enabled && !rdb_reload && (old_contents_exist || journal_conts != NULL) &&
            zone->cat_members == NULL && EMPTY_LIST(zone->include_from) &&
            zf_from != ZONEFILE_LOAD_DIFSE && !includes_configured) {
                zone_redis_err_t err;
@@ -519,6 +521,7 @@ load_end:
                zone_schedule_notify(conf, zone, 0);
        }
        zone_redis_disconnect(db_ctx, true);
+       zone_unset_flag(zone, ZONE_RDB_RELOAD);
        zone_skip_free(&skip);
        zone->started = true;
 
index b8565f0e6ba2cac1a1a9c7ff5290c368be846eff..b7075a829ee9e50245bdf55a1e8cfb5f9b57226b 100644 (file)
@@ -882,6 +882,8 @@ static void rdb_process_event(redisReply *reply, knot_zonedb_t *zone_db,
 
        switch (type) {
        case RDB_EVENT_ZONE:
+               zone_set_flag(zone, ZONE_RDB_RELOAD);
+               // FALLTHROUGH
        case RDB_EVENT_UPD:
                log_zone_debug(zone->name, "rdb, event %s %s, serial %u",
                               since, (type == RDB_EVENT_ZONE ? "zone" : "update"), serial);
index 33be90b5f85d3ad9fa77a27444d93afc4aa33449..5753aae39b1a27a2a4c7c1b88ae677bae2f6a411 100644 (file)
@@ -40,6 +40,7 @@ typedef enum {
        ZONE_USER_FLUSH     = 1 << 8, /*!< User-triggered flush. */
        ZONE_LAST_SIGN_OK   = 1 << 9, /*!< Last full-sign event finished OK. */
        ZONE_PREF_MASTER_2X = 1 << 10, /*!< Preferred master has been overwritten at least once. */
+       ZONE_RDB_RELOAD     = 1 << 11, /*!< Full zone reload from database. */
 
        ZONE_FLAG_MAX       = 1 << 19, /*!< Maximal usable flag below purge_flag_t. */
        ZONE_FLAG_TYPESIZE  = 1 << 30, /*!< Enforces the compiler to use 32-bit variable for this enum. */
diff --git a/tests-extra/tests/redis/retransfer/data/example.com.zone b/tests-extra/tests/redis/retransfer/data/example.com.zone
new file mode 100644 (file)
index 0000000..1f5517a
--- /dev/null
@@ -0,0 +1,17 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@      SOA     dns1 hostmaster 1 10800 3600 1209600 7200
+       NS      dns1
+       NS      dns2
+       MX      10 mail
+       TXT     "version1"
+
+dns1   A       192.0.2.1
+       AAAA    2001:DB8::1
+
+dns2   A       192.0.2.2
+       AAAA    2001:DB8::2
+
+mail   A       192.0.2.3
+       AAAA    2001:DB8::3
diff --git a/tests-extra/tests/redis/retransfer/data/example.com.zone.1 b/tests-extra/tests/redis/retransfer/data/example.com.zone.1
new file mode 100644 (file)
index 0000000..4610258
--- /dev/null
@@ -0,0 +1,17 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@      SOA     dns1 hostmaster 1 10800 3600 1209600 7200
+       NS      dns1
+       NS      dns2
+       MX      10 mail
+       TXT     "version2"
+
+dns1   A       192.0.2.1
+       AAAA    2001:DB8::1
+
+dns2   A       192.0.2.2
+       AAAA    2001:DB8::2
+
+mail   A       192.0.2.3
+       AAAA    2001:DB8::3
diff --git a/tests-extra/tests/redis/retransfer/test.py b/tests-extra/tests/redis/retransfer/test.py
new file mode 100644 (file)
index 0000000..17c19db
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+
+'''Test for zone reload from Redis database.'''
+
+import random
+from dnstest.test import Test
+from dnstest.utils import *
+
+t = Test()
+
+hidden = t.server("knot")
+master = t.server("knot")
+slave = t.server("knot")
+
+zones = t.zone("example.com.", storage=".")
+
+t.link(zones, hidden, master)
+t.link(zones, master)
+t.link(zones, slave)
+
+tls = random.choice([True, False])
+redis_master = t.backend("redis", tls=tls)
+redis_slave = t.backend("redis", tls=tls)
+redis_slave.slave_of(redis_master)
+
+master.db_out(zones, [redis_master], 1)
+slave.db_in(zones, [redis_slave], 1)
+
+t.start()
+
+# Check initial zone contents.
+slave.zones_wait(zones)
+t.xfr_diff(hidden, slave, zones)
+resp = slave.dig("example.com", "TXT")
+resp.check_record(section="answer", rtype="TXT", rdata="version1")
+
+# Replace zone contents with serial unchanged.
+hidden.update_zonefile(zones[0], version=1)
+hidden.reload()
+master.ctl("zone-retransfer")
+
+# Check retransfered different zone contents with the same serial.
+t.sleep(4)
+t.xfr_diff(hidden, slave, zones)
+resp = slave.dig("example.com", "TXT")
+resp.check_record(section="answer", rtype="TXT", rdata="version2", nordata="version1")
+
+t.end()