mirror of
https://github.com/ansible/awx.git
synced 2026-10-31 06:26:15 -02:30
AAP-81173 — Replace 4-way OR with UNION in unified job list RBAC query (#16555)
* AAP-81173 — Replace 4-way OR with UNION in unified job list RBAC query The UnifiedJobAccess.filtered_queryset() method used a 4-way OR to determine which unified jobs a user can see. Under load, the OR forces PostgreSQL to evaluate all four branches in a single plan, preventing branch-specific index optimization and causing 35 hours of DB time per 30-minute Scale Lab window. Split each RBAC branch (template read_role, inventory update, ad-hoc command, org auditor) into separate querysets combined with UNION, giving the planner an independent optimal plan per branch. The UNION result is wrapped in pk__in= for compatibility with BaseAccess.get_queryset() prefetch_related and the workflowapproval filter. This follows the same pattern proven in AAP-83319 (team list UNION fix). * AAP-81173 — Add UnifiedJobPagination to prevent COUNT regression The pk__in UNION pattern used for RBAC filtering forces the large outer table as the driving table for COUNT(*), requiring PostgreSQL to materialize all subquery result sets. On large deployments this produces catastrophic query times (see AAP-83773 for the identical issue on activity_stream). Override the paginator count to use an unfiltered UnifiedJob.objects.count() — the over-count is acceptable for pagination UI. Also fix test_unified_job_list_rando_sees_nothing to assert on results length instead of count, since count is now unfiltered. * AAP-81173 — Add unit tests for UnifiedJobPagination coverage Cover the UnifiedJobPaginator.count cached property and the count_disabled branch in UnifiedJobPagination.paginate_queryset to satisfy SonarCloud's 90% new-code coverage gate. * AAP-81173 — Address review feedback: save/restore paginator class, format consistency - Fix Pagination.paginate_queryset() to save/restore django_paginator_class instead of hardcoding DjangoPaginator in the finally block. This lets subclasses set the class attribute without needing to override the method. - Remove UnifiedJobPagination.paginate_queryset() override — now only needs to set django_paginator_class = UnifiedJobPaginator as a class attribute. - Format by_org_auditor consistently with the other three UNION branches. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
9d67c302fd
commit
c84575c215
+14
-7
@@ -13,7 +13,7 @@ from rest_framework.utils.urls import replace_query_param
|
||||
from rest_framework.settings import api_settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from awx.main.models import ActivityStream
|
||||
from awx.main.models import ActivityStream, UnifiedJob
|
||||
|
||||
|
||||
class DisabledPaginator(DjangoPaginator):
|
||||
@@ -27,18 +27,21 @@ class DisabledPaginator(DjangoPaginator):
|
||||
|
||||
|
||||
class ActivityStreamPaginator(DjangoPaginator):
|
||||
"""Use unfiltered table count for activity stream pagination (AAP-83773).
|
||||
|
||||
The RBAC-filtered COUNT query takes ~36 min on large tables due to the
|
||||
pk__in subquery shape from AAP-81860. An unfiltered count is acceptable
|
||||
for pagination UI -- an approximate over-count is harmless.
|
||||
"""
|
||||
"""Use unfiltered table count for activity stream pagination (AAP-83773)."""
|
||||
|
||||
@cached_property
|
||||
def count(self):
|
||||
return ActivityStream.objects.count()
|
||||
|
||||
|
||||
class UnifiedJobPaginator(DjangoPaginator):
|
||||
"""Use unfiltered table count for unified job pagination."""
|
||||
|
||||
@cached_property
|
||||
def count(self):
|
||||
return UnifiedJob.objects.count()
|
||||
|
||||
|
||||
class Pagination(pagination.PageNumberPagination):
|
||||
page_size_query_param = 'page_size'
|
||||
max_page_size = settings.MAX_PAGE_SIZE
|
||||
@@ -91,6 +94,10 @@ class ActivityStreamPagination(Pagination):
|
||||
django_paginator_class = ActivityStreamPaginator
|
||||
|
||||
|
||||
class UnifiedJobPagination(Pagination):
|
||||
django_paginator_class = UnifiedJobPaginator
|
||||
|
||||
|
||||
class LimitPagination(pagination.BasePagination):
|
||||
default_limit = api_settings.PAGE_SIZE
|
||||
limit_query_param = 'limit'
|
||||
|
||||
@@ -129,7 +129,7 @@ from awx.api.views.mixin import (
|
||||
NoTruncateMixin,
|
||||
UnifiedJobExcludeMixin,
|
||||
)
|
||||
from awx.api.pagination import ActivityStreamPagination, UnifiedJobEventPagination
|
||||
from awx.api.pagination import ActivityStreamPagination, UnifiedJobEventPagination, UnifiedJobPagination
|
||||
from awx.main.utils import set_environ
|
||||
|
||||
logger = logging.getLogger('awx.api.views')
|
||||
@@ -4573,6 +4573,7 @@ class UnifiedJobList(UnifiedJobExcludeMixin, ListAPIView):
|
||||
model = models.UnifiedJob
|
||||
serializer_class = serializers.UnifiedJobListSerializer
|
||||
search_fields = ('description', 'name', 'job__playbook')
|
||||
pagination_class = UnifiedJobPagination
|
||||
resource_purpose = 'unified jobs'
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user