Skip to content

Upstream 16527 - Eliminate LEFT OUTER JOINs in unified job RBAC query#586

Merged
cigamit merged 2 commits into
mainfrom
upstream16527
Jul 18, 2026
Merged

Upstream 16527 - Eliminate LEFT OUTER JOINs in unified job RBAC query#586
cigamit merged 2 commits into
mainfrom
upstream16527

Conversation

@cigamit

@cigamit cigamit commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Upstream uses the new RBAC backend, but we can port the Upstream PR to fix the same issue on the older RBAC.

Upstream Summary

  • Replace field-traversal Q objects in UnifiedJobAccess.filtered_queryset() with pk__in subqueries, eliminating 3 LEFT OUTER JOINs that Django generates for the inventoryupdate and adhoccommand RBAC branches
  • EXPLAIN ANALYZE on scale lab data (385K unified jobs) shows 28% execution time reduction (496ms → 355ms), with larger gains expected under concurrent load due to reduced CPU contention

Problem

Django translates Q(inventoryupdate__inventory_source__inventory__id__in=...) into LEFT OUTER JOINs, which are evaluated unconditionally for every row in main_unifiedjob before filtering. At scale, 99.84% of rows are scanned and discarded. The 4-way OR prevents PostgreSQL from using any column-specific index.

Under production concurrency (21 controller replicas), pg_stat_statements shows this query averages 3,959ms per call across 29K+ calls, consuming 32.5 hours of DB time — the single largest database workload.

Fix

# Before: field traversal → LEFT OUTER JOINs
Q(inventoryupdate__inventory_source__inventory__id__in=inv_pk_qs)
Q(adhoccommand__inventory__id__in=inv_pk_qs)

# After: explicit subqueries → IN (SELECT ...)
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'))

Note: Exists() with OuterRef cannot be used here — django-polymorphic raises AttributeError on Exists nodes. The pk__in pattern is the polymorphic-compatible equivalent.

Test plan

  • All 306 RBAC functional tests pass (awx/main/tests/functional/rbac/)
  • Verified generated SQL has zero LEFT OUTER JOINs via str(qs.query) in dev container
  • EXPLAIN ANALYZE confirms correct results (same 628 rows returned) with improved plan
  • Scale lab A/B comparison after deployment to aap26-next

@cigamit
cigamit requested review from TheWitness and Copilot July 17, 2026 21:11
@cigamit cigamit self-assigned this Jul 17, 2026
@cigamit cigamit added bug Something isn't working python Pull requests that update python code labels Jul 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports an upstream RBAC query optimization to the legacy RBAC backend by rewriting several access-control filtered_queryset() implementations to use pk__in subqueries, reducing LEFT OUTER JOIN pressure and improving query planner/index usage for large unified job tables.

Changes:

  • Refactors JobEventAccess.filtered_queryset() to use *_id__in subqueries instead of relationship traversal.
  • Refactors UnifiedJobTemplateAccess / UnifiedJobAccess RBAC branches to use pk__in subqueries (notably for InventoryUpdate and AdHocCommand branches) to avoid LEFT OUTER JOINs in the unified job query.
  • Refactors LabelAccess.filtered_queryset() to use the M2M through-table subquery instead of a join + distinct().

Comment thread awx/main/access.py
Comment thread awx/main/access.py Outdated
(This won't do much for performance though)
Copilot AI review requested due to automatic review settings July 17, 2026 21:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@cigamit
cigamit merged commit 4a6701a into main Jul 18, 2026
1 check passed
@cigamit
cigamit deleted the upstream16527 branch July 18, 2026 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working python Pull requests that update python code

Development

Successfully merging this pull request may close these issues.

3 participants