[Django]-Transaction managed block ended with pending COMMIT/ROLLBACK

87๐Ÿ‘

โœ…

After getting a similar issue and wasting hours on it I figured out how to debug this situation.

For some reason the @transaction.commit_manually decorator silences exceptions that occur in the function.

Temporarily remove the decorator from your function, youโ€™ll now see the exception, fix it and put the decorator back!

๐Ÿ‘คGert Steyn

9๐Ÿ‘

I had the same problem. The only solution I found was to use a try/finally clause to ensure a commit happens after the render.

@transaction.commit_manually
def xyz(request):
    committed = False
    try:
        if ABC:
            success = something()

            if success:
                status = "success"
                transaction.commit()
                committed = True

            else:
                status = "dataerrors"
                transaction.rollback()
                committed = True
        else:
            status = "uploadproblem"
            transaction.rollback()
            committed = True

        return render(request, "template.html", {
            'status': status,
        })
    finally:
        if not committed:
            transaction.rollback() # or .commit() depending on your error-handling logic

Makes no sense, but it worked for me.

๐Ÿ‘คCerin

2๐Ÿ‘

I had the same issue and learned that even if you properly close the transaction manually in case of exceptions, if you then write to the orm again within the manual-transaction scope, it seems to reopen the transaction somehow and causes the transaction exception.

            with transaction.commit_manually():
                try:
                    <exciting stuff>
                    transaction.commit()                        
                except Exception, e:
                    transaction.rollback()
                    o.error='failed' <== caused transaction exception
๐Ÿ‘คPaul Bormans

2๐Ÿ‘

another reason why you might be seeing this issue is when you have multiple dbโ€™s in the system.

I was able to overcome this error with

@transaction.commit_manually(using='my_other_db')
def foo():
   try:
        <db query>
        transaction.commit(using='my_other_db')
   except:
        transaction.rollback(using='my_other_db')
๐Ÿ‘คsrj

1๐Ÿ‘

This always happens when an unhandled exception occurs somewhere in the code. In my case, for some reason, the exception was not thrown to the debugger which is what caused the confusion for me.

๐Ÿ‘คRob

1๐Ÿ‘

I had similar problem, maybe this code works fine for you:

@transaction.commit_on_success
def xyz(request):
    if ABC:
        success = something()

        if success:
            status = "success"

        else:
            status = "dataerrors"
            transaction.rollback()
    else:
        status = "uploadproblem"
        transaction.rollback()

    return render(request, "template.html", {
        'status': status,
    })
๐Ÿ‘คGriffosx

1๐Ÿ‘

As other people have said, exceptions occurring within the decorated function are โ€œlostโ€ because they are overwritten by the TransactionManagementError exception.

I propose to extend the transaction.commit_manually decorator. My decorator transaction_commit_manually uses the transaction.commit_manually decorator internally; if an exception occurs in the decorated function, my decorator catches the exception, performs a transaction.rollback() and raises the exception again. Thus the transaction is cleared correctly, and the original exception is not lost.

def _create_decorator_transaction_commit_manually(using=None):
    def deco(f):
        def g(*args, **kwargs):
            try:
                out = f(*args, **kwargs)
            except Exception as e:
                if using is not None:
                    transaction.rollback(using=using)
                else:
                    transaction.rollback()
                raise e
            return out
        if using is not None:
            return transaction.commit_manually(using=using)(g)
        return transaction.commit_manually(g)
    return deco

def transaction_commit_manually(*args, **kwargs):
    """
    Improved transaction.commit_manually that does not hide exceptions.

    If an exception occurs, rollback work and raise exception again
    """
    # If 'using' keyword is provided, return a decorator
    if 'using' in kwargs:
        return _create_decorator_transaction_commit_manually(using=kwargs['using'])
    # If 'using' keyword is not provided, act as a decorator:
    # first argument is function to be decorated; return modified function
    f = args[0]
    deco = _create_decorator_transaction_commit_manually()
    return deco(f)
๐Ÿ‘คLuca

0๐Ÿ‘

I was having the same problem and tried various approaches. Here is what worked for me but i am not sure if this is the right way to do it. Change your return statement to:

with transaction.commit_on_success():
    return render(request, "template.html", {
        'status': status,
    })

Django Pros, is this the right approach?

๐Ÿ‘คAnjan

0๐Ÿ‘

Put your code in try /except block .In except block just do a transaction.rollback and log the exception object.

@transaction.commit_manually
def xyz(xyz):
   try:
       some_logic
       transaction.commit()
    except Exception,e:
       transaction.rollback()
       print str(e)
๐Ÿ‘คJoy

0๐Ÿ‘

Take backup of database if needed and remove table ROLLBACK_TEST from your database.

mysql> DROP TABLE `ROLLBACK_TEST`;
๐Ÿ‘คDinesh Patil

Leave a comment