Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscriptions: avoid double deletion #9341

Merged
merged 2 commits into from
Jan 24, 2023
Merged

Subscriptions: avoid double deletion #9341

merged 2 commits into from
Jan 24, 2023

Conversation

stsewd
Copy link
Member

@stsewd stsewd commented Jun 15, 2022

Subscription is set to cascade delete
when an organization is deleted,
with this additional signal
we are cancelling the subscription twice,
and stripe is given an error.

Subscription is set to cascade delete
when an organization is deleted,
with this additional signal
we are cancelling the subscription twice,
and stripe is given an error.
@stsewd stsewd requested a review from a team as a code owner June 15, 2022 22:52
@stsewd stsewd requested a review from benjaoming June 15, 2022 22:52
Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

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

Where are we canceling the subscription on Stripe when the organization is deleted?

@stsewd
Copy link
Member Author

stsewd commented Jun 16, 2022

@humitos

@receiver(pre_delete, sender=Subscription)
def remove_stripe_subscription(sender, instance, using, **kwargs):

Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

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

I'm not sure to understand these changes.

Subscription is set to cascade delete when an organization is deleted

If the Subscription is set to CASCADE, it won't trigger Django signals and then

@receiver(pre_delete, sender=Subscription)
def remove_stripe_subscription(sender, instance, using, **kwargs):
won't be called either. Am I correct?

I think we need to connect remove_stripe_subscription to pre_delete on Organization. Otherwise, it won't be executed.

If I'm not correct, can you please explain to me the flow happening when an organization is deleted by the user?

@stsewd
Copy link
Member Author

stsewd commented Jun 20, 2022

If the Subscription is set to CASCADE, it won't trigger Django signals and then

It will, when you delete an org, all their related objects will be deleted as you manually called .delete() on each one, that's why tests are passing.

@humitos
Copy link
Member

humitos commented Jun 20, 2022

@stsewd

It will, when you delete an org, all their related objects will be deleted as you manually called .delete() on each one, that's why tests are passing.

I think you are confused about this, .delete() is not called. Take a look at the Django documentation https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.CASCADE. It explicitly says that it does not call .delete()

However, the last sentence says,

but the pre_delete and post_delete signals are sent for all deleted objects

I understand that "for all deleted objects" only applies to the original objects, not the CASCADEd ones because Django does not really know what are those objects, so it can't trigger those signals for them.

I want to be 100% sure that we are understanding exactly how it works because I want to prevent any issues with the subscriptions that are hard to detect and affect directly our income and onboarding UX + support time, etc

@stsewd
Copy link
Member Author

stsewd commented Jun 20, 2022

@humitos

When Django deletes an object, by default it emulates the behavior of the SQL constraint ON DELETE CASCADE – in other words, any objects which had foreign keys pointing at the object to be deleted will be deleted along with it. For example:

https://docs.djangoproject.com/en/4.0/topics/db/queries/#topics-db-queries-delete

In any way, we have tests for this at .com (se also #1436 on .com)

@humitos
Copy link
Member

humitos commented Jun 21, 2022

but the pre_delete and post_delete signals are sent for all deleted objects

I understand that "for all deleted objects" only applies to the original objects, not the CASCADEd ones because Django does not really know what are those objects, so it can't trigger those signals for them.

These signals are sent to all deleted objects, including the CASCADEd ones. I just confirmed that by doing a local test. This is the answer I was expecting from you with my original comment asking about how it's the flow of execution calls: #9341 (review)


I executed this code:

o = Organization.objects.create(slug='1')
s = Subscription.objects.create(organization=o, plan=Plan.objects.get(pk=3))
o.delete()

Without this PR's changes:

[info     ] Removing organization completely [readthedocs.organizations.signals] organization_slug=1
[error    ] Called remove_subscription     [readthedocs.subscriptions.signals] organization_slug=1
[error    ] Called remove_stripe_subscription [readthedocs.subscriptions.signals] organization_slug=1

With this PR's changes:

[error    ] Called remove_stripe_subscription [readthedocs.subscriptions.signals]
[info     ] Removing organization completely [readthedocs.organizations.signals] organization_slug=1

@humitos
Copy link
Member

humitos commented Nov 29, 2022

It would be good to update this PR to a mergeable state since we are still hitting this issue.

Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

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

I tested this by myself to find out the answer to my question. Everything looks good, I think. Hopefully, we don't screw things related to subscriptions on production again 🙃

@humitos humitos merged commit 9646178 into main Jan 24, 2023
@humitos humitos deleted the subs-double-delete branch January 24, 2023 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants