-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
ref(issues): Batch requests for non-error groups #121018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -343,9 +343,9 @@ def _prepare_aggregations( | |
|
|
||
| return aggregations | ||
|
|
||
| def _prepare_params_for_category( | ||
| def _prepare_params_for_categories( | ||
| self, | ||
| group_category: int, | ||
| group_categories: Sequence[int], | ||
| visible_group_type_ids: Collection[int], | ||
| query_partial: IntermediateSearchQueryPartial, | ||
| organization: Organization, | ||
|
|
@@ -362,14 +362,6 @@ def _prepare_params_for_category( | |
| actor: Any | None = None, | ||
| aggregate_kwargs: TrendsSortWeights | None = None, | ||
| ) -> SnubaQueryParams | None: | ||
| """ | ||
| :raises UnsupportedSearchQuery: when search_filters includes conditions on a dataset that doesn't support it | ||
| """ | ||
|
|
||
| if group_category in SEARCH_FILTER_UPDATERS: | ||
| # remove filters not relevant to the group_category | ||
| search_filters = SEARCH_FILTER_UPDATERS[group_category](search_filters) | ||
|
|
||
| # convert search_filters to snuba format | ||
| converted_filters = self._convert_search_filters( | ||
| organization.id, project_ids, environments, search_filters | ||
|
|
@@ -386,7 +378,7 @@ def _prepare_params_for_category( | |
| else: | ||
| conditions.append(converted_filter) | ||
|
|
||
| use_issue_platform = group_category is not GroupCategory.ERROR.value | ||
| use_issue_platform = GroupCategory.ERROR.value not in group_categories | ||
| aggregations = self._prepare_aggregations( | ||
| sort_field, start, end, having, aggregate_kwargs, use_issue_platform | ||
| ) | ||
|
|
@@ -402,7 +394,8 @@ def _prepare_params_for_category( | |
| else: | ||
| # 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" | ||
|
saponifi3d marked this conversation as resolved.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we reverse for multiple categories?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The merged query needs to match the downstream |
||
| orderby = [f"-{sort_field}", group_id_sort] | ||
|
|
||
| pinned_query_partial: SearchQueryPartial = cast( | ||
| SearchQueryPartial, | ||
|
|
@@ -414,7 +407,10 @@ def _prepare_params_for_category( | |
| ), | ||
| ) | ||
|
|
||
| query_builder = get_search_strategy(GroupCategory(group_category), visible_group_type_ids) | ||
| query_builder = get_search_strategy( | ||
| [GroupCategory(group_category) for group_category in group_categories], | ||
| visible_group_type_ids, | ||
| ) | ||
| snuba_query_params = query_builder( | ||
| pinned_query_partial, | ||
| selected_columns, | ||
|
|
@@ -502,20 +498,46 @@ def snuba_search( | |
| if group_categories - {GroupCategory.ERROR.value} | ||
| else set() | ||
| ) | ||
| query_params_for_categories = {} | ||
| merge_generic_categories = features.has( | ||
| "organizations:issue-search-merged-generic-query", organization, actor=actor | ||
| ) | ||
| category_filter_groups: list[tuple[list[int], Sequence[SearchFilter]]] = [] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no better type to be had here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i had actually refactored it to: can def bring that back if you feel strongly, was just trying to balance the number of changes outside of the rollout flag. |
||
| for group_category in sorted(group_categories): | ||
| try: | ||
| category_search_filters = ( | ||
| SEARCH_FILTER_UPDATERS[group_category](snuba_search_filters) | ||
| if group_category in SEARCH_FILTER_UPDATERS | ||
| else snuba_search_filters | ||
| ) | ||
| except UnsupportedSearchQuery: | ||
| continue | ||
|
|
||
| if merge_generic_categories and group_category != GroupCategory.ERROR.value: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 🤔 |
||
| for grouped_categories, grouped_search_filters in category_filter_groups: | ||
| if ( | ||
| GroupCategory.ERROR.value not in grouped_categories | ||
| and category_search_filters == grouped_search_filters | ||
| ): | ||
| grouped_categories.append(group_category) | ||
| break | ||
| else: | ||
| category_filter_groups.append(([group_category], category_search_filters)) | ||
| else: | ||
| category_filter_groups.append(([group_category], category_search_filters)) | ||
|
|
||
| for gc in group_categories: | ||
| query_params_for_categories: dict[tuple[int, ...], SnubaQueryParams] = {} | ||
| for grouped_categories, category_search_filters in category_filter_groups: | ||
| try: | ||
| query_params = self._prepare_params_for_category( | ||
| gc, | ||
| query_params = self._prepare_params_for_categories( | ||
| grouped_categories, | ||
| visible_group_type_ids, | ||
| query_partial, | ||
| organization, | ||
| project_ids, | ||
| environments, | ||
| group_ids, | ||
| filters, | ||
| snuba_search_filters, | ||
| category_search_filters, | ||
| sort_field, | ||
| start, | ||
| end, | ||
|
|
@@ -524,15 +546,13 @@ def snuba_search( | |
| actor, | ||
| aggregate_kwargs, | ||
| ) | ||
| except UnsupportedSearchQuery: | ||
| pass | ||
| except EmptyGroupIdIntersectionError: | ||
| # Postgres candidates and the snuba group_id condition are | ||
| # disjoint for this category — it can't match anything. Skip it. | ||
| pass | ||
| else: | ||
| if query_params is not None: | ||
| query_params_for_categories[gc] = query_params | ||
| continue | ||
|
|
||
| if query_params is not None: | ||
| query_params_for_categories[tuple(grouped_categories)] = query_params | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered this, but 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. |
||
|
|
||
| callsite = "PostgresSnubaQueryExecutor.snuba_search" | ||
|
|
||
|
|
@@ -546,14 +566,16 @@ def _run_snuba_query() -> tuple[list[tuple[int, Any]], int]: | |
| "snuba.search.group_category_bulk", | ||
| tags={ | ||
| GroupCategory(gc_val).name.lower(): True | ||
| for gc_val, _ in query_params_for_categories.items() | ||
| for group_category_values in query_params_for_categories | ||
| for gc_val in group_category_values | ||
| }, | ||
| ) | ||
| # one of the parallel bulk raw queries failed (maybe the issue platform dataset), | ||
| # we'll fallback to querying for errors only | ||
| if GroupCategory.ERROR.value in query_params_for_categories.keys(): | ||
| error_query_params = query_params_for_categories.get((GroupCategory.ERROR.value,)) | ||
| if error_query_params is not None: | ||
| bulk_query_results = bulk_raw_query( | ||
| [query_params_for_categories[GroupCategory.ERROR.value]], | ||
| [error_query_params], | ||
| referrer=referrer, | ||
| ) | ||
| else: | ||
|
|
||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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_strategyasserts thatERRORcannot 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.