Amanur Rahman
Amanur Rahman

Reputation: 261

How does django decide which transaction to choose on transaction.on_commit()

I had to use transaction.on_commit() for synchronous behaviour in one of the signals of my project. Though it works fine, I couldn't understand how does transaction.on_commit() decide which transaction to take. I mean there can be multiple transactions at the same time. But how does django know which transaction to take by using transaction.on_commit()

Upvotes: 1

Views: 1827

Answers (3)

itismoej
itismoej

Reputation: 1847

Django uses thread-local storage to manage the transaction state per connection. And also, in Django, each database connection handles one transaction at a time. For more clarification, here it is the on_commit function in transaction module:

def on_commit(func, using=None):
    """
    Register `func` to be called when the current transaction is committed.
    If the current transaction is rolled back, `func` will not be called.
    """
    get_connection(using).on_commit(func)

As you can see, there is a get_connection function which looks like:

def get_connection(using=None):
    """
    Get a database connection by name, or the default database connection
    if no name is provided. This is a private API.
    """
    if using is None:
        using = DEFAULT_DB_ALIAS
    return connections[using]

The connections returns data form a private variable named self._connections which is thread-local using asgiref.local.Local.

So, there is only one unique connection per thread and when you call connections["default"] or import the current connection from django.db import connection, it returns the current thread-local database connection that has a single and unique transaction at a time.

This is why transaction.on_commit() knows which transaction to hook on in Django.

Upvotes: 1

Tim Tisdall
Tim Tisdall

Reputation: 10392

If I understand the question correctly, I think the docs on Savepoints explains this.

Essentially, you can nest any number of transactions, but on_commit() is only called after the top most one commits. However, on_commit() that's nested within a savepoint will only be called if that savepoint was committed and all the ones above it are committed. So, it's tied to which ever one is currently open at the point it's called.

Upvotes: 1

Zhivko Zaikov
Zhivko Zaikov

Reputation: 449

According to the docs

You can also wrap your function in a lambda:

transaction.on_commit(lambda: some_celery_task.delay('arg1')) The function you pass in will be called immediately after a hypothetical database write made where on_commit() is called would be successfully committed.

If you call on_commit() while there isn’t an active transaction, the callback will be executed immediately.

If that hypothetical database write is instead rolled back (typically when an unhandled exception is raised in an atomic() block), your function will be discarded and never called.

If you are using it on post_save method with sender=SomeModel. Probably the on_commit is executed each time a SomeModel object is saved. Without the proper code we would not be able to tell the exact case.

Upvotes: 2

Related Questions