Note:
|
If a transaction includes calls to tpcall(), tpacall(), or tpconnect() for which the flags parameter is explicitly set to TPNOTRAN, the operations performed by the called service do not become part of that transaction. In this case, the calling process does not invite the called service to be a participant in the current transaction. As a result, services performed by the called process are not affected by the outcome of the current transaction. If TPNOTRAN is set for a call that is directed to a service in an XA-compliant server group, the call may be executed outside of transaction mode or in a separate transaction, depending on how the service is configured and coded. For more information, refer to “Implicitly Defining a Global Transaction” on page 9‑15.
|
Table 9‑1 describes the arguments to the
tpbegin() function
|
|
|
The use of 0 or an unrealistically large value for the timeout parameter delays system detection and reporting of errors. The system uses the timeout parameter to ensure that responses to service requests are sent within a reasonable time, and to terminate transactions that encounter problems such as network failures before executing a commit.
In a production system, you should set timeout to a value large enough to accommodate expected delays due to system load and database contention. A small multiple of the expected average response time is often an appropriate choice.
Note:
|
The value assigned to the timeout parameter should be consistent with that of the SCANUNIT parameter set by the Oracle Tuxedo application administrator in the configuration file. The SCANUNIT parameter specifies the frequency with which the system checks, or scans, for timed-out transactions and blocked calls in service requests. The value of this parameter represents the interval of time between these periodic scans, referred to as the scanning unit. You should set the timeout parameter to a value that is greater than the scanning unit. If you set the timeout parameter to a value smaller than the scanning unit, there will be a discrepancy between the time at which a transaction times out and the time at which this timeout is discovered by the system. The default value for SCANUNIT is 10 seconds. You may need to discuss the setting of the timeout parameter with your application administrator to make sure the value you assign to the timeout parameter is compatible with the values assigned to your system parameters.
|
|
|
|
Any process may call tpbegin() unless the process is already in transaction mode or is waiting for outstanding replies. If
tpbegin() is called in transaction mode, the call fails due to a protocol error and
tperrno(5) is set to
TPEPROTO. If the process is in transaction mode, the transaction is unaffected by the failure.
Listing 9‑1 provides a high-level view of how a global transaction is defined.
. . .
if (tpbegin(timeout,flags) == -1)
error routine
program statements
. . .
if (tpcommit(flags) == -1)
error routine
Listing 9‑2 provides a more detailed view of how to define a transaction. This example is excerpted from
audit.c, a client program included in
bankapp, the sample banking application delivered with the Oracle Tuxedo system.
#include <stdio.h> /* UNIX */
#include <string.h> /* UNIX */
#include <atmi.h> /* BEA Tuxedo System */
#include <Uunix.h> /* BEA Tuxedo System */
#include <userlog.h> /* BEA Tuxedo System */
#include "bank.h" /* BANKING #defines */
#include "aud.h" /* BANKING view defines */
#define INVI 0 /* account inquiry */
#define ACCT 1 /* account inquiry */
#define TELL 2 /* teller inquiry */
static int sum_bal _((char *, char *));
static long sitelist[NSITE] = SITEREP; /* list of machines to audit */
static char pgmname[STATLEN]; /* program name = argv[0] */
static char result_str[STATLEN]; /* string to hold results of query */
main(argc, argv)
int argc;
char *argv[];
{
int aud_type=INVI; /* audit type -- invalid unless specified */
int clarg; /* command line arg index from optind */
int c; /* Option character */
int cflgs=0; /* Commit flags, currently unused */
int aflgs=0; /* Abort flags, currently unused */
int nbl=0; /* count of branch list entries */
char svc_name[NAMELEN]; /* service name */
char hdr_type[NAMELEN]; /* heading to appear on output */
int retc; /* return value of sum_bal() */
struct aud *audv; /* pointer to audit buf struct */
int audrl=0; /* audit return length */
long q_branchid; /* branch_id to query */
. . . /* Get Command Line Options and Set Variables */
/* Join application */
if (tpinit((TPINIT *) NULL) == -1) {
(void)userlog("%s: failed to join application\n", pgmname);
exit(1);
}
/* Start global transaction */
if (tpbegin(30, 0) == -1) {
(void)userlog("%s: failed to begin transaction\n", pgmname);
(void)tpterm();
exit(1);
}
if (nbl == 0) { /* no branch id specified so do a global sum */
retc = sum_bal(svc_name, hdr_type); /* sum_bal routine not shown */
} else {
/* Create buffer and set data pointer */
if ((audv = (struct aud *)tpalloc("VIEW", "aud", sizeof(struct aud)))
== (struct aud *)NULL) {
(void)userlog("audit: unable to allocate space for VIEW\n");
exit(1);
}
/* Prepare aud structure */
audv->b_id = q_branchid;
audv->balance = 0.0;
audv->ermsg[0] = '\0';
/* Do tpcall */
if (tpcall(svc_name,(char *)audv,sizeof(struct aud),
(char **)audv,(long *)audrl,0) == -1){
(void)fprintf (stderr,"%s service failed\n%s: %s\n",
svc_name, svc_name, audv->ermsg);
retc = -1;
}else {
(void)sprintf(result_str,"Branch %ld %s balance is $%.2f\n",
audv->b_id, hdr_type, audv->balance);
}
tpfree((char *)audv);
}
/* Commit global transaction */
if (retc < 0) /* sum_bal failed so abort */
(void) tpabort(aflgs);
else {
if (tpcommit(cflgs) == -1) {
(void)userlog("%s: failed to commit transaction\n", pgmname);
(void)tpterm();
exit(1);
}
/*print out results only when transaction has committed successfully*/
(void)printf("%s",result_str);
}
/* Leave application */
if (tpterm() == -1) {
(void)userlog("%s: failed to leave application\n", pgmname);
exit(1);
}
Listing 9‑3 shows how to test for a transaction timeout. Note that the value of
timeout is set to 30 seconds.
if (tpbegin(30, 0) == -1) {
(void)userlog("%s: failed to begin transaction\n", argv[0]);
tpterm();
exit(1);
}
. . .
communication calls
. . .
if (tperrno == TPETIME){
if (tpabort(0) == -1) {
check for errors;
}
else if (tpcommit(0) == -1){
check for errors;
}
. . .
Use the tpsuspend(3c) function to suspend the current transaction. Use the following signature to call the
tpsuspend() function:
Table 9‑2 describes the arguments to the
tpsuspend() function.
Table 9‑3 describes the arguments to the
tpresume() function:
Listing 9‑4 shows how to suspend one transaction, start and commit a second transaction, and resume the initial transaction. For the sake of simplicity, error checking code has been omitted.
DEBIT(SVCINFO *s)
{
TPTRANID t;
tpsuspend(&t,TPNOFLAGS); /* suspend invoking transaction*/
tpbegin(30,TPNOFLAGS); /* begin separate transaction */
Perform work in the separate transaction.
tpcommit(TPNOFLAGS); /* commit separate transaction */
tpresume(&t,TPNOFLAGS); /* resume invoking transaction*/
.
.
.
tpreturn(. . . );
}
Note:
|
If tpcall(), tpacall(), or tpconnect() is called by a process that has explicitly set the flags argument to TPNOTRAN, the operations performed by the called service do not become part of the current transaction. In other words, when you call the tpabort() function, the operations performed by these services are not rolled back.
|
The tpcommit(3c) function commits the current transaction. When
tpcommit() returns successfully, all changes to resources as a result of the current transaction become permanent.
Although the flags argument is not used currently, you must set it to zero to ensure compatibility with future releases.
For tpcommit() to succeed, the following conditions must be true:
If the first condition is false, the call fails and tperrno(5) is set to
TPEPROTO, indicating a protocol error. If the second or third condition is false, the call fails and
tperrno() is set to
TPEABORT, indicating that the transaction has been rolled back. If
tpcommit() is called by the initiator with outstanding transaction replies, the transaction is aborted and those reply descriptors associated with the transaction become invalid. If a participant calls
tpcommit() or
tpabort(), the transaction is unaffected.
When the tpcommit() function is called, it initiates the
two-phase commit protocol. This protocol, as the name suggests, consists of two steps:
The commit sequence begins when the transaction initiator calls the tpcommit() function. The Oracle Tuxedo TMS server process in the designated coordinator group contacts the TMS in each participant group that is to perform the first phase of the commit protocol. The TMS in each group then instructs the resource manager (RM) in that group to commit using the XA protocol that is defined for communications between the Transaction Managers and RMs. The RM writes, to stable storage, the states of the transaction before and after the commit sequence, and indicates success or failure to the TMS. The TMS then passes the response back to the coordinating TMS.
•
|
LOGGED—to require completion of phase 1
|
•
|
COMPLETE—to require completion of phase 2
|
Use the tpabort(3c) function to indicate an abnormal condition and explicitly abort a transaction. This function invalidates the call descriptors of any outstanding transactional replies. None of the changes produced by the transaction are applied to the resource. Use the following signature to call the
tpabort() function:
Although the flags argument is not used currently, you must set it to zero to ensure compatibility with future releases.
Figure 9‑1 illustrates a conversational connection hierarchy that includes a global transaction.
In Listing 9‑5, a client makes a synchronous call to the fictitious
REPORT service (line 18). Then the code checks for participant failures by testing for errors that can be returned on a communication call (lines 19-34).
001 #include <stdio.h>
002 #include "atmi.h"
003
004 main()
005 {
006 char *sbuf, *rbuf;
007 long slen, rlen;
008 if (tpinit((TPINIT *) NULL) == -1)
009 error message, exit program;
010 if (tpbegin(30, 0) == -1)
011
error message, tpterm, exit program;
012 if ((sbuf=tpalloc("STRING", NULL, 100)) == NULL)
013
error message, tpabort, tpterm, exit program;
014 if ((rbuf=tpalloc("STRING", NULL, 2000)) == NULL)
015
error message, tpfree sbuf, tpabort, tpterm, exit program;
016 (void)strcpy(sbuf, "REPORT=accrcv DBNAME=accounts");
017 slen=strlen(sbuf);
018 if (tpcall("REPORT", sbuf, slen, &rbuf, &rlen, 0) == -1) {
019 switch(tperrno) {
020 case TPESVCERR:
021 fprintf(stderr,
022 "REPORT service's tpreturn encountered problems\n");
023 break;
024 case TPESVCFAIL:
025 fprintf(stderr,
026 "REPORT service TPFAILED with return code of %d\n", tpurcode);
027 break;
028 case TPEOTYPE:
029 fprintf(stderr,
030 "REPORT service's reply is not of any known data type\n");
031 break;
032 default:
033 fprintf(stderr,
034 "REPORT service failed with error %d\n", tperrno);
035 break;
036 }
037 if (tpabort(0) == -1){
038
check for errors;
039 }
040 }
041 else
042 if (tpcommit(0) == -1)
043 fprintf(stderr, "Transaction failed at commit time\n");
044 tpfree(rbuf);
045 tpfree(sbuf);
046 tpterm();
047 exit(0);
048 }
If the flags argument is not set to
TPNOTRAN, then the system places the called process in transaction mode through the “rule of propagation.” The system does not check the
AUTOTRAN parameter.
If the flags argument is set to
TPNOTRAN, the services performed by the called process are not included in the current transaction (that is, the propagation rule is suppressed). The system checks the
AUTOTRAN parameter.
•
|
If AUTOTRAN is set to N (or if it is not set), the system does not place the called process in transaction mode.
|
•
|
If AUTOTRAN is set to Y, the system places the called process in transaction mode, but treats it as a new transaction.
|
Note:
|
Because a service can be placed in transaction mode automatically, it is possible for a service with the TPNOTRAN flag set to call services that have the AUTOTRAN parameter set. If such a service requests another service, the flags member of the service information structure returns TPTRAN when queried. For example, if the call is made with the communication flags member set to TPNOTRAN | TPNOREPLY, and the service automatically starts a transaction when called, the flags member of the information structure is set to TPTRAN | TPNOREPLY.
|
•
|
Querying the flags field of the service information structure that is passed to the service routine. The service is in transaction mode if the value is set to TPTRAN.
|
The tpgetlev() function requires no arguments. It returns 0 if the caller is not in a transaction, and 1 if it is.
Listing 9‑6 is a variation of the
OPEN_ACCT service that shows how to test for transaction level using the
tpgetlev() function (line 12). If the process is not already in transaction mode, the application starts a transaction (line 14). If
tpbegin() fails, a message is returned to the status line (line 16) and the
rcode argument of
tpreturn() is set to a code that can be retrieved in the global variable
tpurcode(5) (lines 1 and 17).
If the AUTOTRAN parameter is set to
Y, you do not need to call the
tpbegin(), and
tpcommit() or
tpabort() transaction functions explicitly. As a result, you can avoid the overhead of testing for transaction level. In addition, you can set the
TRANTIME parameter to specify the time-out interval: the amount of time that may elapse after a transaction for a service begins, and before it is rolled back if not completed.
For example, suppose you are revising the OPEN_ACCT service shown in the preceding code listing. Currently,
OPEN_ACCT defines the transaction explicitly and then tests for its existence (see lines 7 and 10-19). To reduce the overhead introduced by these tasks, you can eliminate them from the code. Therefore, you need to require that whenever
OPEN_ACCT is called, it is called in transaction mode. To specify this requirement, enable the
AUTOTRAN and
TRANTIME system parameters in the configuration file.
•
|
TRANTIME configuration parameter in Setting Up an Oracle Tuxedo Application.
|
•
|
Using Tuxedo with Oracle Real Application Clusters (RAC) in Setting Up an Oracle Tuxedo Application.
|