# Compaq ACMS for OpenVMS
Writing Applications

Previous | Contents | Index

# 6.4 Concurrent-Use Licensing with Detached Tasks

For every detached task started, the ACMS$SIGN_IN service is used to sign in a task submitter. If your ACMS system has a concurrent-use license, a set of license units is allocated for each detached task that is started. These license units remain allocated until the task submitter signs out and the detached task stops.

The submitter for a detached task is signed out under the following conditions:

- The task completes successfully.
- The retry limit for the detached task is exceeded.
- The task is canceled as a result of an ACMS/CANCEL TASK or an ACMS/CANCEL USER operator command.
- The application within which the detached task is executing is stopped.
- The task completes with a task failure that ACMS cannot retry.
- An unexpected internal failure occurs; for example, the retry wait timer could not start.

# Chapter 7
Defining Distributed Transactions

The previous chapters show how to write task definitions that perform add, delete, update, and inquiry operations. For the sake of simplicity, the examples assume that only one data source is being accessed. However, there might be times when you need to combine updates to multiple files and/or databases into one operation, or transaction. This chapter describes how to define tasks that use distributed transactions. Specifically, this chapter describes:

- Why you might want to use distributed transactions
- How to include distributed transactions syntax in your task definition
- How to include multiple resource managers in a distributed transaction
- How to include a called task in a distributed transaction
- How distributed transactions affect server context
- How to exclude a processing step from participating in a distributed transaction
- Why transactions can fail

# 7.1 Why Use Distributed Transactions

Often, a business function involves operations on several databases or files. For example, a simple funds transfer function involves debiting one account and crediting another account. For the function to be successful, both operations must complete successfully. Because it is important to treat both operations as one unit of work, include them in an atomic transaction. An atomic transaction is a set of one or more operations where either all the operations take effect or none of them take effect. If any of the operations cannot complete successfully, the transaction is aborted. This means that all the operations are rolled back, or undone. The transaction is said to be committed if all the operations complete successfully and are made permanent.

In the funds transfer example, it is essential that the operations be included in an atomic transaction. Otherwise, the consistency of the database is in jeopardy. For example, if the system fails after the debit operation but before the credit operation, the database is not accurate.

To access information stored in databases and files, you use resource managers. A resource manager controls shared access to a set of recoverable resources. The most common type of resource manager is a database system. Rdb, DBMS, and RMS are resource managers. On the OpenVMS operating system, a resource manager can ensure that a set of database operations involving one database is atomic. An atomic set of operations, such as debits and credits, to a database is a database transaction. In the funds transfer example, if the account to be debited and the account to be credited reside in the same Rdb database, Rdb can ensure that both operations complete successfully or both operations are undone. If both operations are successful, Rdb commits the database transaction. If one of the operations fails, Rdb rolls back the database transaction.

The funds transfer example represents a very simple business function with one database. However, your application might include multiple databases. The OpenVMS operating system provides a set of transaction services, DECdtm services, that ACMS uses to control a group of database transactions involving multiple databases on one or more nodes. The DECdtm services ensure the atomicity of a set of database transactions included in a distributed transaction. A distributed transaction is an atomic transaction that consists of a set of operations involving multiple resources on one or more nodes. Suppose the two accounts in the funds transfer example reside in separate Rdb databases. A distributed transaction would include two database transactions. If the debit and credit operations complete successfully, the DECdtm services end the distributed transaction by instructing the Rdb resource manager to prepare the database transactions to be committed. If Rdb successfully prepares both database transactions, the DECdtm services instruct Rdb to commit both database transactions. If one of the operations fails, the DECdtm services abort the distributed transaction and instruct Rdb to roll back the database transactions.

The resource managers need not be the same type to participate in a distributed transaction. For example, a distributed transaction could include a procedure that updates an Rdb database and a second procedure that writes to an RMS file.

Because the ACMS queuing system uses RMS, you can treat the queuing system as another type of resource manager by setting queue files for journaling. You can coordinate the removal of queued task elements from the task queue with updates to a database. Likewise, you can coordinate the insertion of queued task elements to a task queue with updates to a database. See Chapter 9 for details on how to include queuing operations in a distributed transaction.

See Compaq ACMS for OpenVMS Concepts and Design Guidelines for more information on designing distributed transactions.

# 7.2 Including Distributed Transactions Syntax in the Task Definition

ACMS provides syntax that lets you control distributed transactions in the task definition. You can start a distributed transaction on a root block step, nested block step, root processing step, or a processing step within a block by specifying the phrase DISTRIBUTED TRANSACTION in the attributes part of the step. The DISTRIBUTED keyword is optional. A distributed transaction must end in the action part of the same step on which it started. Therefore, a distributed transaction can span multiple processing steps only if you specify TRANSACTION on the block that contains those processing steps.

To explicitly end a distributed transaction, specify either the COMMIT TRANSACTION action clause or the ROLLBACK TRANSACTION action clause. COMMIT TRANSACTION instructs ACMS to call the transaction services to commit the transaction. If the resource managers participating in the transaction can successfully complete their operations, DECdtm instructs each resource manager to commit its operations, making all the changes permanent. However, if any of the resource managers is unable to complete its operations, DECdtm instructs all resource managers to roll back their updates. ROLLBACK TRANSACTION instructs ACMS to call the DECdtm services to abort the transaction; DECdtm then instructs each resource manager to roll back any updates made during the transaction.

Example 7-1 shows the structure of a task definition that contains a distributed transaction.

Example 7-1 Distributed Transaction on a Nested Block Step

    BLOCK WORK
       EXCHANGE
          .
          .
       BLOCK WORK WITH DISTRIBUTED TRANSACTION
          PROCESSING
              .
              .
          PROCESSING
              .
              .
       END BLOCK;
       COMMIT TRANSACTION;

       EXCHANGE
          .
          .
    END BLOCK;

In this example, the distributed transaction spans the two processing steps within the nested block. Because the distributed transaction starts on the nested block, you must specify the COMMIT TRANSACTION clause in the action part of the same block step. The exchange steps before and after the nested block are not part of the distributed transaction.

If you do not explicitly end a distributed transaction in the action part of the step on which it started, ACMS provides a default of COMMIT TRANSACTION unless the action part of the step contains either the CANCEL TASK or RAISE EXCEPTION sequencing action clause, in which case ACMS provides a default of ROLLBACK TRANSACTION.

You can also start and end distributed transactions in procedures by using the $START_TRANS, $END_TRANS, and $ABORT_TRANS system services. See Compaq ACMS for OpenVMS Systems Interface Programming for information on including these services in agent programs.

# 7.3 Including Multiple Resource Managers in a Distributed Transaction

The AVERTZ sample car rental application includes a reservation task, VR_RESERVE_TASK, which gathers customer and reservation information from the terminal operator and updates several Rdb databases. The task uses several distributed transactions to ensure the integrity of the databases.

Example 7-2 shows a part of the VR_RESERVE_TASK that includes updates to two Rdb databases in a distributed transaction. Although the resources in this example use the same type of resource manager, Rdb, you can include different types of resource managers in a distributed transaction. For example, a distributed transaction might include updates to a DBMS database and an RMS file.

Example 7-2 Multiple Database Updates in a Distributed Transaction

    CANCEL_RES:

    BLOCK WITH TRANSACTION

        PROCESSING
            CALL PROCEDURE  VR_CANCEL_RS_PROC
            IN      VR_UPDATE_SERVER
            USING   VR_RESERVATIONS_WKSP,
                    VR_CONTROL_WKSP;

        ACTION IS
            GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL;
            MOVE "CANCEL  " TO VR_HIST_WKSP.TRANS_TYPE;

        PROCESSING
            CALL PROCEDURE  VR_WRITE_HIST_RECORD_PROC
            IN      VR_LOG_SERVER
            USING   VR_HIST_WKSP,
                    VR_RESERVATIONS_WKSP;

    END BLOCK;

        ACTION IS
            COMMIT TRANSACTION;
            MOVE "     " TO VR_CONTROL_WKSP.CTRL_KEY,
                 "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY;

The CANCEL_RES nested block step appears toward the end of the VR_RESERVE_TASK, and is processed if the terminal user wants to cancel a reservation. The nested block step starts a distributed transaction by specifying WITH TRANSACTION.

The first processing step calls the VR_CANCEL_RS_PROC procedure, which uses the reservation number in the VR_RESERVATIONS_WKSP to locate and delete the particular reservation record in the reservation database. If the procedure completes successfully, the action part of the step moves CANCEL to the TRANS_TYPE field in VR_HIST_WKSP.

The second processing step calls the VR_WRITE_HIST_RECORD_PROC procedure, which records the cancel transaction in the history database.

The COMMIT TRANSACTION clause in the action part of the nested block step instructs ACMS to call DECdtm to make permanent the effects of the two database operations. Because the distributed transaction starts on the nested block step, it must end in the action part of the same step.

# 7.4 Using Task Sequencing Actions in a Distributed Transaction

When writing some task definitions that use distributed transactions, you must be aware of restrictions that ACMS imposes on the use of certain sequencing action clauses. Specifically, use of distributed transactions affects the EXIT TASK, CANCEL TASK, GOTO TASK, and REPEAT TASK action clauses.

- Because a distributed transaction must end in the action part of the step on which it starts, you cannot specify EXIT TASK on the action part of a step within a distributed transaction. Instead, you can use the EXIT BLOCK action clause, which instructs ACMS to pass control to the action part of the block step that started the distributed transaction.
- If you specify CANCEL TASK in the action part of a step that starts a distributed transaction, ACMS provides a default of ROLLBACK TRANSACTION. You can override this default by specifying COMMIT TRANSACTION on the action part of the step that starts the transaction. If you specify CANCEL TASK on a step within a distributed transaction, ACMS always calls DECdtm to abort the distributed transaction.
- You cannot specify the GOTO TASK clause in the action part of a step within a distributed transaction; however, you can specify GOTO TASK in the action part of the step that starts a distributed transaction. If you specify GOTO TASK on a root block, that task cannot be called by a parent task to participate in a distributed transaction.
- You cannot specify the REPEAT TASK clause in the action part of a step within a distributed transaction; however, you can specify REPEAT TASK in the action part of the step that starts a distributed transaction. If you specify REPEAT TASK on a root block, that task cannot be called by a parent task to participate in a distributed transaction. An alternative is to specify the WHILE DO block conditional clause at the start of the root block.
- If you specify the REPEAT STEP clause in the action part of a root block, that task cannot be called by a parent task to participate in a distributed transaction. As with REPEAT TASK, an alternative is to specify WHILE DO at the start of the root block.

# 7.5 Including a Called Task in a Distributed Transaction

Previous sections show how to define a distributed transaction within a single task. You can also define a distributed transaction that starts in a parent task, includes calls to other tasks, and then ends in the parent task. For a called task to be able to participate in a distributed transaction started by a parent task, the called task must conform to the following rules:

- The root block or root processing step must include the TRANSACTION phrase.
- The root block or root processing step cannot include a sequencing action clause other than EXIT TASK, CANCEL TASK, or RAISE EXCEPTION.
- The root block or root processing step cannot include the COMMIT TRANSACTION or ROLLBACK TRANSACTION action clause.
- The root block or root processing step cannot include an exception handler.
- The root block or root processing step cannot include the CANCEL ACTION phrase.

A task that conforms to these rules is said to be a composable task. A parent task cannot exclude a called task from participating in an existing distributed transaction.

In the VR_RESERVE_TASK, when customers make a reservation, they have the option of checking out the car immediately. Rather than require the terminal user to exit from the task, return to the AVERTZ menu, and select a different task to check out the car, you can have the VR_RESERVE_TASK call the VR_COMPLETE_CHECKOUT_TASK to handle the checkout. Example 7-3 shows the nested block in VR_RESERVE_TASK that includes a called task within a distributed transaction.

Example 7-3 Calling a Task to Participate in a Distributed Transaction

    BLOCK WITH TRANSACTION

    UPDATE_CUST_INFO:
    !+
    ! If the user wanted to checkout the car and has updated the
    ! driver license info then
    !-

        PROCESSING
            CALL PROCEDURE  VR_STORE_CU_PROC
            IN      VR_CU_UPDATE_SERVER
            USING   VR_CONTROL_WKSP,
                    VR_CUSTOMERS_WKSP,
                    VR_TRANS_WKSP;

        ACTION IS
            IF (ACMS$T_STATUS_TYPE = "B") THEN
                    GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL;
                    RAISE EXCEPTION VR_UPDATE_ERROR;
            END IF ;
    !+
    ! If want to check car out now (=GTCAR) then call
    ! VR_COMPLETE_CHECKOUT_TASK to do that.
    !-

        PROCESSING
            CALL TASK       VR_COMPLETE_CHECKOUT_TASK
            USING           VR_SENDCTRL_WKSP,
                            VR_CONTROL_WKSP,
                            VR_RESERVATIONS_WKSP,
                            VR_TRANS_WKSP,
                            VR_VEHICLES_WKSP;
    END BLOCK;

        ACTION IS
                    MOVE "     " TO VR_CONTROL_WKSP.CTRL_KEY,
                         "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY;
                    COMMIT TRANSACTION;
                    GOTO STEP DISP_STAT;

The nested block step in VR_RESERVE_TASK starts a distributed transaction with the TRANSACTION phrase. This step uses a distributed transaction because it performs processing work before it calls the VR_COMPLETE_CHECKOUT_TASK. The first processing step calls the VR_STORE_CU_PROC procedure to update the customer's record. If the called task fails, it is important that the effects of the VR_STORE_CU_PROC procedure be rolled back.

The second processing step calls VR_COMPLETE_CHECKOUT_TASK. Because the distributed transaction starts on the nested block step in the parent task, the action part of the same step ends the distributed transaction with the COMMIT TRANSACTION clause.

Example 7-4 shows the complete definition of the VR_COMPLETE_CHECKOUT_TASK, called by VR_RESERVE_TASK.

Example 7-4 Complete Definition of the VR_COMPLETE_CHECKOUT_TASK

    REPLACE TASK AVERTZ_CDD_TASK:VR_COMPLETE_CHECKOUT_TASK

    USE WORKSPACES VR_CONTROL_WKSP,
                   VR_VEHICLES_WKSP,
                   VR_RENTAL_CLASSES_WKSP,
                   VR_TRANS_WKSP,
                   VR_SENDCTRL_WKSP,
                   VR_RESERVATIONS_WKSP,
                   VR_VE_ARRAY_WKSP,
                   VR_HIST_WKSP;

    TASK ARGUMENTS ARE VR_SENDCTRL_WKSP     WITH ACCESS READ,
                       VR_CONTROL_WKSP      WITH ACCESS MODIFY,
                       VR_RESERVATIONS_WKSP WITH ACCESS MODIFY,
                       VR_TRANS_WKSP        WITH ACCESS READ,
                       VR_VEHICLES_WKSP     WITH ACCESS READ;

    BLOCK WITH TRANSACTION
               NO I/O
    !
    PERFORM:
    !+
    ! Perform the checkout process or cancel the reservation depending
    ! on the user's choice
    !
         PROCESSING
            SELECT FIRST TRUE
                    (VR_CONTROL_WKSP.CTRL_KEY = "OK"):
                            CALL PROCEDURE    VR_COMPLETE_CHECKOUT_PROC
                            IN      VR_UPDATE_SERVER
                            USING   VR_RESERVATIONS_WKSP,
                                    VR_VEHICLES_WKSP;
                    (VR_CONTROL_WKSP.CTRL_KEY = "CANCL"):
                            CALL PROCEDURE  VR_CANCEL_RS_PROC
                            IN      VR_UPDATE_SERVER
                            USING   VR_RESERVATIONS_WKSP,
                                    VR_CONTROL_WKSP;
            END SELECT;

        ACTION IS
            SELECT FIRST TRUE OF
            (VR_CONTROL_WKSP.CTRL_KEY = "OK"):
               GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL;
               MOVE "CHECKOUT"  TO VR_HIST_WKSP.TRANS_TYPE,
                  VR_VEHICLES_WKSP.VEHICLE_ID TO VR_HIST_WKSP.VEHICLE_ID;
            (VR_CONTROL_WKSP.CTRL_KEY = "CANCL"):
               GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL;
               MOVE "CANCEL  "  TO VR_HIST_WKSP.TRANS_TYPE,
                  VR_VEHICLES_WKSP.VEHICLE_ID TO VR_HIST_WKSP.VEHICLE_ID;
            NOMATCH:
                    CANCEL TASK;
            END SELECT;

    ! Write to the history record to record the completion of the
    ! checkout or the cancellation of the reservation.

        PROCESSING
            CALL PROCEDURE  VR_WRITE_HIST_RECORD_PROC
            IN      VR_LOG_SERVER
            USING   VR_HIST_WKSP,
                    VR_RESERVATIONS_WKSP;

    END BLOCK;

    !
    ! This is a composable task called by the RESERVE and the CHECKOUT
    ! tasks. In the case of the RESERVE task the distributed transaction
    ! is started in the RESERVE task and therefore committed in the
    ! RESERVE task.  However, the CHECKOUT task does not start the
    ! distributed transaction but the COMPLETE_CHECKOUT task is composable
    ! so the commit is done as a default action by ACMS.
    !
    END DEFINITION;

For the VR_COMPLETE_CHECKOUT_TASK task to be composable, it must include the TRANSACTION phrase at the root block step. At the end of the block step, the task does not commit or roll back the distributed transaction. The transaction must end in the task in which it started, the VR_RESERVE_TASK.

Example 7-4 shows one called task participating in a distributed transaction. You can include multiple called tasks in a distributed transaction. For example, the VR_COMPLETE_CHECKOUT_TASK can include a call to another task as long as that task is composable.

A parent task that does not start a distributed transaction can call a task that includes a distributed transaction. In this case, the called task ends the distributed transaction.

# 7.6 How Distributed Transactions Affect Server Context

By default, any server used by processing steps within a distributed transaction is reserved to that distributed transaction until the transaction ends. As a result, ACMS automatically retains server context between steps within a distributed transaction. You cannot specify the RELEASE SERVER CONTEXT action clause on a step that participates in a distributed transaction. When DECdtm ends a distributed transaction, by either committing it or rolling it back, ACMS automatically releases server context. You cannot specify the RETAIN SERVER CONTEXT or NO SERVER CONTEXT ACTION clause in the action part of a step that starts a distributed transaction.

Within a distributed transaction, if multiple processing steps call one or more procedures in the same server, ACMS allocates just one server process for all the processing steps. Therefore, the first procedure called within a distributed transaction must ready the database for all procedures in the transaction. See Compaq ACMS for OpenVMS Writing Server Procedures for more information on how to write procedures that ready the database.

A called task that participates in a distributed transaction started by the parent task does not share server context with the parent task. In other words, if the parent and called tasks include processing steps that call procedures in the same server, ACMS allocates a server process for the parent task and a second server process for the called task.

Note A task can retain context in multiple servers only if each server participates in a distributed transaction. If a task attempts to start a distributed transaction while retaining context in a server, ACMS cancels the task. It is strongly recommended that you do not include exchange steps within a distributed transaction. Including exchange steps within a distributed transaction increases the system resources used by the application, and can adversely affect performance.

# 7.7 Excluding a Processing Step from a Distributed Transaction

Occasionally, within a block that starts a distributed transaction, you want to include a processing step that accesses a database independently of the distributed transaction. You need to exclude a processing step from a distributed transaction, if the application requires that the effects of the processing step survive even when the transaction is rolled back.

For example, an application requires a security log that records information about users who access or attempt to access sensitive information. In this case, the procedure that updates the security log is not part of the distributed transaction because if the transaction fails you do not want to roll back the security log update.

To exclude a processing step from a distributed transaction, use the NONPARTICIPATING SERVER phrase on the processing step. Because a task can retain context in multiple servers only if each of the servers participates in a distributed transaction, ACMS automatically releases server context at the end of a processing step that specifies NONPARTICIPATING SERVER. You cannot specify the RETAIN SERVER CONTEXT or NO SERVER CONTEXT ACTION clause in the action part of a processing step that specifies NONPARTICIPATING SERVER.

The AVERTZ sample application includes an agent written in C that starts a distributed transaction and calls a task that joins the distributed transaction and queues a task. The called task, VR_FAST_CHECKIN_TASK, uses the NONPARTICIPATING SERVER phrase to exclude two processing steps from the distributed transaction. Example 7-5 shows the VR_FAST_CHECKIN_TASK definition.

Example 7-5 VR_FAST_CHECKIN_TASK with Nonparticipating Processing Steps

    BLOCK WORK WITH DISTRIBUTED TRANSACTION
                    NO I/O

    !
    ! Retrieve the reservation record, using the reservation number/id
    ! entered by the customer and passed by the vr_agent agent.
    !

            PROCESSING WITH NONPARTICIPATING SERVER
                    CALL PROCEDURE  VR_FIND_RES_PROC
                      IN    VR_READ_SERVER
                    USING   VR_FAST_CHECKIN_WKSP,
                            VR_RESERVATIONS_WKSP;

            ACTION IS
                IF (ACMS$T_STATUS_TYPE = "G")
                THEN
                   MOVE VR_FAST_CHECKIN_WKSP.ACTUAL_RETURN_DATE
                    TO VR_VEHICLE_RENTAL_HISTORY_WKSP.ACTUAL_RETURN_DATE,
                       VR_FAST_CHECKIN_WKSP.RETURN_ODOMETER_READING
                    TO
                      VR_VEHICLE_RENTAL_HISTORY_WKSP.RETURN_ODOMETER_READING;
                ELSE
                    CANCEL TASK RETURNING  ACMS$L_STATUS;
                END IF;

    !
    ! RETRIEVE THE VEHICLE AND VEHICLE_RENTAL_HISTORY RECORDS
    !

            PROCESSING WITH NONPARTICIPATING SERVER
                    CALL PROCEDURE  VR_FIND_VE_VRH_PROC
                      IN    VR_READ_SERVER
                    USING   VR_RESERVATIONS_WKSP,
                            VR_VEHICLES_WKSP,
                            VR_VEHICLE_RENTAL_HISTORY_WKSP,
                            VR_RENTAL_CLASSES_WKSP,
                            VR_TRANS_WKSP;

            ACTION IS
                IF (ACMS$T_STATUS_TYPE = "B") THEN
                    CANCEL TASK RETURNING  ACMS$L_STATUS;
                END IF;
    !
    ! QUEUE THE TASK TO BE RUN LATER
    !

            PROCESSING
                    CALL PROCEDURE    VR_ENQ_FAST_CHECKIN
                      IN    VR_QUEUE_SERVER
                    USING   VR_FAST_CHECKIN_WKSP;

            ACTION IS
                IF  (ACMS$T_STATUS_TYPE = "G")
                THEN
                    MOVE  "FASTCHIN"            TO VR_HIST_WKSP.TRANS_TYPE,
                              VR_VEHICLES_WKSP.VEHICLE_ID
                                                TO VR_HIST_WKSP.VEHICLE_ID;
                ELSE
                    CANCEL TASK RETURNING  ACMS$L_STATUS;
                END IF;

    !
    ! WRITE A RECORD OF A SUCCESSFUL CHECK IN TO THE HISTORY DATABASE
    !
            PROCESSING
                CALL PROCEDURE    VR_WRITE_HIST_RECORD_PROC
                IN      VR_LOG_SERVER
                USING   VR_HIST_WKSP,
                        VR_RESERVATIONS_WKSP;

    END BLOCK;

Because the VR_FAST_CHECKIN_TASK is joining a distributed transaction started by the agent, the root block step must use the DISTRIBUTED TRANSACTION phrase.

The first processing step in VR_FAST_CHECKIN_TASK uses the reservation ID, obtained from the customer by the agent, to retrieve the reservation record. The second processing step retrieves the car history record. Because the first two processing steps perform read-only operations, neither step needs to participate in the distributed transaction. Therefore, both steps use the NONPARTICIPATING SERVER phrase.

ACMS automatically releases context at the end of each of the first two processing steps, thereby freeing up the server processes for other tasks.

The third processing step calls the VR_ENQ_FAST_CHECKIN procedure to queue the VR_COMP_FAST_CHKIN_TASK.

Previous | Next | Contents | Index