You define a logical transaction by issuing a COMMIT or ROLLBACK command following the last data source command of the transaction. For simplicity, the remainder of this topic refers to COMMIT only, but unless stated otherwise, both commands are meant. For example, the beginning of your application is the beginning of its first logical transaction. The data source commands that follow are part of the transaction. When the application issues its first COMMIT command, it marks the end of the first transaction.
The data source commands that follow the first COMMIT become part of the second logical transaction. The next COMMIT to be issued marks the end of the second transaction, and so on.
The COMMIT command defines the boundary of the transaction. All data source commands issued between two COMMIT commands are in the same transaction. This explanation describes the simplest case, in which a transaction exists entirely within a single procedure. When a transaction spans procedures, you have several options for deciding how to define a transaction boundary, as described in When an Application Ends With an Open Transaction.
For example, transferring money from a savings account to a checking account requires two update commands. If you want to define the transfer, including both updates, as one logical transaction, you could use the following function:
CASE TransferMoney UPDATE Savings FROM SourceAccts UPDATE Checking FROM TargetAccts COMMIT ENDCASE
A data source command can fail for many reasons. For example, an UDPATE command might try to write to a record that never existed because a key was mistyped, or an INCLUDE command might try to add a record that has already been added by another user.
In some cases, when a command fails, you might want to keep the transaction open and simply resolve the problem that caused the command to fail. For example, in the first case of attempting to update a record that does not exist, you might wish to ask the application user to correctly re-enter the customer code (which is being used as the key of the record). In other cases, you might wish to roll back the entire transaction.
If a data source command fails, it will only cause the logical transaction that contains it to be automatically rolled back in certain circumstances. The deciding factor is when a data source command fails. If a data source command fails when the transaction:
You can evaluate the success of a data source command in an open transaction by testing the value of the FocError system variable immediately after issuing the command. If you wish the failure of the data source command to roll back the transaction, you must issue a ROLLBACK command.
A transaction that is ongoing and has not yet been committed is called an open transaction. To cancel an open transaction, you must issue a ROLLBACK command. ROLLBACK voids any of the data source commands of the transaction that have already been issued so that none of them are written to the data source.
When a logical transaction is committed or rolled back, it resets all position markers in all the data sources that are accessed by the transaction procedures. Resetting the position markers points them to the beginning of the data source segment chains.
A transaction is at its optimal size when it includes only those data source commands that are mutually dependent upon each other for validity. If you include independent commands in the transaction and one of the independent commands fails when you try to commit the transaction, the dependent group of commands will be needlessly rolled back.
For example, in the following banking transaction that transfers funds from a savings account to a checking account, you should not add an INCLUDE command to create a new account, since the validity of transferring money from one account to another does not depend upon creating a new account.
CASE TransferMoney UPDATE Savings FROM SourceAccts UPDATE Checking FROM TargetAccts COMMIT ENDCASE
Another reason for not extending transactions unnecessarily is that, in a multi-user environment, the longer a transaction takes, the more likely it is to compete for records with transactions submitted by other users. Transaction processing in a multi-user environment is described in Concurrent Transaction Processing.
Logical transactions can span multiple Maintain Data procedures. If a Maintain Data procedure with an open transaction passes control to an App Studio procedure, the open transaction is suspended. When control next passes to a Maintain Data procedure, the transaction picks up from where it had left off.
When a transaction spans several procedures, you will usually find it easier to define the boundaries of the transaction if you commit it in the highest procedure in the transaction (that is, in the procedure closest to the root procedure). Committing a transaction in a descendant procedure of a complex application, where it is more difficult to track the flow of execution, makes it difficult to determine the transaction boundaries (that is, to know which data source commands are being included in the transaction).
When a child procedure returns control to its parent procedure, and the child has an open logical transaction, you have two options:
KEEP and RESET are described in Command Reference.
Consider a situation where procedure A calls procedure B, and procedure B then calls procedure C. The entire application contains no COMMIT commands, so the initial logical transaction continues from the root procedure (A) through the descendant procedures (B and C). C and B both return control to their parent procedure using a GOTO END command.
The table below shows how specifying or omitting the KEEP option when procedures B and C return control affects the transaction boundaries of the application (that is, how the choice between KEEP and the implied COMMIT determines where the initial transaction ends, and how many transactions follow).
C returns to B with... |
B returns to A with... |
Transaction boundaries (||) |
---|---|---|
KEEP |
KEEP |
A-B-C-B-A one transaction |
KEEP |
implied COMMIT |
A-B-C-B || A two transactions |
implied COMMIT |
KEEP |
A-B-C || B-A two transactions |
implied COMMIT |
implied COMMIT |
A-B-C || B || A three transactions |
If a transaction writes to multiple types of data sources, each database management system (DBMS) evaluates its part of the transaction independently. When a COMMIT command ends the transaction, the success of the COMMIT against each data source type is independent of the success of the COMMIT against the other data source types. This is known as a broadcast commit. If any part of the broadcast commit fails, the value of FocCurrent is not zero (0).
For example, if you issue a Maintain Data procedure against the FOCUS data sources Employee and JobFile, and a DB2 data source named Salary, the success or failure of the COMMIT against Salary is independent of its success against Employee and JobFile. It is possible for it to be successful against Salary and write that part of the transaction, while being unsuccessful against Employee and JobFile and roll back that part of the transaction. Because it is unsuccessful against Employee and JobFile, the value of FocCurrent is not zero (0).
In an application that spans multiple WebFOCUS Servers, the server defines the maximum scope of a logical transaction. No transaction boundary can extend beyond a WebFOCUS Server. If one of your applications spans several servers, protect its transaction boundaries by ensuring that:
If a procedure with an open transaction calls another procedure that resides on a different WebFOCUS Server, and the situation violates either of the previous restrictions, the data source commands on the new server will comprise a new transaction. When control returns to the calling procedure on the original server, the original open transaction continues from where it had left off.
If an application terminates while a logical transaction is still open, Maintain Data issues an implied COMMIT command to close the open transaction, ensuring that any data source commands issued after the last explicit COMMIT are accounted for. The only exception is if your Maintain Data session abnormally terminates. Maintain Data does not issue the implied COMMIT, and any remaining uncommitted data source commands are rolled back.