If an exception occurs in a called task, you might want to continue executing the parent task rather than letting ACMS cancel it. You can specify an exception handler action in the processing step that called the task or on an outer block step in the parent task. Note that the CONTINUE ON BAD STATUS phrase is a declining feature that has been superseded by the exception handler mechanism. Do not use it in new task definitions. Example 8-4 shows part of a parent task that includes an exception handler action to recover from possible exceptions raised in the called task.
|Example 8-4 Recovering from an Exception Raised in a Called Task|
COMPLETE_PROC: ! CALL THE VR_COMPLETE_CHECKOUT_TASK TO COMPLETE THE CHECKOUT ! TRANSACTION. ! ! PROCESSING CALL TASK VR_COMPLETE_CHECKOUT_TASK USING VR_SENDCTRL_WKSP, VR_CONTROL_WKSP, VR_RESERVATIONS_WKSP, VR_TRANS_WKSP, VR_VEHICLES_WKSP; ! ! THE DISTRIBUTED TRANSACTION IS STARTED IN THE VR_COMPLETE_CHECKOUT_TASK ! BUT IS NOT EXPLICITLY COMMITTED SINCE IT IS A COMPOSABLE TASK. THE ! COMMIT WILL BE PERFORMED BY ACMS (DEFAULT ACTION). ! ACTION IS MOVE " " TO VR_CONTROL_WKSP.CTRL_KEY, "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY; ! ! RETRY IF A TIME OUT ERROR OCCURRED BEFORE CANCELING TASK. ! EXCEPTION HANDLER IF ( (ACMS$L_STATUS = ACMS$_TRANSTIMEDOUT AND VR_CONTROL_WKSP.RETRY_COUNT = 0) ) THEN MOVE 1 TO VR_CONTROL_WKSP.RETRY_COUNT; REPEAT STEP; ELSE GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL; MOVE " " TO VR_CONTROL_WKSP.CTRL_KEY, "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY; GOTO STEP DISP_STAT; END IF; ! ! DISPLAY STATUS TO THE USER. ! DISP_STAT: EXCHANGE SEND RECORD VR_CONTROL_WKSP SENDING VR_CONTROL_WKSP WITH SEND CONTROL VR_SENDCTRL_WKSP;
In Example 8-4, the COMPLETE_PROC processing step calls the VR_COMPLETE_CHECKOUT_TASK to compute the customer's bill and perform final checkout work. The VR_COMPLETE_CHECKOUT_TASK starts a distributed transaction. Because VR_COMPLETE_CHECKOUT_TASK is a composable task, it cannot explicitly commit or roll back the distributed transaction. Because a distributed transaction must end in the action part of the step where it starts, ACMS provides the default transaction action.
The exception handler part of the parent task uses an IF THEN ELSE conditional clause to test the contents of the ACMS$L_STATUS field in the ACMS$PROCESSING_STATUS workspace. If the called task raises the ACMS$_TRANSTIMEDOUT exception code, ACMS retries the called task once.
If a called task completes without raising an exception, ACMS returns the contents of any modify-access and write-access task argument workspaces to the parent task. ACMS then resumes executing the task from the action part of the processing step that called the task.
If a called task completes with an exception, ACMS does not return the
contents of task argument workspaces to the parent task. If a task,
called by a parent task that does not start a distributed transaction,
completes with any type of exception, ACMS raises a step exception in
the parent task. If a called task that participates in a distributed
transaction started by the parent task completes with a step exception,
ACMS raises a step exception in the parent task. If a called task that
participates in a distributed transaction completes with a transaction
or nonrecoverable exception, ACMS raises a transaction exception in the
8.5.3 Recovering from a Transaction Exception
If your task includes a distributed transaction, there are several exceptions you may want to test for and recover from by including an exception handler in the task definition. A communication link between nodes participating in the distributed transaction could be broken, or a resource manager that is part of the distributed transaction might detect an error and be unable to prepare its part of the distributed transaction. Example 8-5 shows how to test for and recover from a transaction exception.
|Example 8-5 Recovering from a Transaction Exception|
BLOCK WITH TRANSACTION !+ ! ONLY UPDATE CUSTOMER RECORD IF SHADOW RECORD INDICATES ! CHANGE HAS BEEN MADE ON PREVIOUS EXCHANGE. IF ( (VR_CUSTOMERS_SHADOW_WKSP.REC_STATUS = "1") OR (VR_TRANS_SHADOW_WKSP.REC_STATUS = "1") OR (VR_SENDCTRL_WKSP.SENDCTRL_KEY = "TRAGN") ) 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; MOVE "TRAGN" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY; EXIT BLOCK; ELSE MOVE " " TO VR_CONTROL_WKSP.CTRL_KEY, " " TO VR_SENDCTRL_WKSP.SENDCTRL_KEY; END IF; END IF; STORE_RESV: !+ ! CREATE RESERVATION NUMBER AND WRITE RESERVATION RECORD TO DB. !- PROCESSING CALL PROCEDURE VR_WRITE_RS_PROC IN VR_UPDATE_SERVER USING VR_CONTROL_WKSP, VR_RESERVATIONS_WKSP, VR_SITES_WKSP, VR_RENTAL_CLASSES_WKSP, VR_CUSTOMERS_WKSP; ACTION MOVE "RESERVE " TO VR_HIST_WKSP.TRANS_TYPE; !+ ! WRITE A RECORD TO THE HISTORY FILE TO LOG THE COMPLETED TRANSACTION ! - HISTORY RECORDS ARE RDB RECORDS (COULD BE ANY TYPE OF FILE SYSTEM ! OR DATABASE). THIS IS PART OF THE DISTRIBUTED TRANSACTION. !- PROCESSING CALL PROCEDURE VR_WRITE_HIST_RECORD_PROC IN VR_LOG_SERVER USING VR_HIST_WKSP, VR_RESERVATIONS_WKSP; ! ! END OF DISTRIBUTED TRANSACTION ! END BLOCK; ACTION IS IF (ACMS$T_STATUS_TYPE = "G") THEN COMMIT TRANSACTION; MOVE " " TO VR_SENDCTRL_WKSP.SENDCTRL_KEY, "Y" TO VR_CONTROL_WKSP.INCREMENT_RETRY_COUNT, 0 TO VR_CONTROL_WKSP.RETRY_COUNT; ELSE ROLLBACK TRANSACTION; GOTO STEP DISPLAY_CUST_INFO; END IF; ! ! EXCEPTION HANDLER FOR ABOVE BLOCK ! ! RETRY THE DISTRIBUTED TRANSACTION 5 TIMES (ONLY IF A ! ACMS$_TRANSTIMEDOUT ERROR OCCURRED) BEFORE CANCELING TASK. ! THE RETRY_COUNT IS INCREMENTED IN EITHER VR_STORE_CU_PROC ! OR VR_WRITE_RS_PROC. ! EXCEPTION HANDLER IF (ACMS$L_STATUS = ACMS$_TRANSTIMEDOUT AND VR_CONTROL_WKSP.RETRY_COUNT < 5) THEN REPEAT STEP; ELSE GET MESSAGE INTO VR_CONTROL_WKSP.MESSAGEPANEL; MOVE "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY, " " TO VR_CONTROL_WKSP.CTRL_KEY; GOTO STEP DISP_STAT; END IF;
In this example, a nested block starts a distributed transaction and includes three processing steps. The first processing step checks to see if the user has changed any customer information. If one of the shadow workspaces indicates that information has changed, the processing step calls the VR_STORE_CU_PROC procedure to update the database.
The STORE_RESV step adds a reservation record to the reservation database. The third processing step updates a transaction log in another database.
If the three processing steps complete successfully, the action part of the nested block step commits the distributed transaction. If an error occurs that causes the distributed transaction to fail before completing, ACMS raises a transaction exception. The exception handler part of the nested block checks for the ACMS$_TRANSTIMEDOUT transaction exception code to see if the distributed transaction did not complete within the time limit specified in the application definition with the TRANSACTION TIMEOUT phrase.
If the ACMS$L_STATUS field contains the ACMS$_TRANSTIMEDOUT exception
code, ACMS repeats the block step up to five times. If ACMS$L_STATUS
contains any other exception code, or if the distributed transaction
has already timed out five times, ACMS passes control to the DISP_STAT
exchange step, which displays an error message, and ends the task.
8.6 How ACMS Performs Exception Handling
To be able to design tasks that make efficient use of exception handling, you need to know how ACMS executes tasks that use exception handling. ACMS takes different actions depending upon the type of exception and where in the task definition the exception was raised; however, for step exceptions and transaction exceptions, ACMS performs the following steps:
The following sections describe how ACMS executes each type of
exception, and how ACMS cancels a task.
8.6.1 Executing a Step Exception Outside of a Distributed Transaction
If ACMS detects a step exception outside the bounds of a distributed
transaction, ACMS first looks for an exception handler on the same step
that raised the exception. If the current step does not include an
exception handler, ACMS checks block steps out to the root block step
for an exception handler. If ACMS does not find an exception handler,
it cancels the task. If ACMS finds an exception handler, it stores the
exception code in ACMS$PROCESSING_STATUS and performs the exception
handler action clauses. If the exception handler does not specify how
to handle the particular exception raised, ACMS searches out to the
root block for another exception handler. If ACMS cannot find an
exception handler that specifies how to handle the particular exception
raised, ACMS raises a nonrecoverable exception and cancels the task.
For example, if the exception handler specifies a recovery action only
for the FORMS$_TIMEOUT exception code, and a different exception is
raised, ACMS cancels the task.
8.6.2 Executing a Step Exception Within a Distributed Transaction
If ACMS detects a step exception within a distributed transaction, ACMS searches for an exception handler on the current step. If the current step does not include an exception handler, ACMS searches any outer block steps within the distributed transaction. If ACMS finds an exception handler, ACMS performs the exception handler action clauses.
If ACMS does not find an exception handler, or if the exception is
raised in a processing step that starts a distributed transaction, ACMS
checks to see whether the distributed transaction was started by the
current task, a parent task, or an agent. If the current task started
the distributed transaction, ACMS aborts the distributed transaction
and raises a transaction exception with the same exception code. If the
current task is a called task that participates in a distributed
transaction started by the parent task or an agent, ACMS cancels the
current task and raises a transaction exception in the parent task, or
returns the exception code to the agent.
8.6.3 Executing a Transaction Exception
As with step exceptions, when ACMS detects a transaction exception, it interrupts the task execution. If the task was not executing a COMMIT TRANSACTION clause when the transaction exception was raised, ACMS calls the server cancel procedure, if defined, for each server in which the task maintains context. If the task was executing COMMIT TRANSACTION, ACMS does not call any server cancel procedures. Before searching for an exception handler, ACMS releases context held in server processes.
ACMS then searches for an exception handler, starting on the step that
started the distributed transaction and searching out to the root block.
8.6.4 Executing Nonrecoverable Exceptions
When ACMS detects a nonrecoverable exception, it interrupts the task execution and begins to cancel the task. ACMS performs the following steps to cancel a task:
When you define the characteristics of a server in the task group definition, you can specify a server cancel procedure. Server cancel procedures perform cleanup work when a task cancels. For example, a server cancel procedure might close a channel that was opened for terminal I/O, release resources used by a server procedure such as an OpenVMS lock, or roll back a database recovery unit. However, calling server cancel procedures after an exception has been raised can adversely affect the performance of your application, because ACMS must make an additional call to each server process.
If an exception causes the server process to be run down, calling a
server cancel procedure increases the time it takes to shut down and
restart the server process. If the server process does not have to be
run down, calling a server cancel procedure causes a delay in
allocating the server process to another task instance. Therefore,
wherever possible, design tasks and step procedures that do not call
server cancel procedures when an exception has been raised. The
following sections describe how ACMS uses the exception type to
determine when to call server cancel procedures.
8.7.1 Step Exceptions and Server Cancel Procedures
If a step exception is raised, and the task definition does not include an exception handler to recover from the exception, ACMS raises a transaction exception or a nonrecoverable exception. If a distributed transaction was active when the step exception was raised, ACMS raises a transaction exception; otherwise, ACMS raises a nonrecoverable exception. ACMS then calls any server cancel procedures and cancels the task.
ACMS does not call server cancel procedures when a step exception is
raised and the task definition includes an exception handler to recover
from the exception.
8.7.2 Nonrecoverable Exceptions Raised by Action Clauses
If a nonrecoverable exception is raised while a task maintains context in one or more server processes, ACMS calls server cancel procedures. If the action part of the step in which the nonrecoverable exception is raised releases server context, ACMS does not call server cancel procedures. The action part of the processing step in the following example explicitly releases server context.
PROCESSING CALL UPDATE_ORDER_IN ORDER_SERVER USING ORDER_RECORD; ACTION IS IF (ACMS$L_STATUS <> 1) THEN RELEASE SERVER CONTEXT; CANCEL TASK RETURNING ACMS$L_STATUS; END IF;
Because ACMS executes server context actions before sequencing actions, ACMS releases server context before canceling the task. Therefore, ACMS does not call server cancel procedures.
The processing step in the previous example did not participate in a distributed transaction. Within a distributed transaction, ACMS requires that server context be retained between steps. Therefore, if a processing step within a distributed transaction cancels the task, ACMS calls server cancel procedures. Example 8-6 shows how to cancel a task within a distributed transaction without calling server cancel procedures.
|Example 8-6 Canceling a Task without Calling Server Cancel Procedures|
BLOCK WORK WITH DISTRIBUTED TRANSACTION PROCESSING CALL UPDATE_ORDER IN ORDER_SERVER USING ORDER_RECORD; ACTION IS IF (ACMS$L_STATUS <> 1) THEN EXIT BLOCK; END IF; PROCESSING CALL WRITE_LOG_RECORD IN LOG_SERVER USING ORDER_RECORD; END BLOCK; ACTION IS IF (ACMS$L_STATUS = 1) THEN COMMIT TRANSACTION; ELSE ROLLBACK TRANSACTION; CANCEL TASK RETURNING ACMS$L_STATUS; END IF;
Example 8-6 starts a distributed transaction on the block step. If
the UPDATE_ORDER procedure returns a status value other than 1, ACMS
passes control to the action part of the block step, rolls back the
distributed transaction, and cancels the task. When ACMS processes the
ROLLBACK TRANSACTION clause, the distributed transaction ends and ACMS
releases server context. As a result, ACMS does not call server cancel
procedures when it cancels the task.
8.7.3 Other Nonrecoverable Exceptions and Transaction Exceptions
When ACMS executes a COMMIT TRANSACTION clause, if a transaction exception is raised because the distributed transaction fails to prepare, ACMS does not call server cancel procedures.
In all other cases, if a transaction exception or a nonrecoverable exception is raised while the task maintains context in one or more server processes, ACMS calls server cancel procedures.
For these and other such requirements, you can use the ACMS queuing
facility to capture and initiate the tasks in your ACMS application.
9.1 Understanding the ACMS Queuing Facility
When you use ACMS queuing, you may include in a processing step a call to an ACMS programming service to enqueue a deferred task on a task queue for later processing. This queued task includes task arguments, together with a task name, and an application name, to comprise a queued task element that is stored on the task queue. At a later time, ACMS executes a queued task (a task submitted by the ACMS queuing facility). ACMS processes the queued task and then deletes the queued task element from the task queue. In other words, a queued task element is stored in a task queue for later processing.
Queued tasks must have certain characteristics (discussed in Section 9.6.1) in order for them to be successfully executed by the ACMS queuing facility. For example, queued tasks cannot perform exchange steps to collect input, because all the input needed by the queued task must be passed to the task as task arguments. Aside from these characteristics, there is no difference between a queued task and any other ACMS task.
ACMS task queues are distinct from OpenVMS batch queues. Many of the concepts are similar, but ACMS and OpenVMS queues are completely independent entities that are managed and used by independent interfaces.
Figure 9-1 shows how to use the ACMS$QUEUE_TASK service to queue tasks, and how to use the QTI to dequeue and initiate tasks in the queue.
Figure 9-1 Queuing, Dequeuing, and Processing Tasks
In Figure 9-1: