3

Within transaction.atomic() I am deleting and re-creating objects. Django sits behind a 3-worker gunicorn, database is postgres.

Issue: When hitting the server with a lot of requests simultaneously, Django is throwing a few IntegrityErrors:

IntegrityError: duplicate key value violates unique constraint "atomic_atomictest_name_key"
DETAIL:  Key (name)=(foo) already exists.

Example:

models.py

class AtomicTest(models.Model):
    name = models.TextField(null=False, unique=True)

views.py

def test_atomic(request):
    with transaction.atomic():
        models.AtomicTest.objects.filter(name='foo').delete()
        models.AtomicTest(name='foo').save()
    return http.HttpResponse('OK')

The IntegrityErrors shouldn't occur in my understanding. Can anyone explain why?

kev
  • 8,928
  • 14
  • 61
  • 103

1 Answers1

3

IntegrityError can still happen with transaction.atomic(). The transactions can happen at the same time; they are only isolated from one another until they are committed, at which point it's up to Postgres to try and reconcile the changes. If Postgres decides one or more transactions is trying to create a row that violates your uniqueness constraint, it will roll back those transactions.

What transaction.atomic() does is make sure that ALL COMMANDS in the block are rolled back if any of them fail. That way you won't have a case where delete() succeeds and is committed but save() fails.

In this case, transaction.atomic() may actually be causing your problem, because it delays delete() from taking effect immediately.

Andrew Gorcester
  • 19,595
  • 7
  • 57
  • 73
  • Thanks for the nice explanation. I guess there is no other way than using locks to prevent the db from throwing the IntegrityError? – kev Jul 03 '14 at 02:28
  • In cases where the `IntegrityError` is being caused by a row edit, you can lock the row. However, in this case it is being caused by a row creation. There is no way to lock the row except perhaps to manage the locking mechanism manually. However, in this case the IntegrityError don't seem to be a problem? After all, the issue is that the row already exists, and there's no need to create it twice. Maybe you can just catch the error and move on. – Andrew Gorcester Jul 03 '14 at 05:38
  • I am doing that now :-) Thanks again! – kev Jul 03 '14 at 07:47