Просмотр исходного кода

ref(hc): Add check_organization_by_slug support in control (#60854)

Since the control silo already has OrganizationMapping, we can resolve
these locally and skip a _lot_ of region RPC requests.

Fixes HC-1008
Matt Duncan 1 год назад
Родитель
Сommit
72d1e82309

+ 30 - 11
src/sentry/services/hybrid_cloud/organization/impl.py

@@ -16,6 +16,7 @@ from sentry.models.groupseen import GroupSeen
 from sentry.models.groupshare import GroupShare
 from sentry.models.groupsubscription import GroupSubscription
 from sentry.models.organization import Organization, OrganizationStatus
+from sentry.models.organizationmapping import OrganizationMapping
 from sentry.models.organizationmember import InviteStatus, OrganizationMember
 from sentry.models.organizationmemberteam import OrganizationMemberTeam
 from sentry.models.outbox import ControlOutbox, OutboxCategory, OutboxScope, outbox_context
@@ -24,6 +25,7 @@ from sentry.models.team import Team, TeamStatus
 from sentry.services.hybrid_cloud import OptionValue, logger
 from sentry.services.hybrid_cloud.app import app_service
 from sentry.services.hybrid_cloud.organization import (
+    OrganizationCheckService,
     OrganizationService,
     OrganizationSignalService,
     RpcOrganization,
@@ -258,17 +260,6 @@ class DatabaseBackedOrganizationService(OrganizationService):
                     return None
         return serialize_member(org_member)
 
-    def check_organization_by_slug(self, *, slug: str, only_visible: bool) -> Optional[int]:
-        try:
-            org = Organization.objects.get_from_cache(slug=slug)
-            if only_visible and org.status != OrganizationStatus.ACTIVE:
-                raise Organization.DoesNotExist
-            return org.id
-        except Organization.DoesNotExist:
-            logger.info("Organization by slug [%s] not found", slug)
-
-        return None
-
     def _query_organizations(
         self, user_id: int, scope: Optional[str], only_visible: bool
     ) -> List[Organization]:
@@ -632,6 +623,34 @@ class DatabaseBackedOrganizationService(OrganizationService):
         return list(map(serialize_member, owner_members))
 
 
+class ControlOrganizationCheckService(OrganizationCheckService):
+    def check_organization_by_slug(self, *, slug: str, only_visible: bool) -> Optional[int]:
+        # See RegionOrganizationCheckService below
+        try:
+            org = OrganizationMapping.objects.get(slug=slug)
+            if only_visible and org.status != OrganizationStatus.ACTIVE:
+                raise OrganizationMapping.DoesNotExist
+            return org.organization_id
+        except OrganizationMapping.DoesNotExist:
+            logger.info("OrganizationMapping by slug [%s] not found", slug)
+
+        return None
+
+
+class RegionOrganizationCheckService(OrganizationCheckService):
+    def check_organization_by_slug(self, *, slug: str, only_visible: bool) -> Optional[int]:
+        # See ControlOrganizationCheckService above
+        try:
+            org = Organization.objects.get_from_cache(slug=slug)
+            if only_visible and org.status != OrganizationStatus.ACTIVE:
+                raise Organization.DoesNotExist
+            return org.id
+        except Organization.DoesNotExist:
+            logger.info("Organization by slug [%s] not found", slug)
+
+        return None
+
+
 class OutboxBackedOrganizationSignalService(OrganizationSignalService):
     def schedule_signal(
         self, signal: Signal, organization_id: int, args: Mapping[str, Optional[Union[str, int]]]

+ 33 - 3
src/sentry/services/hybrid_cloud/organization/service.py

@@ -175,13 +175,13 @@ class OrganizationService(RpcService):
         """
         pass
 
-    @regional_rpc_method(resolve=ByOrganizationSlug(), return_none_if_mapping_not_found=True)
-    @abstractmethod
     def check_organization_by_slug(self, *, slug: str, only_visible: bool) -> Optional[int]:
         """
         If exists and matches the only_visible requirement, returns an organization's id by the slug.
         """
-        pass
+        return _organization_check_service.check_organization_by_slug(
+            slug=slug, only_visible=only_visible
+        )
 
     def get_organization_by_slug(
         self, *, slug: str, only_visible: bool, user_id: Optional[int] = None
@@ -369,6 +369,27 @@ class OrganizationService(RpcService):
         pass
 
 
+class OrganizationCheckService(abc.ABC):
+    @abstractmethod
+    def check_organization_by_slug(self, *, slug: str, only_visible: bool) -> Optional[int]:
+        """
+        If exists and matches the only_visible requirement, returns an organization's id by the slug.
+        """
+        pass
+
+
+def _control_check_organization() -> OrganizationCheckService:
+    from sentry.services.hybrid_cloud.organization.impl import ControlOrganizationCheckService
+
+    return ControlOrganizationCheckService()
+
+
+def _region_check_organization() -> OrganizationCheckService:
+    from sentry.services.hybrid_cloud.organization.impl import RegionOrganizationCheckService
+
+    return RegionOrganizationCheckService()
+
+
 class OrganizationSignalService(abc.ABC):
     @abc.abstractmethod
     def schedule_signal(
@@ -394,6 +415,15 @@ def _signal_from_on_commit() -> OrganizationSignalService:
     return OnCommitBackedOrganizationSignalService()
 
 
+_organization_check_service: OrganizationCheckService = silo_mode_delegation(
+    {
+        SiloMode.REGION: _region_check_organization,
+        SiloMode.CONTROL: _control_check_organization,
+        SiloMode.MONOLITH: _region_check_organization,
+    }
+)
+
+
 _organization_signal_service: OrganizationSignalService = silo_mode_delegation(
     {
         SiloMode.REGION: _signal_from_on_commit,

+ 39 - 0
tests/sentry/services/hybrid_cloud/organization/test_service.py

@@ -0,0 +1,39 @@
+from sentry.models.organization import Organization, OrganizationStatus
+from sentry.services.hybrid_cloud.organization.service import organization_service
+from sentry.testutils.cases import TestCase
+from sentry.testutils.silo import all_silo_test, assume_test_silo_mode_of
+
+
+@all_silo_test
+class CheckOrganizationTest(TestCase):
+    def test_check_active_organization_by_slug(self):
+        self.organization = self.create_organization(slug="test")
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=True)
+            == self.organization.id
+        )
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=False)
+            == self.organization.id
+        )
+
+    def test_check_missing_organization_by_slug(self):
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=True) is None
+        )
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=False) is None
+        )
+
+    def test_check_pending_deletion_organization_by_slug(self):
+        self.organization = self.create_organization(slug="test")
+        self.organization.status = OrganizationStatus.PENDING_DELETION
+        with assume_test_silo_mode_of(Organization):
+            self.organization.save()
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=True) is None
+        )
+        assert (
+            organization_service.check_organization_by_slug(slug="test", only_visible=False)
+            == self.organization.id
+        )