Progress Transaction rollback

nandanak

New Member
I have code like following :

DO TRANSACTION ON ERROR UNDO:
RUN Procedure1(update tables).
RUN Procedure2(update tables).

Another table updating codes....
END.

Assume above code Procedure1, Procedure2 successfully update tables and error occurred when you update other codes in the block, My question is, Is it rollback the all tables updated in Procedure1, Procedure2 ?
If not how can I do that ?
 

Cringer

ProgressTalk.com Moderator
Staff member
Yes any updates in the sub procedures will be rolled back as the transaction is scoped outside the sub procedures.
I should note though, that this is incredibly bad practise. Your transactions should be as small as possible or else you will end up running into all sorts of other problems, like locked records for other users. If you absolutely must have the ability to roll back changes spanning such a large block, then do the work against temp table copies of the data, and then commit it to the database in a tight transaction loop right at the end, after all validation is complete.
 

RealHeavyDude

Well-Known Member
On a side note: Not only - as Cringer noted - should you keep the transaction as small as possible, there is another compelling reason why you don't want to spawn a transaction over procedure/function - or whatever - calls:

Do you want your transaction successfully going through, in addition to your business logic, to depend on the AVM finding a particular procedure in the PROPATH, the parameter signatures match, or the operating system granting access to the procedure?

IMHO - in a transaction you should avoid "outside" dependencies that could cause it to fail as much as possible. Of course you can't prevent the transaction to fail when the database or the operating system crashes. But other than that only the business logic should determine whether a transaction goes through or fails.

Heavy Regards, RealHeavyDude.
 

GregTomkins

Active Member
The OP didn't suggest that Procedure1/Procedure2 does a particularly huge amount of work, did he? I don't see problem with that code, assuming that he's not updating thousands of records. IMO there is nothing necessarily wrong with splitting logic up as his code suggests.

Regardless, what I really wanted to say is - you have to be careful about what exactly constitutes an error. Unfortunately (IMO), Progress treats seemingly similar errors very differently.

Perfect example: suppose your Procedure2 runs a ProcedureX. There are two obvious failure scenarios (many more less obvious ones, of course):

A. ProcedureX doesn't exist. Eons ago, someone at Progress decided that this is a Really Bad Problem and when it happens, Progress does a STOP / backs out everything.

B. ProcedureX exists but it's parameters don't match (ie. you pass no parameters and it expects one). Progress feels that this is not so bad, and will back out the subtransaction attached to Procedure2, but will NOT back out Procedure1.

You might think that DO ON ERROR UNDO would save you, but it doesn't necessarily work the way you might expect. So if this matters to you enough, make sure you understand the consequences of different errors and don't assume "an error is an error" as you might from other languages.

DISCLAIMER: all of this is also probably deeply affected by the relatively new TRY-CATCH logic in more recent versions of Progress. Unfortunately that's out of reach to us so I'm not familiar with it, though from the world of Java and such, it seems like a good thing.
 

andre42

Member
CATCH only catches ERRORs, not STOPs. (Just verified this in the KB, also FINALLY is not executed after STOP.
 

tamhas

ProgressTalk.com Sponsor
One can handle STOP more gracefully with code like this:

Code:
do on stop undo, retry:
    if retry
    then do:
        log-manager:write-message ("STOP condition encountered", "OtherERROR").
        undo, leave.
      end.

    do on error undo, leave:
        session:error-stack-trace = true.
        log-manager:logfile-name = chPrimaryLog.
        log-manager:clear-log ().
        /* Control log detail and event types */
        log-manager:logging-level = 3.
        /* Uncomment for detailed trace during debugging */
        /* log-manager:log-entry-types = "DynObjects.Class,4GLTrace". */
        log-manager:write-message ("Start ABL2DB").

        /* ************************ Begin Actual Work ********************* */
                ...

        /* ************************ End Actual Work ********************* */
   
        catch eobAppError as Progress.Lang.AppError:
            log-manager:write-message ( eobAppError:ReturnValue ).   
            delete object eobAppError.
        end catch.
   
        catch eobSysError as Progress.Lang.SysError :
            log-manager:write-message(substitute("Unexpected Exception: &1", eobSysError:getMessage(1)), "SysERROR").
            if eobSysError:NumMessages > 1
            then do inWhich = 2 to eobSysError:NumMessages:
                log-manager:write-message(substitute("Unexpected Exception (cont.): &1", eobSysError:getMessage(inWhich)), "SysERROR").
            end.
            do inWhich = 1 to num-entries(eobSysError:CallStack,"~n"):
                log-manager:write-message(entry(inWhich,eobSysError:CallStack,"~n"),"CALLSTACK").
            end.
            delete object eobSysError no-error.
        end catch.
       
    catch eobOtherError as Progress.Lang.Error :
      log-manager:write-message(substitute("Other error: &2", eobOtherError:getMessage(1))). 
      if eobOtherError:NumMessages > 1
      then do inWhich = 2 to eobOtherError:NumMessages:
        log-manager:write-message(substitute("Unexpected Exception (cont.): &1", eobOtherError:getMessage(inWhich)), "OtherERROR").
      end.
      do inWhich = 1 to num-entries(eobOtherError:CallStack,"~n"):
        log-manager:write-message(entry(inWhich,eobOtherError:CallStack,"~n"),"CALLSTACK").
      end.
      delete object eobOtherError no-error.
    end catch.

        finally:
            log-manager:write-message ("End ABL2DB").
        end finally.

    end. /* do */
 
end.
 

RealHeavyDude

Well-Known Member
I also use the on stop undo, retry to catch stop conditions and it served me well.

Heavy Regards, RealHeavyDude.
 
Top