Lately I've been trying to improve how our web application employs transactions 
(we don't use them enough). While doing this I became aware of this rather 
general problem. It seems as though database transactions cannot be mixed with 
exceptions unless great vigilance is practiced.  I'm wondering if AspectJ can 
help me in some way.

Consider this pseudo code:


function doOperationX()
{
    connection = get_connection()
    connection.start_transaction()
    try
    {
        updateA(connection)

        try
        {
            updateB(connection)
        }
        catch (C_DoesNotExist)
        {
            //ignore (non fatal)
        }

        connection.commit()
    }
    finally
    {
        connection.end_transaction()  //rolls back uncommitted
        connection.close()
    }
}

function updateA(connection)
{
    connection.execute("UPDATE a SET ...")
}

function updateB(connection)
{
    connection.execute("UPDATE b SET ...")
    updateC(connection)
}

function updateC(connection)
{
    result = connection.execute("SELECT c.id FROM c INNER JOIN b ON ...")
    if (result.next())
        connection.execute("UPDATE c SET ...")
    else
        throw C_DoesNotExist()
}



Don't study the pseudo SQL too closely, the problem isn't there. Read the code 
like this:

1) operationX requires updating of A and, optionally, B.
2) Internally, updating B always requires that C also be updated. All or 
nothing.
3) Sometimes, C cannot be updated because the database is not in the proper 
state at this time. (Not an invalid state, mind you)
4) operationX knows that C might not be updatable, so it catches the exception 
and ignores it.

The problem is:

   updateB() leaves the database in an inconsistent state if an exception is 
thrown
   out of updateC().  The inconsistency is that table B has been updated but D 
has
   not been updated accordingly.

Yet, it's only a problem because operationX caught the exception. Had it let 
the exception continue up, the finally block would have rolled back the entire 
transaction quite nicely.

How can I avoid this problem? The only proper solution that I can see is a 
prolific use of transaction checkpoints (aka savepoints) so that a partial 
rollback can occur if needed:


function updateB(connection)
{
    success = false
    savepoint = connection.setSavepoint()
    try
    {
        connection.execute("UPDATE b SET ...")
        updateD(connection)
        success = true
    }
    finally
    {
        if (not success)
            connection.rollback(savepoint)
    }
}



But this hurts performance considerably and requires programmer vigilance and 
lots of extra code! The programmer must remember that:

  **If your function does more than one update it MUST use a savepoint!**

That's tough pill to swallow (for me). I try to avoid programming models that 
require programmer vigilance. All it takes is one groggy Monday...

What do you think? Am I missing a better solution?  Can AspectJ help?

Videx Inc. 1105 N. E. Circle Blvd. Corvallis OR 97330 (541) 758-0521
CONFIDENTIAL COMMUNICATION: The email message and any attachments are intended 
only for the addressee.  They may be privileged, confidential, and protected 
from disclosure. If you are not the intended recipient, any dissemination, 
distribution, or copying is expressly prohibited.  If you received this email 
message in error, please notify the sender immediately by replying to this 
e-mail message or by telephone

_______________________________________________
aspectj-users mailing list
[email protected]
https://dev.eclipse.org/mailman/listinfo/aspectj-users

Reply via email to