def migrate_subscriptions(apps, schema_editor): QuerySubscription = apps.get_model("sentry", "QuerySubscription") AppSnubaQueryEventType = apps.get_model("sentry", "SnubaQueryEventType") for subscription in RangeQuerySetWrapperWithProgressBar( QuerySubscription.objects.select_related("snuba_query").all()): if subscription.subscription_id is not None: # The migration apps don't build this property, so manually set it. raw_event_types = AppSnubaQueryEventType.objects.filter( snuba_query=subscription.snuba_query).all() event_types = [ SnubaQueryEventType.EventType(ev.type) for ev in raw_event_types ] setattr(subscription.snuba_query, "event_types", event_types) subscription_id = None try: subscription_id = _create_in_snuba(subscription) except Exception as e: logging.exception( f"failed to recreate {subscription.subscription_id}: {e}") continue try: _delete_from_snuba( QueryDatasets(subscription.snuba_query.dataset), subscription.subscription_id, ) except Exception as e: try: # Delete the subscription we just created to avoid orphans _delete_from_snuba( QueryDatasets(subscription.snuba_query.dataset), subscription_id, ) except Exception as oe: logging.exception( f"failed to delete orphan {subscription_id}: {oe}") logging.exception( f"failed to delete {subscription.subscription_id}: {e}") continue QuerySubscription.objects.filter(id=subscription.id).update( subscription_id=subscription_id)
def update_snuba_query(snuba_query, dataset, query, aggregate, time_window, resolution, environment, event_types): """ Updates a SnubaQuery. Triggers updates to any related QuerySubscriptions. :param snuba_query: The `SnubaQuery` to update. :param dataset: The snuba dataset to query and aggregate over :param query: An event search query that we can parse and convert into a set of Snuba conditions :param aggregate: An aggregate to calculate over the time window :param time_window: The time window to aggregate over :param resolution: How often to receive updates/bucket size :param environment: An optional environment to filter by :param event_types: A (currently) optional list of event_types that apply to this query. If not passed, we'll use the existing event types on the query. :return: A list of QuerySubscriptions """ current_event_types = set(snuba_query.event_types) if not event_types: event_types = current_event_types new_event_types = set(event_types) - current_event_types removed_event_types = current_event_types - set(event_types) old_dataset = QueryDatasets(snuba_query.dataset) with transaction.atomic(): query_subscriptions = list(snuba_query.subscriptions.all()) snuba_query.update( dataset=dataset.value, query=query, aggregate=aggregate, time_window=int(time_window.total_seconds()), resolution=int(resolution.total_seconds()), environment=environment, ) if new_event_types: SnubaQueryEventType.objects.bulk_create([ SnubaQueryEventType(snuba_query=snuba_query, type=event_type.value) for event_type in set(new_event_types) ]) if removed_event_types: SnubaQueryEventType.objects.filter( snuba_query=snuba_query, type__in=[et.value for et in removed_event_types]).delete() bulk_update_snuba_subscriptions(query_subscriptions, old_dataset)
def get_attrs(self, item_list, user, **kwargs): result = super().get_attrs(item_list, user, **kwargs) alert_rules = {item.id: item for item in item_list} for alert_rule_id, project_slug in AlertRuleExcludedProjects.objects.filter( alert_rule__in=item_list ).values_list("alert_rule_id", "project__slug"): exclusions = result[alert_rules[alert_rule_id]].setdefault("excluded_projects", []) exclusions.append(project_slug) query_to_alert_rule = {ar.snuba_query_id: ar for ar in item_list} for event_type in SnubaQueryEventType.objects.filter( snuba_query_id__in=[item.snuba_query_id for item in item_list] ): event_types = result[query_to_alert_rule[event_type.snuba_query_id]].setdefault( "event_types", [] ) event_types.append(SnubaQueryEventType.EventType(event_type.type).name.lower()) return result
def create_snuba_query(dataset, query, aggregate, time_window, resolution, environment, event_types=None): """ Creates a SnubaQuery. :param dataset: The snuba dataset to query and aggregate over :param query: An event search query that we can parse and convert into a set of Snuba conditions :param aggregate: An aggregate to calculate over the time window :param time_window: The time window to aggregate over :param resolution: How often to receive updates/bucket size :param environment: An optional environment to filter by :param event_types: A (currently) optional list of event_types that apply to this query. If not passed, we'll infer a default value based on the dataset. :return: A list of QuerySubscriptions """ snuba_query = SnubaQuery.objects.create( dataset=dataset.value, query=query, aggregate=aggregate, time_window=int(time_window.total_seconds()), resolution=int(resolution.total_seconds()), environment=environment, ) if not event_types: event_types = [ SnubaQueryEventType.EventType.ERROR if dataset == QueryDatasets.EVENTS else SnubaQueryEventType.EventType.TRANSACTION ] sq_event_types = [ SnubaQueryEventType(snuba_query=snuba_query, type=event_type.value) for event_type in set(event_types) ] SnubaQueryEventType.objects.bulk_create(sq_event_types) return snuba_query