From 74ceb0206adc5b9e6e2754fafff4341303ff667a Mon Sep 17 00:00:00 2001 From: Sven Luethi Date: Tue, 11 Aug 2026 10:58:51 +0200 Subject: [PATCH] Add unique constraint to IntegerKeyValueSetting.key IntegerKeyValueSetting is used as a singleton-style key/value store: callers do get(key=...) and get_or_create(key=..., ...) and assume at most one row per key. The model, however, did not enforce uniqueness on the key field, so nothing prevented duplicate rows at the database level. This was a real race for _create_soa_rdataset in catalog_zone_manager, which calls get_or_create without select_for_update (unlike increment_soa_serial, which does take a lock). Two concurrent AXFR queries for a catalog zone could both pass the get_or_create existence check and both insert a row for key='catalog-zone-soa-serial'. Every subsequent IntegerKeyValueSetting.objects.get(key=...) call would then raise MultipleObjectsReturned, breaking catalog-zone SOA rendering and serial increments. Add unique=True to the key CharField and ship migration 0003 to enforce the invariant at the database level. Existing deployments are assumed to have clean state (no duplicate keys); if duplicates exist, the migration should be preceded by manual deduplication. --- .../0003_integerkeyvaluesetting_key_unique.py | 16 ++++++++++++++++ netbox_dns_bridge/models.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 netbox_dns_bridge/migrations/0003_integerkeyvaluesetting_key_unique.py diff --git a/netbox_dns_bridge/migrations/0003_integerkeyvaluesetting_key_unique.py b/netbox_dns_bridge/migrations/0003_integerkeyvaluesetting_key_unique.py new file mode 100644 index 0000000..c2480ae --- /dev/null +++ b/netbox_dns_bridge/migrations/0003_integerkeyvaluesetting_key_unique.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("netbox_dns_bridge", "0002_alter_integerkeyvaluesetting_options_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="integerkeyvaluesetting", + name="key", + field=models.CharField(max_length=64, unique=True), + ), + ] diff --git a/netbox_dns_bridge/models.py b/netbox_dns_bridge/models.py index fa599e9..09aa5a1 100644 --- a/netbox_dns_bridge/models.py +++ b/netbox_dns_bridge/models.py @@ -7,7 +7,7 @@ import netbox_dns.models class IntegerKeyValueSetting(models.Model): _netbox_private = True - key = models.CharField(max_length=64) + key = models.CharField(max_length=64, unique=True) value = models.IntegerField() def __str__(self):