Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/sentry/db/deletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from django.db import connections, router
from django.utils import timezone

from sentry.db.postgres.transactions import unset_statement_timeout
from sentry.utils.query import RangeQuerySetWrapper


Expand Down Expand Up @@ -108,5 +109,7 @@ def iterator(self, chunk_size=100, batch_size=10000) -> Generator[tuple[int, ...
result_value_getter=lambda item: item[1],
)

for batch in itertools.batched(wrapper, chunk_size):
yield tuple(item[0] for item in batch)
# Disable statement_timeout for long-running cleanup queries
with unset_statement_timeout(self.using):
for batch in itertools.batched(wrapper, chunk_size):
yield tuple(item[0] for item in batch)
31 changes: 31 additions & 0 deletions src/sentry/db/postgres/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,34 @@ def enforce_constraints(transaction: Atomic) -> Generator[None]:
with transaction:
yield
get_connection(transaction.using or "default").check_constraints()


@contextlib.contextmanager
def unset_statement_timeout(using: str) -> Generator[None]:
"""
Temporarily disables the statement_timeout for long-running database operations.

This is useful for operations like cleanup tasks that need to run expensive queries
that might exceed the default statement_timeout configured on the database.

The previous timeout value is restored after the context exits.
"""
connection = connections[using]

# Only PostgreSQL databases support statement_timeout
if connection.vendor != "postgresql":
yield
return

with connection.cursor() as cursor:
# Save the current statement_timeout value
cursor.execute("SHOW statement_timeout")
previous_timeout = cursor.fetchone()[0]

try:
# Disable statement_timeout (0 means unlimited)
cursor.execute("SET statement_timeout = 0")
yield
finally:
# Restore the previous timeout value
cursor.execute(f"SET statement_timeout = '{previous_timeout}'")