|
1 | 1 | import logging |
2 | 2 | from collections.abc import Sequence |
3 | | -from contextlib import contextmanager |
4 | 3 | from copy import deepcopy |
5 | 4 | from datetime import UTC, datetime |
6 | 5 |
|
7 | | -from django.db import connections, router |
| 6 | +from django.db import connections, router, transaction |
8 | 7 | from django.db.models import ( |
9 | 8 | Case, |
10 | 9 | DateTimeField, |
|
45 | 44 | from sentry.apidocs.utils import inline_sentry_response_serializer |
46 | 45 | from sentry.constants import ObjectStatus |
47 | 46 | from sentry.db.models.manager.base_query_set import BaseQuerySet |
| 47 | +from sentry.db.postgres.transactions import in_test_hide_transaction_boundary |
48 | 48 | from sentry.exceptions import InvalidParams |
49 | 49 | from sentry.incidents.endpoints.bases import OrganizationAlertRuleBaseEndpoint |
50 | 50 | from sentry.incidents.endpoints.serializers.alert_rule import ( |
|
115 | 115 | logger = logging.getLogger(__name__) |
116 | 116 |
|
117 | 117 |
|
118 | | -@contextmanager |
119 | | -def _postgres_jit_disabled(using: str): |
120 | | - """ |
121 | | - Disable PostgreSQL JIT compilation on the given database connection for the duration of |
122 | | - the block. |
123 | | -
|
124 | | - Uses session-level SET/RESET rather than SET LOCAL (which requires a transaction) so that |
125 | | - callers don't need to open a transaction — important when the calling code may issue queries |
126 | | - against multiple databases. |
127 | | - """ |
128 | | - with connections[using].cursor() as cursor: |
129 | | - cursor.execute("SET jit = off") |
130 | | - try: |
131 | | - yield |
132 | | - finally: |
133 | | - with connections[using].cursor() as cursor: |
134 | | - cursor.execute("RESET jit") |
135 | | - |
136 | | - |
137 | 118 | # Sentinel values for incident_status annotation when sorting combined rules |
138 | 119 | # Used to ensure proper sort order for rules without active incidents |
139 | 120 | INCIDENT_STATUS_NONE = -1 # Metric alerts with no active incident |
@@ -549,11 +530,14 @@ def has_type(rule_type: str) -> bool: |
549 | 530 | # Disabling it makes this endpoint considerably faster. |
550 | 531 | # The risk of other regression here should be low; our API endpoint isn't generally doing the sort of bulk |
551 | 532 | # work that benefits from JIT. |
552 | | - # |
553 | | - # Note: Monitor lives on a different database, so we can't use a transaction here |
554 | | - # (SET LOCAL would require one). Session-level SET + RESET is fine for a request-scoped |
555 | | - # connection. |
556 | | - with _postgres_jit_disabled(using=router.db_for_write(Detector)): |
| 533 | + # in_test_hide_transaction_boundary is safe here: this transaction is only |
| 534 | + # used to scope SET LOCAL, not to guard data mutations. No writes happen |
| 535 | + # inside this block, so there's no cross-db atomicity concern to enforce. |
| 536 | + db = router.db_for_write(Detector) |
| 537 | + with in_test_hide_transaction_boundary(), transaction.atomic(using=db): |
| 538 | + with connections[db].cursor() as cursor: |
| 539 | + cursor.execute("SET LOCAL jit = off") |
| 540 | + |
557 | 541 | intermediaries: list[CombinedQuerysetIntermediary] = [] |
558 | 542 | if has_type("alert_rule"): |
559 | 543 | intermediaries.append(CombinedQuerysetIntermediary(metric_detectors, sort_key)) |
|
0 commit comments