From: Daniel Salzman Date: Mon, 12 Jan 2026 14:01:49 +0000 (+0100) Subject: server: force zone reload from database if RDB_EVENT_ZONE X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e378de7c77bfe76e38a4afce1964136db795b2b6;p=thirdparty%2Fknot-dns.git server: force zone reload from database if RDB_EVENT_ZONE --- diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c index 6f6ab55910..3c9112dc50 100644 --- a/src/knot/events/handlers/load.c +++ b/src/knot/events/handlers/load.c @@ -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; diff --git a/src/knot/server/server.c b/src/knot/server/server.c index b8565f0e6b..b7075a829e 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -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); diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 33be90b5f8..5753aae39b 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -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 index 0000000000..1f5517a856 --- /dev/null +++ b/tests-extra/tests/redis/retransfer/data/example.com.zone @@ -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 index 0000000000..4610258e0e --- /dev/null +++ b/tests-extra/tests/redis/retransfer/data/example.com.zone.1 @@ -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 index 0000000000..17c19dbe71 --- /dev/null +++ b/tests-extra/tests/redis/retransfer/test.py @@ -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()