From 2ec768b0743a18051808e7c1590549432e129546 Mon Sep 17 00:00:00 2001 From: Kyle Consalus Date: Wed, 28 Oct 2026 15:01:20 -0700 Subject: [PATCH 1/3] feat(api): Add optional endpoint duration budgets --- src/sentry/api/base.py | 3 + .../issues/endpoints/actionable_items.py | 1 + .../endpoints/debug_group_derived_data.py | 1 + .../issues/endpoints/event_grouping_info.py | 1 + src/sentry/issues/endpoints/event_owners.py | 1 + .../issues/endpoints/group_activities.py | 2 + .../issues/endpoints/group_attachments.py | 2 + .../issues/endpoints/group_current_release.py | 2 + src/sentry/issues/endpoints/group_details.py | 1 + .../issues/endpoints/group_event_details.py | 2 + src/sentry/issues/endpoints/group_events.py | 1 + .../endpoints/group_first_last_release.py | 2 + src/sentry/issues/endpoints/group_hashes.py | 1 + .../endpoints/group_integration_details.py | 2 + .../issues/endpoints/group_integrations.py | 2 + src/sentry/issues/endpoints/group_notes.py | 2 + .../issues/endpoints/group_notes_details.py | 2 + .../issues/endpoints/group_pull_requests.py | 1 + .../issues/endpoints/group_reprocessing.py | 2 + .../issues/endpoints/group_similar_issues.py | 2 + .../group_similar_issues_embeddings.py | 1 + src/sentry/issues/endpoints/group_stats.py | 2 + .../issues/endpoints/group_tagkey_details.py | 1 + .../issues/endpoints/group_tagkey_values.py | 1 + src/sentry/issues/endpoints/group_tags.py | 2 + .../issues/endpoints/group_tombstone.py | 1 + .../endpoints/group_tombstone_details.py | 1 + .../issues/endpoints/group_user_reports.py | 2 + ...organization_code_mapping_repo_prefixes.py | 1 + .../organization_codeowners_associations.py | 1 + .../organization_derive_code_mappings.py | 1 + .../issues/endpoints/organization_eventid.py | 1 + .../endpoints/organization_group_index.py | 1 + .../organization_group_index_stats.py | 1 + .../organization_group_search_view_details.py | 1 + ...ation_group_search_view_details_starred.py | 1 + ...ization_group_search_view_starred_order.py | 1 + .../organization_group_search_view_visit.py | 1 + .../organization_group_search_views.py | 1 + ...organization_group_search_views_starred.py | 1 + .../endpoints/organization_issues_count.py | 1 + ...organization_issues_resolved_in_release.py | 1 + .../organization_issues_with_supergroups.py | 1 + .../organization_release_previous_commits.py | 1 + .../issues/endpoints/organization_shortid.py | 1 + .../endpoints/project_codeowners_details.py | 1 + .../endpoints/project_codeowners_index.py | 1 + .../issues/endpoints/project_event_details.py | 2 + src/sentry/issues/endpoints/project_events.py | 1 + .../issues/endpoints/project_group_index.py | 1 + .../issues/endpoints/project_group_stats.py | 1 + .../project_issues_resolved_in_release.py | 1 + .../issues/endpoints/project_ownership.py | 1 + .../endpoints/project_stacktrace_link.py | 1 + .../project_stacktrace_source_context.py | 1 + src/sentry/issues/endpoints/related_issues.py | 1 + .../issues/endpoints/shared_group_details.py | 1 + .../endpoints/team_all_unresolved_issues.py | 1 + .../issues/endpoints/team_groups_old.py | 1 + .../issues/endpoints/team_issue_breakdown.py | 1 + src/sentry/middleware/stats.py | 26 +++++- tests/sentry/middleware/test_stats.py | 82 ++++++++++++++++++- 62 files changed, 183 insertions(+), 2 deletions(-) diff --git a/src/sentry/api/base.py b/src/sentry/api/base.py index c44cc6fdc177..e7ecef62ad68 100644 --- a/src/sentry/api/base.py +++ b/src/sentry/api/base.py @@ -284,6 +284,9 @@ class Endpoint(APIView): cursor_name = "cursor" owner: ApiOwner = ApiOwner.UNOWNED + # Request duration budgets are expressed in seconds. A mapping applies budgets only to the + # listed HTTP methods; missing methods are not budgeted. + max_acceptable_duration: dict[HTTP_METHOD_NAME, float] | float | None = None publish_status: dict[HTTP_METHOD_NAME, ApiPublishStatus] = {} # Non-safe methods that a client may resend after a timeout or 5xx without # changing the outcome. Published as `x-sentry-idempotent`; see apidocs/README.md. diff --git a/src/sentry/issues/endpoints/actionable_items.py b/src/sentry/issues/endpoints/actionable_items.py index 058ff0c274bb..e76e78499934 100644 --- a/src/sentry/issues/endpoints/actionable_items.py +++ b/src/sentry/issues/endpoints/actionable_items.py @@ -31,6 +31,7 @@ class ActionableItemsEndpoint(ProjectEndpoint): "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 def get(self, request: Request, project: Project, event_id: str) -> Response: # Retrieve information about actionable items (source maps, event errors, etc.) for a given event. diff --git a/src/sentry/issues/endpoints/debug_group_derived_data.py b/src/sentry/issues/endpoints/debug_group_derived_data.py index e618ee19f313..80e688aa5833 100644 --- a/src/sentry/issues/endpoints/debug_group_derived_data.py +++ b/src/sentry/issues/endpoints/debug_group_derived_data.py @@ -25,6 +25,7 @@ def _state_to_dict(pipeline: Pipeline[Any], state: State) -> dict[str, Any]: @cell_silo_endpoint class DebugGroupDerivedDataEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/event_grouping_info.py b/src/sentry/issues/endpoints/event_grouping_info.py index 2921f347e81d..ddcaf5b50c64 100644 --- a/src/sentry/issues/endpoints/event_grouping_info.py +++ b/src/sentry/issues/endpoints/event_grouping_info.py @@ -19,6 +19,7 @@ @cell_silo_endpoint class EventGroupingInfoEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/event_owners.py b/src/sentry/issues/endpoints/event_owners.py index 9e9f3f97cf47..88981526ee72 100644 --- a/src/sentry/issues/endpoints/event_owners.py +++ b/src/sentry/issues/endpoints/event_owners.py @@ -19,6 +19,7 @@ @cell_silo_endpoint class EventOwnersEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_activities.py b/src/sentry/issues/endpoints/group_activities.py index 1dc4e3d649eb..a1fb2fbf2e90 100644 --- a/src/sentry/issues/endpoints/group_activities.py +++ b/src/sentry/issues/endpoints/group_activities.py @@ -19,6 +19,8 @@ @cell_silo_endpoint class GroupActivitiesEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_attachments.py b/src/sentry/issues/endpoints/group_attachments.py index 275610c44aaa..97e5828f94ab 100644 --- a/src/sentry/issues/endpoints/group_attachments.py +++ b/src/sentry/issues/endpoints/group_attachments.py @@ -68,6 +68,8 @@ def get_event_ids_from_filters( @cell_silo_endpoint class GroupAttachmentsEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_current_release.py b/src/sentry/issues/endpoints/group_current_release.py index 101be95c3312..472f9bfeeb42 100644 --- a/src/sentry/issues/endpoints/group_current_release.py +++ b/src/sentry/issues/endpoints/group_current_release.py @@ -22,6 +22,8 @@ @cell_silo_endpoint class GroupCurrentReleaseEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + owner = ApiOwner.COMMUNITY publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_details.py b/src/sentry/issues/endpoints/group_details.py index 95a4192aac0e..5894e605000b 100644 --- a/src/sentry/issues/endpoints/group_details.py +++ b/src/sentry/issues/endpoints/group_details.py @@ -101,6 +101,7 @@ class GroupDetailsPermission(GroupPermission): @cell_silo_endpoint class GroupDetailsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (GroupDetailsPermission,) publish_status = { "DELETE": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_event_details.py b/src/sentry/issues/endpoints/group_event_details.py index 776a9a0ab344..023716b57bc7 100644 --- a/src/sentry/issues/endpoints/group_event_details.py +++ b/src/sentry/issues/endpoints/group_event_details.py @@ -168,6 +168,8 @@ class GroupEventDetailsFormattedResponse(GroupEventDetailsResponse, total=False) @extend_schema(tags=["Events"]) @cell_silo_endpoint class GroupEventDetailsEndpoint(FormattableResponseMixin, GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/group_events.py b/src/sentry/issues/endpoints/group_events.py index 68236aa462e4..8a69a87c576c 100644 --- a/src/sentry/issues/endpoints/group_events.py +++ b/src/sentry/issues/endpoints/group_events.py @@ -68,6 +68,7 @@ class GroupEventsEndpoint(GroupEndpoint): "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.ISSUES + max_acceptable_duration = 10.0 def get_per_page( self, request: Request, default_per_page: int | None = None, max_per_page: int | None = None diff --git a/src/sentry/issues/endpoints/group_first_last_release.py b/src/sentry/issues/endpoints/group_first_last_release.py index 8a5b94d6de7b..f078349988b7 100644 --- a/src/sentry/issues/endpoints/group_first_last_release.py +++ b/src/sentry/issues/endpoints/group_first_last_release.py @@ -14,6 +14,8 @@ @cell_silo_endpoint class GroupFirstLastReleaseEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + owner = ApiOwner.COMMUNITY publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_hashes.py b/src/sentry/issues/endpoints/group_hashes.py index 6491739e3a4a..ec109c91dff3 100644 --- a/src/sentry/issues/endpoints/group_hashes.py +++ b/src/sentry/issues/endpoints/group_hashes.py @@ -50,6 +50,7 @@ class GroupHashesResult(TypedDict): @cell_silo_endpoint class GroupHashesEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "PUT": ApiPublishStatus.PRIVATE, "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_integration_details.py b/src/sentry/issues/endpoints/group_integration_details.py index 8f6a96c15c9a..e3153b7c73b8 100644 --- a/src/sentry/issues/endpoints/group_integration_details.py +++ b/src/sentry/issues/endpoints/group_integration_details.py @@ -137,6 +137,8 @@ def serialize( @extend_schema(tags=["Integration"]) @cell_silo_endpoint class GroupIntegrationDetailsEndpoint(GroupEndpoint): + max_acceptable_duration = 10.0 + owner = ApiOwner.INTEGRATION_PLATFORM publish_status = { "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_integrations.py b/src/sentry/issues/endpoints/group_integrations.py index de41319c3c94..50a74794be43 100644 --- a/src/sentry/issues/endpoints/group_integrations.py +++ b/src/sentry/issues/endpoints/group_integrations.py @@ -108,6 +108,8 @@ def serialize( @extend_schema(tags=["Integration"]) @cell_silo_endpoint class GroupIntegrationsEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + owner = ApiOwner.INTEGRATION_PLATFORM publish_status = { "GET": ApiPublishStatus.PUBLIC_EXPERIMENTAL, diff --git a/src/sentry/issues/endpoints/group_notes.py b/src/sentry/issues/endpoints/group_notes.py index faa455d8e5b6..43ce58e5edf0 100644 --- a/src/sentry/issues/endpoints/group_notes.py +++ b/src/sentry/issues/endpoints/group_notes.py @@ -30,6 +30,8 @@ @cell_silo_endpoint class GroupNotesEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, "POST": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_notes_details.py b/src/sentry/issues/endpoints/group_notes_details.py index 3468b494cf41..d57617f13063 100644 --- a/src/sentry/issues/endpoints/group_notes_details.py +++ b/src/sentry/issues/endpoints/group_notes_details.py @@ -32,6 +32,8 @@ @cell_silo_endpoint class GroupNotesDetailsEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "DELETE": ApiPublishStatus.PRIVATE, "PUT": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_pull_requests.py b/src/sentry/issues/endpoints/group_pull_requests.py index f5997712ebef..0d41d6fd6f8c 100644 --- a/src/sentry/issues/endpoints/group_pull_requests.py +++ b/src/sentry/issues/endpoints/group_pull_requests.py @@ -190,6 +190,7 @@ def _get_pull_request_status( @cell_silo_endpoint class GroupPullRequestsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_reprocessing.py b/src/sentry/issues/endpoints/group_reprocessing.py index 4ae115895ad4..0e30d7e8199e 100644 --- a/src/sentry/issues/endpoints/group_reprocessing.py +++ b/src/sentry/issues/endpoints/group_reprocessing.py @@ -12,6 +12,8 @@ @cell_silo_endpoint class GroupReprocessingEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "POST": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_similar_issues.py b/src/sentry/issues/endpoints/group_similar_issues.py index 667d8fe3fc64..cb73576aac10 100644 --- a/src/sentry/issues/endpoints/group_similar_issues.py +++ b/src/sentry/issues/endpoints/group_similar_issues.py @@ -23,6 +23,8 @@ def _fix_label(label: tuple[str, ...] | str) -> str: @cell_silo_endpoint class GroupSimilarIssuesEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + """ This endpoint uses the legacy MinHash similarity system which has been replaced by embeddings-based grouping for SaaS. diff --git a/src/sentry/issues/endpoints/group_similar_issues_embeddings.py b/src/sentry/issues/endpoints/group_similar_issues_embeddings.py index d345f68bd551..4b8c07a3d58d 100644 --- a/src/sentry/issues/endpoints/group_similar_issues_embeddings.py +++ b/src/sentry/issues/endpoints/group_similar_issues_embeddings.py @@ -44,6 +44,7 @@ class FormattedSimilarIssuesEmbeddingsData(TypedDict): @cell_silo_endpoint class GroupSimilarIssuesEmbeddingsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_stats.py b/src/sentry/issues/endpoints/group_stats.py index c4348cf72605..13e54e5ac917 100644 --- a/src/sentry/issues/endpoints/group_stats.py +++ b/src/sentry/issues/endpoints/group_stats.py @@ -16,6 +16,8 @@ @cell_silo_endpoint class GroupStatsEndpoint(GroupEndpoint, StatsMixin): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_tagkey_details.py b/src/sentry/issues/endpoints/group_tagkey_details.py index 613835f26bd6..d89dbbe6cfd5 100644 --- a/src/sentry/issues/endpoints/group_tagkey_details.py +++ b/src/sentry/issues/endpoints/group_tagkey_details.py @@ -35,6 +35,7 @@ class GroupTagKeyDetailsEndpoint(GroupEndpoint): "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/group_tagkey_values.py b/src/sentry/issues/endpoints/group_tagkey_values.py index 237cc54db262..96e883da713e 100644 --- a/src/sentry/issues/endpoints/group_tagkey_values.py +++ b/src/sentry/issues/endpoints/group_tagkey_values.py @@ -36,6 +36,7 @@ class GroupTagKeyValuesEndpoint(GroupEndpoint): "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/group_tags.py b/src/sentry/issues/endpoints/group_tags.py index 803f95fa0614..93c42345660c 100644 --- a/src/sentry/issues/endpoints/group_tags.py +++ b/src/sentry/issues/endpoints/group_tags.py @@ -25,6 +25,8 @@ @cell_silo_endpoint class GroupTagsEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_tombstone.py b/src/sentry/issues/endpoints/group_tombstone.py index ef411afacae7..8c24cf2a0530 100644 --- a/src/sentry/issues/endpoints/group_tombstone.py +++ b/src/sentry/issues/endpoints/group_tombstone.py @@ -14,6 +14,7 @@ @cell_silo_endpoint class GroupTombstoneEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_tombstone_details.py b/src/sentry/issues/endpoints/group_tombstone_details.py index 07f8f59ef549..5565ce63d408 100644 --- a/src/sentry/issues/endpoints/group_tombstone_details.py +++ b/src/sentry/issues/endpoints/group_tombstone_details.py @@ -15,6 +15,7 @@ @cell_silo_endpoint class GroupTombstoneDetailsEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "DELETE": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_user_reports.py b/src/sentry/issues/endpoints/group_user_reports.py index 7f4a31842356..1b763fb8818e 100644 --- a/src/sentry/issues/endpoints/group_user_reports.py +++ b/src/sentry/issues/endpoints/group_user_reports.py @@ -16,6 +16,8 @@ @cell_silo_endpoint class GroupUserReportsEndpoint(GroupEndpoint): + max_acceptable_duration = 3.0 + publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py b/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py index 5ac8b9ba4997..b8e0503ce6d9 100644 --- a/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py +++ b/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py @@ -111,6 +111,7 @@ def _get_repo_prefixes( @cell_silo_endpoint class OrganizationCodeMappingRepoPrefixesEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = {"GET": ApiPublishStatus.EXPERIMENTAL} permission_classes = (OrganizationIntegrationsLoosePermission,) diff --git a/src/sentry/issues/endpoints/organization_codeowners_associations.py b/src/sentry/issues/endpoints/organization_codeowners_associations.py index af722a7a07ed..32fa14b0904c 100644 --- a/src/sentry/issues/endpoints/organization_codeowners_associations.py +++ b/src/sentry/issues/endpoints/organization_codeowners_associations.py @@ -30,6 +30,7 @@ class CodeOwnersProjectAssociationsResponse(TypedDict): @cell_silo_endpoint class OrganizationCodeOwnersAssociationsEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/organization_derive_code_mappings.py b/src/sentry/issues/endpoints/organization_derive_code_mappings.py index f997c30f4e51..e46417f46f99 100644 --- a/src/sentry/issues/endpoints/organization_derive_code_mappings.py +++ b/src/sentry/issues/endpoints/organization_derive_code_mappings.py @@ -39,6 +39,7 @@ class OrganizationDeriveCodeMappingsEndpoint(OrganizationEndpoint): """ owner = ApiOwner.ISSUES + max_acceptable_duration = 10.0 publish_status = { "GET": ApiPublishStatus.EXPERIMENTAL, "POST": ApiPublishStatus.EXPERIMENTAL, diff --git a/src/sentry/issues/endpoints/organization_eventid.py b/src/sentry/issues/endpoints/organization_eventid.py index a4fedcfe64fa..61f17932a76e 100644 --- a/src/sentry/issues/endpoints/organization_eventid.py +++ b/src/sentry/issues/endpoints/organization_eventid.py @@ -40,6 +40,7 @@ class EventIdLookupResponse(TypedDict): @extend_schema(tags=["Organizations"]) class EventIdLookupEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/organization_group_index.py b/src/sentry/issues/endpoints/organization_group_index.py index 02bfb9c0eb78..671ba561d1ed 100644 --- a/src/sentry/issues/endpoints/organization_group_index.py +++ b/src/sentry/issues/endpoints/organization_group_index.py @@ -273,6 +273,7 @@ class OrganizationGroupIndexEndpoint(OrganizationEndpoint): } idempotent_methods = frozenset() owner = ApiOwner.ISSUES + max_acceptable_duration = {"GET": 3.0, "PUT": 10.0, "DELETE": 10.0} permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True diff --git a/src/sentry/issues/endpoints/organization_group_index_stats.py b/src/sentry/issues/endpoints/organization_group_index_stats.py index 39a2151835c1..64d9d624e313 100644 --- a/src/sentry/issues/endpoints/organization_group_index_stats.py +++ b/src/sentry/issues/endpoints/organization_group_index_stats.py @@ -27,6 +27,7 @@ class OrganizationGroupIndexStatsEndpoint(OrganizationEndpoint): permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 rate_limits = RateLimitConfig( limit_overrides={ diff --git a/src/sentry/issues/endpoints/organization_group_search_view_details.py b/src/sentry/issues/endpoints/organization_group_search_view_details.py index 5bd145226447..76d05ca86b34 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_details.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_details.py @@ -40,6 +40,7 @@ class OrganizationGroupSearchViewDetailsEndpoint(OrganizationEndpoint): "DELETE": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (GroupSearchViewPermission,) def get(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py b/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py index 97bb82139828..f62bdc7c480f 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py @@ -34,6 +34,7 @@ class OrganizationGroupSearchViewDetailsStarredEndpoint(OrganizationEndpoint): "POST": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (MemberPermission,) def post(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py b/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py index 87e57e3858ac..502d5bd412ef 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py @@ -31,6 +31,7 @@ def validate_view_ids(self, view_ids: list[int]) -> list[int]: class OrganizationGroupSearchViewStarredOrderEndpoint(OrganizationEndpoint): publish_status = {"PUT": ApiPublishStatus.EXPERIMENTAL} owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (MemberPermission,) def put(self, request: Request, organization: Organization) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_visit.py b/src/sentry/issues/endpoints/organization_group_search_view_visit.py index 53428443006e..a498bc5bb246 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_visit.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_visit.py @@ -25,6 +25,7 @@ class OrganizationGroupSearchViewVisitEndpoint(OrganizationEndpoint): "POST": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (MemberPermission,) def post(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_views.py b/src/sentry/issues/endpoints/organization_group_search_views.py index 689d3184fd47..1f7e343bddfa 100644 --- a/src/sentry/issues/endpoints/organization_group_search_views.py +++ b/src/sentry/issues/endpoints/organization_group_search_views.py @@ -100,6 +100,7 @@ class OrganizationGroupSearchViewsEndpoint(OrganizationEndpoint): } idempotent_methods = frozenset() owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (MemberPermission,) @extend_schema( diff --git a/src/sentry/issues/endpoints/organization_group_search_views_starred.py b/src/sentry/issues/endpoints/organization_group_search_views_starred.py index f74e3f63dae0..7f9de6b6fdd9 100644 --- a/src/sentry/issues/endpoints/organization_group_search_views_starred.py +++ b/src/sentry/issues/endpoints/organization_group_search_views_starred.py @@ -23,6 +23,7 @@ class OrganizationGroupSearchViewsStarredEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (MemberPermission,) def get(self, request: Request, organization: Organization) -> Response: diff --git a/src/sentry/issues/endpoints/organization_issues_count.py b/src/sentry/issues/endpoints/organization_issues_count.py index d82c1b265ada..ab5020cef9d8 100644 --- a/src/sentry/issues/endpoints/organization_issues_count.py +++ b/src/sentry/issues/endpoints/organization_issues_count.py @@ -35,6 +35,7 @@ class OrganizationIssuesCountEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 enforce_rate_limit = True rate_limits = RateLimitConfig( limit_overrides={ diff --git a/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py b/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py index 1454bf19fc28..f956ca9de38b 100644 --- a/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py +++ b/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py @@ -16,6 +16,7 @@ @cell_silo_endpoint class OrganizationIssuesResolvedInReleaseEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/organization_issues_with_supergroups.py b/src/sentry/issues/endpoints/organization_issues_with_supergroups.py index b598bcc36fee..320012e00366 100644 --- a/src/sentry/issues/endpoints/organization_issues_with_supergroups.py +++ b/src/sentry/issues/endpoints/organization_issues_with_supergroups.py @@ -34,6 +34,7 @@ class OrganizationIssuesWithSupergroupsEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True diff --git a/src/sentry/issues/endpoints/organization_release_previous_commits.py b/src/sentry/issues/endpoints/organization_release_previous_commits.py index 8f64ce34cd40..f5b3a5f39e0b 100644 --- a/src/sentry/issues/endpoints/organization_release_previous_commits.py +++ b/src/sentry/issues/endpoints/organization_release_previous_commits.py @@ -23,6 +23,7 @@ class OrganizationReleasePreviousCommitsEndpoint(OrganizationReleasesBaseEndpoin "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 rate_limits = RateLimitConfig(group="CLI") def get(self, request: Request, organization: Organization, version: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_shortid.py b/src/sentry/issues/endpoints/organization_shortid.py index 7814580c3e2a..38d9f7ff9371 100644 --- a/src/sentry/issues/endpoints/organization_shortid.py +++ b/src/sentry/issues/endpoints/organization_shortid.py @@ -33,6 +33,7 @@ class ShortIdLookupResponse(TypedDict): @cell_silo_endpoint class ShortIdLookupEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/project_codeowners_details.py b/src/sentry/issues/endpoints/project_codeowners_details.py index 515156e83052..fff40f234b91 100644 --- a/src/sentry/issues/endpoints/project_codeowners_details.py +++ b/src/sentry/issues/endpoints/project_codeowners_details.py @@ -56,6 +56,7 @@ @extend_schema(tags=["Projects"]) class ProjectCodeOwnersDetailsEndpoint(ProjectCodeOwnersBase): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "DELETE": ApiPublishStatus.PUBLIC, "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/project_codeowners_index.py b/src/sentry/issues/endpoints/project_codeowners_index.py index f5d73b6a3045..bf459d885a63 100644 --- a/src/sentry/issues/endpoints/project_codeowners_index.py +++ b/src/sentry/issues/endpoints/project_codeowners_index.py @@ -39,6 +39,7 @@ @extend_schema(tags=["Projects"]) class ProjectCodeOwnersEndpoint(ProjectCodeOwnersBase): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, "POST": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/project_event_details.py b/src/sentry/issues/endpoints/project_event_details.py index 369bb633b78e..8d85d0a7287f 100644 --- a/src/sentry/issues/endpoints/project_event_details.py +++ b/src/sentry/issues/endpoints/project_event_details.py @@ -113,6 +113,7 @@ def wrap_event_response( @cell_silo_endpoint class ProjectEventDetailsEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, } @@ -211,6 +212,7 @@ def get( @cell_silo_endpoint class EventJsonEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.EXPERIMENTAL, } diff --git a/src/sentry/issues/endpoints/project_events.py b/src/sentry/issues/endpoints/project_events.py index bd7d02b0eee1..30fed1e60b33 100644 --- a/src/sentry/issues/endpoints/project_events.py +++ b/src/sentry/issues/endpoints/project_events.py @@ -33,6 +33,7 @@ @cell_silo_endpoint class ProjectEventsEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 10.0 publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/project_group_index.py b/src/sentry/issues/endpoints/project_group_index.py index 794a0f565727..aedc7706c2dc 100644 --- a/src/sentry/issues/endpoints/project_group_index.py +++ b/src/sentry/issues/endpoints/project_group_index.py @@ -50,6 +50,7 @@ @cell_silo_endpoint class ProjectGroupIndexEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "DELETE": ApiPublishStatus.EXPERIMENTAL, "GET": ApiPublishStatus.EXPERIMENTAL, diff --git a/src/sentry/issues/endpoints/project_group_stats.py b/src/sentry/issues/endpoints/project_group_stats.py index 91b6e683d317..803107f6e8ec 100644 --- a/src/sentry/issues/endpoints/project_group_stats.py +++ b/src/sentry/issues/endpoints/project_group_stats.py @@ -19,6 +19,7 @@ @cell_silo_endpoint class ProjectGroupStatsEndpoint(ProjectEndpoint, StatsMixin): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/project_issues_resolved_in_release.py b/src/sentry/issues/endpoints/project_issues_resolved_in_release.py index b74f8b33bc77..d986973c1497 100644 --- a/src/sentry/issues/endpoints/project_issues_resolved_in_release.py +++ b/src/sentry/issues/endpoints/project_issues_resolved_in_release.py @@ -16,6 +16,7 @@ @cell_silo_endpoint class ProjectIssuesResolvedInReleaseEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.EXPERIMENTAL, } diff --git a/src/sentry/issues/endpoints/project_ownership.py b/src/sentry/issues/endpoints/project_ownership.py index a94508aa06c5..838f280030cc 100644 --- a/src/sentry/issues/endpoints/project_ownership.py +++ b/src/sentry/issues/endpoints/project_ownership.py @@ -237,6 +237,7 @@ def __modify_auto_assignment(self, ownership: ProjectOwnership) -> bool: @extend_schema(tags=["Projects"]) class ProjectOwnershipEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = {"GET": 3.0, "PUT": 10.0} publish_status = { "GET": ApiPublishStatus.PUBLIC, "PUT": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/project_stacktrace_link.py b/src/sentry/issues/endpoints/project_stacktrace_link.py index 78ba1c6baf6e..201066cf5f11 100644 --- a/src/sentry/issues/endpoints/project_stacktrace_link.py +++ b/src/sentry/issues/endpoints/project_stacktrace_link.py @@ -183,6 +183,7 @@ class ProjectStacktraceLinkEndpoint(ProjectEndpoint): """ owner = ApiOwner.ISSUES + max_acceptable_duration = 10.0 @extend_schema( operation_id="getProjectStacktraceLink", diff --git a/src/sentry/issues/endpoints/project_stacktrace_source_context.py b/src/sentry/issues/endpoints/project_stacktrace_source_context.py index 395cb9d4d43c..5a38d04160f2 100644 --- a/src/sentry/issues/endpoints/project_stacktrace_source_context.py +++ b/src/sentry/issues/endpoints/project_stacktrace_source_context.py @@ -37,6 +37,7 @@ class ProjectStacktraceSourceContextEndpoint(ProjectEndpoint): """ owner = ApiOwner.ISSUES + max_acceptable_duration = 10.0 def get(self, request: Request, project: Project) -> Response: if not project.get_option("sentry:scm_source_context_enabled", False): diff --git a/src/sentry/issues/endpoints/related_issues.py b/src/sentry/issues/endpoints/related_issues.py index bd85aa27e0f2..596ed3c757da 100644 --- a/src/sentry/issues/endpoints/related_issues.py +++ b/src/sentry/issues/endpoints/related_issues.py @@ -26,6 +26,7 @@ class RequestSerializer(serializers.Serializer[None]): @cell_silo_endpoint class RelatedIssuesEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = {"GET": ApiPublishStatus.EXPERIMENTAL} enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/shared_group_details.py b/src/sentry/issues/endpoints/shared_group_details.py index 0fd6699d5561..f425292cbc73 100644 --- a/src/sentry/issues/endpoints/shared_group_details.py +++ b/src/sentry/issues/endpoints/shared_group_details.py @@ -15,6 +15,7 @@ @cell_silo_endpoint class SharedGroupDetailsEndpoint(Endpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_all_unresolved_issues.py b/src/sentry/issues/endpoints/team_all_unresolved_issues.py index 18f8af8eebb4..f2800c91899b 100644 --- a/src/sentry/issues/endpoints/team_all_unresolved_issues.py +++ b/src/sentry/issues/endpoints/team_all_unresolved_issues.py @@ -166,6 +166,7 @@ def calculate_unresolved_counts( @cell_silo_endpoint class TeamAllUnresolvedIssuesEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_groups_old.py b/src/sentry/issues/endpoints/team_groups_old.py index 68f02e5ecd08..cb8d8dc19be0 100644 --- a/src/sentry/issues/endpoints/team_groups_old.py +++ b/src/sentry/issues/endpoints/team_groups_old.py @@ -17,6 +17,7 @@ @cell_silo_endpoint class TeamGroupsOldEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_issue_breakdown.py b/src/sentry/issues/endpoints/team_issue_breakdown.py index 9041618c51df..f4a1fb17b387 100644 --- a/src/sentry/issues/endpoints/team_issue_breakdown.py +++ b/src/sentry/issues/endpoints/team_issue_breakdown.py @@ -29,6 +29,7 @@ @cell_silo_endpoint class TeamIssueBreakdownEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/middleware/stats.py b/src/sentry/middleware/stats.py index d3f827aa8493..24b3c55c3994 100644 --- a/src/sentry/middleware/stats.py +++ b/src/sentry/middleware/stats.py @@ -44,6 +44,14 @@ def process_view( if path and path.startswith(self.allowed_paths): setattr(request, "_view_path", path) setattr(request, "_start_time", time.monotonic()) + + view_class = getattr(view_func, "view_class", None) + max_acceptable_duration = getattr(view_class, "max_acceptable_duration", None) + if isinstance(max_acceptable_duration, dict): + max_acceptable_duration = max_acceptable_duration.get(request.method) + + setattr(request, "_max_acceptable_duration", max_acceptable_duration) + setattr(request, "_view_owner", getattr(view_class, "owner", None)) return None def process_response(self, request: Request, response: Response) -> Response: @@ -83,7 +91,8 @@ def _record_time(request: Request, status_code: int) -> None: if start_time is None: return - ms = int((time.monotonic() - start_time) * 1000) + duration = time.monotonic() - start_time + ms = int(duration * 1000) metrics.distribution( "view.duration", ms, @@ -91,3 +100,18 @@ def _record_time(request: Request, status_code: int) -> None: tags={"method": request.method}, unit="millisecond", ) + + max_acceptable_duration = getattr(request, "_max_acceptable_duration", None) + if max_acceptable_duration is not None and duration > max_acceptable_duration: + owner = getattr(request, "_view_owner", None) + metrics.distribution( + "view.duration_over_budget", + (duration - max_acceptable_duration) * 1000, + instance=view_path, + tags={ + "method": request.method, + "owner": getattr(owner, "value", owner), + }, + sample_rate=1.0, + unit="millisecond", + ) diff --git a/tests/sentry/middleware/test_stats.py b/tests/sentry/middleware/test_stats.py index 493ab16b7445..35b7530c0b42 100644 --- a/tests/sentry/middleware/test_stats.py +++ b/tests/sentry/middleware/test_stats.py @@ -1,11 +1,12 @@ from functools import cached_property from typing import Any -from unittest.mock import MagicMock, Mock, patch, sentinel +from unittest.mock import MagicMock, Mock, call, patch, sentinel from django.test import RequestFactory, override_settings from django.urls.base import resolve from rest_framework.permissions import AllowAny +from sentry.api.api_owners import ApiOwner from sentry.api.base import Endpoint from sentry.middleware.ratelimit import RatelimitMiddleware from sentry.middleware.stats import RequestTimingMiddleware @@ -26,6 +27,15 @@ def get(self) -> None: raise NotImplementedError +class BudgetedEndpoint(Endpoint): + owner = ApiOwner.ISSUES + max_acceptable_duration = 3.0 + + +class MethodBudgetedEndpoint(Endpoint): + max_acceptable_duration = {"GET": 3.0, "POST": 5.0} + + class RequestTimingMiddlewareTest(TestCase): middleware = cached_property(RequestTimingMiddleware) @@ -105,3 +115,73 @@ def test_records_ui_request(self, incr: MagicMock) -> None: }, skip_internal=False, ) + + @patch("sentry.utils.metrics.distribution") + @patch("sentry.middleware.stats.time.monotonic", side_effect=[10.0, 14.0]) + def test_records_duration_over_budget( + self, mock_time: MagicMock, distribution: MagicMock + ) -> None: + view = BudgetedEndpoint.as_view() + request = self.factory.get("/") + request.resolver_match = resolve("/") + response = Mock(status_code=500) + + with patch.object(RequestTimingMiddleware, "allowed_paths", (view.__module__,)): + self.middleware.process_view(request, view, [], {}) + self.middleware.process_response(request, response) + + assert mock_time.call_count == 2 + distribution.assert_has_calls( + [ + call( + "view.duration", + 4000, + instance=request._view_path, + tags={"method": "GET"}, + unit="millisecond", + ), + call( + "view.duration_over_budget", + 1000.0, + instance=request._view_path, + tags={"method": "GET", "owner": "issues-feed"}, + sample_rate=1.0, + unit="millisecond", + ), + ] + ) + + @patch("sentry.utils.metrics.distribution") + @patch("sentry.middleware.stats.time.monotonic", side_effect=[10.0, 13.0]) + def test_does_not_record_duration_at_budget( + self, mock_time: MagicMock, distribution: MagicMock + ) -> None: + view = BudgetedEndpoint.as_view() + request = self.factory.get("/") + request.resolver_match = resolve("/") + response = Mock(status_code=200) + + with patch.object(RequestTimingMiddleware, "allowed_paths", (view.__module__,)): + self.middleware.process_view(request, view, [], {}) + self.middleware.process_response(request, response) + + assert mock_time.call_count == 2 + distribution.assert_called_once_with( + "view.duration", + 3000, + instance=request._view_path, + tags={"method": "GET"}, + unit="millisecond", + ) + + def test_resolves_method_specific_duration_budget(self) -> None: + view = MethodBudgetedEndpoint.as_view() + get_request = self.factory.get("/") + delete_request = self.factory.delete("/") + + with patch.object(RequestTimingMiddleware, "allowed_paths", (view.__module__,)): + self.middleware.process_view(get_request, view, [], {}) + self.middleware.process_view(delete_request, view, [], {}) + + assert get_request._max_acceptable_duration == 3.0 + assert delete_request._max_acceptable_duration is None From 15c3ec8b4fae1da74a6b682975d76cba262167b6 Mon Sep 17 00:00:00 2001 From: Kyle Consalus Date: Wed, 28 Oct 2026 15:09:53 -0700 Subject: [PATCH 2/3] ui request --- src/sentry/middleware/stats.py | 1 + tests/sentry/middleware/test_stats.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry/middleware/stats.py b/src/sentry/middleware/stats.py index 24b3c55c3994..03ddd08cacf9 100644 --- a/src/sentry/middleware/stats.py +++ b/src/sentry/middleware/stats.py @@ -111,6 +111,7 @@ def _record_time(request: Request, status_code: int) -> None: tags={ "method": request.method, "owner": getattr(owner, "value", owner), + "ui_request": tags["ui_request"], }, sample_rate=1.0, unit="millisecond", diff --git a/tests/sentry/middleware/test_stats.py b/tests/sentry/middleware/test_stats.py index 35b7530c0b42..32b925c2f9b0 100644 --- a/tests/sentry/middleware/test_stats.py +++ b/tests/sentry/middleware/test_stats.py @@ -144,7 +144,7 @@ def test_records_duration_over_budget( "view.duration_over_budget", 1000.0, instance=request._view_path, - tags={"method": "GET", "owner": "issues-feed"}, + tags={"method": "GET", "owner": "issues-feed", "ui_request": False}, sample_rate=1.0, unit="millisecond", ), From 7db067f17b4ae738698628e59b66b82e99d864b0 Mon Sep 17 00:00:00 2001 From: Kyle Consalus Date: Fri, 30 Oct 2026 16:52:12 -0700 Subject: [PATCH 3/3] var with default --- src/sentry/api/base.py | 8 +++++++- src/sentry/issues/endpoints/actionable_items.py | 4 ++-- src/sentry/issues/endpoints/debug_group_derived_data.py | 4 ++-- src/sentry/issues/endpoints/event_grouping_info.py | 4 ++-- src/sentry/issues/endpoints/event_owners.py | 4 ++-- src/sentry/issues/endpoints/group_activities.py | 4 ++-- src/sentry/issues/endpoints/group_attachments.py | 4 ++-- src/sentry/issues/endpoints/group_current_release.py | 4 ++-- src/sentry/issues/endpoints/group_details.py | 4 ++-- src/sentry/issues/endpoints/group_event_details.py | 4 ++-- src/sentry/issues/endpoints/group_first_last_release.py | 4 ++-- src/sentry/issues/endpoints/group_hashes.py | 4 ++-- src/sentry/issues/endpoints/group_integrations.py | 4 ++-- src/sentry/issues/endpoints/group_notes.py | 4 ++-- src/sentry/issues/endpoints/group_notes_details.py | 4 ++-- src/sentry/issues/endpoints/group_pull_requests.py | 4 ++-- src/sentry/issues/endpoints/group_reprocessing.py | 4 ++-- src/sentry/issues/endpoints/group_similar_issues.py | 4 ++-- .../issues/endpoints/group_similar_issues_embeddings.py | 4 ++-- src/sentry/issues/endpoints/group_stats.py | 4 ++-- src/sentry/issues/endpoints/group_tagkey_details.py | 4 ++-- src/sentry/issues/endpoints/group_tagkey_values.py | 4 ++-- src/sentry/issues/endpoints/group_tags.py | 4 ++-- src/sentry/issues/endpoints/group_tombstone.py | 4 ++-- src/sentry/issues/endpoints/group_tombstone_details.py | 4 ++-- src/sentry/issues/endpoints/group_user_reports.py | 4 ++-- .../endpoints/organization_code_mapping_repo_prefixes.py | 4 ++-- .../endpoints/organization_codeowners_associations.py | 4 ++-- src/sentry/issues/endpoints/organization_eventid.py | 4 ++-- src/sentry/issues/endpoints/organization_group_index.py | 4 ++-- .../issues/endpoints/organization_group_index_stats.py | 4 ++-- .../endpoints/organization_group_search_view_details.py | 4 ++-- .../organization_group_search_view_details_starred.py | 4 ++-- .../organization_group_search_view_starred_order.py | 4 ++-- .../endpoints/organization_group_search_view_visit.py | 4 ++-- .../issues/endpoints/organization_group_search_views.py | 4 ++-- .../endpoints/organization_group_search_views_starred.py | 4 ++-- src/sentry/issues/endpoints/organization_issues_count.py | 4 ++-- .../endpoints/organization_issues_resolved_in_release.py | 4 ++-- .../endpoints/organization_issues_with_supergroups.py | 4 ++-- .../endpoints/organization_release_previous_commits.py | 4 ++-- src/sentry/issues/endpoints/organization_shortid.py | 4 ++-- src/sentry/issues/endpoints/project_codeowners_details.py | 4 ++-- src/sentry/issues/endpoints/project_codeowners_index.py | 4 ++-- src/sentry/issues/endpoints/project_event_details.py | 6 +++--- src/sentry/issues/endpoints/project_group_index.py | 4 ++-- src/sentry/issues/endpoints/project_group_stats.py | 4 ++-- .../endpoints/project_issues_resolved_in_release.py | 4 ++-- src/sentry/issues/endpoints/project_ownership.py | 4 ++-- src/sentry/issues/endpoints/related_issues.py | 4 ++-- src/sentry/issues/endpoints/shared_group_details.py | 4 ++-- src/sentry/issues/endpoints/team_all_unresolved_issues.py | 4 ++-- src/sentry/issues/endpoints/team_groups_old.py | 4 ++-- src/sentry/issues/endpoints/team_issue_breakdown.py | 4 ++-- 54 files changed, 114 insertions(+), 108 deletions(-) diff --git a/src/sentry/api/base.py b/src/sentry/api/base.py index e7ecef62ad68..cc323b01c590 100644 --- a/src/sentry/api/base.py +++ b/src/sentry/api/base.py @@ -112,6 +112,12 @@ ONE_HOUR = ONE_MINUTE * 60 ONE_DAY = ONE_HOUR * 24 +# A reasonable `Endpoint.max_acceptable_duration` (in seconds) for endpoints with no known reason +# to be slow. Endpoints have no budget unless they set one; use this when opting in without a +# more specific value. This value may change, so use an explicit number when the budget was +# chosen for that endpoint. +DEFAULT_DURATION_BUDGET = 3.0 + CURSOR_LINK_HEADER = ( '<{uri}&cursor={cursor}>; rel="{name}"; results="{has_results}"; cursor="{cursor}"' ) @@ -285,7 +291,7 @@ class Endpoint(APIView): owner: ApiOwner = ApiOwner.UNOWNED # Request duration budgets are expressed in seconds. A mapping applies budgets only to the - # listed HTTP methods; missing methods are not budgeted. + # listed HTTP methods; missing methods are not budgeted. See DEFAULT_DURATION_BUDGET. max_acceptable_duration: dict[HTTP_METHOD_NAME, float] | float | None = None publish_status: dict[HTTP_METHOD_NAME, ApiPublishStatus] = {} # Non-safe methods that a client may resend after a timeout or 5xx without diff --git a/src/sentry/issues/endpoints/actionable_items.py b/src/sentry/issues/endpoints/actionable_items.py index e76e78499934..e67e43836851 100644 --- a/src/sentry/issues/endpoints/actionable_items.py +++ b/src/sentry/issues/endpoints/actionable_items.py @@ -5,7 +5,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.helpers.actionable_items_helper import ( ActionPriority, @@ -31,7 +31,7 @@ class ActionableItemsEndpoint(ProjectEndpoint): "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET def get(self, request: Request, project: Project, event_id: str) -> Response: # Retrieve information about actionable items (source maps, event errors, etc.) for a given event. diff --git a/src/sentry/issues/endpoints/debug_group_derived_data.py b/src/sentry/issues/endpoints/debug_group_derived_data.py index 80e688aa5833..4b7c25c1d3a1 100644 --- a/src/sentry/issues/endpoints/debug_group_derived_data.py +++ b/src/sentry/issues/endpoints/debug_group_derived_data.py @@ -5,7 +5,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.issues.derived.framework import Pipeline, State from sentry.issues.derived.processing import PIPELINE from sentry.issues.derived.store import GroupDerivedDataStore @@ -25,7 +25,7 @@ def _state_to_dict(pipeline: Pipeline[Any], state: State) -> dict[str, Any]: @cell_silo_endpoint class DebugGroupDerivedDataEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/event_grouping_info.py b/src/sentry/issues/endpoints/event_grouping_info.py index ddcaf5b50c64..90ad32522f81 100644 --- a/src/sentry/issues/endpoints/event_grouping_info.py +++ b/src/sentry/issues/endpoints/event_grouping_info.py @@ -4,7 +4,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.grouping.api import load_grouping_config @@ -19,7 +19,7 @@ @cell_silo_endpoint class EventGroupingInfoEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/event_owners.py b/src/sentry/issues/endpoints/event_owners.py index 88981526ee72..7ef2cc9d3ac5 100644 --- a/src/sentry/issues/endpoints/event_owners.py +++ b/src/sentry/issues/endpoints/event_owners.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.serializers import serialize from sentry.api.serializers.models.actor import ActorSerializer @@ -19,7 +19,7 @@ @cell_silo_endpoint class EventOwnersEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_activities.py b/src/sentry/issues/endpoints/group_activities.py index a1fb2fbf2e90..90a64e4b01de 100644 --- a/src/sentry/issues/endpoints/group_activities.py +++ b/src/sentry/issues/endpoints/group_activities.py @@ -4,7 +4,7 @@ from rest_framework.response import Response from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.serializers import serialize from sentry.api.serializers.models.groupactionlogentry import get_serialized_activity_items @@ -19,7 +19,7 @@ @cell_silo_endpoint class GroupActivitiesEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_attachments.py b/src/sentry/issues/endpoints/group_attachments.py index 97e5828f94ab..cfd41afdc3f6 100644 --- a/src/sentry/issues/endpoints/group_attachments.py +++ b/src/sentry/issues/endpoints/group_attachments.py @@ -6,7 +6,7 @@ from sentry import features from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environments @@ -68,7 +68,7 @@ def get_event_ids_from_filters( @cell_silo_endpoint class GroupAttachmentsEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_current_release.py b/src/sentry/issues/endpoints/group_current_release.py index 472f9bfeeb42..be70500d2805 100644 --- a/src/sentry/issues/endpoints/group_current_release.py +++ b/src/sentry/issues/endpoints/group_current_release.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environments from sentry.api.serializers import serialize @@ -22,7 +22,7 @@ @cell_silo_endpoint class GroupCurrentReleaseEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET owner = ApiOwner.COMMUNITY publish_status = { diff --git a/src/sentry/issues/endpoints/group_details.py b/src/sentry/issues/endpoints/group_details.py index 5894e605000b..3457fbd5716c 100644 --- a/src/sentry/issues/endpoints/group_details.py +++ b/src/sentry/issues/endpoints/group_details.py @@ -14,7 +14,7 @@ from sentry.analytics.events.issue_viewed import IssueViewedEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.client import client from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environment_func, get_environments @@ -101,7 +101,7 @@ class GroupDetailsPermission(GroupPermission): @cell_silo_endpoint class GroupDetailsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (GroupDetailsPermission,) publish_status = { "DELETE": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_event_details.py b/src/sentry/issues/endpoints/group_event_details.py index 023716b57bc7..714a4c095aed 100644 --- a/src/sentry/issues/endpoints/group_event_details.py +++ b/src/sentry/issues/endpoints/group_event_details.py @@ -17,7 +17,7 @@ from sentry import features from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.event_search import ( ParenExpression, SearchBoolean, @@ -168,7 +168,7 @@ class GroupEventDetailsFormattedResponse(GroupEventDetailsResponse, total=False) @extend_schema(tags=["Events"]) @cell_silo_endpoint class GroupEventDetailsEndpoint(FormattableResponseMixin, GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_first_last_release.py b/src/sentry/issues/endpoints/group_first_last_release.py index f078349988b7..44a34c761bac 100644 --- a/src/sentry/issues/endpoints/group_first_last_release.py +++ b/src/sentry/issues/endpoints/group_first_last_release.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.environments import get_environments from sentry.api.helpers.group_index import get_first_last_release from sentry.issues.endpoints.bases.group import GroupEndpoint @@ -14,7 +14,7 @@ @cell_silo_endpoint class GroupFirstLastReleaseEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET owner = ApiOwner.COMMUNITY publish_status = { diff --git a/src/sentry/issues/endpoints/group_hashes.py b/src/sentry/issues/endpoints/group_hashes.py index ec109c91dff3..2676c3de9aad 100644 --- a/src/sentry/issues/endpoints/group_hashes.py +++ b/src/sentry/issues/endpoints/group_hashes.py @@ -10,7 +10,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.paginator import GenericOffsetPaginator from sentry.api.serializers import EventSerializer, SimpleEventSerializer, serialize @@ -50,7 +50,7 @@ class GroupHashesResult(TypedDict): @cell_silo_endpoint class GroupHashesEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "PUT": ApiPublishStatus.PRIVATE, "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/group_integrations.py b/src/sentry/issues/endpoints/group_integrations.py index 50a74794be43..4ac2330fef4b 100644 --- a/src/sentry/issues/endpoints/group_integrations.py +++ b/src/sentry/issues/endpoints/group_integrations.py @@ -12,7 +12,7 @@ from sentry import features, options from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.serializers import Serializer, serialize from sentry.apidocs.parameters import CursorQueryParam, GlobalParams, IssueParams @@ -108,7 +108,7 @@ def serialize( @extend_schema(tags=["Integration"]) @cell_silo_endpoint class GroupIntegrationsEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET owner = ApiOwner.INTEGRATION_PLATFORM publish_status = { diff --git a/src/sentry/issues/endpoints/group_notes.py b/src/sentry/issues/endpoints/group_notes.py index 43ce58e5edf0..bdc770f35736 100644 --- a/src/sentry/issues/endpoints/group_notes.py +++ b/src/sentry/issues/endpoints/group_notes.py @@ -5,7 +5,7 @@ from rest_framework.response import Response from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.paginator import DateTimePaginator from sentry.api.serializers.models.activity import ( @@ -30,7 +30,7 @@ @cell_silo_endpoint class GroupNotesEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_notes_details.py b/src/sentry/issues/endpoints/group_notes_details.py index d57617f13063..c98d6b9f3c6a 100644 --- a/src/sentry/issues/endpoints/group_notes_details.py +++ b/src/sentry/issues/endpoints/group_notes_details.py @@ -5,7 +5,7 @@ from rest_framework.response import Response from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.deprecation import deprecated from sentry.api.serializers.models.activity import ( @@ -32,7 +32,7 @@ @cell_silo_endpoint class GroupNotesDetailsEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "DELETE": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_pull_requests.py b/src/sentry/issues/endpoints/group_pull_requests.py index 0d41d6fd6f8c..f62eb3e6ba9f 100644 --- a/src/sentry/issues/endpoints/group_pull_requests.py +++ b/src/sentry/issues/endpoints/group_pull_requests.py @@ -12,7 +12,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.serializers import serialize from sentry.api.serializers.models.pullrequest import ( LinkedPullRequestResponse, @@ -190,7 +190,7 @@ def _get_pull_request_status( @cell_silo_endpoint class GroupPullRequestsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_reprocessing.py b/src/sentry/issues/endpoints/group_reprocessing.py index 0e30d7e8199e..ec982db6fe64 100644 --- a/src/sentry/issues/endpoints/group_reprocessing.py +++ b/src/sentry/issues/endpoints/group_reprocessing.py @@ -2,7 +2,7 @@ from rest_framework.response import Response from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.constants import CELL_API_DEPRECATION_DATE from sentry.issues.endpoints.bases.group import GroupEndpoint @@ -12,7 +12,7 @@ @cell_silo_endpoint class GroupReprocessingEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "POST": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_similar_issues.py b/src/sentry/issues/endpoints/group_similar_issues.py index cb73576aac10..ab0786825594 100644 --- a/src/sentry/issues/endpoints/group_similar_issues.py +++ b/src/sentry/issues/endpoints/group_similar_issues.py @@ -5,7 +5,7 @@ from sentry import similarity from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.serializers import serialize from sentry.constants import CELL_API_DEPRECATION_DATE @@ -23,7 +23,7 @@ def _fix_label(label: tuple[str, ...] | str) -> str: @cell_silo_endpoint class GroupSimilarIssuesEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET """ This endpoint uses the legacy MinHash similarity system which has been replaced diff --git a/src/sentry/issues/endpoints/group_similar_issues_embeddings.py b/src/sentry/issues/endpoints/group_similar_issues_embeddings.py index 4b8c07a3d58d..94b8d778746b 100644 --- a/src/sentry/issues/endpoints/group_similar_issues_embeddings.py +++ b/src/sentry/issues/endpoints/group_similar_issues_embeddings.py @@ -10,7 +10,7 @@ from sentry.api.analytics import GroupSimilarIssuesEmbeddingsCountEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.serializers import serialize from sentry.auth.principal import acting_user_id @@ -44,7 +44,7 @@ class FormattedSimilarIssuesEmbeddingsData(TypedDict): @cell_silo_endpoint class GroupSimilarIssuesEmbeddingsEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_stats.py b/src/sentry/issues/endpoints/group_stats.py index 13e54e5ac917..411ee9afc589 100644 --- a/src/sentry/issues/endpoints/group_stats.py +++ b/src/sentry/issues/endpoints/group_stats.py @@ -3,7 +3,7 @@ from sentry import tsdb from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import StatsMixin, cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, StatsMixin, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environment_id @@ -16,7 +16,7 @@ @cell_silo_endpoint class GroupStatsEndpoint(GroupEndpoint, StatsMixin): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_tagkey_details.py b/src/sentry/issues/endpoints/group_tagkey_details.py index d89dbbe6cfd5..c9e9fd720291 100644 --- a/src/sentry/issues/endpoints/group_tagkey_details.py +++ b/src/sentry/issues/endpoints/group_tagkey_details.py @@ -5,7 +5,7 @@ from sentry import tagstore from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environment_id @@ -35,7 +35,7 @@ class GroupTagKeyDetailsEndpoint(GroupEndpoint): "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/group_tagkey_values.py b/src/sentry/issues/endpoints/group_tagkey_values.py index 96e883da713e..60497c542a3f 100644 --- a/src/sentry/issues/endpoints/group_tagkey_values.py +++ b/src/sentry/issues/endpoints/group_tagkey_values.py @@ -6,7 +6,7 @@ from sentry.analytics.events.eventuser_endpoint_request import EventUserEndpointRequest from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environments @@ -36,7 +36,7 @@ class GroupTagKeyValuesEndpoint(GroupEndpoint): "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/group_tags.py b/src/sentry/issues/endpoints/group_tags.py index 93c42345660c..d95ebf96b51e 100644 --- a/src/sentry/issues/endpoints/group_tags.py +++ b/src/sentry/issues/endpoints/group_tags.py @@ -7,7 +7,7 @@ from sentry import tagstore from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environments from sentry.api.helpers.mobile import get_readable_device_name @@ -25,7 +25,7 @@ @cell_silo_endpoint class GroupTagsEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/group_tombstone.py b/src/sentry/issues/endpoints/group_tombstone.py index 8c24cf2a0530..8803c765385c 100644 --- a/src/sentry/issues/endpoints/group_tombstone.py +++ b/src/sentry/issues/endpoints/group_tombstone.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.paginator import OffsetPaginator from sentry.api.serializers import serialize @@ -14,7 +14,7 @@ @cell_silo_endpoint class GroupTombstoneEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_tombstone_details.py b/src/sentry/issues/endpoints/group_tombstone_details.py index 5565ce63d408..64f9e57ead07 100644 --- a/src/sentry/issues/endpoints/group_tombstone_details.py +++ b/src/sentry/issues/endpoints/group_tombstone_details.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases import ProjectEndpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.utils import to_valid_int_id @@ -15,7 +15,7 @@ @cell_silo_endpoint class GroupTombstoneDetailsEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "DELETE": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/group_user_reports.py b/src/sentry/issues/endpoints/group_user_reports.py index 1b763fb8818e..8d5ac3fa5a61 100644 --- a/src/sentry/issues/endpoints/group_user_reports.py +++ b/src/sentry/issues/endpoints/group_user_reports.py @@ -2,7 +2,7 @@ from rest_framework.response import Response from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.environments import get_environment from sentry.api.paginator import DateTimePaginator @@ -16,7 +16,7 @@ @cell_silo_endpoint class GroupUserReportsEndpoint(GroupEndpoint): - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, diff --git a/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py b/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py index b8e0503ce6d9..756c6bff9a94 100644 --- a/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py +++ b/src/sentry/issues/endpoints/organization_code_mapping_repo_prefixes.py @@ -9,7 +9,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import ( OrganizationEndpoint, OrganizationIntegrationsLoosePermission, @@ -111,7 +111,7 @@ def _get_repo_prefixes( @cell_silo_endpoint class OrganizationCodeMappingRepoPrefixesEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = {"GET": ApiPublishStatus.EXPERIMENTAL} permission_classes = (OrganizationIntegrationsLoosePermission,) diff --git a/src/sentry/issues/endpoints/organization_codeowners_associations.py b/src/sentry/issues/endpoints/organization_codeowners_associations.py index 32fa14b0904c..584688d1f23a 100644 --- a/src/sentry/issues/endpoints/organization_codeowners_associations.py +++ b/src/sentry/issues/endpoints/organization_codeowners_associations.py @@ -7,7 +7,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import ( OrganizationEndpoint, OrganizationIntegrationsLoosePermission, @@ -30,7 +30,7 @@ class CodeOwnersProjectAssociationsResponse(TypedDict): @cell_silo_endpoint class OrganizationCodeOwnersAssociationsEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/organization_eventid.py b/src/sentry/issues/endpoints/organization_eventid.py index 61f17932a76e..da145a5da09d 100644 --- a/src/sentry/issues/endpoints/organization_eventid.py +++ b/src/sentry/issues/endpoints/organization_eventid.py @@ -9,7 +9,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.serializers import serialize @@ -40,7 +40,7 @@ class EventIdLookupResponse(TypedDict): @extend_schema(tags=["Organizations"]) class EventIdLookupEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/organization_group_index.py b/src/sentry/issues/endpoints/organization_group_index.py index 671ba561d1ed..c6c1c9b632e0 100644 --- a/src/sentry/issues/endpoints/organization_group_index.py +++ b/src/sentry/issues/endpoints/organization_group_index.py @@ -16,7 +16,7 @@ from sentry.analytics.events.issue_search_endpoint_queried import IssueSearchEndpointQueriedEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases import OrganizationEventPermission from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.event_search import SearchFilter @@ -273,7 +273,7 @@ class OrganizationGroupIndexEndpoint(OrganizationEndpoint): } idempotent_methods = frozenset() owner = ApiOwner.ISSUES - max_acceptable_duration = {"GET": 3.0, "PUT": 10.0, "DELETE": 10.0} + max_acceptable_duration = {"GET": DEFAULT_DURATION_BUDGET, "PUT": 10.0, "DELETE": 10.0} permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True diff --git a/src/sentry/issues/endpoints/organization_group_index_stats.py b/src/sentry/issues/endpoints/organization_group_index_stats.py index 64d9d624e313..fb407c9c5190 100644 --- a/src/sentry/issues/endpoints/organization_group_index_stats.py +++ b/src/sentry/issues/endpoints/organization_group_index_stats.py @@ -4,7 +4,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases import OrganizationEventPermission from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.helpers.group_index import build_query_params_from_request, calculate_stats_period @@ -27,7 +27,7 @@ class OrganizationGroupIndexStatsEndpoint(OrganizationEndpoint): permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET rate_limits = RateLimitConfig( limit_overrides={ diff --git a/src/sentry/issues/endpoints/organization_group_search_view_details.py b/src/sentry/issues/endpoints/organization_group_search_view_details.py index 76d05ca86b34..4c4fe54c0a9c 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_details.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_details.py @@ -7,7 +7,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.serializers.base import serialize from sentry.api.serializers.models.groupsearchview import GroupSearchViewSerializer @@ -40,7 +40,7 @@ class OrganizationGroupSearchViewDetailsEndpoint(OrganizationEndpoint): "DELETE": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (GroupSearchViewPermission,) def get(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py b/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py index f62bdc7c480f..53b6689d26ed 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_details_starred.py @@ -4,7 +4,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.api.utils import to_valid_int_id from sentry.models.groupsearchview import GroupSearchView, GroupSearchViewVisibility @@ -34,7 +34,7 @@ class OrganizationGroupSearchViewDetailsStarredEndpoint(OrganizationEndpoint): "POST": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (MemberPermission,) def post(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py b/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py index 502d5bd412ef..357ae496a07d 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_starred_order.py @@ -5,7 +5,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.models.groupsearchviewstarred import GroupSearchViewStarred from sentry.models.organization import Organization @@ -31,7 +31,7 @@ def validate_view_ids(self, view_ids: list[int]) -> list[int]: class OrganizationGroupSearchViewStarredOrderEndpoint(OrganizationEndpoint): publish_status = {"PUT": ApiPublishStatus.EXPERIMENTAL} owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (MemberPermission,) def put(self, request: Request, organization: Organization) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_view_visit.py b/src/sentry/issues/endpoints/organization_group_search_view_visit.py index a498bc5bb246..745f3746a877 100644 --- a/src/sentry/issues/endpoints/organization_group_search_view_visit.py +++ b/src/sentry/issues/endpoints/organization_group_search_view_visit.py @@ -5,7 +5,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.api.utils import to_valid_int_id from sentry.models.groupsearchview import GroupSearchView @@ -25,7 +25,7 @@ class OrganizationGroupSearchViewVisitEndpoint(OrganizationEndpoint): "POST": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (MemberPermission,) def post(self, request: Request, organization: Organization, view_id: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_group_search_views.py b/src/sentry/issues/endpoints/organization_group_search_views.py index 1f7e343bddfa..ae1a3b823067 100644 --- a/src/sentry/issues/endpoints/organization_group_search_views.py +++ b/src/sentry/issues/endpoints/organization_group_search_views.py @@ -11,7 +11,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.api.paginator import ChainPaginator from sentry.api.serializers import serialize @@ -100,7 +100,7 @@ class OrganizationGroupSearchViewsEndpoint(OrganizationEndpoint): } idempotent_methods = frozenset() owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (MemberPermission,) @extend_schema( diff --git a/src/sentry/issues/endpoints/organization_group_search_views_starred.py b/src/sentry/issues/endpoints/organization_group_search_views_starred.py index 7f9de6b6fdd9..821ddc5b85c8 100644 --- a/src/sentry/issues/endpoints/organization_group_search_views_starred.py +++ b/src/sentry/issues/endpoints/organization_group_search_views_starred.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.api.serializers import serialize from sentry.api.serializers.models.groupsearchviewstarred import GroupSearchViewStarredSerializer @@ -23,7 +23,7 @@ class OrganizationGroupSearchViewsStarredEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (MemberPermission,) def get(self, request: Request, organization: Organization) -> Response: diff --git a/src/sentry/issues/endpoints/organization_issues_count.py b/src/sentry/issues/endpoints/organization_issues_count.py index ab5020cef9d8..4aec14431499 100644 --- a/src/sentry/issues/endpoints/organization_issues_count.py +++ b/src/sentry/issues/endpoints/organization_issues_count.py @@ -8,7 +8,7 @@ from sentry import search from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.helpers.group_index import validate_search_filter_permissions from sentry.api.helpers.group_index.validators import ValidationError @@ -35,7 +35,7 @@ class OrganizationIssuesCountEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET enforce_rate_limit = True rate_limits = RateLimitConfig( limit_overrides={ diff --git a/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py b/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py index f956ca9de38b..8a0f1e326071 100644 --- a/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py +++ b/src/sentry/issues/endpoints/organization_issues_resolved_in_release.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import OrganizationEndpoint, OrganizationPermission from sentry.api.helpers.releases import get_group_ids_resolved_in_release from sentry.api.serializers import serialize @@ -16,7 +16,7 @@ @cell_silo_endpoint class OrganizationIssuesResolvedInReleaseEndpoint(OrganizationEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/organization_issues_with_supergroups.py b/src/sentry/issues/endpoints/organization_issues_with_supergroups.py index 320012e00366..8126801912d5 100644 --- a/src/sentry/issues/endpoints/organization_issues_with_supergroups.py +++ b/src/sentry/issues/endpoints/organization_issues_with_supergroups.py @@ -10,7 +10,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases import OrganizationEventPermission from sentry.api.bases.organization import OrganizationEndpoint from sentry.api.helpers.group_index import calculate_stats_period @@ -34,7 +34,7 @@ class OrganizationIssuesWithSupergroupsEndpoint(OrganizationEndpoint): "GET": ApiPublishStatus.EXPERIMENTAL, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET permission_classes = (OrganizationEventPermission,) enforce_rate_limit = True diff --git a/src/sentry/issues/endpoints/organization_release_previous_commits.py b/src/sentry/issues/endpoints/organization_release_previous_commits.py index f5b3a5f39e0b..c130e61ac919 100644 --- a/src/sentry/issues/endpoints/organization_release_previous_commits.py +++ b/src/sentry/issues/endpoints/organization_release_previous_commits.py @@ -6,7 +6,7 @@ from sentry.analytics.events.release_get_previous_commits import ReleaseGetPreviousCommitsEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.organization import NoProjects, OrganizationReleasesBaseEndpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.serializers import serialize @@ -23,7 +23,7 @@ class OrganizationReleasePreviousCommitsEndpoint(OrganizationReleasesBaseEndpoin "GET": ApiPublishStatus.PRIVATE, } owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET rate_limits = RateLimitConfig(group="CLI") def get(self, request: Request, organization: Organization, version: str) -> Response: diff --git a/src/sentry/issues/endpoints/organization_shortid.py b/src/sentry/issues/endpoints/organization_shortid.py index 38d9f7ff9371..0b877b2afc6a 100644 --- a/src/sentry/issues/endpoints/organization_shortid.py +++ b/src/sentry/issues/endpoints/organization_shortid.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.serializers import serialize from sentry.api.serializers.models.group import BaseGroupSerializerResponse from sentry.apidocs.constants import ( @@ -33,7 +33,7 @@ class ShortIdLookupResponse(TypedDict): @cell_silo_endpoint class ShortIdLookupEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, } diff --git a/src/sentry/issues/endpoints/project_codeowners_details.py b/src/sentry/issues/endpoints/project_codeowners_details.py index fff40f234b91..fd8a0d14c5f0 100644 --- a/src/sentry/issues/endpoints/project_codeowners_details.py +++ b/src/sentry/issues/endpoints/project_codeowners_details.py @@ -13,7 +13,7 @@ from sentry.analytics.events.codeowners_updated import CodeOwnersUpdated from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.serializers import serialize from sentry.api.serializers.models.projectcodeowners import ( @@ -56,7 +56,7 @@ @extend_schema(tags=["Projects"]) class ProjectCodeOwnersDetailsEndpoint(ProjectCodeOwnersBase): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "DELETE": ApiPublishStatus.PUBLIC, "GET": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/project_codeowners_index.py b/src/sentry/issues/endpoints/project_codeowners_index.py index bf459d885a63..73c793defed7 100644 --- a/src/sentry/issues/endpoints/project_codeowners_index.py +++ b/src/sentry/issues/endpoints/project_codeowners_index.py @@ -9,7 +9,7 @@ from sentry.analytics.events.codeowners_created import CodeOwnersCreated from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.serializers import serialize from sentry.api.serializers.models.projectcodeowners import ( DEFAULT_CODEOWNERS_EXPAND, @@ -39,7 +39,7 @@ @extend_schema(tags=["Projects"]) class ProjectCodeOwnersEndpoint(ProjectCodeOwnersBase): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, "POST": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/project_event_details.py b/src/sentry/issues/endpoints/project_event_details.py index 8d85d0a7287f..87a822576e30 100644 --- a/src/sentry/issues/endpoints/project_event_details.py +++ b/src/sentry/issues/endpoints/project_event_details.py @@ -11,7 +11,7 @@ from sentry import options from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.serializers import EventSerializer, IssueEventSerializer, serialize from sentry.api.serializers.models.event import EventSerializerResponse, GroupEventDetailsResponse @@ -113,7 +113,7 @@ def wrap_event_response( @cell_silo_endpoint class ProjectEventDetailsEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PUBLIC, } @@ -212,7 +212,7 @@ def get( @cell_silo_endpoint class EventJsonEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.EXPERIMENTAL, } diff --git a/src/sentry/issues/endpoints/project_group_index.py b/src/sentry/issues/endpoints/project_group_index.py index aedc7706c2dc..2eaf20ee6d51 100644 --- a/src/sentry/issues/endpoints/project_group_index.py +++ b/src/sentry/issues/endpoints/project_group_index.py @@ -10,7 +10,7 @@ from sentry.analytics.events.project_issue_searched import ProjectIssueSearchEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint, ProjectEventPermission from sentry.api.helpers.environments import get_environment_func from sentry.api.helpers.group_index import ( @@ -50,7 +50,7 @@ @cell_silo_endpoint class ProjectGroupIndexEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "DELETE": ApiPublishStatus.EXPERIMENTAL, "GET": ApiPublishStatus.EXPERIMENTAL, diff --git a/src/sentry/issues/endpoints/project_group_stats.py b/src/sentry/issues/endpoints/project_group_stats.py index 803107f6e8ec..add451b80736 100644 --- a/src/sentry/issues/endpoints/project_group_stats.py +++ b/src/sentry/issues/endpoints/project_group_stats.py @@ -4,7 +4,7 @@ from sentry import tsdb from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import StatsMixin, cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, StatsMixin, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.environments import get_environment_id @@ -19,7 +19,7 @@ @cell_silo_endpoint class ProjectGroupStatsEndpoint(ProjectEndpoint, StatsMixin): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/project_issues_resolved_in_release.py b/src/sentry/issues/endpoints/project_issues_resolved_in_release.py index d986973c1497..0eb2301a0df6 100644 --- a/src/sentry/issues/endpoints/project_issues_resolved_in_release.py +++ b/src/sentry/issues/endpoints/project_issues_resolved_in_release.py @@ -3,7 +3,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint, ProjectPermission from sentry.api.helpers.environments import get_environment_func from sentry.api.helpers.releases import get_group_ids_resolved_in_release @@ -16,7 +16,7 @@ @cell_silo_endpoint class ProjectIssuesResolvedInReleaseEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.EXPERIMENTAL, } diff --git a/src/sentry/issues/endpoints/project_ownership.py b/src/sentry/issues/endpoints/project_ownership.py index 838f280030cc..c643900cf0f3 100644 --- a/src/sentry/issues/endpoints/project_ownership.py +++ b/src/sentry/issues/endpoints/project_ownership.py @@ -11,7 +11,7 @@ from sentry import audit_log, features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint, ProjectOwnershipPermission from sentry.api.serializers import serialize from sentry.api.serializers.models.projectownership import ( @@ -237,7 +237,7 @@ def __modify_auto_assignment(self, ownership: ProjectOwnership) -> bool: @extend_schema(tags=["Projects"]) class ProjectOwnershipEndpoint(ProjectEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = {"GET": 3.0, "PUT": 10.0} + max_acceptable_duration = {"GET": DEFAULT_DURATION_BUDGET, "PUT": 10.0} publish_status = { "GET": ApiPublishStatus.PUBLIC, "PUT": ApiPublishStatus.PUBLIC, diff --git a/src/sentry/issues/endpoints/related_issues.py b/src/sentry/issues/endpoints/related_issues.py index 596ed3c757da..3c2cc015995f 100644 --- a/src/sentry/issues/endpoints/related_issues.py +++ b/src/sentry/issues/endpoints/related_issues.py @@ -4,7 +4,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.helpers.deprecation import deprecated from sentry.api.helpers.projects import filter_projects_by_permissions from sentry.constants import CELL_API_DEPRECATION_DATE, ObjectStatus @@ -26,7 +26,7 @@ class RequestSerializer(serializers.Serializer[None]): @cell_silo_endpoint class RelatedIssuesEndpoint(GroupEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = {"GET": ApiPublishStatus.EXPERIMENTAL} enforce_rate_limit = True rate_limits = RateLimitConfig( diff --git a/src/sentry/issues/endpoints/shared_group_details.py b/src/sentry/issues/endpoints/shared_group_details.py index f425292cbc73..e8b7b1f357b8 100644 --- a/src/sentry/issues/endpoints/shared_group_details.py +++ b/src/sentry/issues/endpoints/shared_group_details.py @@ -5,7 +5,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import Endpoint, cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, Endpoint, cell_silo_endpoint from sentry.api.exceptions import ResourceDoesNotExist from sentry.api.helpers.environments import get_environment_func from sentry.api.serializers import SharedGroupSerializer, serialize @@ -15,7 +15,7 @@ @cell_silo_endpoint class SharedGroupDetailsEndpoint(Endpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_all_unresolved_issues.py b/src/sentry/issues/endpoints/team_all_unresolved_issues.py index f2800c91899b..b2689f482004 100644 --- a/src/sentry/issues/endpoints/team_all_unresolved_issues.py +++ b/src/sentry/issues/endpoints/team_all_unresolved_issues.py @@ -13,7 +13,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.team import TeamEndpoint from sentry.api.helpers.environments import get_environments from sentry.api.utils import get_date_range_from_params @@ -166,7 +166,7 @@ def calculate_unresolved_counts( @cell_silo_endpoint class TeamAllUnresolvedIssuesEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_groups_old.py b/src/sentry/issues/endpoints/team_groups_old.py index cb8d8dc19be0..ad0fd6d3c6b4 100644 --- a/src/sentry/issues/endpoints/team_groups_old.py +++ b/src/sentry/issues/endpoints/team_groups_old.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.team import TeamEndpoint from sentry.api.helpers.environments import get_environment_func, get_environments from sentry.api.serializers import GroupSerializer, serialize @@ -17,7 +17,7 @@ @cell_silo_endpoint class TeamGroupsOldEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, } diff --git a/src/sentry/issues/endpoints/team_issue_breakdown.py b/src/sentry/issues/endpoints/team_issue_breakdown.py index f4a1fb17b387..470783b30f7f 100644 --- a/src/sentry/issues/endpoints/team_issue_breakdown.py +++ b/src/sentry/issues/endpoints/team_issue_breakdown.py @@ -10,7 +10,7 @@ from sentry import features from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import cell_silo_endpoint +from sentry.api.base import DEFAULT_DURATION_BUDGET, cell_silo_endpoint from sentry.api.bases.team import TeamEndpoint from sentry.api.helpers.environments import get_environments from sentry.api.utils import get_date_range_from_params @@ -29,7 +29,7 @@ @cell_silo_endpoint class TeamIssueBreakdownEndpoint(TeamEndpoint): owner = ApiOwner.ISSUES - max_acceptable_duration = 3.0 + max_acceptable_duration = DEFAULT_DURATION_BUDGET publish_status = { "GET": ApiPublishStatus.PRIVATE, }