[Progress Communities] [Progress OpenEdge ABL] Forum Post: Unsafe error behavior in legacy ABL loops

Status
Not open for further replies.
D

dbeavon

Guest
In our legacy ABL/4GL code we often use loops to update records. When writing these loops, the programmers often assumed that the ABL language will commit all records in an atomic transaction. However, there have been many occasions over the years when we discovered that an ABL transaction was not being committed atomically. On many occasions, several iterations of a loop would update records, but certain records would appear to be left alone. In other words, some records were inexplicably excluded from a complex transaction. At that point the normal course of action for the programmer is to shrug and write a "one-time" program to clean things up after-the-fact. I've got a small sample below that demonstrates the type of code that results in a transaction which is NOT atomic. /* ************************************************************************ */ /* Locals */ /* ************************************************************************ */ DEFINE TEMP-TABLE TT_Activity FIELD ActivityRecord AS INTEGER. DEFINE VARIABLE v_Loop AS INTEGER NO-UNDO. DEFINE VARIABLE v_DatabaseUpdate AS DECIMAL DECIMALS 5 NO-UNDO. DEFINE VARIABLE v_Display AS CHARACTER NO-UNDO. FORM WITH FRAME x-frame DOWN. /* ************************************************************************ */ /* Setup work */ /* Eleven things need to be done in a loop. */ /* ************************************************************************ */ DO v_Loop = -5 TO 5: CREATE TT_Activity. TT_Activity.ActivityRecord = v_Loop. END. /* ************************************************************************ */ /* Transaction operates on all items atomically */ /* ************************************************************************ */ DO TRANSACTION: MESSAGE "STARTING TRANSACTION {&FILE-NAME} {&LINE-NUMBER}". PAUSE. /* ********************************************************************* */ /* Operate on each of the 11 records */ /* ********************************************************************* */ FOR EACH TT_Activity EXCLUSIVE-LOCK : /* Update Some Data */ v_DatabaseUpdate = 10.0 / TT_Activity.ActivityRecord. RUN DoWork(v_DatabaseUpdate). /* Show results */ DISPLAY " AFTER DoWork COMPLETION FOR " + STRING(TT_Activity.ActivityRecord) @ v_Display FORMAT "X(70)" WITH FRAME x-frame DOWN. DOWN WITH FRAME x-frame. END. /* TRANSACTION */ /* ****************************************************************** */ /* Finished */ /* ****************************************************************** */ MESSAGE "FINISHING TRANSACTION {&FILE-NAME} {&LINE-NUMBER}". PAUSE. END. RETURN. /* ************************************************************************ */ /* Do some work */ /* Generates an error condition */ /* ************************************************************************ */ PROCEDURE DoWork PRIVATE: DEFINE INPUT PARAMETER p_Input AS INTEGER NO-UNDO. IF p_Input = ? THEN RETURN ERROR "Failure". DISPLAY "DoWork: UPDATING DATABASE WITH INPUT " + STRING(p_Input) @ v_Display FORMAT "X(70)" WITH FRAME x-frame DOWN. DOWN WITH FRAME x-frame. END. Note that the DoWork method is a simplification of some complex operation that might generate an ERROR condition. ERROR conditions may be raised manually or by the AVM itself. You will notice that, even when an error condition is raised, the loop runs to completion and the transaction commits. Only 10 iterations of the loop are successful. Half-way thru the loop there is a serious data error that occurs, but the transaction goes along its merry way like nothing had happened. This seems like unsafe transaction behavior in that it doesn't commit data atomically. I'm surprised that it is the default behavior. Can someone tell me what the fix would be to avoid partially committed data? I'm looking for the "legacy" approach. (In my OO-based source code I use "BLOCK-LEVEL ON ERROR UNDO, THROW" at the top of source files, but this would not have been available ten years ago when the legacy code was written. I'd like a fix that would be familiar to an old-school ABL programmer, who is not comfortable with either OO or SEH.) One option I could think of was to label the "DO TRANSACTION:" loop (eg. "OUTER_TRANS:"). Then the FOR EACH loop would also be modified to have an "ON ERROR" phrase like so: "ON ERROR UNDO OUTER_TRANS, RETURN". This is the most straightforward option I can think of applying in a general way to our legacy ABL programs. Unfortunately it is a bit laborious to introduce "ON ERROR" phrases to every loop in the system that may be participating in a transaction. Even this approach wouldn't totally avoid the risk of partially committed data because, if the program was participating in a transaction that was already started in the calling program, then there would be no way of UNDO'ing it by name. Any recommendations for fixing legacy ABL loops would be greatly appreciated. It is going to be no small task.

Continue reading...
 
Status
Not open for further replies.
Top