Skip to content

fix(eco): Ensures user mappings are deleted alongside their associated organization membership#109249

Open
GabeVillalobos wants to merge 3 commits intomasterfrom
fix-orphaned-user-mappings
Open

fix(eco): Ensures user mappings are deleted alongside their associated organization membership#109249
GabeVillalobos wants to merge 3 commits intomasterfrom
fix-orphaned-user-mappings

Conversation

@GabeVillalobos
Copy link
Member

@GabeVillalobos GabeVillalobos commented Feb 24, 2026

Currently, when OrganizationMembers are deleted, we don't clean up the assocaited ExternalActor objects that reference their user and organization IDs. As a result, we orphan these mappings, causing collisions in the future when that same user attempts to re-add their external mapping.

This ensures these deletions are done in an eventually consistent manner, post-deletion.

Fixes ISWF-2122

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Feb 24, 2026
@GabeVillalobos GabeVillalobos requested a review from a team February 24, 2026 21:48
@GabeVillalobos GabeVillalobos changed the title fix(eco): Ensures user mappings are deleted alongside their associated organization mapping fix(eco): Ensures user mappings are deleted alongside their associated organization membership Feb 24, 2026
@GabeVillalobos GabeVillalobos marked this pull request as ready for review February 24, 2026 22:24
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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@linear
Copy link

linear bot commented Feb 24, 2026


if instance.user_id is not None:
# We need to clean up external actors (user mappings for integrations),
# but only if the org member is not an invite.
Copy link
Member

Choose a reason for hiding this comment

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

What does it mean to be an invited member and how are we filtering those out? I see the test for it but I'm still a little confused.

Copy link
Member Author

Choose a reason for hiding this comment

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

OrganizationMember objects have 2 major states:

  1. Active organization user in the Sentry org, has a fk to user, roles, etc...
  2. Invited user, not yet active, which only contains an email address, no user fk, and a separate invite status to track whether they've accepted the invite or not, along with the usual role information.

It's sort of confusing that we do it this way, but the lack of a user ID on the org member is a way to check whether or not the "org member" is actually an active user.

Check the invite code for an example of this:

Comment on lines +14 to +19
relations.append(
ModelRelation(
ExternalActor,
{"user_id": instance.user_id, "organization_id": instance.organization_id},
)
)
Copy link
Member

Choose a reason for hiding this comment

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

This will work well when members are removed as part of an organization being deleted. However, the simple case of sending a DELETE request to OrganizationMemberDetails won't hit this path, as it uses the django ORM directly.

I think we would also need to remove related actors in the model.delete() situation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm could I just update that path to use the scheduled deletion, or synchronously execute the deletion task in that code? This reminds me of the previous challenges we hit in HC where we need consistency in ways the ORM lets you bypass in weird ways 😓

I guess the heavyweight approach could be to use outboxes to drive this instead?

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.

3 participants