Previous | Contents | Index |
The MACRO library SYS$LIBRARY:ACMSMAC.MLB is supplied for MACRO programmers. See the MACRO documentation for information on using macro libraries. SYS$LIBRARY:ACMSMAC.MLB contains the following interface definition macros for agent programs:
Use these macros to include the appropriate definitions for the services used. These macros take one parameter, either <=> or <==>. The parameter determines whether the constant definitions are made locally or globally.
Each of these macros:
The source file SYS$LIBRARY:ACMSPAS.PAS is supplied for Pascal programmers. The system manager must process this file into a PASCAL ENVIRONMENT file. See the Pascal documentation for information on creating and using environment definitions.
The source file:
If you use the nonpositional syntax form of parameter association, you must use PROCEDURE_ rather than PROCEDURE as the formal parameter name for ACMS$GET_PROCEDURE_INFO, because PROCEDURE is a reserved word in Pascal. See VAX Pascal Reference Manual for more information. |
The text library SYS$LIBRARY:ACMSPLI.TLB is supplied for PL/I programmers. See PL/I documentation for information on using text library files in programs.
This text library provides the following modules for agent programs:
These modules:
Programmers in other languages must define:
A distributed transaction is the grouping of operations on multiple recoverable resources (such as files and databases) into a single recovery unit or logical database transaction. Distributed transactions can include more than one type of resource manager and have the properties of atomicity, isolation, and durability.
An agent program, which can run either under the control of ACMS or external to ACMS, can call tasks that are executed as part of a distributed transaction. For example, an agent program can access a database locally and then call an ACMS task in an application on a remote node. The task can call a step procedure that accesses a second database locally on the remote node. You can coordinate both database accesses as part of the same distributed transaction. See Section 3.3.
An agent program uses a set of OpenVMS system services to start and end distributed transactions. Table 3-1 contains these services.
System Service | Use to |
---|---|
$START_TRANS | Start transaction |
$START_TRANSW | Start transaction and wait |
$END_TRANS | End transaction |
$END_TRANSW | End transaction and wait |
$ABORT_TRANS | Roll back transaction |
$ABORT_TRANSW | Roll back transaction and wait |
The optional TRANSW (wait) system services complete synchronously; that is, they return to the caller after the request has actually completed. |
For more information on DECdtm services, refer to documentation for OpenVMS Version 5.4 or higher.
For a task to execute as part of a distributed transaction started in an agent program, the agent program and the application containing the task must conform to a number of composability rules. Lists of these rules follow.
When an agent program calls a task, it is important for the agent program to control the task's participation in an active distributed transaction. To do this, the agent program must use the TID argument in the ACMS$START_CALL and ACMS$CALL services in either one of two ways:
The following services accept a TID:
ACMS$CALL
ACMS$CALL_A
ACMS$START_CALL
ACMS$START_CALL_A
The calling sequences for these services are listed in Chapter 5.
When an agent program passes a TID to one of these services, ACMS attempts to pass the TID on to the task. If an agent program tries to pass a TID to a task that cannot join a distributed transaction, either the ACMS$WAIT_FOR_CALL_END or the ACMS$CALL service returns ACMS$_TASKNOTCOMP, indicating that the task is not composable.
If ACMS attempts to pass a TID to a task in an application that does not follow composability rules, one of the following errors is returned:
Example 3-1 illustrates the logic of an agent program that processes records from a data file and calls tasks as a single distributed transaction.
Example 3-1 A Specialized User-Written Agent |
---|
status = $OPEN ! Open a data file status = $CONNECT ! Connect a record stream status = $OPEN ! Open an error file status = $CONNECT ! Connect a record stream UNTIL <no more records to process> BEGIN trx_aborted = FALSE ! Assume everything will work status = $START_TRANSW ! Start a transaction status = $GET ! Read a record from the file status = $ACMS$CALL ! Call a task IF .status ! If task completed OK, THEN ! then... BEGIN status = $DELETE ! Delete the record status = $END_TRANSW ! and end the transaction IF NOT .status ! If transaction rolled back, THEN ! then... txn_aborted = TRUE ! Set the flag so we know END IF NOT .status ! If something went wrong (task THEN ! failed or txn rolled back, BEGIN ! then... IF NOT .txn_aborted ! If txn hasn't rolled back, THEN ! then... $ABORT_TRANS ! Roll back transaction $START_TRANSW ! Start a new transaction $GET ! Reread record from file $PUT ! Store record in error file $DELETE ! Delete rec. from data file $END_TRANSW ! And end the transaction END $DISCONNECT ! Disconnect both $DISCONNECT ! record streams $CLOSE ! Close both the data $CLOSE ! and error files |
An agent program can use the $ABORT_TRANS service to roll back a transaction and, therefore, cancel the associated task. The task is cancelled with the OpenVMS status DDTM$_ABORTED.
If you require a more meaningful reason for the cancellation, you can use the ACMS$CANCEL_CALL service to cancel the task. If the task is still active, the EXC cancels the task. However, the message sent from the agent program to cancel a task may not reach EXC before the task completes and EXC returns the status to the agent. In this case, the agent program should check the task completion status to determine whether or not to roll back the transaction. The following example illustrates this condition:
status = ACMS$START_CALL < additional processing > IF .error_condition_detected THEN cancel_status = ACMS$CANCEL_CALL task_status = ACMS$WAIT_FOR_CALL_END IF .error_condition_detected OR NOT .task_status THEN IF .task_status THEN cancel_status = $ABORT_TRANS < task termination processing > |
An agent program that starts a distributed transaction can roll back the transaction by a call to $ABORT_TRANS at any of the following times:
Compaq ACMS for OpenVMS Concepts and Design Guidelines discusses ways in which you can access remote data in a distributed transaction. Two methods for an ACMS application on one node (A) to access data on another node (B) are the following:
Briefly stated, the advantages of the first method over the second are that it minimizes the following:
When you use the first method, ACMS remote access, a step procedure on Node A acts as an agent program by using the SI system service call ACMS$CALL to invoke a task in an application on Node B. ACMS$CALL passes the TID to the application on Node B and forces the called task on Node B to join the distributed transaction that starts in the calling task on Node A. The called task on Node B executes and performs database access by calling appropriate processing step procedures locally.
Figure 3-1 illustrates the use of a step procedure as an agent program to call a task in an application on a remote node.
Figure 3-1 Using a Step Procedure as an Agent Program
Example 3-2 contains the task definition that calls the procedure BANKING_SAMPLE_TXN. The processing step in the task definition starts a distributed transaction.
Example 3-2 Task Definition that Calls a Procedure Used as an Agent |
---|
REPLACE TASK BANKING_SAMPLE_TSK /DIAGNOSTIC DEFAULT FORM IS BANKING_SAMPLE_FORM; WORKSPACE IS BANKING_SAMPLE_WORKSPACE WITH TYPE TASK; BLOCK WORK WITH FORM I/O INPUT_REQUEST: EXCHANGE TRANSCEIVE RECORD BANKING_SAMPLE_REC, BANKING_SAMPLE_REC IN BANKING_SAMPLE_FORM SENDING BANKING_SAMPLE_WORKSPACE RECEIVING BANKING_SAMPLE_WORKSPACE; BANKING_SAMPLE_PROCESSING: PROCESSING WITH DISTRIBUTED TRANSACTION CALL BANKING_SAMPLE_TXN IN BANKING_SAMPLE_SERVER USING BANKING_SAMPLE_WORKSPACE; !+ ! Check the return status in the field ACMS$L_STATUS. ! This field contains 1 is success, -913 if deadlock. ! If deadlock, try again. Otherwise, cancel task. !- ACTION IS SELECT FIRST TRUE OF (ACMS$L_STATUS = 1): COMMIT TRANSACTION; (ACMS$L_STATUS = -913): ROLLBACK TRANSACTION; GOTO STEP BANKING_SAMPLE_PROCESSING; NOMATCH: ROLLBACK TRANSACTION; CANCEL TASK; END SELECT; EXCEPTION ACTION IS CANCEL TASK; OUTPUT_REQUEST: EXCHANGE TRANSCEIVE RECORD BANKING_SAMPLE_REC, BANKING_SAMPLE_REC IN BANKING_SAMPLE_FORM SENDING BANKING_SAMPLE_WORKSPACE RECEIVING BANKING_SAMPLE_WORKSPACE; !+ ! Examine the control field. If the request returns Y, ! then leave the task; else, repeat the task. !- ACTION IS IF (BANKING_SAMPLE_WORKSPACE.WORKSPACE_EXIT_SWITCH = "Y") THEN EXIT TASK; ELSE GOTO STEP BANKING_SAMPLE_PROCESSING; END IF; END BLOCK WORK; END DEFINITION; |
The agent program BANKING_SAMPLE_TXN, shown in Example 3-4, performs local updates of branch and teller data. It then invokes a remote task to update an account on a remote node. The remote task, BANKING_SAMPLE_ACTUPD_TSK, joins in the distributed transaction by declaring WITH DISTRIBUTED TRANSACTION on a block step in the task definition. The remote task calls a procedure on the remote node, BANKING_SAMPLE_ACTUPD_TXN, to update the database locally on the remote node.
Example 3-3 contains the task definition in the application on the remote node.
Example 3-3 Task Definition that Calls a Procedure to Update Remote Data |
---|
REPLACE TASK BANKING_SAMPLE_ACTUPD_TSK /DIAGNOSTIC WORKSPACE IS BANKING_SAMPLE_WORKSPACE WITH TYPE TASK; TASK ARGUMENT IS BANKING_SAMPLE_WORKSPACE WITH ACCESS MODIFY; BLOCK WORK WITH DISTRIBUTED TRANSACTION NO I/O BANKING_SAMPLE_PROCESSING: PROCESSING CALL BANKING_SAMPLE_ACTUPD_TXN IN BANKING_SAMPLE_ACTUPD_SERVER USING BANKING_SAMPLE_WORKSPACE; END BLOCK WORK; ACTION IS IF (ACMS$L_STATUS <> 1) THEN CANCEL TASK RETURNING ACMS$L_STATUS; END IF; END DEFINITION; |
Following is the flow of events in the agent program shown in Example 3-4. The numbers coincide with those in the sample program.
Steps 10 through 13 are actually substeps within step 6. |
Example 3-4 Step Procedure in C that Acts as an Agent Program |
---|
/*************************************************************************/ /* */ /* Sample Banking Application */ /* Branch/Teller Update Transaction */ /* -------------------------------- */ /* */ /* This is the server procedure called by the BANKING_SAMPLE_TSK task */ /* to perform local update of branch and teller data and invoke the */ /* remote task, BANKING_SAMPLE_ACTUPD_TASK, for update of an account */ /* on a remote node. */ /*************************************************************************/ #include ssdef #include stdio #include descrip #include string #include ACMS$SUBMITTER /* DECdtm transaction ID structure */ struct tid_structure (1) { long int field_1; long int field_2; short int field_3; short int field_4; long int field_5; } tid; /* SQL Transaction context structure */ struct context_structure (2) { long int version; long int type; long int length; struct tid_structure in_tid; long int end; }; char txn_time[9],txn_date[7]; $DESCRIPTOR(date_time_dscrptr,"dd-mmm-yyyy hh:mm:ss.hh"); /* Data Structure for ACMS/SI */ (3) struct ACMS$SUBMITTER_ID submitter_id; struct ACMS$PROCEDURE_ID procedure_id; struct dsc$descriptor wksp_dscrptr; struct dsc$descriptor tsk_dscrptr; struct dsc$descriptor appl_dscrptr; char appl_name[32]; struct single_item_list_structure { short int proc_bufsize; short int proc_itmcode; char *proc_bufaddr; char *proc_retlen; int item_last; } single_item_list; struct arg_list_struct { long int count; char *sel_str; char *ext_sts; char *tsk_io; char *ws_1; } arg_list; /* SQL and Database Declaration */ EXEC SQL INCLUDE SQLCA; EXEC SQL DECLARE SCHEMA FOR FILENAME BANK_SAMPLE; EXEC SQL INCLUDE FROM DICTIONARY BANKING_SAMPLE_WORKSPACE; (4) /*********************************************************/ /* Banking Sample Transaction. */ /*********************************************************/ banking_sample_txn(workspace_area) struct banking_workspace *workspace_area; { struct banking_workspace WSBuff; char ActBranchBuff[5],ActAccountBuff[7]; static struct context_structure context={1,1,16,{0,0,0,0,0},0}; char ErrMsgText[300]; int ErrMsgLength; int status; $DESCRIPTOR(ErrMsgBuffDsc,""); /* Copy some data values from workspace to local buffers due */ /* to the lack of pointer support in Embedded SQL for C. */ WSBuff.workspace_branch = workspace_area->workspace_branch; WSBuff.workspace_teller = workspace_area->workspace_teller; strncpy(ActBranchBuff, workspace_area->workspace_account.workspace_acc_branch, 4); strncpy(ActAccountBuff, workspace_area->workspace_account.workspace_acc_account, 6); WSBuff.workspace_delta = workspace_area->workspace_delta; /* Get TID from ACMS and store into SQL context structure */ status = ACMS$GET_TID(&tid); (5) if (!(status & SS$_NORMAL)) return(status); context.in_tid = tid; EXEC SQL WHENEVER SQLERROR GOTO sql_error_check; /* Initialization in preparation for ACMS RPC call. */ status = InitializeACMSRPC(); (6) if (!(status & SS$_NORMAL)) return(status); transaction_restart: /***********************************************/ /* Modify Branch. */ /***********************************************/ /* Perform update on the MONEY field for appropriate branch in BRANCH relation. */ EXEC SQL USING CONTEXT :context UPDATE BRANCH B SET B.BR_MONEY_FIELD = B.BR_MONEY_FIELD + :WSBuff.workspace_delta WHERE B.BR_BRANCH = :WSBuff.workspace_branch; /***********************************************/ /* Modify Teller. */ /***********************************************/ /* Perform update on MONEY field for appropriate teller in the TELLER relation. */ EXEC SQL USING CONTEXT :context UPDATE TELLER T SET T.TEL_MONEY_FIELD = T.TEL_MONEY_FIELD + :WSBuff.workspace_delta WHERE (T.TEL_BRANCH = :WSBuff.workspace_branch) AND (T.TEL_TELLER = :WSBuff.workspace_teller); /**********************************************/ /* Invoke remote task to update Account and */ (7) /* store History records via ACMS$CALL. */ /**********************************************/ wksp_dscrptr.dsc$a_pointer = workspace_area; wksp_dscrptr.dsc$w_length = sizeof(*workspace_area); status = ACMS$CALL (&submitter_id, &procedure_id, &arg_list, &tid); /* Check return status code - if failure, log error msg */ (8) if (!(status & SS$_NORMAL)) { error_logging("ACMS$CALL error.", status, WSBuff.workspace_branch, WSBuff.workspace_teller, ActBranchBuff, ActAccountBuff, WSBuff.workspace_delta); return(status); } /* End of Transaction. */ workspace_area->workspace_delta = 0; status = SS$_NORMAL; return(status); /* Log error message if SQL error. */ (9) sql_error_check: ErrMsgBuffDsc.dsc$a_pointer = ErrMsgText; SQL$GET_ERROR_TEXT(&ErrMsgBuffDsc,&ErrMsgLength); ErrMsgText[ErrMsgLength] = '\0'; error_logging(ErrMsgText, SQLCA.SQLCODE, WSBuff.workspace_branch, WSBuff.workspace_teller, ActBranchBuff, ActAccountBuff, WSBuff.workspace_delta); return(SQLCA.SQLCODE); } InitializeACMSRPC() { int status; /* initialize ACMS RPC data structure */ tsk_dscrptr.dsc$a_pointer = "BANKING_SAMPLE_ACTUPD_TSK"; (10) tsk_dscrptr.dsc$w_length = 25; strcpy(&appl_name[0],"SLVSTR::BANK_SAMPLE_APP"); appl_dscrptr.dsc$a_pointer = &appl_name[0]; appl_dscrptr.dsc$w_length = strlen(&appl_name[0]); /* Sign-in to ACMS as a task submitter. */ status = ACMS$SIGN_IN(&submitter_id,0,0,0,0); (11) if (!(status & SS$_NORMAL)) { error_logging("ACMS sign-in error.",status,0,0," "," ",0); return(status); } /* Prepare ACMS$GET_PROCEDURE_INFO item list for remote task */ (12) single_item_list.proc_bufsize = ACMS$S_PROCEDURE_ID; single_item_list.proc_itmcode = ACMS$K_PROC_PROCEDURE_ID; single_item_list.proc_retlen = 0; single_item_list.item_last = 0; /* Get remote procedure information */ single_item_list.proc_bufaddr = &procedure_id; status = ACMS$GET_PROCEDURE_INFO (&submitter_id, &tsk_dscrptr, &appl_dscrptr, &single_item_list); if (!(status & SS$_NORMAL)) { error_logging("ACMS GET_PROCEDURE_INFO error.",status, 0,0," "," ",0); return(status); } /* Prepare ACMS$CALL() argument list for remote task */ arg_list.count = 4; arg_list.sel_str = 0; arg_list.ext_sts = 0; arg_list.tsk_io = 0; arg_list.ws_1 = &wksp_dscrptr; (13) return(1); } |
Previous | Next | Contents | Index |