-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
chore(spans-migration): add post and put validation for EAP extrapolation mode #102970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d0a52e
f14a7d7
0842aae
b037625
6ae1833
ca57cb3
4fcd9d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,7 @@ | |
| from sentry.sentry_apps.services.app import app_service | ||
| from sentry.sentry_apps.utils.errors import SentryAppBaseError | ||
| from sentry.snuba.dataset import Dataset | ||
| from sentry.snuba.models import ExtrapolationMode | ||
| from sentry.uptime.types import ( | ||
| DATA_SOURCE_UPTIME_SUBSCRIPTION, | ||
| GROUP_TYPE_UPTIME_DOMAIN_CHECK_FAILURE, | ||
|
|
@@ -100,6 +101,9 @@ def create_metric_alert( | |
| "Creation of transaction-based alerts is disabled, as we migrate to the span dataset. Create span-based alerts (dataset: events_analytics_platform) with the is_transaction:true filter instead." | ||
| ) | ||
|
|
||
| if data.get("extrapolation_mode") == ExtrapolationMode.SERVER_WEIGHTED.name.lower(): | ||
| raise ValidationError("server_weighted extrapolation mode is not supported for new alerts.") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Extrapolation Validation: Type Mismatch BugThe validation compares |
||
|
|
||
| if project: | ||
| data["projects"] = [project.slug] | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,12 @@ | |
| from sentry.seer.anomaly_detection.store_data_workflow_engine import send_new_detector_data | ||
| from sentry.snuba.dataset import Dataset | ||
| from sentry.snuba.metrics.extraction import should_use_on_demand_metrics | ||
| from sentry.snuba.models import QuerySubscription, SnubaQuery, SnubaQueryEventType | ||
| from sentry.snuba.models import ( | ||
| ExtrapolationMode, | ||
| QuerySubscription, | ||
| SnubaQuery, | ||
| SnubaQueryEventType, | ||
| ) | ||
| from sentry.snuba.snuba_query_validator import SnubaQueryValidator | ||
| from sentry.snuba.subscriptions import update_snuba_query | ||
| from sentry.tasks.relay import schedule_invalidate_project_config | ||
|
|
@@ -135,6 +140,19 @@ def validate_conditions(self, value): | |
| return value | ||
|
|
||
|
|
||
| def is_invalid_extrapolation_mode(old_extrapolation_mode, new_extrapolation_mode) -> bool: | ||
| if type(new_extrapolation_mode) is int: | ||
| new_extrapolation_mode = ExtrapolationMode(new_extrapolation_mode).name.lower() | ||
| if type(old_extrapolation_mode) is int: | ||
| old_extrapolation_mode = ExtrapolationMode(old_extrapolation_mode).name.lower() | ||
| if ( | ||
| new_extrapolation_mode == ExtrapolationMode.SERVER_WEIGHTED.name.lower() | ||
| and old_extrapolation_mode != ExtrapolationMode.SERVER_WEIGHTED.name.lower() | ||
| ): | ||
| return True | ||
| return False | ||
|
|
||
|
|
||
| class MetricIssueDetectorValidator(BaseDetectorTypeValidator): | ||
| data_sources = serializers.ListField( | ||
| child=SnubaQueryValidator(timeWindowSeconds=True), required=False | ||
|
|
@@ -162,6 +180,12 @@ def _validate_transaction_dataset_deprecation(self, dataset: Dataset) -> None: | |
| "Creation of transaction-based alerts is disabled, as we migrate to the span dataset. Create span-based alerts (dataset: events_analytics_platform) with the is_transaction:true filter instead." | ||
| ) | ||
|
|
||
| def _validate_extrapolation_mode(self, extrapolation_mode: str) -> None: | ||
| if extrapolation_mode == ExtrapolationMode.SERVER_WEIGHTED.value: | ||
| raise serializers.ValidationError( | ||
| "server_weighted extrapolation mode is not supported for new detectors." | ||
| ) | ||
|
|
||
| def get_quota(self) -> DetectorQuota: | ||
| organization = self.context.get("organization") | ||
| request = self.context.get("request") | ||
|
|
@@ -224,6 +248,16 @@ def update_data_source(self, instance: Detector, data_source: SnubaQueryDataSour | |
| "Updates to transaction-based alerts is disabled, as we migrate to the span dataset. Create span-based alerts (dataset: events_analytics_platform) with the is_transaction:true filter instead." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Type mismatch in 🔍 Detailed AnalysisA type mismatch occurs in the validation logic for 💡 Suggested FixEnsure 🤖 Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
| ) | ||
|
|
||
| old_extrapolation_mode = snuba_query.extrapolation_mode | ||
| new_extrapolation_mode = data_source.get( | ||
| "extrapolation_mode", snuba_query.extrapolation_mode | ||
| ) | ||
| if data_source.get("dataset") == Dataset.EventsAnalyticsPlatform: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if is_invalid_extrapolation_mode(old_extrapolation_mode, new_extrapolation_mode): | ||
| raise serializers.ValidationError( | ||
| "Invalid extrapolation mode for this detector type." | ||
| ) | ||
|
|
||
| update_snuba_query( | ||
| snuba_query=snuba_query, | ||
| query_type=data_source.get("query_type", snuba_query.type), | ||
|
|
@@ -234,6 +268,9 @@ def update_data_source(self, instance: Detector, data_source: SnubaQueryDataSour | |
| resolution=timedelta(seconds=data_source.get("resolution", snuba_query.resolution)), | ||
| environment=data_source.get("environment", snuba_query.environment), | ||
| event_types=data_source.get("event_types", [event_type for event_type in event_types]), | ||
| extrapolation_mode=data_source.get( | ||
| "extrapolation_mode", snuba_query.extrapolation_mode | ||
| ), | ||
| ) | ||
|
|
||
| def update(self, instance: Detector, validated_data: dict[str, Any]): | ||
|
|
@@ -267,6 +304,7 @@ def create(self, validated_data: dict[str, Any]): | |
| if "data_sources" in validated_data: | ||
| for validated_data_source in validated_data["data_sources"]: | ||
| self._validate_transaction_dataset_deprecation(validated_data_source.get("dataset")) | ||
| self._validate_extrapolation_mode(validated_data_source.get("extrapolation_mode")) | ||
|
|
||
| detector = super().create(validated_data) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |||
| ) | ||||
| from sentry.snuba.dataset import Dataset | ||||
| from sentry.snuba.models import ( | ||||
| ExtrapolationMode, | ||||
| QuerySubscription, | ||||
| QuerySubscriptionDataSourceHandler, | ||||
| SnubaQuery, | ||||
|
|
@@ -617,3 +618,74 @@ def test_transaction_dataset_deprecation_generic_metrics_update(self) -> None: | |||
| with_feature("organizations:discover-saved-queries-deprecation"), | ||||
| ): | ||||
| update_validator.save() | ||||
|
|
||||
| def test_invalid_extrapolation_mode_create(self) -> None: | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should a valid extrapolation mode be added to any of the existing tests? This isn't live yet but I have an open PR with a simple valid update test:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have a default in place if there is no explicit extrapolation mode passed so it's not necessary to be passed in 😌 |
||||
| data = { | ||||
| **self.valid_data, | ||||
| "dataSources": [ | ||||
| { | ||||
| "queryType": SnubaQuery.Type.PERFORMANCE.value, | ||||
| "dataset": Dataset.EventsAnalyticsPlatform.value, | ||||
| "query": "test query", | ||||
| "aggregate": "count()", | ||||
| "timeWindow": 3600, | ||||
| "environment": self.environment.name, | ||||
| "eventTypes": [SnubaQueryEventType.EventType.TRACE_ITEM_SPAN.name.lower()], | ||||
| "extrapolation_mode": ExtrapolationMode.SERVER_WEIGHTED.value, | ||||
| }, | ||||
| ], | ||||
| } | ||||
|
|
||||
| validator = MetricIssueDetectorValidator(data=data, context=self.context) | ||||
| assert validator.is_valid(), validator.errors | ||||
| with self.assertRaisesMessage( | ||||
| ValidationError, | ||||
| expected_message="server_weighted extrapolation mode is not supported for new detectors.", | ||||
| ): | ||||
| validator.save() | ||||
|
|
||||
| def test_invalid_extrapolation_mode_update(self) -> None: | ||||
| data = { | ||||
| **self.valid_data, | ||||
| "dataSources": [ | ||||
| { | ||||
| "queryType": SnubaQuery.Type.PERFORMANCE.value, | ||||
| "dataset": Dataset.EventsAnalyticsPlatform.value, | ||||
| "query": "test query", | ||||
| "aggregate": "count()", | ||||
| "timeWindow": 3600, | ||||
| "environment": self.environment.name, | ||||
| "eventTypes": [SnubaQueryEventType.EventType.TRACE_ITEM_SPAN.name.lower()], | ||||
| "extrapolation_mode": ExtrapolationMode.CLIENT_AND_SERVER_WEIGHTED.value, | ||||
| }, | ||||
| ], | ||||
| } | ||||
|
|
||||
| validator = MetricIssueDetectorValidator(data=data, context=self.context) | ||||
| assert validator.is_valid(), validator.errors | ||||
|
|
||||
| detector = validator.save() | ||||
|
|
||||
| update_data = { | ||||
| "dataSources": [ | ||||
| { | ||||
| "queryType": SnubaQuery.Type.PERFORMANCE.value, | ||||
| "dataset": Dataset.EventsAnalyticsPlatform.value, | ||||
| "query": "updated query", | ||||
| "aggregate": "count()", | ||||
| "timeWindow": 3600, | ||||
| "environment": self.environment.name, | ||||
| "eventTypes": [SnubaQueryEventType.EventType.TRACE_ITEM_SPAN.name.lower()], | ||||
| "extrapolation_mode": ExtrapolationMode.SERVER_WEIGHTED.value, | ||||
| } | ||||
| ], | ||||
| } | ||||
| update_validator = MetricIssueDetectorValidator( | ||||
| instance=detector, data=update_data, context=self.context, partial=True | ||||
| ) | ||||
| assert update_validator.is_valid(), update_validator.errors | ||||
| with self.assertRaisesMessage( | ||||
| ValidationError, | ||||
| expected_message="Invalid extrapolation mode for this detector type.", | ||||
| ): | ||||
| update_validator.save() | ||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Extrapolation Mode Validation Bypass
The validation checks
data.get("dataset")from the request instead of checking the actual alert rule's dataset (alert_rule.snuba_query.dataset). This allows bypassing the extrapolation mode validation during partial updates by omitting thedatasetfield, enabling users to setserver_weightedon EAP alerts.