Permissions performance in v2.9 #4029

Closed
opened 2025-12-29 18:32:41 +01:00 by adam · 3 comments
Owner

Originally created by @jeremmfr on GitHub (Aug 26, 2020).

Environment

  • Python version: 3.6.9
  • NetBox version: 2.9.1

Steps to Reproduce

  1. upgrade Netbox v2.8.x to v2.9.1 with permissions already present.
  2. time curl -H 'Accept: application/json' -H "Authorization: token xxx" "http://localhost:8001/api/ipam/ip-addresses/?vminterface_id=3"
    real 0m7.694s
    user 0m0.011s
    sys 0m0.000s
  3. add EXEMPT_VIEW_PERMISSIONS = [ "ipam.ipaddress" ] in configuration.py
  4. time curl -H 'Accept: application/json' -H "Authorization: token xxx" "http://localhost:8001/api/ipam/ip-addresses/?vminterface_id=3"
    real 0m1.095s
    user 0m0.005s
    sys 0m0.006s

Expected Behavior

Minimum time difference between configurations with and without permissions

Observed Behavior

6 more seconds with permissions

Debug

With DEBUG=True, I didn't see where the problem could come from.
I add https://github.com/ymyzk/wsgi_lineprof for debug. The response time has been multiplied by 10 (~60seconds) but I can see this in output :

File: /srv/netbox/netbox/netbox/authentication.py
Name: has_perm
Total time: 49.3849 [sec]
  Line      Hits         Time  Per Hit  % Time  Code
====================================================
    43                                              def has_perm(self, user_obj, perm, obj=None):
    44         1        21860  21860.0     0.0          app_label, action, model_name = resolve_permission(perm)
    45
    46                                                  # Superusers implicitly have all permissions
    47         1         2703   2703.0     0.0          if user_obj.is_active and user_obj.is_superuser:
    48                                                      return True
    49
    50                                                  # Permission is exempt from enforcement (i.e. listed in EXEMPT_VIEW_PERMISSIONS)
    51         1        46219  46219.0     0.0          if permission_is_exempt(perm):
    52                                                      return True
    53
    54                                                  # Handle inactive/anonymous users
    55         1        12502  12502.0     0.0          if not user_obj.is_active or user_obj.is_anonymous:
    56                                                      return False
    57
    58                                                  # If no applicable ObjectPermissions have been created for this user/permission, deny permission
    59         1  49384849456 49384849456.0   100.0          if perm not in self.get_all_permissions(user_obj):
    60                                                      return False
    61
    62                                                  # If no object has been specified, grant permission. (The presence of a permission in this set tells
    63                                                  # us that the user has permission for *some* objects, but not necessarily a specific object.)
    64         1         1155   1155.0     0.0          if obj is None:
    65         1         1116   1116.0     0.0              return True
    66
    67                                                  # Sanity check: Ensure that the requested permission applies to the specified object
    68                                                  model = obj._meta.model
--
File: /srv/netbox/netbox/netbox/authentication.py
Name: get_all_permissions
Total time: 49.3848 [sec]
  Line      Hits         Time  Per Hit  % Time  Code
====================================================
    16                                              def get_all_permissions(self, user_obj, obj=None):
    17         2        10583   5291.5     0.0          if not user_obj.is_active or user_obj.is_anonymous:
    18                                                      return dict()
    19         2         4272   2136.0     0.0          if not hasattr(user_obj, '_object_perm_cache'):
    20         1  49384825885 49384825885.0   100.0              user_obj._object_perm_cache = self.get_object_permissions(user_obj)
    21         2         2676   1338.0     0.0          return user_obj._object_perm_cache

File: /srv/netbox/netbox/netbox/authentication.py
Name: get_object_permissions
Total time: 48.9858 [sec]
  Line      Hits         Time  Per Hit  % Time  Code
====================================================
    23                                              def get_object_permissions(self, user_obj):
    24                                                  """
    25                                                  Return all permissions granted to the user by an ObjectPermission.
    26                                                  """
    27                                                  # Retrieve all assigned and enabled ObjectPermissions
    28         1        34600  34600.0     0.0          object_permissions = ObjectPermission.objects.filter(
    29         1       134755 134755.0     0.0              Q(users=user_obj) | Q(groups__user=user_obj),
    30         1     11628388 11628388.0     0.0              enabled=True
    31         1       358499 358499.0     0.0          ).prefetch_related('object_types')
    32
    33                                                  # Create a dictionary mapping permissions to their constraints
    34         1         3820   3820.0     0.0          perms = defaultdict(list)
    35     32980  40036510224 1213963.3    81.7          for obj_perm in object_permissions:
    36     65476   8489475277 129657.8    17.3              for object_type in obj_perm.object_types.all():
    37     64997    102883190   1582.9     0.2                  for action in obj_perm.actions:
    38     32500     67318031   2071.3     0.1                      perm_name = f"{object_type.app_label}.{action}_{object_type.model}"
    39     32500    277425451   8536.2     0.6                      perms[perm_name].extend(obj_perm.list_constraints())
    40
    41         1          937    937.0     0.0          return perms```
Originally created by @jeremmfr on GitHub (Aug 26, 2020). ### Environment * Python version: 3.6.9 * NetBox version: 2.9.1 ### Steps to Reproduce 1. upgrade Netbox v2.8.x to v2.9.1 with permissions already present. 2. time curl -H 'Accept: application/json' -H "Authorization: token xxx" "http://localhost:8001/api/ipam/ip-addresses/?vminterface_id=3" real 0m7.694s user 0m0.011s sys 0m0.000s 3. add EXEMPT_VIEW_PERMISSIONS = [ "ipam.ipaddress" ] in configuration.py 4. time curl -H 'Accept: application/json' -H "Authorization: token xxx" "http://localhost:8001/api/ipam/ip-addresses/?vminterface_id=3" real 0m1.095s user 0m0.005s sys 0m0.006s ### Expected Behavior Minimum time difference between configurations with and without permissions ### Observed Behavior 6 more seconds with permissions ### Debug With DEBUG=True, I didn't see where the problem could come from. I add https://github.com/ymyzk/wsgi_lineprof for debug. The response time has been multiplied by 10 (~60seconds) but I can see this in output : ``` File: /srv/netbox/netbox/netbox/authentication.py Name: has_perm Total time: 49.3849 [sec] Line Hits Time Per Hit % Time Code ==================================================== 43 def has_perm(self, user_obj, perm, obj=None): 44 1 21860 21860.0 0.0 app_label, action, model_name = resolve_permission(perm) 45 46 # Superusers implicitly have all permissions 47 1 2703 2703.0 0.0 if user_obj.is_active and user_obj.is_superuser: 48 return True 49 50 # Permission is exempt from enforcement (i.e. listed in EXEMPT_VIEW_PERMISSIONS) 51 1 46219 46219.0 0.0 if permission_is_exempt(perm): 52 return True 53 54 # Handle inactive/anonymous users 55 1 12502 12502.0 0.0 if not user_obj.is_active or user_obj.is_anonymous: 56 return False 57 58 # If no applicable ObjectPermissions have been created for this user/permission, deny permission 59 1 49384849456 49384849456.0 100.0 if perm not in self.get_all_permissions(user_obj): 60 return False 61 62 # If no object has been specified, grant permission. (The presence of a permission in this set tells 63 # us that the user has permission for *some* objects, but not necessarily a specific object.) 64 1 1155 1155.0 0.0 if obj is None: 65 1 1116 1116.0 0.0 return True 66 67 # Sanity check: Ensure that the requested permission applies to the specified object 68 model = obj._meta.model -- File: /srv/netbox/netbox/netbox/authentication.py Name: get_all_permissions Total time: 49.3848 [sec] Line Hits Time Per Hit % Time Code ==================================================== 16 def get_all_permissions(self, user_obj, obj=None): 17 2 10583 5291.5 0.0 if not user_obj.is_active or user_obj.is_anonymous: 18 return dict() 19 2 4272 2136.0 0.0 if not hasattr(user_obj, '_object_perm_cache'): 20 1 49384825885 49384825885.0 100.0 user_obj._object_perm_cache = self.get_object_permissions(user_obj) 21 2 2676 1338.0 0.0 return user_obj._object_perm_cache File: /srv/netbox/netbox/netbox/authentication.py Name: get_object_permissions Total time: 48.9858 [sec] Line Hits Time Per Hit % Time Code ==================================================== 23 def get_object_permissions(self, user_obj): 24 """ 25 Return all permissions granted to the user by an ObjectPermission. 26 """ 27 # Retrieve all assigned and enabled ObjectPermissions 28 1 34600 34600.0 0.0 object_permissions = ObjectPermission.objects.filter( 29 1 134755 134755.0 0.0 Q(users=user_obj) | Q(groups__user=user_obj), 30 1 11628388 11628388.0 0.0 enabled=True 31 1 358499 358499.0 0.0 ).prefetch_related('object_types') 32 33 # Create a dictionary mapping permissions to their constraints 34 1 3820 3820.0 0.0 perms = defaultdict(list) 35 32980 40036510224 1213963.3 81.7 for obj_perm in object_permissions: 36 65476 8489475277 129657.8 17.3 for object_type in obj_perm.object_types.all(): 37 64997 102883190 1582.9 0.2 for action in obj_perm.actions: 38 32500 67318031 2071.3 0.1 perm_name = f"{object_type.app_label}.{action}_{object_type.model}" 39 32500 277425451 8536.2 0.6 perms[perm_name].extend(obj_perm.list_constraints()) 40 41 1 937 937.0 0.0 return perms```
adam closed this issue 2025-12-29 18:32:41 +01:00
Author
Owner

@jeremmfr commented on GitHub (Aug 26, 2020):

Hi,

After refactor permissions to simplify and delete duplicate relationships (object/user vs object/group),
the response time is much more acceptable :

real	0m0.377s
user	0m0.017s
sys	0m0.007s

Best regards

@jeremmfr commented on GitHub (Aug 26, 2020): Hi, After refactor permissions to simplify and delete duplicate relationships (object/user vs object/group), the response time is much more acceptable : ``` real 0m0.377s user 0m0.017s sys 0m0.007s ``` Best regards
Author
Owner

@jeremystretch commented on GitHub (Aug 26, 2020):

Thank you for opening a bug report. I was unable to reproduce the reported behavior on NetBox v2.9.1. Please re-confirm the reported behavior on the current stable release and adjust your post above as necessary. Remember to provide detailed steps that someone else can follow using a clean installation of NetBox to reproduce the issue. Remember to include the steps taken to create any initial objects or other data.

@jeremystretch commented on GitHub (Aug 26, 2020): Thank you for opening a bug report. I was unable to reproduce the reported behavior on NetBox v2.9.1. Please re-confirm the reported behavior on the current stable release and adjust your post above as necessary. Remember to provide detailed steps that someone else can follow using a clean installation of NetBox to reproduce the issue. Remember to include the steps taken to create any initial objects or other data.
Author
Owner

@jeremystretch commented on GitHub (Aug 31, 2020):

Seems this has been resolved by the reporter.

@jeremystretch commented on GitHub (Aug 31, 2020): Seems this has been resolved by the reporter.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#4029