A few years back, I had a discussion about the correct way to build a retry-capable IS Trigger Service:
[url]wmusers.com
Recently, I was building a retry-capable IS trigger service where the steps involved a transaction, and I found some trickiness to it - so I thought I’d put what I learned out here.
A generic outline of the ‘Retry-capable trigger service – with transactions’ service is below.
Service Structure
Try/Catch - Outer Sequence
Try Sequence
<Work outside of transaction boundary>
call startTransaction (e.g., insert multiple records into database)
<Work within the transaction boundary>
call commitTransaction
Catch Sequence
call getLastError
If pipeline indicates transaction started ...
...(e.g. BRANCH on /lastError/pipeline/startTransactionOutput == /.+/)
call rollbackTransaction
If Exception is recoverable (e.g., database down)
Set 'error' flag to ‘true’
If Exception is irrecoverable (e.g., database constraint violated)
EXIT with failure (logs pipeline in PRT)
BRANCH on 'error' flag
If 'true', throw pub.flow:throwExceptionForRetry
The service does its work in a transaction in the ‘Try’ block. If an exception is thrown, control passes to the ‘Catch’ block where a BRANCH (immediately after ‘getLastError’) calls ‘rollbackTransaction’ to safely roll back the transaction without interfering with the retry mechanism.
The ‘rollbackTransaction’ call must be placed here. This is because it not only rolls back the transaction, but also seems to roll back the state of the pipeline to before ‘startTransaction’ (in the ‘Try’ block).
If rollback is invoked further down the ‘Catch’ block (e.g. after setting the ‘error’ variable),
it destroys pipeline variables created after ‘startTransaction’ (e.g. the ‘error’ variable). This
consequently breaks the service retry process (i.e. ‘throwExceptionForRetry’ does not get called.)