Monday, May 16, 2016

Re: Possible bug with transaction.on_commit

I believe that's as designed. There's this part in the documentation of "Database transactions":

Callbacks are not run until autocommit is restored on the connection following the commit (because otherwise any queries done in a callback would open an implicit transaction, preventing the connection from going back into autocommit mode).

So on_commit is meant as a connection level hook, not a transaction level one.

On Mon, May 16, 2016 at 3:10 PM, <barthelemy@infobart.com> wrote:
Hi,

I believe that I encountered a bug or at least a serious limitation with transaction.on_commit in Django 1.9.x. Essentially, the on_commit hooks seem to be tied to the database connection instead of the transaction. This means that unrelated transactions may trigger on_commit hooks, which results in undesired execution order. Here is an example:

from django.db import transaction
from foobar.models import Model1, Model2


@transaction.atomic
def outer():
    print("START - OUTER")
    for i in range(4):
        f1(i)
    print("END - OUTER")


@transaction.atomic
def f1(i):
    model = Model1(description="test {0}".format(i))
    model.save()
    transaction.on_commit(lambda: commit_hook(model, i))


def commit_hook(model, i):
    print("START - on_commit hook")
    f2(i)
    print("STOP - on_commit hook")


@transaction.atomic
def f2(i):
    print("CALLING F2")
    model = Model2(description="test {0}".format(i))
    model.save()


Some quick explanations: 

- outer is the outermost atomic block. f1 will only create a savepoint(). This works as expected, there is only one commit (trace erased for simplicity). 
- f2 is wrapped in an "outermost" atomic block and indeed, f2 will be called four times and there will be four commits (trace erased for simplicity).
- f1 register a commit_hook. I expect that at the end of outer(), all commit hooks will be called.


The expected trace is:
START - OUTER
END - OUTER
START - on_commit hook
CALLING F2
STOP - on_commit hook
START - on_commit hook
CALLING F2
STOP - on_commit hook
START - on_commit hook
CALLING F2
STOP - on_commit hook
START - on_commit hook
CALLING F2
STOP - on_commit hook

The actual trace (f2 is triggering the on_commit hooks registered in f1/outer):
START - OUTER
END - OUTER
START - on_commit hook
CALLING F2
START - on_commit hook
CALLING F2
START - on_commit hook
CALLING F2
START - on_commit hook
CALLING F2
STOP - on_commit hook
STOP - on_commit hook
STOP - on_commit hook
STOP - on_commit hook

I wanted to know if someone encountered this issue or if I am misunderstanding on_commit before opening a ticket.

Regards,
Bart

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/5ddb1ee0-c27f-4cee-b28a-d8cca6d1cf5f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAD4ANxVxO8xLVfn48-vu6RnGhDSaAX_aMFXYbHAE%3DaSfugjXLw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment