Savepoint support for MySQL backend
|Reported by:||lamby||Owned by:||nobody|
|Component:||Database layer (models, ORM)||Version:||1.2|
|Cc:||lamby, tomasz.zielinski@…||Triage Stage:||Accepted|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
Currently, it is not possible for the following code to deadlock when using the MySQL backend:
from django.db import models, transaction class Parent(models.Model): lookup = models.CharField(max_length=20, unique=True) num_children = models.IntegerField(default=0) class Child(models.Model): parent = models.ForeignKey(Parent, related_name='children') name = models.CharField(max_length=20) def save(self, *args, **kwargs): created = not self.pk super(Child, self).save(*args, **kwargs) if created: Parent.objects.filter(pk=self.parent_id).update( num_children=models.F('num_children') + 1, ) @transaction.commit_on_success def my_view(request): parent = Parent.objects.get_or_create(lookup='a') parent.children.create(name='foo')
This is because the INSERT in get_or_create gains an IX lock on that row even though it fails and raises IntegrityError. This lock causes the deadlock later on when UPDATE is called from Child.save.
Adding savepoint support would fix this as the rollback to savepoint made just before the INSERT would remove the IX lock.
Patch attached. Tested on 5.0.84-1.
Change History (17)
comment:1 Changed 3 years ago by russellm
- Needs documentation unset
- Needs tests unset
- Patch needs improvement set
- Triage Stage changed from Unreviewed to Accepted