From 8bb47dad0f9f0d6d0b11bcf685a0c47f47233fdc Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Fri, 8 Aug 2025 14:14:55 -0500 Subject: [PATCH] Fixes #20023: Add GiST index on Prefix.prefix for net contains ops (#20059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves performance issue where prefix deletion with 2000+ children took 5-10 minutes due to sequential scans in hierarchy depth/children calculations. Adding PostgreSQL GiST index with inet_ops enables efficient network containment operators (>>, <<, <<=) in annotate_hierarchy() queries. Performance impact: - 30-60x speedup: 5-10 minutes → 10 seconds for large prefix deletions - Real-world validation: 4s migration time on 1.24M prefix dataset - Storage cost: 47MB index (11% of table storage, 38 bytes per prefix) Works in conjunction with existing B-tree indexes on vrf_id for optimal query performance. Benefits all network containment operations including hierarchy navigation, aggregate views, and available IP/prefix calculations. --- ..._add_prefix_network_containment_indexes.py | 20 +++++++++++++++++++ netbox/ipam/models/ip.py | 8 ++++++++ 2 files changed, 28 insertions(+) create mode 100644 netbox/ipam/migrations/0082_add_prefix_network_containment_indexes.py diff --git a/netbox/ipam/migrations/0082_add_prefix_network_containment_indexes.py b/netbox/ipam/migrations/0082_add_prefix_network_containment_indexes.py new file mode 100644 index 000000000..abae5dccd --- /dev/null +++ b/netbox/ipam/migrations/0082_add_prefix_network_containment_indexes.py @@ -0,0 +1,20 @@ +from django.contrib.postgres.indexes import GistIndex +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('dcim', '0210_macaddress_ordering'), + ('extras', '0129_fix_script_paths'), + ('ipam', '0081_remove_service_device_virtual_machine_add_parent_gfk_index'), + ('tenancy', '0020_remove_contactgroupmembership'), + ] + + operations = [ + migrations.AddIndex( + model_name='prefix', + index=GistIndex(fields=['prefix'], name='ipam_prefix_gist_idx', opclasses=['inet_ops']), + ), + ] diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index db116e9e4..fea8af3ed 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -1,5 +1,6 @@ import netaddr from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.postgres.indexes import GistIndex from django.core.exceptions import ValidationError from django.db import models from django.db.models import F @@ -281,6 +282,13 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary ordering = (F('vrf').asc(nulls_first=True), 'prefix', 'pk') # (vrf, prefix) may be non-unique verbose_name = _('prefix') verbose_name_plural = _('prefixes') + indexes = [ + GistIndex( + fields=['prefix'], + name='ipam_prefix_gist_idx', + opclasses=['inet_ops'], + ), + ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)