Browse Source

chore(hybrid-cloud): breaking foreign keys (#45203)

I need to remerge https://github.com/getsentry/sentry/pull/45095 after
reverting prematurely.
Zach Collins 2 years ago
parent
commit
ede26403b8

+ 2 - 2
src/sentry/api/endpoints/project_transaction_threshold.py

@@ -90,7 +90,7 @@ class ProjectTransactionThresholdEndpoint(ProjectEndpoint):
                 )
                 project_threshold.threshold = data.get("threshold") or project_threshold.threshold
                 project_threshold.metric = data.get("metric") or project_threshold.metric
-                project_threshold.edited_by = request.user
+                project_threshold.edited_by_id = request.user.id
                 project_threshold.save()
 
                 created = False
@@ -101,7 +101,7 @@ class ProjectTransactionThresholdEndpoint(ProjectEndpoint):
                     organization=project.organization,
                     threshold=data.get("threshold", 300),
                     metric=data.get("metric", TransactionMetric.DURATION.value),
-                    edited_by=request.user,
+                    edited_by_id=request.user.id,
                 )
 
                 created = True

+ 1 - 1
src/sentry/api/endpoints/project_transaction_threshold_override.py

@@ -117,7 +117,7 @@ class ProjectTransactionThresholdOverrideEndpoint(OrganizationEventsV2EndpointBa
                 defaults={
                     "threshold": data["threshold"],
                     "metric": data["metric"],
-                    "edited_by": request.user,
+                    "edited_by_id": request.user.id,
                 },
             )
 

+ 24 - 6
src/sentry/api/helpers/group_index/update.py

@@ -49,7 +49,7 @@ from sentry.models.group import STATUS_UPDATE_CHOICES
 from sentry.models.grouphistory import record_group_history_from_activity_type
 from sentry.models.groupinbox import GroupInboxRemoveAction, add_group_to_inbox
 from sentry.notifications.types import SUBSCRIPTION_REASON_MAP, GroupSubscriptionReason
-from sentry.services.hybrid_cloud.user import RpcUser
+from sentry.services.hybrid_cloud.user import RpcUser, user_service
 from sentry.signals import (
     issue_ignored,
     issue_mark_reviewed,
@@ -61,7 +61,6 @@ from sentry.tasks.integrations import kick_off_status_syncs
 from sentry.tasks.merge import merge_groups
 from sentry.types.activity import ActivityType
 from sentry.utils import metrics
-from sentry.utils.functional import extract_lazy_object
 
 from . import ACTIVITIES_COUNT, BULK_MUTATION_LIMIT, SearchFunction, delete_group_list
 from .validators import GroupValidator, ValidationError
@@ -280,10 +279,15 @@ def update_groups(
                 # no version yet
                 "version": ""
             }
+
+            serialized_user = user_service.serialize_many(
+                filter=dict(user_ids=[user.id]), as_user=user
+            )
             status_details = {
                 "inNextRelease": True,
-                "actor": serialize(extract_lazy_object(user), user),
             }
+            if serialized_user:
+                status_details["actor"] = serialized_user[0]
             res_type = GroupResolution.Type.in_next_release
             res_type_str = "in_next_release"
             res_status = GroupResolution.Status.pending
@@ -301,10 +305,15 @@ def update_groups(
                 # no version yet
                 "version": release.version
             }
+
+            serialized_user = user_service.serialize_many(
+                filter=dict(user_ids=[user.id]), as_user=user
+            )
             status_details = {
                 "inRelease": release.version,
-                "actor": serialize(extract_lazy_object(user), user),
             }
+            if serialized_user:
+                status_details["actor"] = serialized_user[0]
             res_type = GroupResolution.Type.in_release
             res_type_str = "in_release"
             res_status = GroupResolution.Status.resolved
@@ -318,10 +327,15 @@ def update_groups(
             commit = statusDetails["inCommit"]
             activity_type = ActivityType.SET_RESOLVED_IN_COMMIT.value
             activity_data = {"commit": commit.id}
+            serialized_user = user_service.serialize_many(
+                filter=dict(user_ids=[user.id]), as_user=user
+            )
+
             status_details = {
                 "inCommit": serialize(commit, user),
-                "actor": serialize(extract_lazy_object(user), user),
             }
+            if serialized_user:
+                status_details["actor"] = serialized_user[0]
             res_type_str = "in_commit"
         else:
             res_type_str = "now"
@@ -572,14 +586,18 @@ def update_groups(
                                 "actor_id": user.id if user.is_authenticated else None,
                             },
                         )
+                        serialized_user = user_service.serialize_many(
+                            filter=dict(user_ids=[user.id]), as_user=user
+                        )
                         result["statusDetails"] = {
                             "ignoreCount": ignore_count,
                             "ignoreUntil": ignore_until,
                             "ignoreUserCount": ignore_user_count,
                             "ignoreUserWindow": ignore_user_window,
                             "ignoreWindow": ignore_window,
-                            "actor": serialize(extract_lazy_object(user), user),
                         }
+                        if serialized_user:
+                            result["statusDetails"]["actor"] = serialized_user[0]
                 else:
                     GroupSnooze.objects.filter(group__in=group_ids).delete()
                     ignore_until = None

+ 20 - 12
src/sentry/api/serializers/models/alert_rule.py

@@ -1,4 +1,5 @@
 from collections import defaultdict
+from typing import MutableMapping
 
 from django.db.models import Max, prefetch_related_objects
 
@@ -23,6 +24,7 @@ from sentry.models import (
     fetch_actors_by_actor_ids,
 )
 from sentry.services.hybrid_cloud.app import app_service
+from sentry.services.hybrid_cloud.user import RpcUser, user_service
 from sentry.snuba.models import SnubaQueryEventType
 
 
@@ -70,26 +72,32 @@ class AlertRuleSerializer(Serializer):
             rule_result = result[alert_rules[alert_rule_id]].setdefault("projects", [])
             rule_result.append(project_slug)
 
-        for rule_activity in AlertRuleActivity.objects.filter(
-            alert_rule__in=item_list, type=AlertRuleActivityType.CREATED.value
-        ).select_related("alert_rule", "user"):
-            if rule_activity.user:
-                user = {
-                    "id": rule_activity.user.id,
-                    "name": rule_activity.user.get_display_name(),
-                    "email": rule_activity.user.email,
-                }
+        rule_activities = list(
+            AlertRuleActivity.objects.filter(
+                alert_rule__in=item_list, type=AlertRuleActivityType.CREATED.value
+            )
+        )
+
+        use_by_user_id: MutableMapping[int, RpcUser] = {
+            user.id: user
+            for user in user_service.get_many(
+                filter=dict(user_ids=[r.user_id for r in rule_activities])
+            )
+        }
+        for rule_activity in rule_activities:
+            rpc_user = use_by_user_id.get(rule_activity.user_id)
+            if rpc_user:
+                user = dict(id=rpc_user.id, name=rpc_user.get_display_name(), email=rpc_user.email)
             else:
                 user = None
+            result[alert_rules[rule_activity.alert_rule_id]]["created_by"] = user
 
-            result[alert_rules[rule_activity.alert_rule.id]].update({"created_by": user})
-
-        resolved_actors = {}
         owners_by_type = defaultdict(list)
         for item in item_list:
             if item.owner_id is not None:
                 owners_by_type[actor_type_to_string(item.owner.type)].append(item.owner_id)
 
+        resolved_actors = {}
         for k, v in ACTOR_TYPES.items():
             resolved_actors[k] = {
                 a.actor_id: a.id

+ 12 - 18
src/sentry/api/serializers/models/discoversavedquery.py

@@ -1,34 +1,28 @@
 from collections import defaultdict
 
-from django.db.models.query import prefetch_related_objects
-
-from sentry.api.serializers import Serializer, register, serialize
-from sentry.api.serializers.models.user import UserSerializer
+from sentry.api.serializers import Serializer, register
 from sentry.constants import ALL_ACCESS_PROJECTS
 from sentry.discover.models import DiscoverSavedQuery
+from sentry.services.hybrid_cloud.user import user_service
 from sentry.utils.dates import outside_retention_with_modified_start, parse_timestamp
 
 
 @register(DiscoverSavedQuery)
 class DiscoverSavedQuerySerializer(Serializer):
     def get_attrs(self, item_list, user):
-        prefetch_related_objects(item_list, "created_by")
-
         result = defaultdict(lambda: {"created_by": {}})
 
-        user_serializer = UserSerializer()
-        serialized_users = {
-            user["id"]: user
-            for user in serialize(
-                [
-                    discover_saved_query.created_by
+        service_serialized = user_service.serialize_many(
+            filter={
+                "user_ids": [
+                    discover_saved_query.created_by_id
                     for discover_saved_query in item_list
-                    if discover_saved_query.created_by
-                ],
-                user=user,
-                serializer=user_serializer,
-            )
-        }
+                    if discover_saved_query.created_by_id
+                ]
+            },
+            as_user=user,
+        )
+        serialized_users = {user["id"]: user for user in service_serialized}
 
         for discover_saved_query in item_list:
             result[discover_saved_query]["created_by"] = serialized_users.get(

+ 19 - 13
src/sentry/api/serializers/models/exporteddata.py

@@ -1,25 +1,31 @@
-from sentry.api.serializers import Serializer, register, serialize
+from sentry.api.serializers import Serializer, register
 from sentry.data_export.base import ExportQueryType
 from sentry.data_export.models import ExportedData
-from sentry.models import User
+from sentry.services.hybrid_cloud.user import user_service
 
 
 @register(ExportedData)
 class ExportedDataSerializer(Serializer):
     def get_attrs(self, item_list, user, **kwargs):
         attrs = {}
-        users = User.objects.filter(id__in={item.user_id for item in item_list})
-        user_lookup = {user.id: user for user in users}
+        serialized_users = {
+            u["id"]: u
+            for u in user_service.serialize_many(
+                filter=dict(user_ids=[item.user_id for item in item_list])
+            )
+        }
         for item in item_list:
-            user = user_lookup[item.user_id]
-            serialized_user = serialize(user)
-            attrs[item] = {
-                "user": {
-                    "id": serialized_user["id"],
-                    "email": serialized_user["email"],
-                    "username": serialized_user["username"],
+            if str(item.user_id) in serialized_users:
+                serialized_user = serialized_users[str(item.user_id)]
+                attrs[item] = {
+                    "user": {
+                        "id": serialized_user["id"],
+                        "email": serialized_user["email"],
+                        "username": serialized_user["username"],
+                    }
                 }
-            }
+            else:
+                attrs[item] = {}
         return attrs
 
     def serialize(self, obj, attrs, user, **kwargs):
@@ -33,7 +39,7 @@ class ExportedDataSerializer(Serializer):
 
         return {
             "id": obj.id,
-            "user": attrs["user"],
+            "user": attrs.get("user"),
             "dateCreated": obj.date_added,
             "dateFinished": obj.date_finished,
             "dateExpired": obj.date_expired,

+ 21 - 18
src/sentry/api/serializers/models/group.py

@@ -15,6 +15,7 @@ from typing import (
     Optional,
     Protocol,
     Sequence,
+    Set,
     Tuple,
     TypedDict,
     Union,
@@ -35,7 +36,6 @@ from sentry.auth.superuser import is_active_superuser
 from sentry.constants import LOG_LEVELS
 from sentry.issues.grouptype import GroupCategory
 from sentry.models import (
-    ActorTuple,
     Commit,
     Environment,
     Group,
@@ -183,19 +183,26 @@ class GroupSerializerBase(Serializer, ABC):
         self.collapse = collapse
         self.expand = expand
 
-    def _serialize_assigness(
-        self, actor_dict: Mapping[int, ActorTuple]
-    ) -> Mapping[int, Union[Team, Any]]:
-        actors_by_type: MutableMapping[Any, List[ActorTuple]] = defaultdict(list)
-        for actor in actor_dict.values():
-            actors_by_type[actor.type].append(actor)
+    def _serialize_assignees(self, item_list: Sequence[Group]) -> Mapping[int, Union[Team, Any]]:
+        gas = GroupAssignee.objects.filter(group__in=item_list)
+        result: MutableMapping[int, Union[Team, Any]] = {}
+        all_team_ids: MutableMapping[int, Set[int]] = defaultdict(set)
+        all_user_ids: MutableMapping[int, Set[int]] = defaultdict(set)
+
+        for g in gas:
+            if g.team_id:
+                all_team_ids[g.team_id].add(g.group_id)
+            if g.user_id:
+                all_user_ids[g.user_id].add(g.group_id)
+
+        for team in Team.objects.filter(id__in=all_team_ids.keys()):
+            for group_id in all_team_ids[team.id]:
+                result[group_id] = team
+        for user in user_service.get_many(filter=dict(user_ids=list(all_user_ids.keys()))):
+            for group_id in all_user_ids[user.id]:
+                result[group_id] = user
 
-        resolved_actors = {}
-        for t, actors in actors_by_type.items():
-            serializable = ActorTuple.resolve_many(actors)
-            resolved_actors[t] = {actor.id: actor for actor in serializable}
-
-        return {key: resolved_actors[value.type][value.id] for key, value in actor_dict.items()}
+        return result
 
     def get_attrs(
         self, item_list: Sequence[Group], user: Any, **kwargs: Any
@@ -223,11 +230,7 @@ class GroupSerializerBase(Serializer, ABC):
             seen_groups = {}
             subscriptions = defaultdict(lambda: (False, False, None))
 
-        assignees: Mapping[int, ActorTuple] = {
-            a.group_id: a.assigned_actor()
-            for a in GroupAssignee.objects.filter(group__in=item_list)
-        }
-        resolved_assignees = self._serialize_assigness(assignees)
+        resolved_assignees = self._serialize_assignees(item_list)
 
         ignore_items = {g.group_id: g for g in GroupSnooze.objects.filter(group__in=item_list)}
 

+ 4 - 6
src/sentry/api/serializers/models/incident.py

@@ -44,9 +44,7 @@ class IncidentSerializer(Serializer):
 
         if "seen_by" in self.expand:
             incident_seen_list = list(
-                IncidentSeen.objects.filter(incident__in=item_list)
-                .select_related("user")
-                .order_by("-last_seen")
+                IncidentSeen.objects.filter(incident__in=item_list).order_by("-last_seen")
             )
             incident_seen_dict = defaultdict(list)
             for incident_seen, serialized_seen_by in zip(
@@ -108,9 +106,9 @@ class DetailedIncidentSerializer(IncidentSerializer):
         subscribed_incidents = set()
         if user.is_authenticated:
             subscribed_incidents = set(
-                IncidentSubscription.objects.filter(incident__in=item_list, user=user).values_list(
-                    "incident_id", flat=True
-                )
+                IncidentSubscription.objects.filter(
+                    incident__in=item_list, user_id=user.id
+                ).values_list("incident_id", flat=True)
             )
 
         for item in item_list:

+ 4 - 8
src/sentry/api/serializers/models/incidentactivity.py

@@ -1,20 +1,16 @@
 from django.db.models import prefetch_related_objects
 
-from sentry.api.serializers import Serializer, register, serialize
-from sentry.api.serializers.models.user import UserSerializer
+from sentry.api.serializers import Serializer, register
 from sentry.incidents.models import IncidentActivity
+from sentry.services.hybrid_cloud.user import user_service
 
 
 @register(IncidentActivity)
 class IncidentActivitySerializer(Serializer):
     def get_attrs(self, item_list, user, **kwargs):
         prefetch_related_objects(item_list, "incident__organization")
-        prefetch_related_objects(item_list, "user")
-        user_serializer = UserSerializer()
-        serialized_users = serialize(
-            {item.user for item in item_list if item.user_id},
-            user=user,
-            serializer=user_serializer,
+        serialized_users = user_service.serialize_many(
+            filter={"user_ids": [i.user_id for i in item_list if i.user_id]}, as_user=user
         )
         user_lookup = {user["id"]: user for user in serialized_users}
         return {item: {"user": user_lookup.get(str(item.user_id))} for item in item_list}

+ 11 - 5
src/sentry/api/serializers/models/incidentseen.py

@@ -1,14 +1,20 @@
-from django.db.models import prefetch_related_objects
-
-from sentry.api.serializers import Serializer, register, serialize
+from sentry.api.serializers import Serializer, register
 from sentry.incidents.models import IncidentSeen
+from sentry.services.hybrid_cloud.user import user_service
 
 
 @register(IncidentSeen)
 class IncidentSeenSerializer(Serializer):
     def get_attrs(self, item_list, user):
-        prefetch_related_objects(item_list, "user")
-        user_map = {d["id"]: d for d in serialize({i.user for i in item_list}, user)}
+        user_map = {
+            d["id"]: d
+            for d in user_service.serialize_many(
+                filter={
+                    "user_ids": [i.user_id for i in item_list],
+                },
+                as_user=user,
+            )
+        }
 
         result = {}
         for item in item_list:

Some files were not shown because too many files changed in this diff