Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform table/row locks in Django

In production environments where Django is running on Apache or with multiple Gunicorn workers, it runs the risk of concurrency issues.

As such, I was pretty surprised to find that Django's ORM doesn't explicitly support table/row locking. It supports transactions very handedly, but that only solves half of the concurrency problem.

With a MySQL backend, what is the correct way to perform locking in Django? Or is there something else at play in Django's framework that makes them unnecessary?

like image 570
Luke Sapan Avatar asked Nov 13 '15 13:11

Luke Sapan


People also ask

Which method in Django ORM acquires a lock on specific rows?

We use select_for_update on our queryset to tell the database to lock the object until the transaction is done. Locking a row in the database requires a database transaction. We use Django's decorator transaction. atomic() to scope the transaction.

What is row-level locking and table level locking?

Table locks. A statement can lock the entire table. Table-level locking systems always lock entire tables. Row-level locking systems can lock entire tables if the WHERE clause of a statement cannot use an index. For example, UPDATES that cannot use an index lock the entire table.

How does Django implement concurrency?

When you run multiple workers of your Django application, you will run into concurrency issues when the same queryset is updated by different processes at the same time. To prevent this, use select_for_update inside a transaction block to fetch your queryset so that it is locked until the transaction is completed.

Does mysql have row locking?

InnoDB implements standard row-level locking where there are two types of locks, shared ( S ) locks and exclusive ( X ) locks. A shared ( S ) lock permits the transaction that holds the lock to read a row. An exclusive ( X ) lock permits the transaction that holds the lock to update or delete a row.


1 Answers

Django does not explicitly provide an API to perform table locking. In my experience, well-designed code rarely needs to lock a whole table, and most concurrency issues can be solved with row-level locking. It's an last-ditch effort: it doesn't solve concurrency, it simply kills any attempt at concurrency.

If you really need table-level locking, you can use a cursor and execute raw SQL statements:

from django.db import connection

with connection.cursor() as cursor:
    cursor.execute("LOCK TABLES %s READ", [tablename])
    try:
        ...
    finally:
        cursor.execute("UNLOCK TABLES;")
like image 102
knbk Avatar answered Sep 29 '22 08:09

knbk