Skip to content
Merged
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
15 changes: 12 additions & 3 deletions src/sentry/deletions/tasks/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@

import sentry_sdk
from taskbroker_client.retry import Retry
from taskbroker_client.worker.workerchild import ProcessingDeadlineExceeded

from sentry import deletions
from sentry.deletions.defaults.group import GROUP_CHUNK_SIZE
from sentry.deletions.tasks.scheduled import MAX_RETRIES, logger
from sentry.exceptions import DeleteAborted
from sentry.models.group import Group
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry, track_group_async_operation
from sentry.tasks.base import instrumented_task, track_group_async_operation
from sentry.taskworker.namespaces import deletion_tasks


@instrumented_task(
name="sentry.deletions.tasks.groups.delete_groups_for_project",
namespace=deletion_tasks,
retry=Retry(times=MAX_RETRIES, delay=60 * 5),
retry=Retry(
times=MAX_RETRIES,
delay=60 * 5,
on=(
Exception,
ProcessingDeadlineExceeded,
),
ignore=(DeleteAborted,),
),
silo_mode=SiloMode.CELL,
silenced_exceptions=(DeleteAborted,),
)
@retry(exclude=(DeleteAborted,), timeouts=True)
@track_group_async_operation
def delete_groups_for_project(
project_id: int,
Expand Down
7 changes: 4 additions & 3 deletions src/sentry/deletions/tasks/nodestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from sentry.silo.base import SiloMode
from sentry.snuba.dataset import Dataset
from sentry.snuba.referrer import Referrer
from sentry.tasks.base import instrumented_task, retry, track_group_async_operation
from sentry.tasks.base import instrumented_task, track_group_async_operation
from sentry.taskworker.namespaces import deletion_tasks
from sentry.utils import metrics
from sentry.utils.retries import ConditionalRetryPolicy, exponential_delay
Expand All @@ -45,13 +45,14 @@ class RetryTask(Exception):
namespace=deletion_tasks,
processing_deadline_duration=60 * 20,
retry=Retry(
on=(RetryTask,),
on=(Exception,),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead RetryTask class after retry migration

Low Severity

The RetryTask exception class is now unused dead code. It was previously referenced in Retry(on=(RetryTask,)), but this change replaced that with Retry(on=(Exception,)). The class definition remains, and the TODO comment on line 145 — "raise RetryTask when appropriate" — is now stale and misleading, since with on=(Exception,) there's no reason to raise a specific RetryTask subclass for retry purposes.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a171e2. Configure here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module local RetryTask does look unused.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove that in a follow up 😁

times=MAX_RETRIES,
delay=60 * 5,
ignore=(DeleteAborted,),
),
silo_mode=SiloMode.CELL,
silenced_exceptions=(DeleteAborted,),
)
@retry(exclude=(DeleteAborted,))
@track_group_async_operation
def delete_events_for_groups_from_nodestore_and_eventstore(
organization_id: int,
Expand Down
11 changes: 8 additions & 3 deletions src/sentry/deletions/tasks/scheduled.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.utils import timezone
from taskbroker_client.retry import LastAction, Retry
from taskbroker_client.task import Task
from taskbroker_client.worker.workerchild import ProcessingDeadlineExceeded

from sentry.deletions.models.scheduleddeletion import (
BaseScheduledDeletion,
Expand All @@ -17,7 +18,7 @@
from sentry.exceptions import DeleteAborted
from sentry.signals import pending_delete
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import deletion_control_tasks, deletion_tasks
from sentry.utils.env import in_test_environment

Expand Down Expand Up @@ -102,10 +103,12 @@ def _run_scheduled_deletions(
times=MAX_RETRIES,
times_exceeded=LastAction.Discard,
delay=60 * 5,
on=(Exception, ProcessingDeadlineExceeded),
ignore=(DeleteAborted,),
),
silo_mode=SiloMode.CONTROL,
silenced_exceptions=(DeleteAborted,),
)
@retry(exclude=(DeleteAborted,), timeouts=True)
def run_deletion_control(deletion_id: int, first_pass: bool = True, **kwargs: Any) -> None:
_run_deletion(
deletion_id=deletion_id,
Expand All @@ -123,10 +126,12 @@ def run_deletion_control(deletion_id: int, first_pass: bool = True, **kwargs: An
times=MAX_RETRIES,
times_exceeded=LastAction.Discard,
delay=60 * 5,
on=(Exception, ProcessingDeadlineExceeded),
ignore=(DeleteAborted,),
),
silo_mode=SiloMode.CELL,
silenced_exceptions=(DeleteAborted,),
)
@retry(exclude=(DeleteAborted,), timeouts=True)
def run_deletion(deletion_id: int, first_pass: bool = True, **kwargs: Any) -> None:
_run_deletion(
deletion_id=deletion_id,
Expand Down
4 changes: 2 additions & 2 deletions src/sentry/tasks/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sentry.organizations.services.organization.service import organization_service
from sentry.silo.base import SiloMode
from sentry.silo.safety import unguarded_write
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import auth_control_tasks, auth_tasks
from sentry.types.cell import CellMappingNotFound
from sentry.users.services.user import RpcUser
Expand Down Expand Up @@ -199,10 +199,10 @@ def call_to_action(self, org: Organization, user: RpcUser, member: OrganizationM
namespace=auth_tasks,
retry=Retry(
delay=60 * 5,
on=(Exception,),
),
silo_mode=SiloMode.CELL,
)
@retry
def remove_2fa_non_compliant_members(org_id, actor_id=None, actor_key_id=None, ip_address=None):
TwoFactorComplianceTask().remove_non_compliant_members(
org_id, actor_id, actor_key_id, ip_address
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/tasks/codeowners/code_owners_auto_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
from sentry.models.projectownership import ProjectOwnership
from sentry.notifications.notifications.codeowners_auto_sync import AutoSyncNotification
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import issues_tasks


@instrumented_task(
name="sentry.tasks.code_owners_auto_sync",
namespace=issues_tasks,
retry=Retry(times=3, delay=60),
retry=Retry(times=3, delay=60, on=(Commit.DoesNotExist,)),
processing_deadline_duration=60,
silo_mode=SiloMode.CELL,
silenced_exceptions=(Commit.DoesNotExist,),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retry only on DoesNotExist silently discards all other errors

Medium Severity

The Retry(on=(Commit.DoesNotExist,)) combined with silenced_exceptions=(Commit.DoesNotExist,) means that Commit.DoesNotExist is both retried AND silenced from Sentry. If all retries are exhausted and the commit still doesn't exist, the final failure will never be reported to Sentry, making permanent failures completely invisible. The old on_silent in the @retry decorator only suppressed reporting during retry attempts — the final RetryTaskError after exhaustion could still surface. Now there's no visibility into cases where a commit permanently doesn't exist after all retries.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 799d45c. Configure here.

)
@retry(on=(), on_silent=(Commit.DoesNotExist,))
def code_owners_auto_sync(commit_id: int, **kwargs: Any) -> None:
from django.db.models import BooleanField, Case, Exists, OuterRef, Subquery, When

Expand Down
5 changes: 2 additions & 3 deletions src/sentry/tasks/codeowners/update_code_owners_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry import features
from sentry.models.organization import Organization, OrganizationStatus
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, load_model_from_db, retry
from sentry.tasks.base import instrumented_task, load_model_from_db
from sentry.taskworker.namespaces import issues_tasks

logger = logging.getLogger(__name__)
Expand All @@ -18,10 +18,9 @@
@instrumented_task(
name="sentry.tasks.update_code_owners_schema",
namespace=issues_tasks,
retry=Retry(times=5, delay=5),
retry=Retry(times=5, delay=5, on=(Exception,)),
silo_mode=SiloMode.CELL,
)
@retry
def update_code_owners_schema(
organization: int,
integration: int | None = None,
Expand Down
11 changes: 8 additions & 3 deletions src/sentry/tasks/commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from sentry.plugins.base import bindings
from sentry.shared_integrations.exceptions import IntegrationError, IntegrationResourceNotFoundError
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import issues_tasks
from sentry.users.models.user import User
from sentry.users.services.user import RpcUser
Expand Down Expand Up @@ -324,10 +324,15 @@ def fetch_commits_for_ref_with_lifecycle(
name="sentry.tasks.commits.fetch_commits",
namespace=issues_tasks,
processing_deadline_duration=60 * 15 + 5,
retry=Retry(times=5, delay=60 * 5),
retry=Retry(
times=5,
delay=60 * 5,
on=(Exception,),
ignore=(Release.DoesNotExist, User.DoesNotExist),
),
silo_mode=SiloMode.CELL,
silenced_exceptions=(Release.DoesNotExist, User.DoesNotExist),
)
@retry(exclude=(Release.DoesNotExist, User.DoesNotExist))
def fetch_commits(
release_id: int,
user_id: int,
Expand Down
8 changes: 3 additions & 5 deletions src/sentry/tasks/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from sentry.auth import access
from sentry.models.group import Group
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import notifications_control_tasks, notifications_tasks
from sentry.users.services.user.model import RpcUser
from sentry.users.services.user.service import user_service
Expand Down Expand Up @@ -74,10 +74,9 @@ def _send_email(message: dict[str, Any]) -> None:
name="sentry.tasks.email.send_email",
namespace=notifications_tasks,
processing_deadline_duration=90,
retry=Retry(times=2, delay=60 * 5),
retry=Retry(times=2, delay=60 * 5, on=(TemporaryEmailError,)),
silo_mode=SiloMode.CELL,
)
@retry(on=(TemporaryEmailError,))
def send_email(message: dict[str, Any]) -> None:
_send_email(message)

Expand All @@ -86,9 +85,8 @@ def send_email(message: dict[str, Any]) -> None:
name="sentry.tasks.email.send_email_control",
namespace=notifications_control_tasks,
processing_deadline_duration=90,
retry=Retry(times=2, delay=60 * 5),
retry=Retry(times=2, delay=60 * 5, on=(TemporaryEmailError,)),
silo_mode=SiloMode.CONTROL,
)
@retry(on=(TemporaryEmailError,))
def send_email_control(message: dict[str, Any]) -> None:
_send_email(message)
6 changes: 3 additions & 3 deletions src/sentry/tasks/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from taskbroker_client.retry import Retry

from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import deletion_control_tasks, deletion_tasks
from sentry.utils.db import atomic_transaction

Expand Down Expand Up @@ -60,10 +60,10 @@ def delete_file(file_blob_model, path, checksum, **kwargs):
retry=Retry(
times=MAX_RETRIES,
delay=60 * 5,
on=(Exception,),
),
silo_mode=SiloMode.CELL,
)
@retry
def delete_unreferenced_blobs_region(blob_ids):
from sentry.models.files.fileblob import FileBlob
from sentry.models.files.fileblobindex import FileBlobIndex
Expand All @@ -77,10 +77,10 @@ def delete_unreferenced_blobs_region(blob_ids):
retry=Retry(
times=MAX_RETRIES,
delay=60 * 5,
on=(Exception,),
),
silo_mode=SiloMode.CONTROL,
)
@retry
def delete_unreferenced_blobs_control(blob_ids):
from sentry.models.files.control_fileblob import ControlFileBlob
from sentry.models.files.control_fileblobindex import ControlFileBlobIndex
Expand Down
5 changes: 2 additions & 3 deletions src/sentry/tasks/groupowner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sentry.models.project import Project
from sentry.models.release import Release
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import issues_tasks
from sentry.users.api.serializers.user import UserSerializerResponse
from sentry.utils import metrics
Expand Down Expand Up @@ -187,10 +187,9 @@ def _process_suspect_commits(
name="sentry.tasks.process_suspect_commits",
namespace=issues_tasks,
processing_deadline_duration=TASK_DURATION_S,
retry=Retry(times=5, delay=5),
retry=Retry(times=5, delay=5, on=(Exception,)),
silo_mode=SiloMode.CELL,
)
@retry
def process_suspect_commits(
event_id,
event_platform,
Expand Down
5 changes: 2 additions & 3 deletions src/sentry/tasks/reprocessing2.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from sentry.services import eventstore
from sentry.services.eventstore.models import Event
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.tasks.process_buffer import buffer_incr
from sentry.taskworker.namespaces import issues_tasks
from sentry.types.activity import ActivityType
Expand Down Expand Up @@ -141,10 +141,9 @@ def reprocess_group(
name="sentry.tasks.reprocessing2.handle_remaining_events",
namespace=issues_tasks,
processing_deadline_duration=60 * 5,
retry=Retry(times=5),
retry=Retry(times=5, on=(Exception,)),
silo_mode=SiloMode.CELL,
)
@retry
def handle_remaining_events(
project_id: int,
new_group_id: int,
Expand Down
14 changes: 9 additions & 5 deletions src/sentry/tasks/summaries/weekly_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from sentry.models.organizationmember import OrganizationMember
from sentry.notifications.services import notifications_service
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.tasks.summaries.metrics import (
WeeklyReportHaltReason,
WeeklyReportOperationType,
Expand Down Expand Up @@ -101,11 +101,16 @@ def delete_min_org_id(self) -> None:
@instrumented_task(
name="sentry.tasks.summaries.weekly_reports.schedule_organizations",
namespace=reports_tasks,
retry=Retry(times=5),
retry=Retry(
times=5,
on=(
Exception,
ProcessingDeadlineExceeded,
),
),
processing_deadline_duration=timedelta(minutes=30),
silo_mode=SiloMode.CELL,
)
@retry(timeouts=True)
def schedule_organizations(
dry_run: bool = False, timestamp: float | None = None, duration: int | None = None
) -> None:
Expand Down Expand Up @@ -162,10 +167,9 @@ def schedule_organizations(
name="sentry.tasks.summaries.weekly_reports.prepare_organization_report",
namespace=reports_tasks,
processing_deadline_duration=60 * 10,
retry=Retry(times=5, delay=5),
retry=Retry(times=5, delay=5, on=(Exception,)),
silo_mode=SiloMode.CELL,
)
@retry
def prepare_organization_report(
timestamp: float,
duration: int,
Expand Down
5 changes: 2 additions & 3 deletions src/sentry/tasks/weekly_escalating_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry.models.group import Group, GroupStatus
from sentry.models.project import Project
from sentry.silo.base import SiloMode
from sentry.tasks.base import instrumented_task, retry
from sentry.tasks.base import instrumented_task
from sentry.taskworker.namespaces import issues_tasks
from sentry.types.group import GroupSubStatus
from sentry.utils.iterators import chunked
Expand Down Expand Up @@ -55,10 +55,9 @@ def run_escalating_forecast() -> None:
name="sentry.tasks.weekly_escalating_forecast.generate_forecasts_for_projects",
namespace=issues_tasks,
processing_deadline_duration=60 * 2,
retry=Retry(times=3, delay=60),
retry=Retry(times=3, delay=60, on=(Exception,)),
silo_mode=SiloMode.CELL,
)
@retry
def generate_forecasts_for_projects(project_ids: list[int]) -> None:
for until_escalating_groups in chunked(
RangeQuerySetWrapper(
Expand Down
12 changes: 0 additions & 12 deletions tests/sentry/tasks/test_code_owners.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from unittest.mock import MagicMock, patch

import pytest
from rest_framework.exceptions import NotFound
from taskbroker_client.retry import RetryTaskError

from sentry.api.validators.project_codeowners import build_codeowners_associations
from sentry.integrations.models.external_actor import ExternalActor
Expand Down Expand Up @@ -303,16 +301,6 @@ def test_post_bulk_create_commit_file_changes_triggers_auto_sync_task(

mock_code_owners_auto_sync.delay.assert_called_once_with(commit_id=commit.id)

def test_codeowners_auto_sync_retries_on_missing_commit(self) -> None:
non_existent_commit_id = 999999999

with self.tasks() and self.feature({"organizations:integrations-codeowners": True}):
with pytest.raises(RetryTaskError):
code_owners_auto_sync(non_existent_commit_id)

code_owners = ProjectCodeOwners.objects.get(id=self.code_owners.id)
assert code_owners.raw == self.data["raw"]

@patch(
"sentry.integrations.github.integration.GitHubIntegration.get_codeowner_file",
side_effect=NotImplementedError("Integration does not support CODEOWNERS"),
Expand Down
Loading
Loading