Skip to content

Conversation

cathteng
Copy link
Member

@cathteng cathteng commented Oct 3, 2025

Deleting a sentry app installation should use async deletions.

The tests for async sentry app deletion cover the below case:

  • Sentry app installations are already collected when we delete the sentry app so we shouldn't need to have extra deletions queued up / done before the task.

ModelRelation(SentryAppInstallation, {"sentry_app_id": instance.id}),

Required for #100813

@cathteng cathteng requested review from a team as code owners October 3, 2025 22:28
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Oct 3, 2025
sentry_app_installation=install, user=request.user, action="deleted"
).run()
deletions.exec_sync(install)
ScheduledDeletion.schedule(install, days=0, actor=request.user)
Copy link
Member Author

Choose a reason for hiding this comment

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

Is it necessary to do this if we already collect installations in the sentry app deletion task?

ModelRelation(SentryAppInstallation, {"sentry_app_id": instance.id}),

I'm not sure if this is necessary at all. It seems like we delete the installations regardless of whether the sentry app is internal or not

@cathteng cathteng changed the title chore(eco): async deletions for sentry app installations chore(eco): remove redundant deletion for sentry app installations Oct 3, 2025
cursor[bot]

This comment was marked as outdated.

@cathteng cathteng changed the title chore(eco): remove redundant deletion for sentry app installations chore(eco): async deletions for sentry app installations Oct 3, 2025
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Notification Timing Mismatch on App Deletion

Sentry App installation "deleted" notifications are sent immediately, but the actual installation deletion is now delayed to a scheduled task. This timing mismatch means external systems receive deletion notifications for installations that still exist in the database.

src/sentry/sentry_apps/api/endpoints/sentry_app_details.py#L223-L234

if not sentry_app.is_internal:
for install in sentry_app.installations.all():
try:
with transaction.atomic(using=router.db_for_write(SentryAppInstallation)):
assert (
request.user.is_authenticated
), "User must be authenticated to delete installation"
SentryAppInstallationNotifier(
sentry_app_installation=install, user=request.user, action="deleted"
).run()
except RequestException as exc:
sentry_sdk.capture_exception(exc)

Fix in Cursor Fix in Web


except RequestException as exc:
sentry_sdk.capture_exception(exc)
deletions.exec_sync(sentry_app_installation)
ScheduledDeletion.schedule(sentry_app_installation, days=0, actor=request.user)
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Async Deletion Causes Audit Log Inconsistency

Switching to asynchronous deletion means the API returns success (204) and audit/analytics events are recorded before the installation is actually removed, creating a race condition and inconsistent state. The audit log entry also lacks a link to the scheduled deletion, and the scheduled deletion record itself may be routed to the wrong database.

Fix in Cursor Fix in Web

Copy link

codecov bot commented Oct 3, 2025

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
28630 1 28629 241
View the top 1 failed test(s) by shortest run time
tests.acceptance.test_organization_sentry_app_detailed_view.OrganizationSentryAppDetailedView::test_uninstallation
Stack Traces | 41.3s run time
#x1B[1m#x1B[31mtests/acceptance/test_organization_sentry_app_detailed_view.py#x1B[0m:52: in test_uninstallation
    assert not SentryAppInstallation.objects.filter(
#x1B[1m#x1B[31mE   AssertionError: assert not <ParanoidQuerySet [<SentryAppInstallation at 0x7fd6d8b38100: id=8>]>#x1B[0m
#x1B[1m#x1B[31mE    +  where <ParanoidQuerySet [<SentryAppInstallation at 0x7fd6d8b38100: id=8>]> = <bound method QuerySet.filter of <sentry.db.models.manager.base.SentryAppInstallationForProviderManager object at 0x7fd70798bb60>>(organization_id=4556876071698432, sentry_app=<SentryApp at 0x7fd6d8b8f250: id=9>)#x1B[0m
#x1B[1m#x1B[31mE    +    where <bound method QuerySet.filter of <sentry.db.models.manager.base.SentryAppInstallationForProviderManager object at 0x7fd70798bb60>> = <sentry.db.models.manager.base.SentryAppInstallationForProviderManager object at 0x7fd70798bb60>.filter#x1B[0m
#x1B[1m#x1B[31mE    +      where <sentry.db.models.manager.base.SentryAppInstallationForProviderManager object at 0x7fd70798bb60> = SentryAppInstallation.objects#x1B[0m
#x1B[1m#x1B[31mE    +    and   4556876071698432 = <Organization at 0x7fd6d8b38aa0: id=4556876071698432, owner_id=None, name='baz', slug='baz'>.id#x1B[0m
#x1B[1m#x1B[31mE    +      where <Organization at 0x7fd6d8b38aa0: id=4556876071698432, owner_id=None, name='baz', slug='baz'> = <sentry.testutils.silo.OrganizationSentryAppDetailedView testMethod=test_uninstallation>.organization#x1B[0m
#x1B[1m#x1B[31mE    +    and   <SentryApp at 0x7fd6d8b8f250: id=9> = <sentry.testutils.silo.OrganizationSentryAppDetailedView testMethod=test_uninstallation>.sentry_app#x1B[0m

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant