Building Distributed
Applications
Using the Progress AppServer


Implementing Automatic Transactions

To implement an automatic transaction, you provide a specially-coded remote procedure (transaction initiating procedure) that initializes a transaction object in the Application Server process where it executes. This transaction object is a permanent object in the session context that provides methods and attributes on a transaction object handle that allow the AppServer session to commit, rollback, or set and check the status of an automatic transaction. Because an automatic transaction terminates when the context of the transaction initiating procedure is deleted, you must execute the transaction initiating procedure persistently to make practical use of the transaction that it initiates.

Initializing the Automatic Transaction Object

To initialize the transaction object, you must create a transaction initiating procedure by specifying the TRANSACTION-MODE statement as the first executable statement in the procedure file:

SYNTAX
TRANSACTION-MODE AUTOMATIC [ CHAINED ] 

When the client calls this transaction initiating procedure as a remote persistent procedure, an automatic transaction starts in the context of the AppServer session and can remain open after the remote procedure call returns to the client. The context for this open automatic transaction is the entire AppServer session, not (as you might think) the context internal to the transaction initiating procedure instantiated in the AppServer session. Thus, any remote procedure (persistent or non-persistent) that is subsequently executed participates in this open automatic transaction.

Controlling the Transaction

You can access the transaction object handle for the initialized transaction object using the TRANSACTION attribute on the procedure of any procedure executing in the AppServer session by using the THIS-PROCEDURE system handle:

DEFINE VARIABLE hTran AS HANDLE NO-UNDO.
hTran = THIS-PROCEDURE:TRANSACTION. 

You can then access the transaction object methods and attributes to control the transaction, as described in Table 4–2.

Table 4–2: Transaction Handle Methods and Attributes

Method/Attribute
Return/Data
Type

Description
DEFAULT-COMMIT
LOGICAL
A writable attribute whose setting tells the transaction object how to complete any open automatic transaction if the transaction terminates with no prior invocation of the SET-COMMIT( ) or SET-ROLLBACK( ) method (for example, if the transaction initiating procedure is deleted). Setting it to TRUE ensures that the transaction is committed. Setting it to FALSE ensures that the transaction is rolled back. The default value is FALSE.
IS-OPEN
LOGICAL
A read-only attribute that returns TRUE if a transaction is active. The function of this attribute is identical to the 4GL TRANSACTION function. See also the TRANS-INIT-PROCEDURE attribute.
SET-COMMIT( )
LOGICAL
Tells the transaction object to commit any active automatic transaction after the current request returns execution to the client. If no automatic transaction is active, the method returns FALSE.
Note that you cannot invoke this method after invoking a prior SET-ROLLBACK( ) within the same client request.
SET-ROLLBACK( )
LOGICAL
Tells the transaction object to roll back any active automatic transaction after the current request returns execution to the client. If no automatic transaction is active, the method returns FALSE.
Note that you can invoke this method after invoking a prior SET-COMMIT( ) within the same client request.
TRANS-INIT-PROCEDURE
HANDLE
A read-only attribute that returns the procedure handle to the transaction initiating procedure for any active automatic transaction. If no automatic transaction is active this attribute returns an invalid handle. (Validate with the VALID-HANDLE function.)

Terminating Automatic Transactions

An automatic transaction remains active as long as the context of the transaction initiating procedure remains active and you do not otherwise terminate the transaction using a transaction handle method. Thus, you can terminate an automatic transaction using two techniques:

Restarting Automatic Transactions

After an automatic transaction terminates, how an automatic transaction can restart depends on how it terminates. If the transaction terminates by deleting the transaction initiating procedure, only a client application can restart an automatic transaction by again remotely instantiating a transaction initiating procedure.

If a transaction initiating procedure is still active in the AppServer session, you can support the restarting of an automatic transaction using one of the following techniques, depending on how you code the transaction initiating procedure:

CAUTION: In general, try to keep automatic transactions on an AppServer as short as possible. The multi-request feature of automatic transactions tend to encourage long transactions, and long transactions can make large or important segments of a database inaccessible to other Progress sessions for an indefinite period. This can create database deadlocks and traffic jams that can seriously retard the performance of multiple sessions. For more information on techniques to shorten automatic transactions, see the information on transaction management considerations in Design and Implementation Considerations."
Automatic Transaction Example

By including appropriate calls to internal procedures of the transaction initiating procedure in your remote procedures, you can encapsulate the management of a multi-request automatic transaction in a manner completely hidden from the client. However, the following example is relatively simple in that the client calls all of the internal procedures of the transaction initiating procedure directly, thus more openly exposing the transaction mechanism to the client.

The following two procedures include a transaction initiating procedure and a 4GL client procedure that accesses it.

The transaction initiating procedure (a-txtest.p) implements an unchained automatic transaction. This procedure provides three internal procedures that allow the client to explicitly start (startme), commit (stopme), and roll back (abortme) transactions.

a-txtest.p
TRANSACTION-MODE AUTOMATIC.  /*this statement makes this procedure a */
                             /*transaction initiating procedure          */

PROCEDURE startme:  /*this empty internal proc starts a transaction*/
END.

PROCEDURE stopme: /*this internal proc arranges for a commit*/
  DEFINE VARIABLE x AS HANDLE.
  x = THIS-PROCEDURE:TRANSACTION.
  x:SET-COMMIT().
END.

PROCEDURE abortme: /*this internal proc arranges for a rollback*/
  DEFINE VARIABLE x AS HANDLE.
  x = THIS-PROCEDURE:TRANSACTION.
  x:SET-ROLLBACK().
END. 

The client procedure (a-txclnt.p) establishes the initial transaction object by instantiating a-txtest.p on the AppServer. After calling two database update procedures, the client commits the transaction with a call to stopme. It then begins a new transaction by calling startme, and after calling the same two database update procedures, rolls back the transaction by calling abortme.

As noted in the comments a-txtest.p establishes an unchained transaction object. If the procedure instead establishes a chained transaction object, each time the current transaction terminates (stopme, abortme), the Application Server process automatically starts a new transaction with no need for the client to call a procedure (startme) to initiate it.

a-txclnt.p1
DEFINE VARIABLE ph AS HANDLE.
/*create a transaction initiating procedure and start the transaction*/
RUN a-txtest.p ON SERVER h TRANSACTION DISTINCT PERSISTENT SET ph.

RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.

RUN stopme IN ph.  /*causes the transaction to commit including both
                      custupdate and orderupdate*/

/*start another transaction by running an internal proc of a-txtest.p*/
RUN startme IN ph.  /*starts a new transaction -- this is
                       unnecessary if the TRANSACTION-MODE statement
                       in a-txtest.p specifies the CHAINED option*/

RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.

RUN abortme IN ph.  /*causes the transaction to roll back including
                        both custupdate and orderupdate*/

DELETE PROCEDURE ph. 
  1. The RUN statements in this example use the 4GL syntax for executing remote procedures of various types. For more information, see Programming the Client Application."

Note that while these examples show a close correlation between transaction directives and remote procedure requests, there is no requirement that this be so. You can completely hide any sense that the client is ultimately initiating and managing a transaction by hiding all calls to the internal procedures of the transaction initiating procedure within the logic of your application API. For example, you can encapsulate the remote procedure calls in a-txclnt.p in higher-level procedures, such as start-updates.p, rerun-updates.p, and end-updates.p.


Copyright © 2004 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095