mirror of
https://github.com/ansible/awx.git
synced 2026-07-02 11:58:03 -02:30
AAP-81082 — Eliminate LEFT OUTER JOINs in unified job RBAC query (#16527)
* AAP-81082 Eliminate LEFT OUTER JOINs in unified job RBAC query UnifiedJobAccess.filtered_queryset() used field-traversal Q objects (inventoryupdate__inventory_source__inventory__id__in=...) which Django translates into LEFT OUTER JOINs. This forces PostgreSQL to scan all rows in main_unifiedjob before filtering — at scale, 99.84% are discarded. Replace with pk__in subqueries that generate IN (SELECT ...) instead, allowing PostgreSQL to skip the unconditional join. EXPLAIN ANALYZE shows a 28% reduction in execution time (496ms -> 355ms), with larger gains expected under concurrent load. * AAP-81082 Eliminate LEFT OUTER JOINs in RBAC filtered_queryset methods Replace field-traversal Q objects with pk__in subqueries across all Access classes that query polymorphic or M2M tables, preventing Django from generating unconditional LEFT OUTER JOINs. Also migrate legacy _accessible_pk_qs / accessible_pk_qs calls to DAB RBAC access_ids_qs. Affected: UnifiedJobAccess, UnifiedJobTemplateAccess, JobAccess, JobEventAccess, LabelAccess. * Fix docstring
This commit is contained in:
@@ -1665,11 +1665,11 @@ class JobAccess(BaseAccess):
|
||||
def filtered_queryset(self):
|
||||
qs = self.model.objects
|
||||
|
||||
qs_jt = qs.filter(job_template__in=JobTemplate.access_qs(self.user, 'view'))
|
||||
|
||||
org_access_qs = Organization.objects.filter(Q(admin_role__members=self.user) | Q(auditor_role__members=self.user))
|
||||
org_access_qs = Organization.objects.filter(
|
||||
Q(pk__in=Organization.access_ids_qs(self.user, 'change')) | Q(pk__in=Organization.access_ids_qs(self.user, 'audit_organization'))
|
||||
)
|
||||
if not org_access_qs.exists():
|
||||
return qs_jt
|
||||
return qs.filter(job_template__in=JobTemplate.access_qs(self.user, 'view'))
|
||||
|
||||
return qs.filter(Q(job_template__in=JobTemplate.access_qs(self.user, 'view')) | Q(organization__in=org_access_qs)).distinct()
|
||||
|
||||
@@ -2309,7 +2309,7 @@ class JobHostSummaryAccess(BaseAccess):
|
||||
|
||||
class JobEventAccess(BaseAccess):
|
||||
"""
|
||||
I can see job event records whenever I can read both job and host.
|
||||
I can see job event records whenever I can read the job or the host.
|
||||
"""
|
||||
|
||||
model = JobEvent
|
||||
@@ -2320,8 +2320,8 @@ class JobEventAccess(BaseAccess):
|
||||
|
||||
def filtered_queryset(self):
|
||||
return self.model.objects.filter(
|
||||
Q(host__inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role'))
|
||||
| Q(job__job_template__in=JobTemplate.accessible_pk_qs(self.user, 'read_role'))
|
||||
Q(host_id__in=Host.objects.filter(inventory__in=Inventory.access_ids_qs(self.user, 'view')).values('pk'))
|
||||
| Q(job_id__in=Job.objects.filter(job_template__in=JobTemplate.access_ids_qs(self.user, 'view')).values('pk'))
|
||||
)
|
||||
|
||||
def can_add(self, data):
|
||||
@@ -2451,7 +2451,11 @@ class UnifiedJobTemplateAccess(BaseAccess):
|
||||
def filtered_queryset(self):
|
||||
return self.model.objects.filter(
|
||||
Q(pk__in=self.model.accessible_pk_qs(self.user, 'read_role'))
|
||||
| Q(inventorysource__inventory__id__in=Inventory._accessible_pk_qs(Inventory, self.user, 'read_role'))
|
||||
| Q(
|
||||
pk__in=InventorySource.objects.filter(
|
||||
inventory__id__in=Inventory.access_ids_qs(self.user, 'view'),
|
||||
).values('unifiedjobtemplate_ptr_id')
|
||||
)
|
||||
)
|
||||
|
||||
def can_start(self, obj, validate_license=True):
|
||||
@@ -2497,12 +2501,20 @@ class UnifiedJobAccess(BaseAccess):
|
||||
# )
|
||||
|
||||
def filtered_queryset(self):
|
||||
inv_pk_qs = Inventory._accessible_pk_qs(Inventory, self.user, 'read_role')
|
||||
inv_pk_qs = Inventory.access_ids_qs(self.user, 'view')
|
||||
qs = self.model.objects.filter(
|
||||
Q(unified_job_template_id__in=UnifiedJobTemplate.accessible_pk_qs(self.user, 'read_role'))
|
||||
| Q(inventoryupdate__inventory_source__inventory__id__in=inv_pk_qs)
|
||||
| Q(adhoccommand__inventory__id__in=inv_pk_qs)
|
||||
| Q(organization__in=Organization.accessible_pk_qs(self.user, 'auditor_role'))
|
||||
| Q(
|
||||
pk__in=InventoryUpdate.objects.filter(
|
||||
inventory_source__inventory__id__in=inv_pk_qs,
|
||||
).values('pk')
|
||||
)
|
||||
| Q(
|
||||
pk__in=AdHocCommand.objects.filter(
|
||||
inventory__id__in=inv_pk_qs,
|
||||
).values('pk')
|
||||
)
|
||||
| Q(organization__in=Organization.access_ids_qs(self.user, 'audit_organization'))
|
||||
)
|
||||
return qs
|
||||
|
||||
@@ -2622,9 +2634,13 @@ class LabelAccess(BaseAccess):
|
||||
|
||||
def filtered_queryset(self):
|
||||
return self.model.objects.filter(
|
||||
Q(organization__in=Organization.accessible_pk_qs(self.user, 'read_role'))
|
||||
| Q(unifiedjobtemplate_labels__in=UnifiedJobTemplate.accessible_pk_qs(self.user, 'read_role'))
|
||||
).distinct()
|
||||
Q(organization__in=Organization.access_ids_qs(self.user, 'view'))
|
||||
| Q(
|
||||
pk__in=UnifiedJobTemplate.labels.through.objects.filter(
|
||||
unifiedjobtemplate_id__in=UnifiedJobTemplate.accessible_pk_qs(self.user, 'read_role'),
|
||||
).values('label_id')
|
||||
)
|
||||
)
|
||||
|
||||
@check_superuser
|
||||
def can_add(self, data):
|
||||
|
||||
Reference in New Issue
Block a user