Просмотр исходного кода

Fixes #20023: Add GiST index on Prefix.prefix for net contains ops (#20059)

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.
Jason Novinger 6 месяцев назад
Родитель
Сommit
8bb47dad0f

+ 20 - 0
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']),
+        ),
+    ]

+ 8 - 0
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)