From b2bda96e89eab249ede1364ad0f12b0b9b186c63 Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 02:18:09 -0700 Subject: [PATCH 1/6] chore: Move function out of receivers --- src/sentry/receivers/core.py | 21 +-------------------- src/sentry/utils/db.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index 673218ad913c06..e5f94663febc56 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -1,12 +1,8 @@ -import logging -from functools import wraps - from click import echo from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.db import connections, router, transaction from django.db.models.signals import post_save -from django.db.utils import OperationalError, ProgrammingError from sentry.hybridcloud.models.outbox import outbox_context from sentry.loader.dynamic_sdk_options import get_default_loader_data @@ -19,6 +15,7 @@ from sentry.signals import post_upgrade, project_created from sentry.silo.base import SiloMode, region_silo_function from sentry.users.services.user.service import user_service +from sentry.utils.db import handle_db_failure from sentry.utils.env import in_test_environment from sentry.utils.settings import is_self_hosted @@ -30,22 +27,6 @@ DEFAULT_SENTRY_PROJECT_ID = 1 -def handle_db_failure(func, using=None, wrap_in_transaction=True): - @wraps(func) - def wrapped(*args, **kwargs): - try: - if wrap_in_transaction: - with transaction.atomic(using or router.db_for_write(Organization)): - return func(*args, **kwargs) - else: - return func(*args, **kwargs) - except (ProgrammingError, OperationalError): - logging.exception("Failed processing signal %s", func.__name__) - return - - return wrapped - - def create_default_projects(**kwds): if not (in_test_environment() or is_self_hosted() or settings.DEBUG): # No op in production SaaS environments. diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index 461d558d12ed07..a9d6cfd53d6269 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -1,11 +1,30 @@ +import logging from collections.abc import Sequence from contextlib import ExitStack +from functools import wraps import sentry_sdk from django.db import DEFAULT_DB_ALIAS, connections, transaction +from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration +def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): + @wraps(func) + def wrapped(*args, **kwargs): + try: + if wrap_in_transaction: + with transaction.atomic(using): + return func(*args, **kwargs) + else: + return func(*args, **kwargs) + except (ProgrammingError, OperationalError): + logging.exception("Failed processing signal %s", func.__name__) + return + + return wrapped + + def atomic_transaction( using: str | Sequence[str], savepoint: bool = True ) -> transaction.Atomic | ExitStack: From b90324f07f5423dca86d81387525ebc19b8ea455 Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 02:23:00 -0700 Subject: [PATCH 2/6] Revert "chore: Move function out of receivers" This reverts commit b2bda96e89eab249ede1364ad0f12b0b9b186c63. --- src/sentry/receivers/core.py | 21 ++++++++++++++++++++- src/sentry/utils/db.py | 19 ------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index e5f94663febc56..673218ad913c06 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -1,8 +1,12 @@ +import logging +from functools import wraps + from click import echo from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.db import connections, router, transaction from django.db.models.signals import post_save +from django.db.utils import OperationalError, ProgrammingError from sentry.hybridcloud.models.outbox import outbox_context from sentry.loader.dynamic_sdk_options import get_default_loader_data @@ -15,7 +19,6 @@ from sentry.signals import post_upgrade, project_created from sentry.silo.base import SiloMode, region_silo_function from sentry.users.services.user.service import user_service -from sentry.utils.db import handle_db_failure from sentry.utils.env import in_test_environment from sentry.utils.settings import is_self_hosted @@ -27,6 +30,22 @@ DEFAULT_SENTRY_PROJECT_ID = 1 +def handle_db_failure(func, using=None, wrap_in_transaction=True): + @wraps(func) + def wrapped(*args, **kwargs): + try: + if wrap_in_transaction: + with transaction.atomic(using or router.db_for_write(Organization)): + return func(*args, **kwargs) + else: + return func(*args, **kwargs) + except (ProgrammingError, OperationalError): + logging.exception("Failed processing signal %s", func.__name__) + return + + return wrapped + + def create_default_projects(**kwds): if not (in_test_environment() or is_self_hosted() or settings.DEBUG): # No op in production SaaS environments. diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index a9d6cfd53d6269..461d558d12ed07 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -1,30 +1,11 @@ -import logging from collections.abc import Sequence from contextlib import ExitStack -from functools import wraps import sentry_sdk from django.db import DEFAULT_DB_ALIAS, connections, transaction -from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration -def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): - @wraps(func) - def wrapped(*args, **kwargs): - try: - if wrap_in_transaction: - with transaction.atomic(using): - return func(*args, **kwargs) - else: - return func(*args, **kwargs) - except (ProgrammingError, OperationalError): - logging.exception("Failed processing signal %s", func.__name__) - return - - return wrapped - - def atomic_transaction( using: str | Sequence[str], savepoint: bool = True ) -> transaction.Atomic | ExitStack: From 18988e33d1558033d790167fc9ce559c1fb3c22b Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 02:23:13 -0700 Subject: [PATCH 3/6] Revert "Revert "chore: Move function out of receivers"" This reverts commit b90324f07f5423dca86d81387525ebc19b8ea455. --- src/sentry/receivers/core.py | 21 +-------------------- src/sentry/utils/db.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index 673218ad913c06..e5f94663febc56 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -1,12 +1,8 @@ -import logging -from functools import wraps - from click import echo from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.db import connections, router, transaction from django.db.models.signals import post_save -from django.db.utils import OperationalError, ProgrammingError from sentry.hybridcloud.models.outbox import outbox_context from sentry.loader.dynamic_sdk_options import get_default_loader_data @@ -19,6 +15,7 @@ from sentry.signals import post_upgrade, project_created from sentry.silo.base import SiloMode, region_silo_function from sentry.users.services.user.service import user_service +from sentry.utils.db import handle_db_failure from sentry.utils.env import in_test_environment from sentry.utils.settings import is_self_hosted @@ -30,22 +27,6 @@ DEFAULT_SENTRY_PROJECT_ID = 1 -def handle_db_failure(func, using=None, wrap_in_transaction=True): - @wraps(func) - def wrapped(*args, **kwargs): - try: - if wrap_in_transaction: - with transaction.atomic(using or router.db_for_write(Organization)): - return func(*args, **kwargs) - else: - return func(*args, **kwargs) - except (ProgrammingError, OperationalError): - logging.exception("Failed processing signal %s", func.__name__) - return - - return wrapped - - def create_default_projects(**kwds): if not (in_test_environment() or is_self_hosted() or settings.DEBUG): # No op in production SaaS environments. diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index 461d558d12ed07..a9d6cfd53d6269 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -1,11 +1,30 @@ +import logging from collections.abc import Sequence from contextlib import ExitStack +from functools import wraps import sentry_sdk from django.db import DEFAULT_DB_ALIAS, connections, transaction +from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration +def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): + @wraps(func) + def wrapped(*args, **kwargs): + try: + if wrap_in_transaction: + with transaction.atomic(using): + return func(*args, **kwargs) + else: + return func(*args, **kwargs) + except (ProgrammingError, OperationalError): + logging.exception("Failed processing signal %s", func.__name__) + return + + return wrapped + + def atomic_transaction( using: str | Sequence[str], savepoint: bool = True ) -> transaction.Atomic | ExitStack: From 482790b7f010c010ed300759a1ff25e4da8b0856 Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 03:21:31 -0700 Subject: [PATCH 4/6] move import again --- src/sentry/db/postgres/helpers.py | 22 +++++++++++++++++++++- src/sentry/receivers/core.py | 8 ++++---- src/sentry/utils/db.py | 19 ------------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/sentry/db/postgres/helpers.py b/src/sentry/db/postgres/helpers.py index ae8ffd5deabfbc..d5bd6202bb2fa8 100644 --- a/src/sentry/db/postgres/helpers.py +++ b/src/sentry/db/postgres/helpers.py @@ -1,5 +1,25 @@ +import logging +from functools import wraps + import psycopg2 -from django.db.utils import DatabaseError, InterfaceError +from django.db import router, transaction +from django.db.utils import DatabaseError, InterfaceError, OperationalError, ProgrammingError + + +def handle_db_failure(func, model, wrap_in_transaction: bool = True): + @wraps(func) + def wrapped(*args, **kwargs): + try: + if wrap_in_transaction: + with transaction.atomic(router.db_for_write(model)): + return func(*args, **kwargs) + else: + return func(*args, **kwargs) + except (ProgrammingError, OperationalError): + logging.exception("Failed processing signal %s", func.__name__) + return + + return wrapped def can_reconnect(exc): diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index e5f94663febc56..c37c54179fdd00 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -4,6 +4,7 @@ from django.db import connections, router, transaction from django.db.models.signals import post_save +from sentry.db.postgres.helpers import handle_db_failure from sentry.hybridcloud.models.outbox import outbox_context from sentry.loader.dynamic_sdk_options import get_default_loader_data from sentry.models.organization import Organization @@ -15,7 +16,6 @@ from sentry.signals import post_upgrade, project_created from sentry.silo.base import SiloMode, region_silo_function from sentry.users.services.user.service import user_service -from sentry.utils.db import handle_db_failure from sentry.utils.env import in_test_environment from sentry.utils.settings import is_self_hosted @@ -132,20 +132,20 @@ def freeze_option_epoch_for_project(instance, created, app=None, **kwargs): # Anything that relies on default objects that may not exist with default # fields should be wrapped in handle_db_failure post_upgrade.connect( - handle_db_failure(create_default_projects, wrap_in_transaction=False), + handle_db_failure(create_default_projects, model=Organization, wrap_in_transaction=False), dispatch_uid="create_default_project", weak=False, sender=SiloMode.MONOLITH, ) post_save.connect( - handle_db_failure(freeze_option_epoch_for_project), + handle_db_failure(freeze_option_epoch_for_project, model=Organization), sender=Project, dispatch_uid="freeze_option_epoch_for_project", weak=False, ) post_save.connect( - handle_db_failure(create_keys_for_project), + handle_db_failure(create_keys_for_project, model=Organization), sender=Project, dispatch_uid="create_keys_for_project", weak=False, diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index a9d6cfd53d6269..461d558d12ed07 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -1,30 +1,11 @@ -import logging from collections.abc import Sequence from contextlib import ExitStack -from functools import wraps import sentry_sdk from django.db import DEFAULT_DB_ALIAS, connections, transaction -from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration -def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): - @wraps(func) - def wrapped(*args, **kwargs): - try: - if wrap_in_transaction: - with transaction.atomic(using): - return func(*args, **kwargs) - else: - return func(*args, **kwargs) - except (ProgrammingError, OperationalError): - logging.exception("Failed processing signal %s", func.__name__) - return - - return wrapped - - def atomic_transaction( using: str | Sequence[str], savepoint: bool = True ) -> transaction.Atomic | ExitStack: From d9645b9a047c7f5e22cc10408265eff22fb99a2e Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 03:39:29 -0700 Subject: [PATCH 5/6] Revert "move import again" This reverts commit 482790b7f010c010ed300759a1ff25e4da8b0856. --- src/sentry/db/postgres/helpers.py | 22 +--------------------- src/sentry/receivers/core.py | 8 ++++---- src/sentry/utils/db.py | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/sentry/db/postgres/helpers.py b/src/sentry/db/postgres/helpers.py index d5bd6202bb2fa8..ae8ffd5deabfbc 100644 --- a/src/sentry/db/postgres/helpers.py +++ b/src/sentry/db/postgres/helpers.py @@ -1,25 +1,5 @@ -import logging -from functools import wraps - import psycopg2 -from django.db import router, transaction -from django.db.utils import DatabaseError, InterfaceError, OperationalError, ProgrammingError - - -def handle_db_failure(func, model, wrap_in_transaction: bool = True): - @wraps(func) - def wrapped(*args, **kwargs): - try: - if wrap_in_transaction: - with transaction.atomic(router.db_for_write(model)): - return func(*args, **kwargs) - else: - return func(*args, **kwargs) - except (ProgrammingError, OperationalError): - logging.exception("Failed processing signal %s", func.__name__) - return - - return wrapped +from django.db.utils import DatabaseError, InterfaceError def can_reconnect(exc): diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index c37c54179fdd00..e5f94663febc56 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -4,7 +4,6 @@ from django.db import connections, router, transaction from django.db.models.signals import post_save -from sentry.db.postgres.helpers import handle_db_failure from sentry.hybridcloud.models.outbox import outbox_context from sentry.loader.dynamic_sdk_options import get_default_loader_data from sentry.models.organization import Organization @@ -16,6 +15,7 @@ from sentry.signals import post_upgrade, project_created from sentry.silo.base import SiloMode, region_silo_function from sentry.users.services.user.service import user_service +from sentry.utils.db import handle_db_failure from sentry.utils.env import in_test_environment from sentry.utils.settings import is_self_hosted @@ -132,20 +132,20 @@ def freeze_option_epoch_for_project(instance, created, app=None, **kwargs): # Anything that relies on default objects that may not exist with default # fields should be wrapped in handle_db_failure post_upgrade.connect( - handle_db_failure(create_default_projects, model=Organization, wrap_in_transaction=False), + handle_db_failure(create_default_projects, wrap_in_transaction=False), dispatch_uid="create_default_project", weak=False, sender=SiloMode.MONOLITH, ) post_save.connect( - handle_db_failure(freeze_option_epoch_for_project, model=Organization), + handle_db_failure(freeze_option_epoch_for_project), sender=Project, dispatch_uid="freeze_option_epoch_for_project", weak=False, ) post_save.connect( - handle_db_failure(create_keys_for_project, model=Organization), + handle_db_failure(create_keys_for_project), sender=Project, dispatch_uid="create_keys_for_project", weak=False, diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index 461d558d12ed07..a9d6cfd53d6269 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -1,11 +1,30 @@ +import logging from collections.abc import Sequence from contextlib import ExitStack +from functools import wraps import sentry_sdk from django.db import DEFAULT_DB_ALIAS, connections, transaction +from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration +def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): + @wraps(func) + def wrapped(*args, **kwargs): + try: + if wrap_in_transaction: + with transaction.atomic(using): + return func(*args, **kwargs) + else: + return func(*args, **kwargs) + except (ProgrammingError, OperationalError): + logging.exception("Failed processing signal %s", func.__name__) + return + + return wrapped + + def atomic_transaction( using: str | Sequence[str], savepoint: bool = True ) -> transaction.Atomic | ExitStack: From aed3d5147d8b6cd900b74df65499c9895a201785 Mon Sep 17 00:00:00 2001 From: schew2381 Date: Thu, 15 Aug 2024 04:35:03 -0700 Subject: [PATCH 6/6] allow passing model --- src/sentry/receivers/core.py | 6 +++--- src/sentry/utils/db.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sentry/receivers/core.py b/src/sentry/receivers/core.py index e5f94663febc56..a6614833cf0f59 100644 --- a/src/sentry/receivers/core.py +++ b/src/sentry/receivers/core.py @@ -132,20 +132,20 @@ def freeze_option_epoch_for_project(instance, created, app=None, **kwargs): # Anything that relies on default objects that may not exist with default # fields should be wrapped in handle_db_failure post_upgrade.connect( - handle_db_failure(create_default_projects, wrap_in_transaction=False), + handle_db_failure(create_default_projects, model=Organization, wrap_in_transaction=False), dispatch_uid="create_default_project", weak=False, sender=SiloMode.MONOLITH, ) post_save.connect( - handle_db_failure(freeze_option_epoch_for_project), + handle_db_failure(freeze_option_epoch_for_project, model=Organization), sender=Project, dispatch_uid="freeze_option_epoch_for_project", weak=False, ) post_save.connect( - handle_db_failure(create_keys_for_project), + handle_db_failure(create_keys_for_project, model=Organization), sender=Project, dispatch_uid="create_keys_for_project", weak=False, diff --git a/src/sentry/utils/db.py b/src/sentry/utils/db.py index a9d6cfd53d6269..e6035f46b5acfe 100644 --- a/src/sentry/utils/db.py +++ b/src/sentry/utils/db.py @@ -4,17 +4,17 @@ from functools import wraps import sentry_sdk -from django.db import DEFAULT_DB_ALIAS, connections, transaction +from django.db import DEFAULT_DB_ALIAS, connections, router, transaction from django.db.utils import OperationalError, ProgrammingError from sentry_sdk.integrations import Integration -def handle_db_failure(func, using: str = "default", wrap_in_transaction: bool = True): +def handle_db_failure(func, model, wrap_in_transaction: bool = True): @wraps(func) def wrapped(*args, **kwargs): try: if wrap_in_transaction: - with transaction.atomic(using): + with transaction.atomic(router.db_for_write(model)): return func(*args, **kwargs) else: return func(*args, **kwargs)