Skip to content

ref(issues): Batch requests for non-error groups - #121018

Open
saponifi3d wants to merge 3 commits into
masterfrom
jcallender/reduce-queries
Open

ref(issues): Batch requests for non-error groups#121018
saponifi3d wants to merge 3 commits into
masterfrom
jcallender/reduce-queries

Conversation

@saponifi3d

@saponifi3d saponifi3d commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

Update the _prepare_params_for_category to be a batch method. Instead of producing filters such as:

occurrence_type_id IN (DB_QUERY_TYPES)
occurrence_type_id IN (HTTP_CLIENT_TYPES)
occurrence_type_id IN (MOBILE_TYPES)

it produces one union:

occurrence_type_id IN (
    DB_QUERY_TYPES,
    HTTP_CLIENT_TYPES,
    MOBILE_TYPES,
    ...
)

Allowing us to make 1 query for errors, and 1 query for all other types; where previously we had 7 total concurrent queries.

Uses organizations:issue-search-merged-generic-query as a feature flag to safely rollout the changes.

Dashboard to monitor impact and rollout

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jul 31, 2026
@saponifi3d

Copy link
Copy Markdown
Contributor Author

@cursor review this pr

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bd6ce7e. Configure here.

Comment thread src/sentry/search/snuba/executors.py Outdated
@saponifi3d
saponifi3d marked this pull request as ready for review July 31, 2026 22:19
@saponifi3d
saponifi3d requested a review from a team as a code owner July 31, 2026 22:19
@saponifi3d
saponifi3d force-pushed the jcallender/reduce-queries branch from 5b2de8a to 7889e8c Compare July 31, 2026 22:32
except UnsupportedSearchQuery:
continue

if merge_generic_categories and group_category != GroupCategory.ERROR.value:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

don't love how much this is changing vs what's happening today; the tl;dr is that it's just building a reference dict that can be used the same way as we do today or batch the generic categories. 🤔

@saponifi3d
saponifi3d requested a review from wedamija July 31, 2026 23:07

@kcons kcons left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mostly educating myself, since I don't know this code. It looks pretty reasonable, I need to look a bit more to feel confident.

# Get the top matching groups by score, i.e. the actual search results
# in the order that we want them.
orderby = [f"-{sort_field}", "group_id"] # ensure stable sort within the same score
group_id_sort = "-group_id" if len(group_categories) > 1 else "group_id"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why do we reverse for multiple categories?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The merged query needs to match the downstream SequencePaginator, which sorts (score, group_id) with reverse=True; equal-score rows are therefore consumed by descending group_id. Once multiple categories share one Snuba LIMIT/OFFSET, fetching ties in ascending ID order would select the opposite end of the tie block and can skip or repeat rows across pages. The singleton path stays ascending to preserve the existing behavior (and EAP comparison) while the feature flag is off. The conditional is non-obvious, but the pagination test exercises it.

conditions.append(converted_filter)

use_issue_platform = group_category is not GroupCategory.ERROR.value
use_issue_platform = GroupCategory.ERROR.value not in group_categories

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we only use issue platform if we're not searching for errors.. which means we can choose to use issue platform or not for other categories and we're okay making errors determine that vs using more queries?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is not choosing the dataset for a mixed error/generic query. Error is explicitly excluded from category merging above, and get_search_strategy asserts that ERROR cannot appear with generic categories. So the only inputs here are [ERROR], which uses Events, or one or more non-error categories, which use Issue Platform. Errors still get their own query and do not force other categories onto Events.

continue

if query_params is not None:
query_params_for_categories[tuple(grouped_categories)] = query_params

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

there may be an argument for factoring out a function that returns query_params_for_categories.

@saponifi3d saponifi3d Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I considered this, but _prepare_params_for_categories already owns construction of each SnubaQueryParams; extracting the full loop would mostly move the same long argument list into another method. The genuinely new part is building category_filter_groups, so that is the smaller extraction I would make if we want to shorten snuba_search further.

tl;dr, it felt important to keep this as focused fix to try and fix the N+1 issue; i think we could do quite a bit of cleanup work here.

Comment thread src/sentry/search/snuba/executors.py
merge_generic_categories = features.has(
"organizations:issue-search-merged-generic-query", organization, actor=actor
)
category_filter_groups: list[tuple[list[int], Sequence[SearchFilter]]] = []

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.

no better type to be had here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i had actually refactored it to: list[tuple[list[GroupCategory], Sequence[SearchFilter]]] but that required a bunch of other changes -- needing to refactor a few other areas in this. i ended up reverting that diff because i wanted to keep the change focused to fixing the query problem.

can def bring that back if you feel strongly, was just trying to balance the number of changes outside of the rollout flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants