catalog zone accepts duplicate members via UPDATE

We update a catalog zone with dynamic UPDATEs. Although members in a catalog must be unique, the member gets accepted in the catalog zone and later confuses the catalog logic, and causes error on server restart:

template:
  - id: default
    storage: "/var/lib/knot"
    file: "zones/%s.zone"
    master: primary
    acl: acl_primary
    refresh-min-interval: 86400 # once a day
  - id: catz
    storage: "/var/lib/knot"
    file: "catz.zone"

zone:
  - domain: catz.
    catalog-role: interpret
    catalog-template: default
    template: catz
    acl: [update_acl, axfr_acl]
  1. ./update.py add 12345 example.com
21:55:49 knotd[3833]: debug: [catz.] ACL, allowed, action update, remote 127.0.0.1@53240, key 'knot_test.'
21:55:49 knotd[3833]: info: [catz.] DDNS, processing 1 updates
21:55:49 knotd[3833]: info: [catz.] catalog reloaded, 1 updates
21:55:49 knotd[3833]: info: [catz.] DDNS, finished, serial 1645132466 -> 1645132467, 0.03 seconds
21:55:49 knotd[3833]: info: [catz.] zone file updated, serial 1645132466 -> 1645132467
21:55:54 knotd[3833]: info: [example.com.] zone will be loaded
21:55:54 knotd[3833]: info: [example.com.] zone added from catalog
21:55:54 knotd[3833]: info: [example.com.] failed to parse zone file '/var/lib/knot/zones/example.com.zone' (not exists)
21:55:54 knotd[3833]: info: [example.com.] zone will be bootstrapped
21:55:55 knotd[3833]: info: [example.com.] AXFR, incoming, remote 127.0.0.1@14001, started
21:55:55 knotd[3833]: info: [example.com.] AXFR, incoming, remote 127.0.0.1@14001, finished, 0.00 seconds, 3 messages, 314 bytes
21:55:55 knotd[3833]: info: [example.com.] refresh, remote 127.0.0.1@14001, zone updated, 0.01 seconds, serial none -> 2022021701
21:55:55 knotd[3833]: info: [example.com.] zone file updated, serial 2022021701
  1. ./update.py add 456789 example.com
21:56:19 knotd[3833]: debug: [catz.] ACL, allowed, action update, remote 127.0.0.1@53616, key 'knot_test.'
21:56:19 knotd[3833]: info: [catz.] DDNS, processing 1 updates
21:56:19 knotd[3833]: info: [catz.] catalog reloaded, 1 updates
21:56:19 knotd[3833]: info: [catz.] DDNS, finished, serial 1645132467 -> 1645132468, 0.04 seconds
21:56:19 knotd[3833]: info: [catz.] zone file updated, serial 1645132467 -> 1645132468
21:56:19 knotd[3833]: error: [example.com.] member zone already configured by catalog, ignoring

Now, Knot servers example.com and zone "catz" contains the member twice:

root@reg-amy1:~# dig axfr catz @95.179.xx.xx -p 1053|grep example.com
12345.zones.catz.       0       IN      PTR     example.com.
456789.zones.catz.      0       IN      PTR     example.com.
  1. Restart Knot
21:56:33 knotd[3833]: info: stopping server
21:56:33 knotd[3833]: info: updating persistent timer DB
21:56:35 knotd[3833]: info: shutting down

22:11:31 commandlog[10961]: darilion/root : systemctl start knot
22:11:31 knotc[10964]: Configuration is valid
22:11:31 knotd[10976]: info: Knot DNS 3.1.5 starting
22:11:31 knotd[10976]: info: loaded configuration file '/etc/knot/knot.conf', mapsize 512 MiB
22:11:31 knotd[10976]: info: using UDP reuseport, incoming TCP Fast Open
22:11:31 knotd[10976]: info: binding to interface 0.0.0.0@1053
22:11:31 knotd[10976]: info: binding to interface ::@1053
22:11:31 knotd[10976]: info: loading 1 zones
22:11:31 knotd[10976]: info: [catz.] zone will be loaded
22:11:31 knotd[10976]: info: [000000000000000000000.at] zone will be loaded
[loading thousands of zones]
22:11:34 knotd[10976]: error: [catz.] failed to process catalog zone (already exists)
22:11:34 knotd[10976]: error: [catz.] zone event 'load' failed (already exists)

a) In the error case it would be nice if Knot logs the duplicate member zone name

Regarding the duplicate member I think there are 2 options - both could be implemented:

  1. just ignore the duplicate member: keep the duplicate in the catalog zone and serve the member zone - for the member zone it does not matter as all member zones have the same configuration inherited by the template. In this case, Knot would behave as before the restart.
  2. Make sure that the catalog zone does not receive a duplicate member. If the zone that receives dynamic updates is a catalog zone, the UPDATE should only be accepted (or accepted but not applied) if it does cause duplicate members.