Try Catch example code

August 9, 2011 at 19:04
filed under Dynamics AX
Tagged , , , , ,

Hi all!

The code below contains a try/catch that I use a lot when developing batch jobs, especially multithreaded ones.
It deals with frequently occurring exceptions that, in some cases, can be easily solved by retrying:

Duplicate key conflicts are more rare than update conflicts, but I’ve seen them pop up under heavy load on InventDim records.

    #OCCRetryCount
   
    try
    {
        ttsbegin;

        // do stuff here
       
        ttsCommit;
    }
    catch (Exception::Deadlock)
    {
        // retry on deadlock
        retry;
    }
    catch (Exception::UpdateConflict)
    {
        // try to resolve update conflict
        if (appl.ttsLevel() == 0)
        {
            if (xSession::currentRetryCount() >= #RetryNum)
            {
                throw Exception::UpdateConflictNotRecovered;
            }
            else
            {
                retry;
            }
        }
        else
        {
            throw Exception::UpdateConflict;
        }
    }
    catch(Exception::DuplicateKeyException)
    {
        // retry in case of an duplicate key conflict
        if (appl.ttsLevel() == 0)
        {
            if (xSession::currentRetryCount() >= #RetryNum)
            {
                throw Exception::DuplicateKeyExceptionNotRecovered;
            }
            else
            {
                retry;
            }
        }
        else
        {
            throw Exception::DuplicateKeyException;
        }
    }

11 comments

RSS / trackback

  1. Navy

    Thanks for sharing, it’s very useful.

  2. Tommy Skaue

    Hi

    Thanks for sharing. Please explain why you don’t have to test retrycount when you catch the deadlock. You simply retry. Wouldn’t that cause an “endless loop”?

  3. Klaas Deforche

    Hi Tommy,

    That a good question :) I’ve been thinking about that too, and I think I know why.
    When you look at standard X++ code in AX, you can see that it is used a lot in this way.
    However, sometimes this is used:

        catch(Exception::Deadlock)
        {
            if (xSession::currentRetryCount() >= #RetryNum)
            {
                throw Exception::Deadlock;
            }
            else
            {
                retry;
            }
        }

    Perhaps the code above is a safeguard against “excessive retrying” when it is not really needed.

    I don’t believe retrying in case of a deadlock can cause an endless loop. If you catch a deadlock exception, it would mean that you are in the “unlucky” process that was kicked out by SQL because it detected a deadlock. When in the catch, your transaction is aborted and rolled back, which means that the deadlock was resolved. The other process will have finished by the time your process locks that resource again.

    I believe the reason why you can’t simply retry in case of UpdateConflict and DuplicateKeyException is because these errors can also be catched inside a transaction, and that there is no implicit abort when you catch those exceptions. That’s the reason why my example throws that same error when the ttslevel is not 0, so it would be caught by a catch statement that is not in a transaction, causing a ttsabort. That would be this case if this code was called from within an other transaction. If you were to simply retry, you would get unexpected results because nothing was rolled back.
    Sometimes, you do extra stuff in the catch, like logging. I think the retrycount check comes in handy when the problem is situated in the catch itself, when the developer wrote code that didn’t clean everyting up correctly (while handling a dupplicatekey or updateconfict exception), causing a dupplicatekey or updateconfict exception, and as a result, endless retrying.
    In case of deadlock you get a “fresh start” in the beginning of each retry, so you shouldn’t experience this endless looping.

    I wrote this short article for you to demonstrate try/catch in combination with transactions:
    http://www.artofcreation.be/2011/08/10/try-catch-and-transactions/

    Hope this clears things up – and that I’m not telling lies, correct me if I’m wrong :-).

  4. Tommy Skaue

    Ok. That makes sense. The clue here is that a DeadLock will implicitly cause a ttsabort and you are likely to get through on the next attempt. You might be right about the unnecessary “safeguard”. Good to know!

  5. d1l1yd

    new dax dev here…
    it really helps me understanding dax :D

  6. Ian Schofield

    #OCCRetryCount //Macro for the #RetryNum var

  7. Klaas Deforche

    Correct, added it to the code, thanks.

  8. shivam

    why we use the #occretrycount marcos

  9. Klaas Deforche

    Hi Shivam,
    Because it contains the #RetryNum macro that is set to 5, so we retry 5 times everywhere in AX. Should we want to change that, we only have to do it in one place.

  10. RP

    You might want to add a comment in the catch block for DuplicateKeyException, just noting that it doesn’t make sense to retry unless you add code that tries to change the value for one of the fields in the table’s primary key. If you don’t change any value, there is no sense retrying because your values will still be a duplicate of a existing key.

  11. Trackbacks

respond