Dynamic Roll-Back [ HP Transact Reference Manual ] MPE/iX 5.0 Documentation
HP Transact Reference Manual
Dynamic Roll-Back
The TurboIMAGE/iX dynamic roll-back feature is supported by Transact/iX.
This section briefly explains how dynamic roll-back works.
TurboIMAGE/iX, through the use of Transaction Manager (XM), allows
uncommitted logical transactions to be rolled back dynamically (online)
while other database activity is occurring. This feature is accomplished
through the use of three TurboIMAGE intrinsics: DBXBEGIN, DBXEND, and
DBXUNDO. These intrinsics are supported in Transact/iX by using the
LOGTRAN verb with modifiers XBEGIN, XEND, and XUNDO.
Transact can compile dynamic roll-back code correctly on MPE V and in
compatibility mode on MPE/iX, but will issue a warning indicating that
this is not supported. Transact/V and Transact/CM will issue an error
message if they encounter the dynamic roll-back instruction p-code while
processing the p-code file.
For more detailed descriptions of dynamic roll-back, see the
TurboIMAGE/XL Database Management System Reference Manual.
Locking
To use dynamic roll-back, TurboIMAGE requires that applications have
strong locking (second level consistency). Strong locking means that
DBLOCK is the first call for the transaction and DBUNLOCK is the last
call in the dynamic transaction. (DBUNLOCK should be called immediately
after DBXEND.) Because of this requirement, a dynamic transaction does
not allow the database to be opened in mode 2, which does not enforce
locking.
Transact requires two events for dynamic roll-back transactions:
* Automatic locking must be disabled by issuing a SET(OPTION) NOLOCK
statement prior to the LOGTRAN statement. If this is not done,
strong locking will not be possible and an error message will be
issued.
* Assuming that SET(OPTION) NOLOCK has been issued, Transact
automatically performs strong locking when the LOCK option is used
with LOGTRAN(XBEGIN).
If the LOCK option is not included on the LOGTRAN(XBEGIN) verb, the user
takes all responsibilities for locking.
If the user performs redundant locks or unlocks in the transaction before
ending it (if opened in mode 1), a TurboIMAGE error message is issued and
the transaction is aborted.
If the necessary locking is not done prior to the LOGTRAN(XBEGIN), the
transaction cannot begin and a TurboIMAGE error message is issued.
If an error occurs inside a database transaction, any database access
verbs used after that error will return a status of -222. (See the
TurboIMAGE/XL Database Management System Reference Manual for an
explanation of return status.) Only a LOGTRAN(XUNDO) is allowed when a
database transaction encounters an error.
Examples of Locking Strategy With LOGTRAN
The first example is a simplified roster program for an airline
reservation system. Each flight has a master data set record that
contains the number of seats available on that flight. This record is
linked to a detail data set chain containing the list of passengers. The
data dictionary is assumed to contain all the necessary data about the
database.
The ADD FLIGHT command sequence uses a PUT statement to add a flight
record to the master data set. NO-OF-SEATS contains the total number of
seats on the flight when the PUT statement is executed. Since optimized
locking is in effect, FLIGHT-MAST is locked at the data set level. If
optimized locking were not specified, the entire database would be
locked.
The ADD PASSENGER command sequence uses a GET statement to retrieve the
number of seats available on that flight from FLIGHT-MAST. Automatic
locking would lock at the data set level if the LOCK option were
specified, but in this example no locking is done at all. If the number
of seats is not zero, then the program subtracts 1 from NO-OF-SEATS and
uses an UPDATE statement to update the flight record and a PUT statement
to add a passenger record to ROSTER-DETL. Otherwise, a message is issued
and a record is not added.
SYSTEM FLIGHT,BASE=FLIGHT("SEATS",1,1)
$$ADD:
$FLIGHT:
PROMPT(PATH) FLIGHT;
PROMPT NO-OF-SEATS("Total number of seats available");
PUT FLIGHT-MAST;
$PASSENGER:
PROMPT PASSENGER;
PROMPT(PATH) FLIGHT;
LIST NO-OF-SEATS;
GET FLIGHT-MAST,LIST=(@);
IF STATUS=0 THEN
IF (NO-OF-SEATS) <> 0 THEN
DO
LET (NO-OF-SEATS) = (NO-OF-SEATS) - 1;
UPDATE FLIGHT-MAST,LIST=(@),STATUS;
IF STATUS=0 THEN
PUT ROSTER-DETL,LIST=(PASSENGER:FLIGHT);
DOEND
ELSE
DO
DISPLAY "Sorry, no more seats available."
DOEND;
$$ROSTERS:
LIST(AUTO) FLIGHT-MAST; << Flight,no-of-seats,passenger >>
FIND(SERIAL) FLIGHT-MAST,PERFORM= OUTPUT-ROSTER,LIST=(@);
END(SEQUENCE);
OUTPUT-ROSTER:
DISPLAY FLIGHT:NO-OF-SEATS;
SET(KEY) LIST(FLIGHT);
FORMAT PASSENGER;
OUTPUT(CHAIN) ROSTER-DETL,LIST=(PASSENGER);
RETURN;
If the program relies only on automatic locking, a problem can arise with
this transaction in a multi-user environment. If another process alters
the same flight record in FLIGHT-MAST between the GET statement and the
UPDATE statement, then the quantity in NO-OF-SEATS will be incorrect.
Also, other processes accessing both FLIGHT-MAST and ROSTER-DETL could
get erroneous results between the UPDATE statement and the PUT statement
in the ADD PASSENGER transaction because the database is in an
inconsistent state. To insure data integrity both the FLIGHT-MAST and
ROSTER-DETL data sets should be locked just before the GET statement and
unlocked at the end of the command sequence.
The following code does that:
SET(OPTION) NOLOCK;
LOGTRAN(BEGIN) $HOME, "Add passenger transaction",
LOCK(FLIGHT-MAST,ROSTER-DETL);
GET FLIGHT-MAST,LIST=(@);
IF (NO-OF-SEATS) <> 0 THEN
DO
LET (NO-OF-SEATS) = (NO-OF-SEATS) - 1;
UPDATE FLIGHT-MAST,LIST=(@),STATUS;
IF STATUS=0 THEN
PUT ROSTER-DETL,LIST=(PASSENGER:FLIGHT);
DOEND
ELSE
DO
DISPLAY "Sorry, no more seats available."
DOEND;
LOGTRAN(END) $HOME, "End of add passenger";
RESET(OPTION) LOCK;
Note that the LOGTRAN(END) statement is placed so that Transact
encounters the statement no matter which way the IF statement branches.
Finally, the ROSTERS command sequence (see preceding example) uses FIND
and OUTPUT statements to produce a report. FLIGHT-MAST supplies the
flight identification and number of available seats; ROSTER-DETL supplies
the names of the passengers. Automatic locking does no locking because
these are reporting statements and the LOCK option was not included. If
the FIND statement had LOCK on it, Transact would lock the entire
database for the entire FIND(SERIAL) because of the PERFORM= modifier. A
LOGTRAN(BEGIN) statement with a LOCK option including the two data sets
can be added just before the FIND(SERIAL). This would lock just the two
data sets for the entire transaction. Then a LOGTRAN(END) statement
immediately after the FIND(SERIAL) would unlock the data sets.
The next example is the same as the example above, except that it
includes dynamic transaction logging instead of user logging.
To insure data integrity, both FLIGHT-MAST and ROSTER-DETL data sets are
locked prior to the GET statement (by the LOGTRAN verb). If something
goes wrong while adding a passenger to the ROSTER-DETL or updating the
NO-OF-SEATS in the FLIGHT-MAST data set, the transaction will be rolled
back to the state it was in prior to the LOGTRAN(XBEGIN).
The following example shows how dynamic transactions are used:
SET(OPTION) NOLOCK;
LOGTRAN(XBEGIN) $HOME, "Add passenger transaction",
LOCK(FLIGHT-MAST,ROSTER-DETL);
GET FLIGHT-MAST,LIST=(@);
IF (NO-OF-SEATS) <> 0 THEN
DO
LET (NO-OF-SEATS) = (NO-OF-SEATS) - 1;
UPDATE FLIGHT-MAST,LIST=(@),STATUS;
IF STATUS = 0 THEN
PUT ROSTER-DETL,LIST=(PASSENGER:FLIGHT),STATUS;
DOEND
ELSE
DO
DISPLAY "Sorry, no more seats available."
DOEND;
IF STATUS <> 0 THEN
LOGTRAN(XUNDO) $HOME, "Roll-back passenger - cannot add"
ELSE
LOGTRAN(XEND) $HOME, "End of add passenger";
RESET(OPTION) LOCK;
Limitations
Transaction Manager (XM) allows a transaction to be up to 1 MB in length.
This poses a limitation for transactions. Some Transact verbs perform
many iterations within a statement, such as DELETE(CHAIN) and
REPLACE(SERIAL), in which transactions can reach the 1 MB limit. If that
limit is reached, the dynamic transaction is aborted and the partial
transaction is backed out.
The Transact verbs affected by dynamic transactions are PUT, UPDATE,
DELETE, and REPLACE. The PUT and UPDATE verbs are single iteration verbs
so the limitation should not affect them. However, DELETE and REPLACE
are affected by this limitation so you may want to use the following
workarounds:
DELETE Verb.
The DELETE verb can have many iterations when using the SERIAL, RSERIAL,
CHAIN, or RCHAIN modifier that can cause the Transaction Manager
limitation to be reached. If there is a strong possibility of reaching
the limit, you should use the FIND verb with the PERFORM option as a
workaround. The PERFORM routine should call dynamic transaction logging
for every n number of DELETE(CURRENT) records (where n is the number or
records deleted for each dynamic transaction that is shorter in length
than the limitation), or select a smaller set of records to delete by
using the match register. For more information, see the TurboIMAGE/XL
Database Management System Reference Manual.
REPLACE Verb.
The REPLACE verb can have many iterations when using the SERIAL, RSERIAL,
CHAIN, or RCHAIN modifier that can cause the Transaction Manager
limitation to be reached. If there is a strong possibility of reaching
the limit, you should use the FIND verb with the PERFORM option as a
workaround. The PERFORM routine should call dynamic transaction logging
for every n number of REPLACE (CURRENT) records (where n is the number or
records deleted for each dynamic transaction that is shorter in length
than the limitation), or select a smaller set of records to replace by
using the match register.
Database Unlocking
The first call to DBUNLOCK unlocks all previously-set locks for that
database. The first call to DBUNLOCK can be issued in several ways:
* A PROC statement.
* A LOGTRAN(XEND) statement, provided that the LOCK option was
specified on the LOGTRAN(XBEGIN) statement.
* A LOGTRAN(XUNDO) statement, provided that the LOCK option was
specified in the LOGTRAN(XBEGIN) statement.
* The end of the Transact/iX program.
If you are calling DBLOCK and DBUNLOCK with the PROC statement, we
recommend that you do all your locking/unlocking with the PROC statement.
Combining LOGTRAN locking/unlocking with calls to DBLOCK/DBUNLOCK can
produce unexpected results or errors.
MPE/iX 5.0 Documentation