The final processing step writes a record of the transaction into the
database. If the procedure completes successfully, the task ends, and
the agent calls DECdtm to commit the distributed transaction.
7.8 Handling Deadlocks and Transaction Failures
If your application contains tasks that use distributed transactions, deadlock problems are possible. One type of deadlock involves multiple tasks attempting to access the same server process. For example, suppose that two tasks each use two servers. Each server has one active server process. The first task accesses the first server, and the second task accesses the second server. If both tasks then attempt to access the other server, they become deadlocked waiting to use the server process being used by the other task.
If your application and databases are distributed across multiple systems that are not part of a single OpenVMS Cluster system, deadlocks can occur when multiple tasks attempt to access the same database records. The OpenVMS Lock Manager is able to detect deadlocks only within a single system or an OpenVMS Cluster system.
By specifying the TRANSACTION TIMEOUT subclause in the application definition, you can instruct ACMS to call the transaction services to abort a transaction if it has not completed within a certain number of seconds. See Chapter 11 for an example of an application definition that includes TRANSACTION TIMEOUT.
In addition to deadlocks, there are a number of reasons why distributed transactions might fail, such as:
ACMS lets you test for and recover from distributed transaction failures. For information on how to write task definitions that handle distributed transaction failures, see Chapter 8.
Chapter 2 shows how to handle some common task errors, such as when a requested database record cannot be found, by using conditional clauses to test workspace field values. However, this method cannot detect and handle certain events that prevent tasks from completing successfully. To handle all types of task execution errors, ACMS provides an exception handler action component of the task definition language. This chapter describes:
Use of exception handling is optional. By default, if ACMS encounters an error that prevents it from executing a task, ACMS cancels the task. In some situations, you might want to handle the error and continue task execution. For example, if the task includes one or more distributed transactions, you might want to include exception handler action syntax to test for, and recover from, distributed transaction failures. If you do not specify an action to take when a distributed transaction fails, ACMS cancels the task. Instead, depending upon the reason the transaction failed, you might want to retry the distributed transaction or transfer control to another part of the task definition.
You might also want to use exception handling to test for, and recover from, DECforms timeouts. For example, if a terminal user does not fill in a DECforms field within the timeout period specified in the exchange step, DECforms returns an error. Rather than let ACMS cancel the task, you can specify an exception handler action to test for the timeout error and repeat the exchange step, reminding the terminal user to fill in the panel.
You can also use exception handling to enable a parent task to continue
executing when a called task fails. In short, exception handling gives
you flexibility in designing tasks that can detect a variety of
execution errors and respond in a predictable manner.
8.2 What is an Exception
An exception is an event or an error condition that interrupts the normal flow of an executing task. Each exception has a unique OpenVMS longword exception code that indicates the failure condition. ACMS raises, or generates, an exception based on errors or events occurring in the following sources:
An exception handler is an optional part of an exchange, processing, or block step that lets you control task execution when an exception is raised in the work or action part of the step. Note that the work part of a block step includes the work, action, and exception handler parts of all exchange, processing, and nested block steps in that block step. After an exception has been raised, you can test for its presence with a conditional clause, and specify the action to take in the exception handler part of the step. The class of exception determines where in the task definition you can specify an exception handler, and whether you can handle the exception at all. The three classes of exceptions are:
A step exception is an error that you can handle in the exception handler part of the step on which the exception occurs or in an exception handler on an outer block step. Table 8-1 shows the complete list of step exceptions.
|Source of Exception||Description of Exception|
|RAISE EXCEPTION action clause||You can specify an exception code with RAISE EXCEPTION; if you do not, ACMS provides a default exception code based on where in the task definition you specify RAISE EXCEPTION. Section 8.3 describes how to use the RAISE EXCEPTION action clause.|
|ACMS$RAISE_STEP_EXCEPTION service||You can use this service to raise a step exception when the procedure detects an error that it cannot handle. You can specify actions to recover from the error in an exception handler part of the task definition. See Compaq ACMS for OpenVMS Writing Server Procedures for more information on using this service in step procedures.|
|DECforms||A common reason for ACMS to raise a step exception is that the terminal user did not fill in a DECforms field within the specified timeout period. ACMS also raises a step exception when DECforms encounters data conversion problems while moving data from a task workspace field to a form.|
|TDMS||A common reason for ACMS to raise a step exception is that TDMS encounters data conversion problems while moving data from a task workspace field to a form.|
|User-written request procedure (URP)||ACMS raises a step exception if a user-written request procedure returns an error status.|
|Stream I/O operation||ACMS raises a step exception if an agent completes a stream I/O request by passing an error status to the ACMS$REPLY_TO_STREAM_IO service.|
|Called 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. See Section 8.5.2 for details on handling exceptions that occur in called tasks.|
A transaction exception is an error that causes a distributed transaction to fail. Table 8-2 shows the complete list of transaction exceptions.
|Source of Exception||Description of Exception|
|Transaction timeout error||In the application definition, you can use the TRANSACTION TIMEOUT phrase to specify a time limit within which the distributed transaction must complete. If the transaction does not end within the specified number of seconds, ACMS raises a transaction exception with the ACMS$_TRANSTIMEDOUT exception code. If the transaction started in the current task, ACMS calls the DECdtm services to abort the transaction. If the task was called to participate in a transaction started by a parent task, ACMS calls DECdtm to abort the transaction when ACMS resumes executing the parent task. If the task was called to participate in a transaction started by an agent, the agent must abort the transaction when the task ends and the ACMS$CALL or ACMS$WAIT_FOR_CALL_END service completes.|
|ACMS$RAISE_TRANS_EXCEPTION service||You can use this service to raise a transaction exception when the procedure detects an error that it cannot handle. You can specify actions to recover from the error in an exception handler part of the task definition. See Compaq ACMS for OpenVMS Writing Server Procedures for more information on using this service in step procedures.|
|Transaction is aborted||A transaction exception can be raised when a distributed transaction is aborted while ACMS is executing the task. For example, if a network link to a remote database server process that is participating in a distributed transaction fails, ACMS raises a transaction exception.|
|SYS$ABORT_TRANS[W] system service||If a user-written agent calls the SYS$ABORT_TRANS[W] service while ACMS executes a composed task, ACMS raises a transaction exception.|
|Called task||If a called task that participates in a distributed transaction started by the parent task completes with a transaction exception or a nonrecoverable exception, ACMS raises a transaction exception in the parent task.|
A step procedure that participates in a distributed transaction started by a task or an agent must not call the SYS$ABORT_TRANS[W] service, because the results are unpredictable.
A nonrecoverable exception is an error from which the task cannot recover. When a nonrecoverable exception is raised, ACMS cancels the task. Table 8-3 shows the list of nonrecoverable exceptions.
|Source of Exception||Description of Exception|
|CANCEL TASK action clause||As with the RAISE EXCEPTION clause, the CANCEL TASK clause lets you raise an exception. However, while RAISE EXCEPTION lets you recover from the exception and continue executing the task, CANCEL TASK raises a nonrecoverable exception that always cancels the task.|
|ACMS$RAISE_NONREC_EXCEPTION system service||You can call ACMS$RAISE_NONREC_EXCEPTION from a step procedure to raise a nonrecoverable exception when the procedure detects an error from which it cannot recover. Compaq ACMS for OpenVMS Writing Server Procedures has more information on how to use the ACMS$RAISE_NONREC_EXCEPTION system service.|
|Server procedure||ACMS raises a nonrecoverable exception when a step procedure encounters a fatal error, such as an access violation.|
|Server process||ACMS raises a nonrecoverable exception if a server process dies while a task is executing a step procedure or a DCL command in the server process, or is retaining context in the server process.|
|Submitter-requested cancellation||If a terminal user presses [Ctrl/Y] to cancel a task, and the task definition does not include the NOT CANCELABLE phrase, ACMS raises a nonrecoverable exception.|
|Operator-requested cancellation||If a system operator invokes the ACMS/CANCEL TASK command to cancel a task, ACMS raises a nonrecoverable exception.|
|RMS file or database recovery unit||A file or database recovery unit might fail to start because a realm or relation is temporarily locked. A constraint check failure might prevent a recovery unit from committing successfully.|
|Unknown distributed transaction failure||If a distributed transaction fails for an unknown reason, ACMS raises a nonrecoverable exception.|
|Any other task cancellation||Any event or error condition that causes ACMS to unconditionally cancel the task causes ACMS to raise a nonrecoverable exception.|
To explicitly raise a step exception in the task definition, use the RAISE EXCEPTION clause in the action part of the step where the error is detected. You do not have to specify an exception code with RAISE EXCEPTION; ACMS provides a default exception code of ACMS$_EXCPTN_TASKACTN when you raise an exception in the action part of a step. If you use the RAISE EXCEPTION clause in an exception handler, and you do not specify an exception code, ACMS raises a step exception using the exception code associated with the current exception. If you do specify an exception code, it must be a failure status. Otherwise, ACMS cancels the task. Example 8-1 shows a processing step that uses the RAISE EXCEPTION clause.
|Example 8-1 RAISE EXCEPTION Clause in a Processing Step|
PROCESSING WORK CALL ENTER_ORDER IN DIST_CTR_DATABASE_UPDATE_SERVER USING ORDER_ENTRY_RECORD, RESTOCK_RECORD, STATUS_RECORD; ACTION IS IF (DATA_IS_VALID <> "Y") THEN RAISE EXCEPTION APPL_INVALID_DATA END IF;
In Example 8-1, the processing step calls a procedure to add a record to a database. The action part of the step tests the DATA_IS_VALID workspace field to see if the procedure returned a Y, indicating that the data to be stored in the database is valid. If the procedure returns a value other than Y, the processing step raises a step exception. APPL_INVALID_DATA is a user-defined exception code.
When an exception is raised, ACMS searches for an exception handler in the current step. If the current step does not contain an exception handler, ACMS searches the outer block or blocks until it finds an exception handler. If ACMS finds an exception handler, ACMS moves the exception code into the ACMS$L_STATUS field of the ACMS$PROCESSING_STATUS system workspace. You can then use a conditional clause in the exception handler action part of the step to test for the exception code and to direct the task flow. If ACMS does not find an exception handler, it cancels the task.
Exception handler actions make up the third part of a step, following the work and action parts. The exception handler action part is optional. If you use it, it must appear after the work and action parts of the step. The action part of a step is optional. Use the EXCEPTION HANDLER clause to introduce the exception handler component. Figure 8-1 shows how exception handlers fit into the structure of a block step.
Figure 8-1 Block Step Structure
You use the same action clauses in the exception handler component as you use in the action part of a step with the exception of transaction actions and recovery actions. You cannot use a transaction action clause (COMMIT TRANSACTION or ROLLBACK TRANSACTION) or a recovery unit action clause in an exception handler. Because there is no logical default sequencing action for an exception handler, ACMS requires that you include a sequencing action clause. Regardless of the order in which the exception handler action clauses appear in the task definition, ACMS executes the clauses in the following order:
Example 8-2 shows how to include exception handling in a task definition.
|Example 8-2 Performing Exception Handling in a Task|
BLOCK WORK WITH FORM I/O EXCHANGE RECEIVE FORM RECORD ORDER_ENTRY_RECORD IN ORDER_ENTRY_FORM RECEIVING ORDER_ENTRY_WKSP; WRITE_ORDER: BLOCK WORK WITH DISTRIBUTED TRANSACTION PROCESSING CALL ENTER_ORDER IN DIST_CTR_DATABASE_UPDATE_SERVER USING ORDER_ENTRY_WKSP, RESTOCK_WKSP, STATUS_WKSP; ACTION IS IF (DATA_IS_VALID <> "Y") THEN RAISE EXCEPTION APPL_INVALID_DATA END IF; PROCESSING IF (ORDERED_AMOUNT > IN_STOCK_AMOUNT) THEN CALL QUEUE_REPLENISH_INVENTORY_TASK_PROC IN QUEUE_SERVER USING RESTOCK_WKSP; END IF; END BLOCK; ACTION IS COMMIT TRANSACTION; EXIT TASK; EXCEPTION HANDLER ACTION IS IF (ACMS$L_STATUS = APPL_INVALID_DATA) THEN GOTO STEP REENTER_DATA; END IF; REENTER_DATA: EXCHANGE TRANSCEIVE FORM RECORD STATUS_RECORD, ORDER_ENTRY_RECORD IN ORDER_ENTRY_FORM SENDING STATUS_WKSP RECEIVING ORDER_ENTRY_WKSP; ACTION IS IF (RETRY_TRANSACTION = "Y") THEN GOTO STEP WRITE_ORDER; ELSE EXIT TASK; END IF; END BLOCK;
In Example 8-2, if the first processing step raises the APPL_INVALID_DATA exception, ACMS first looks for an exception handler in the same step. Because the processing step does not include an exception handler, ACMS searches the end of the nested block step where it finds an EXCEPTION HANDLER ACTION clause. If the nested block step does not include an exception handler, ACMS then looks at the end of the root block.
If an exception is raised other than the ones you test for in the
exception handler part of a step, ACMS provides a default action of
RAISE EXCEPTION. For example, in the above task definition, if an
exception other than APPL_INVALID_DATA is raised, ACMS raises another
exception, which you can handle with an exception handler on the root
8.5 Examples of Exception Handling
Because ACMS exception handling is an all-purpose task execution error
handling tool, you can use it in task definitions to detect and recover
from a variety of exceptions. The following sections show how to use
exception handling to recover from a DECforms time-out exception, a
task-call-task exception, and a transaction exception.
8.5.1 Recovering from a DECforms Time-Out Exception
DECforms lets you specify a time limit within which the terminal user must fill in a DECforms field. You may want to provide a way to recover from a DECforms time-out error so that ACMS does not cancel the task. Example 8-3 shows how to test for and recover from a DECforms time-out exception.
|Example 8-3 Recovering from a DECforms Time-Out Exception|
DISPLAY_RESV_NO: EXCHANGE ! ! DISPLAY RESERVATION # AND PROMPT TO SEE IF CUSTOMER WANTS TO CHECK ! CAR OUT NOW. ! TRANSCEIVE RECORD RES_INFO, RES_CHECKOUT_DETAILS SENDING VR_CONTROL_WKSP, VR_RESERVATIONS_WKSP RECEIVING VR_CONTROL_WKSP, VR_RESERVATIONS_WKSP, VR_CUSTOMERS_WKSP, VR_TRANS_WKSP WITH TIMEOUT 30 SEND CONTROL VR_SENDCTRL_WKSP; ACTION IS CONTROL FIELD VR_CONTROL_WKSP.CTRL_KEY "QUIT" : EXIT TASK; "CANCL": MOVE "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY, 0 TO VR_CONTROL_WKSP.RETRY_COUNT, "Y" TO VR_CONTROL_WKSP.INCREMENT_RETRY_COUNT; GOTO STEP CANCEL_RES; "REPET": GOTO STEP DISPLAY_RESV_NO; "GTCAR": GOTO STEP FIND_CAR; " ": MOVE "ACTWT" TO VR_SENDCTRL_WKSP.SENDCTRL_KEY; GET MESSAGE NUMBER VR_RESVCOMP INTO VR_CONTROL_WKSP.MESSAGEPANEL; GOTO STEP DISP_STAT; END CONTROL; ! IF THE FORM TIMES OUT, REMIND THE USER ONCE TO MAKE A SELCTION - ! THEN CANCEL THE TASK. EXCEPTION HANDLER SELECT FIRST TRUE OF (ACMS$L_STATUS = FORMS$_TIMEOUT AND VR_CONTROL_WKSP.RETRY_COUNT = 0): MOVE 1 TO VR_CONTROL_WKSP.RETRY_COUNT, "RETRY" TO VR_CONTROL_WKSP.CTRL_KEY; GET MESSAGE NUMBER VR_INACTIVE INTO VR_CONTROL_WKSP.MESSAGEPANEL; GOTO STEP DISPLAY_RESV_NO; (ACMS$L_STATUS = FORMS$_TIMEOUT AND VR_CONTROL_WKSP.RETRY_COUNT > 0): 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 SELECT;
In Example 8-3, the exchange step specifies that the terminal user must fill in each DECforms field within 30 seconds. The exception handler part of the exchange step tests the ACMS$L_STATUS field of the ACMS$PROCESSING_STATUS workspace for the presence of the FORMS$_TIMEOUT exception code. If ACMS raises the time-out exception, the exception handler action repeats the exchange step to give the terminal user a second chance to complete the DECforms field. If the time-out exception occurs a second time, ACMS passes control to the DISP_STAT exchange step, which displays an error message, then ends the task.
If your application is distributed across a front-end node that performs terminal I/O and a back-end node that performs computation and database operations, ACMS Version 3.2 or higher must be installed on both nodes, if the task definition is to recover from exceptions raised by exchange steps.