[Hot-Fix] ISS-281939: Two-phase query optimisation for IssueViewSet#282
Merged
ritchaddha merged 3 commits intodevfrom Feb 25, 2026
Merged
Conversation
…nd WorkspaceViewIssuesViewSet
Applies the same two-phase strategy already proven on WorkspaceViewIssuesViewSet
to IssueViewSet (GET /api/workspaces/{slug}/projects/{id}/issues/).
Phase 1 – lean queryset (Issue.objects, no JOINs/annotations beyond cycle_id)
handles filtering, ordering, and pagination across the full dataset.
Phase 2 – _build_detail_queryset runs the 3 count subqueries on ~100 IDs.
Phase 3 – _enrich_issues_with_relations batch-loads label_ids, assignee_ids,
module_ids, cycle_id, and custom_property_values via 5 flat IN-queries.
Also fixes:
- create() was broken after get_queryset() was made lean (FieldError on
sub_issues_count / link_count / attachment_count); now uses _build_detail_queryset
+ _enrich_issues_with_relations.
- Dead import issue_on_results removed.
- Phase-1/Phase-2 null-state mismatch closed by adding state_id__isnull=False
to Phase 1 filter (Django's exclude+IN adds OR IS NULL; Phase 2 inner-joins
on state, which drops null-state rows).
- Debug logger statements added to IssueViewSet for Phase 1 and Phase 2 SQL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…project-issues-endpoint-optimisation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ritchaddha
approved these changes
Feb 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Applies the same two-phase query strategy already shipped on
WorkspaceViewIssuesViewSettoIssueViewSet(GET /api/workspaces/{slug}/projects/{id}/issues/).The problem: The old
get_queryset()ran 5 correlated subqueries + 4select_relatedJOINs + 3prefetch_relatedcalls against the entire unfiltered dataset before pagination. On large projects (10k+ issues) this was the primary bottleneck.The fix — three phases:
get_queryset): leanIssue.objectsqueryset — only scalar filters + onecycle_idannotation. Handles filtering, ordering, and pagination._build_detail_queryset): count subqueries (sub_issues_count,link_count,attachment_count) run against ~100 paginated IDs only._enrich_issues_with_relations): 5 flatIN-queries batch-loadlabel_ids,assignee_ids,module_ids,cycle_id,custom_property_values.Additional fixes in this PR:
create()was broken afterget_queryset()was made lean — it requestedsub_issues_count,link_count,attachment_countwhich were no longer annotated, causing aFieldError. Now uses_build_detail_queryset+_enrich_issues_with_relations.issue_on_resultsremoved.state_id__isnull=Falseto the Phase 1 filter in bothIssueViewSetandWorkspaceViewIssuesViewSet. Django's.exclude(state_id__in=...)silently addsOR state_id IS NULL, letting null-state rows through Phase 1; Phase 2'sissue_objectsinner-joins on state and would drop them, causing a page count mismatch.logger.debugstatements added toIssueViewSetfor Phase 1 and Phase 2 SQL (mirrors what was done onWorkspaceViewIssuesViewSetbefore production).Ticket Information
Link: ISS-281939
How to Test
GET /api/workspaces/{slug}/projects/{id}/issues/?layout=list— confirm issues returned with correctlabel_ids,assignee_ids,module_ids,cycle_id,custom_property_values.group_by=assignees__id— confirm grouped response; assignees correctly split per group row.group_by=labels__id&sub_group_by=state_id— confirm sub-grouped response.group_by=priority(non-M2M) — confirm ordering preserved correctly.POST /api/workspaces/{slug}/projects/{id}/issues/— confirm the 201 response includessub_issues_count,link_count,attachment_count,label_ids,cycle_id.role=5,guest_view_all_features=False) — confirm onlycreated_by=meissues returned.DEBUGlevel — Phase 1 and Phase 2 SQL should be visible forIssueViewSet.