Hi,
I have a table of Endpoints which looks like this:
class Endpoint(db_models.Model):
...
op = db_models.ForeignKey(ReportOp, on_delete=db_models.CASCADE)
client = db_models.ForeignKey(Client, on_delete=db_models.CASCADE, null=True, blank=True)
client_private = db_models.IntegerField(_('Client id or zero'), blank=True)
netloc = db_models.URLField(_('URL'), help_text=_('...'))
...
... various data fields...
...
class Meta:
unique_together = ('op', 'netloc', 'client_private')
I modify one row at a time in this table from separate Linux gunicorn/django processes, using a single point in the code, which looks like this:
def locked_instance(self, ...): // Ensure the row exists. with transaction.atomic(): instance, created = Endpoint.objects.get_or_create(kwargs | {'client': client}, **kwargs) ...initialise the various data fields... // Return locked reference to the row qs = Endpoint.objects.select_for_update().filter(id=instance.id) return qs
To my surprise, I got a deadlock where the Postgres server log shows two DIFFERENT rows (id = 1 and id = 2 below) being involved. Here is the server log:
ERROR: deadlock detected
DETAIL: Process 15576 waits for ShareLock on transaction 31053599; blocked by process 16953.
Process 16953 waits for ShareLock on transaction 31053597; blocked by process 15576.
Process 15576: SELECT “paiyroll_endpoint”.“id”, “paiyroll_endpoint”.“op_id”, “paiyroll_endpoint”.“client_id”, “paiyroll_endpoint”.“client_private”, “paiyroll_endpoint”.“netloc”, “paiyroll_endpoint”.“calls”, “paiyroll_endpoint”.“ms”, “paiyroll_endpoint”.“history”, “paiyroll_endpoint”.“current_history” FROM “paiyroll_endpoint” WHERE “paiyroll_endpoint”.“id” = 1 FOR UPDATE
Process 16953: SELECT “paiyroll_endpoint”.“id”, “paiyroll_endpoint”.“op_id”, “paiyroll_endpoint”.“client_id”, “paiyroll_endpoint”.“client_private”, “paiyroll_endpoint”.“netloc”, “paiyroll_endpoint”.“calls”, “paiyroll_endpoint”.“ms”, “paiyroll_endpoint”.“history”, “paiyroll_endpoint”.“current_history” FROM “paiyroll_endpoint” WHERE “paiyroll_endpoint”.“id” = 2 FOR UPDATE
HINT: See server log for query details.
CONTEXT: while locking tuple (7,15) in relation “paiyroll_endpoint”
STATEMENT: SELECT “paiyroll_endpoint”.“id”, “paiyroll_endpoint”.“op_id”, “paiyroll_endpoint”.“client_id”, “paiyroll_endpoint”.“client_private”, “paiyroll_endpoint”.“netloc”, “paiyroll_endpoint”.“calls”, “paiyroll_endpoint”.“ms”, “paiyroll_endpoint”.“history”, “paiyroll_endpoint”.“current_history” FROM “paiyroll_endpoint” WHERE “paiyroll_endpoint”.“id” = 1 FOR UPDATE
How can there be a deadlock between two separate rows? I guess this means there is a problem with my understanding and/or the code, but I’m not sure what that could be. I have looked into the “select_for_update(no_key=True)” rowlock docs for Postgres, but I confess to being somewhat out of my depth. Any pointers would be most welcome…
Thanks, Shaheed