Programming an Oracle Tuxedo ATMI Application Using C

     Previous  Next    Open TOC in new window    View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

Managing Errors

This topic includes the following sections:

 


System Errors

The Oracle Tuxedo system uses the tperrno(5) variable to supply information to a process when a function fails. All ATMI functions that normally return an integer or pointer return -1 or NULL, respectively, on error and set tperrno() to a value that describes the nature of the error. When a function does not return to its caller, as in the case of tpreturn() or tpforward(), which are used to terminate a service routine, the only way the system can communicate success or failure is through the variable tperrno() in the requester.

The tperrordetail (3c) and tpstrerrordetail (3c) functions can be used to obtain additional detail about an error in the most recent Oracle Tuxedo system call on the current thread. tperrordetail() returns an integer (with an associated symbolic name) which is then used as an argument to tpstrerrordetail() to retrieve a pointer to a string that contains the error message. The pointer can then be used as an argument to userlog (3c) or fprintf(). For a list of the symbolic names that can be returned, refer to tperrordetail (3c) in the Oracle Tuxedo ATMI C Function Reference.

tpurcode(5) is used to communicate user-defined conditions only. The system sets the value of tpurcode to the value of the rcode argument of tpreturn(). The system sets tpurcode, regardless of the value of the rval argument of tpreturn(), unless an error is encountered by tpreturn() or a transaction timeout occurs.

The codes returned in tperrno(5) represent categories of errors, which are listed in Table 11-1.

Table 11-1 tperrno Error Categories
Error Category
tperrno Values
Abort
TPEABORT2
Oracle Tuxedo system1
TPESYSTEM
Call descriptor
TPELIMIT and TPEBADDESC
Conversational
TPEVENT
Duplicate operation
TPEMATCH
General communication
TPESVCFAIL, TPESVCERR, TPEBLOCK, and TPGOTSIG
Heuristic decision
TPEHAZARD2 and TPEHEURISTIC2
Invalid argument1
TPEINVAL
MIB
TPEMIB
No entry
TPENOENT
Operating system1
TPEOS
Permission
TPEPERM
Protocol1
TPEPROTO
Queueing
TPEDIAGNOSTIC
Release compatibility
TPERELEASE
Resource manager
TPERMERR
Timeout
TPETIME
Transaction
TPETRAN2
Typed buffer mismatch
TPEITYPE and TPEOTYPE

1Applicable to all ATMI functions for which failure is reported by the value returned in tperrno(5).

2Refer to Fatal Transaction Errors for more information on this error category.

As footnote 1 shows, four categories of errors are reported by tperrno(5) and are applicable to all ATMI functions. The remaining categories are used only for specific ATMI functions.The following sections describe some error categories in detail.

 


Abort Errors

For information on the errors that lead to abort, refer to Fatal Transaction Errors.

 


Oracle Tuxedo System Errors

Oracle Tuxedo system errors indicate problems at the system level, rather than at the application level. When Oracle Tuxedo system errors occur, the system writes messages explaining the exact nature of the errors to the central event log, and returns TPESYSTEM in tperrno(5). For more information, refer to the Central Event Log. Because these errors occur in the system, rather than in the application, you may need to consult the system administrator to correct them.

 


Call Descriptor Errors

Call descriptor errors occur as a result of exceeding the maximum limit of call descriptors or referencing an invalid value. Asynchronous and conversational calls return TPELIMIT when the maximum number of outstanding call descriptors has been exceeded. TPEBADDESC is returned when an invalid call descriptor value is specified for an operation.

Call descriptor errors occur only during asynchronous calls or conversational calls. (Call descriptors are not used for synchronous calls.) Asynchronous calls depend on call descriptors to associate replies with the corresponding requests. Conversational send and receive functions depend on call descriptors to identify the connection; the call that initiates the connection depends on the availability of a call descriptor.

Troubleshooting of call descriptor errors can be done by checking for specific errors at the application level.

Limit Errors

The system allows up to 50 outstanding call descriptors (replies) per context (or Oracle Tuxedo application association). This limit is enforced by the system; it cannot be redefined by your application.

The limit for call descriptors for simultaneous conversational connections is more flexible than the limit for replies. The application administrator defines the limit in the configuration file. When the application is not running, the administrator can modify the MAXCONV parameter in the RESOURCES section of the configuration file. When the application is running, the administrator can modify the MACHINES section dynamically. Refer to tmconfig, wtmconfig(1) in the Oracle Tuxedo Command Reference for more information.

Invalid Descriptor Errors

A call descriptor can become invalid and, if referenced, cause an error to be returned to tperrno(5) in either of two situations:

A call descriptor might become stale, for example, in the following circumstances:

 


Conversational Errors

When an unknown descriptor is specified for conversational services, the tpsend(), tprecv(), and tpdiscon() functions return TPEBADDESC.

When tpsend() and tprecv() fail with a TPEEVENT error after a conversational connection is established, an event has occurred. Data may or may not be sent by tpsend(), depending on the event. The system returns TPEEVENT in the revent parameter passed to the function call and the course of action is dictated by the particular event.

For a complete description of conversational events, refer to Understanding Conversational Communication Events.

 


Duplicate Object Error

The TPEMATCH error code is returned in tperrno(5) when an attempt is made to perform an operation that results in a duplicate object. The following table lists the functions that may return the TPEMATCH error code and the associated cause.

Function
Cause
The svcname specified is already advertised for the server but with a function other than func. Although the function fails, svcname remains advertised with its current function (that is, func does not replace the current function name).
The tranid points to a transaction identifier that another process has already resumed. In this case, the caller’s state with respect to the transaction is not changed.
The specified subscription information has already been listed with the EventBroker.

For more information on these functions, refer to the Oracle Tuxedo ATMI C Function Reference

 


General Communication Call Errors

General communication call errors can occur during any communication calls, regardless of whether those calls are synchronous or asynchronous. Any of the following errors may be returned in tperrno(5): TPESVCFAIL, TPESVCERR, TPEBLOCK, or TPGOTSIG.

TPESVCFAIL and TPESVCERR Errors

If the reply portion of a communication fails as a result of a call to tpcall() or tpgetrply(), the system returns TPESVCERR or TPSEVCFAIL to tperrno(5). The system determines the error by the arguments that are passed to tpreturn() and the processing that is performed by this function.

If tpreturn() encounters an error in processing or handling arguments, the system returns an error to the original requester and sets tperrno(5) to TPESVCERR. The receiver determines that an error has occurred by checking the value of tperrno(). The system does not send the data from the tpreturn() function, and if the failure occurred on tpgetrply(), it renders the call descriptor invalid.

If tpreturn() does not encounter the TPESVCERR error, then the value returned in rval determines the success or failure of the call. If the application specifies TPFAIL in the rval parameter, the system returns TPESVCFAIL in tperrno(5) and sends the data message to the caller. If rval is set to TPSUCCESS, the system returns successfully to the caller, tperrno() is not set, and the caller receives the data.

TPEBLOCK and TPGOTSIG Errors

The TPEBLOCK and TPGOTSIG error codes may be returned at the request or the reply end of a message and, as a result, can be returned for all communication calls.

The system returns TPEBLOCK when a blocking condition exists and the process sending a request (synchronously or asynchronously) indicates, by setting its flags parameter to TPPNOBLOCK, that it does not want to wait on a blocking condition. A blocking condition can exist when a request is being sent if, for example, all the system queues are full.

When tpcall() indicates a no blocking condition, only the sending part of the communication is affected. If a call successfully sends a request, the system does not return TPEBLOCK, regardless of any blocking situation that may exist while the call waits for the reply.

The system returns TPEBLOCK for tpgetrply() when a call is made with flags set to TPNOBLOCK and a blocking condition is encountered while tpgetrply() is awaiting the reply. This may occur, for example, if a message is not currently available.

The TPGOTSIG error indicates an interruption of a system call by a signal; this situation is not actually an error condition. If the flags parameter for the communication functions is set to TPSIGRSTRT, the calls do not fail and the system does not return the TPGOTSIG error code in tperrno(5).

 


Invalid Argument Errors

Invalid argument errors indicate that an invalid argument was passed to a function. Any ATMI function that takes arguments can fail if you pass it arguments that are invalid. In the case of a function that returns to the caller, the function fails and causes tperrno(5) to be set to TPEINVAL. In the case of tpreturn() or tpforward(), the system sets tperrno() to TPESVCERR for either the tpcall() or tpgetrply() function that initiated the request and is waiting for results to be returned.

You can correct an invalid argument error at the application level by ensuring that you pass only valid arguments to functions.

 


MIB Error

The tpadmcall (3c) function returns TPEMIB in tperrno(5) in the event an administrative request fails. outbuf is updated and returned to the caller with FML32 fields indicating the cause of the error. For more information on the cause of the error, refer to MIB(5) and TM_MIB(5) in File Formats, Data Descriptions, MIBs, and System Processes Reference.

 


No Entry Errors

No entry errors result from a lack of entries in the system tables or the data structure used to identify buffer types. The meaning of the no entry type error, TPENOENT, depends on the function that is returning it. Table 11-2 lists the functions that return this error and describes various causes of error.

Table 11-2 No Entry Errors
Function
Cause
The system does not know about the type of buffer requested. For a buffer type and/or subtype to be known, there must be an entry for it in a type switch data structure that is defined in the Oracle Tuxedo system libraries. Refer to tuxtypes(5) and typesw(5) in the File Formats, Data Descriptions, MIBs, and System Processes Reference for more information.
On an application level, ensure that you have referenced a known type; otherwise, check with the system administrator.
The calling process cannot join the application because there is no space left in the bulletin board to make an entry for it. Check with the system administrator.
The calling process references a service called that is not known to the system since there is no entry for it in the bulletin board. On an application level, ensure that you have referenced the service correctly; otherwise, check with the system administrator.
The system cannot connect to the specified name because the service named does not exist or it is not a conversational service.
The calling process seeks a request priority when no request has been made. This is an application-level error.
The system cannot unadvertise the service name because the name is not currently advertised by the calling process.
tpenqueue (3c) tpdequeue(3c)
The system cannot access the queue space because the associated TMQUEUE(5)server is not available. Refer to the File Formats, Data Descriptions, MIBs, and System Processes Reference for more information.
The system cannot access the Oracle Tuxedo system Event Broker. Refer to Writing Event-based Clients and Servers for more information.

 


Operating System Errors

Operating system errors indicate that an operating system call has failed. The system returns TPEOS in tperrno(5). On UNIX systems, the system returns a numeric value identifying the failed system call in the global variable Uunixerr. To resolve operating system errors, you may need to consult your system administrator.

 


Permission Errors

If a calling process does not have the correct permissions to join the application, the tpinit() call fails, returning TPEPERM in tperrno(5). Permissions are set in the configuration file, outside of the application. If you encounter this error, check with the application administrator to make sure the necessary permissions are set in the configuration file.

 


Protocol Errors

Protocol errors occur when an ATMI function is invoked, either in the wrong order or using an incorrect process. For example, a client may try to begin communicating with a server before joining the application. Or tpcommit() may be called by a transaction participant instead of the initiator.

You can correct a protocol error at the application level by enforcing the rules of order and proper usage of ATMI calls.

To determine the cause of a protocol error, answer the following questions:

Protocol errors return the TPEPROTO value in tperrno(5).

Refer to “Introduction to the C Application-Transaction Monitor Interface” in the Oracle Tuxedo ATMI C Function Reference for more information.

 


Queuing Error

The tpenqueue (3c) or tpdequeue (3c) function returns TPEDIAGNOSTIC in tperrno(5) if the enqueuing or dequeuing on a specified queue fails. The reason for failure can be determined by the diagnostic returned via the ctl buffer. For a list of valid ctl flags, refer to tpenqueue (3c) or tpdequeue (3c) in the Oracle Tuxedo ATMI C Function Reference

 


Release Compatibility Error

The Oracle Tuxedo system returns TPERELEASE in tperrno(5) if a compatibility issue exists between multiple releases of an Oracle Tuxedo system participating in an application domain.

For example, the TPERELEASE error may be returned if the TPACK flag is set when issuing the tpnotify (3c) function (indicating that the caller blocks until an acknowledgment message is received from the target client), but the target client is using an earlier release of the Oracle Tuxedo system that does not support the TPACK acknowledgement protocol.

 


Resource Manager Errors

Resource manager errors can occur with calls to tpopen (3c) and tpclose (3c), in which case the system returns the value of TPERMERR in tperrno(5). This error code is returned for tpopen() when the resource manager fails to open correctly. Similarly, this error code is returned for tpclose() when the resource manager fails to close correctly. To maintain portability, the Oracle Tuxedo system does not return a more detailed explanation of this type of failure. To determine the exact nature of a resource manager error, you must interrogate the resource manager.

 


Timeout Errors

The Oracle Tuxedo system supports timeout errors to establish a limit on the amount of time that the application waits for a service request or transaction. The Oracle Tuxedo system supports two types of configurable timeout mechanisms: blocking and transaction.

A blocking timeout specifies the maximum amount of time that an application waits for a reply to a service request. The application administrator defines the blocking timeout for the system in the configuration file.

A transaction timeout defines the duration of a transaction, which may involve several service requests. To define the transaction timeout for an application, pass the timeout argument to tpbegin().

The system may return timeout errors on communication calls for either blocking or transaction timeouts, and on tpcommit() for transaction timeouts only. In each case, if a process is in transaction mode and the system returns TPETIME on a failed call, a transaction timeout has occurred.

By default, if a process is not in transaction mode, the system performs blocking timeouts. When you set the flags parameter of a communication call to TPNOTIME, the flag setting applies to blocking timeouts only. If a process is in transaction mode, blocking timeouts are not performed and the TPNOTIME flag setting is not relevant.

If a process is not in transaction mode and a blocking timeout occurs on an asynchronous call, the communication call that blocked fails, but the call descriptor is still valid and may be used on a reissued call. Other communication is not affected.

When a transaction timeout occurs, the call descriptor to an asynchronous transaction reply (specified without the TPNOTRAN flag) becomes stale and may no longer be referenced.

TPETIME indicates a blocking timeout on a communication call if the call was not made in transaction mode or if the flags parameter was not set to TPNOBLOCK.

Note: If you set the TPNOBLOCK flag, a blocking timeout cannot occur because the call returns immediately if a blocking condition exists.

For additional information on handling timeout errors, refer to Transaction Considerations.

 


Transaction Errors

For information on transactions and the non-fatal and fatal errors that can occur, refer to Transaction Considerations.

 


Typed Buffer Errors

Typed buffer errors are returned when requests or replies to processes are sent in buffers of an unknown type. The tpcall(), tpacall(), and tpconnect() functions return TPEITYPE when a request data buffer is sent to a service that does not recognize the type of the buffer.

Processes recognize buffer types that are identified in both the configuration file and the Oracle Tuxedo system libraries that are linked into the process. These libraries define and initialize a data structure that identifies the typed buffers that the process recognizes. You can tailor the library to each process, or an application can supply its own copy of a file that defines the buffer types. An application can set up the buffer type data structure (referred to as a buffer type switch) on a process-specific basis. For more information, see tuxtypes(5)and typesw(5) in the File Formats, Data Descriptions, MIBs, and System Processes Reference.

The tpcall(), tpgetrply(), tpdequeue (3c), and tprecv() functions return TPEOTYPE when a reply message is sent in a buffer that is not recognized or not allowed by the caller. In the latter case, the buffer type is included in the type switch, but the type returned does not match the type that was allocated to receive the reply and a change in buffer type is not allowed by the caller. The caller indicates this preference by setting flags to TPNOCHANGE. In this case, strong type checking is enforced; the system returns TPEOTYPE when it is violated. By default, weak type checking is used. In this case, a buffer type other than the type originally allocated may be returned, as long as that type is recognized by the caller. The rules for sending replies are that the reply buffer must be recognized by the caller and, if strong type checking has been indicated, you must observe it.

 


Application Errors

Within an application, you can pass information about user-defined errors to calling programs using the rcode argument of tpreturn(). Also, the system sets the value of tpurcode to the value of the rcode argument of tpreturn(). For more information about tpreturn (3c) or tpurcode(5), refer to the Oracle Tuxedo ATMI C Function Reference and the File Formats, Data Descriptions, MIBs, and System Processes Reference, respectively.

 


Handling Errors

Your application logic should test for error conditions for the calls that have return values, and take appropriate action when an error occurs. Specifically, you should:

The ATMI supports three functions, tpstrerrordetail (3c), tpstrerror (3c), and Fstrerror, Fstrerror32(3fml), for retrieving the text of an error message from the message catalogs for the Oracle Tuxedo system and FML. The functions return pointers to the appropriate error messages. Your program can use a pointer to direct the referenced text to userlog (3c) or to another destination. For details, refer to tpstrerrordetail (3c) and tpstrerror (3c) in the Oracle Tuxedo ATMI C Function Reference, and Fstrerror, Fstrerror32(3fml) in the Oracle Tuxedo ATMI FML Function Reference.

Listing 11-1 shows a typical method of handling errors. The atmicall() function in this example represents a generic ATMI call. Note the code after the switch statement (line 21): it shows how tpurcode can be used to interpret an application-defined return code.

Listing 11-1 Handling Errors
001   #include <stdio.h>
002 #include "atmi.h"
003
004 main()
005
006 {
007 int rtnval;
008
009 if (tpinit((TPINIT *) NULL) == -1)
010 error message, exit program;
011 if (tpbegin(30, 0) == -1)
012 error message, tpterm, exit program;
013
014 allocate any buffers,
015 make atmi calls
016 check return value
017
018 rtnval = atmicall();
019
020 if (rtnval == -1) {
021 switch(tperrno) {
022 case TPEINVAL:
023 fprintf(stderr, "Invalid arguments were given to atmicall\n");
024 fprintf(stderr, "e.g., service name was null or flags wrong\n");
025 break;
026 case ...:
027 fprintf(stderr, ". . .");
028 break;
029
030 Include all error cases described in the atmicall(3) reference 031 page.
032 Other return codes are not possible, so there should be no 033 default within the switch statement.
034
035 if (tpabort(0) == -1) {
036 char *p;
037 fprintf(stderr, "abort was attempted but failed\n");
038 p = tpstrerror(tperrno);
039 userlog("%s", p);
040 }
041 }
042 else
043 if (tpcommit(0) == -1)
044 fprintf(stderr, "REPORT program failed at commit time\n");
045
046 The following code fragment shows how an application-specific
047 return code can be examined.
048 .
049 .
050 .
051 ret = tpcall("servicename", (char*)sendbuf, 0, (char **)&rcvbuf, &rcvlen, \
052 (long)0);
053 .
054 .
055 .
056 (void) fprintf(stdout, "Returned tpurcode is: %d\n", tpurcode);
057
058
059 free all buffers
060 tpterm();
061 exit(0);
062 }

The values of tperrno(5) provide details about the nature of each problem and suggest the level at which it can be corrected. If your application defines a list of error conditions specific to your processing, the same can be said for the values of tpurcode.

Listing 11-2 shows how to use the tpstrerrordetail (3c) function to obtain additional detail when an error is encountered.

Listing 11-2 Handling Errors Using tpstrerrordetail( )
001   #include <stdio.h>
002 #include <string.h>
003 #include <atmi.h> /* BEA Tuxedo Header File */
004 #define LOOP_ITER 100
005 #if defined(__STDC__) || defined(__cplusplus)
006 main(int argc, char *argv[])
007 #else
008 main(argc, argv)
009 int argc;
010 char *argv[];
011 #endif
012 {
013 char *sendbuf, *rcvbuf;
014 long sendlen, rcvlen;
015 int ret;
016 int i;
017 if(argc != 2) {
018 (void) fprintf(stderr, "Usage: simpcl string\n");
019 exit(1);
020 }
021 /* Attach to BEA Tuxedo System as a Client Process */
022 if (tpinit((TPINIT *) NULL) == -1) {
023 (void) fprintf(stderr, "Tpinit failed\n");
024 exit(1);
025 }
026 sendlen = strlen(argv[1]);
027
028 /* Allocate STRING buffers for the request and the reply */
029
030 if((sendbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) {
031 (void) fprintf(stderr,"Error allocating send buffer\n");
032 tpterm();
033 exit(1);
034 }
035
036 if((rcvbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) {
037 (void) fprintf(stderr,"Error allocating receive buffer\n");
038 tpfree(sendbuf);
039 tpterm();
040 exit(1);
041 }
042
043 for( i=0; i<LOOP_ITER; i++) {
044 (void) strcpy(sendbuf, argv[1]);
045
046 /* Request the service TOUPPER, waiting for a reply */
047 ret = tpcall("TOUPPER", (char *)sendbuf, 0, (char **)&rcvbuf, &rcvlen, (long)0);
048
049 if(ret == -1) {
050 (void) fprintf(stderr, "Can't send request to service
TOUPPER\n");
051 (void) fprintf(stderr, "Tperrno = %d, %s\n", tperrno, tpstrerror(tperrno));
052
053 ret = tperrordetail(0);
054 if(ret == -1) {
055 (void) fprintf(stderr, "tperrodetail() failed!\n");
056 (void) fprintf(stderr, "Tperrno = %d, %s\n", tperrno, tpstrerror(tperrno));
057 }
058 else if (ret != 0) {
059 (void) fprintf( stderr, "errordetail:%s\n",
060 tpstrerrordetail( ret, 0));
061 }
062 tpfree(sendbuf);
063 tpfree(rcvbuf);
064 tpterm();
065 exit(1);
066 }
067 (void) fprintf(stdout, "Returned string is: %s\n", rcvbuf);
068 }
069
070 /* Free Buffers & Detach from System/T */
071 tpfree(sendbuf);
072 tpfree(rcvbuf);
073 tpterm();
074 return(0);
}

 


Transaction Considerations

The following sections describe how various programming features work when used in transaction mode. The first section provides rules of basic communication etiquette that should be observed in code written for transaction mode.

 


Communication Etiquette

When writing code to be run in transaction mode, you must observe the following rules of basic communication etiquette:

 


Transaction Errors

The following sections describe transaction-related errors.

Non-fatal Transaction Errors

When transaction errors occur, the system returns TPETRAN in tperrno(5). The precise meaning of such an error, however, depends on the function that is returning it. Table 11-3 lists the functions that return transaction errors and describes possible causes of them.

Table 11-3 Transaction Errors
Function
Cause
Usually caused by a transient system error that occur during an attempt to start the transaction. The problem may clear up with a repeated call.
The function was called for a transaction reply after a request was made without the TPNOTRAN flag.
The Oracle Tuxedo system is unable to resume a global transaction because the caller is currently participating in work outside the global transaction with one or more resource managers. All such work must be completed before the global transaction can be resumed. The caller’s state with respect to the local transaction is unchanged.
A call was made in transaction mode to a service that does not support transactions. Some services belong to server groups that access a database management system (DBMS) that, in turn, support transactions. Other services, however, do not belong to such groups. In addition, some services that support transactions may require interoperation with software that does not. For example, a service that prints a form may work with a printer that does not support transactions. Services that do not support transactions may not function as participants in a transaction.
The grouping of services into servers and server groups is an administrative task. In order to determine which services support transactions, check with your application administrator.
You can correct transaction-level errors at the application level by enabling the TPNOTRAN flag or by accessing the service for which an error was returned outside of the transaction.

Fatal Transaction Errors

When a fatal transaction error occurs, the application should explicitly abort the transaction by having the initiator call tpabort(). Therefore, it is important to understand the errors that are fatal to transactions. Three conditions cause a transaction to fail:

The only protocol error that is fatal to transactions is calling tpcommit() from the wrong participant in a transaction. This error can be corrected in the application during the development phase.

If tpcommit() is called after an initiator/participant failure or transaction timeout, the result is an implicit abort error. Then, because the commit failed, the transaction should be aborted.

If the system returns TPESVCERR, TPESVCFAIL, TPEOTYPE, or TPETIME for any communication call, the transaction should be aborted explicitly with a call to tpabort(). You need not wait for outstanding call descriptors before explicitly aborting the transaction. However, because these descriptors are considered stale after the call is aborted, any attempt to access them after the transaction is terminated returns TPEBADDESC.

In the case of TPESVCERR, TPESVCFAIL, and TPEOTYPE, communication calls continue to be allowed as long as the transaction has not timed out. When these errors are returned, the transaction is marked abort-only. To preserve the results of any further work, you should call any communication functions with the flags parameter set to TPNOTRAN. By setting this flag, you ensure that the work performed for the transaction marked “abort-only” will not be rolled back when the transaction is aborted.

When a transaction timeout occurs, communication can continue, but communication requests cannot:

Therefore, to make asynchronous calls, you must set the flags parameter to TPNOREPLY, TPNOBLOCK, or TPNOTRAN.

Heuristic Decision Errors

The tpcommit() function may return TPEHAZARD or TPEHEURISTIC, depending on how TP_COMMIT_CONTROL is set.

If you set TP_COMMIT_CONTROL to TP_CMT_LOGGED, the application obtains control before the second phase of a two-phase commit is performed. In this case, the application may not be aware of a heuristic decision that occurs during the second phase.

TPEHAZARD or TPEHEURISTIC can be returned in a one-phase commit, however, if a single resource manager is involved in the transaction and it returns a heuristic decision or a hazard indication during a one-phase commit.

If you set TP_COMMIT_CONTROL to TP_CMT_COMPLETE, then the system returns TPEHEURISTIC if any resource manager reports a heuristic decision, and TPEHAZARD if any resource manager reports a hazard. TPEHAZARD specifies that a participant failed during the second phase of commit (or during a one-phase commit) and that it is not known whether a transaction completed successfully.

 


Transaction Timeouts

As described in Transaction Errors, two types of timeouts can occur in an Oracle Tuxedo application: blocking and transaction. The following sections describe how various programming features are affected by transaction timeouts. Refer to Transaction Errors for more information on timeouts.

Effect on the tpcommit() Function

What is the state of a transaction if a timeout occurs after a call to tpcommit()? If the transaction timed out and the system knows that it was aborted, the system reports these events by setting tperrno(5) to TPEABORT. If the status of the transaction is unknown, the system sets the error code to TPETIME.

When the state of a transaction is in doubt, you must query the resource manager. First, verify whether or not any of the changes that were part of the transaction were applied. Then you can determine whether the transaction was committed or aborted.

Effect on the TPNOTRAN Flag

When a process is in transaction mode and makes a communication call with flags set to TPNOTRAN, it prohibits the called service from becoming a participant in the current transaction. Whether the service request succeeds or fails has no impact on the outcome of the transaction. The transaction can still timeout while waiting for a reply that is due from a service, whether it is part of the transaction or not.

For additional information on using the TPNOTRAN flag, refer to tpreturn( ) and tpforward( ) Functions.

 


tpreturn( ) and tpforward( ) Functions

If you call a process while running in transaction mode, tpreturn() and tpforward() place the service portion of the transaction in a state that allows it to be either committed or aborted when the transaction completes. You can call a service several times on behalf of the same transaction. The system does not fully commit or abort the transaction until the initiator of the transaction calls tpcommit() or tpabort().

Neither tpreturn() nor tpforward() should be called until all outstanding descriptors for the communication calls made within the service have been retrieved. If you call tpreturn() with outstanding descriptors for which rval is set to TPSUCCESS, the system encounters a protocol error and returns TPESVCERR to the process waiting on tpgetrply(). If the process is in transaction mode, the system marks the caller as “abort-only.” Even if the initiator of the transaction calls tpcommit(), the system implicitly aborts the transaction. If you call tpreturn() with outstanding descriptors for which rval is set to TPFAIL, the system returns TPESVCFAIL to the process waiting on tpgetrply(). The effect on the transaction is the same.

When you call tpreturn() while running in transaction mode, this function can affect the result of the transaction by the processing errors that it encounters or that are retrieved from the value placed in rval by the application.

You can use tpforward() to indicate that success has been achieved up to a particular point in the processing of a request. If no application errors have been detected, the system invokes tpforward(); otherwise, the system invokes tpreturn() with TPFAIL. If you call tpforward() improperly, the system considers the call a processing error and returns a failed message to the requester.

 


tpterm( ) Function

Use the tpterm() function to remove a client context from an application.

If the client context is in transaction mode, the call fails with TPEPROTO returned in tperrno(5), and the client context remains part of the application and in transaction mode.

When the call is successful, the client context is allowed no further communication or participation in transactions because the current thread of execution is no longer part of the application.

 


Resource Managers

When you use an ATMI function to define transactions, the Oracle Tuxedo system executes an internal call to pass any global transaction information to each resource manager participating in the transaction. When you call tpcommit() or tpabort(), for example, the system makes internal calls to direct each resource manager to commit or abort the work it did on behalf of the caller’s global transaction.

When a global transaction has been initiated, either explicitly or implicitly, you should not make explicit calls to the resource manager’s transaction functions in your application code. Failure to follow this transaction rule causes indeterminate results. You can use the tpgetlev() function to determine whether a process is already in a global transaction before calling the resource manager’s transaction function.

Some resource managers allow programmers to configure certain parameters (such as the transaction consistency level) by specifying options available in the interface to the resource managers themselves. Such options are made available in two forms:

Consult the documentation for your resource managers for additional information.

The method of setting options varies for each resource manager. In the Oracle Tuxedo System SQL resource manager, for example, the set transaction statement is used to negotiate specific options (consistency level and access mode) for a transaction that has already been started by the Oracle Tuxedo system.

 


Sample Transaction Scenarios

The following sections provide some considerations for the following transaction scenarios:

Called Service in Same Transaction as Caller

When a caller in transaction mode calls another service to participate in the current transaction, the following facts apply:

Called Service in Different Transaction with AUTOTRAN Set

If you issue a communication call with the TPNOTRAN flag set and the called service is configured such that a transaction automatically starts when the service is called, the system places both the calling and called processes in transaction mode, but the two constitute different transactions. In this situation, the following facts apply:

Called Service That Starts a New Explicit Transaction

If a communication call is made with TPNOTRAN, and the called service is not automatically placed in transaction mode by a configuration option, the service can define multiple transactions using explicit calls to tpbegin(), tpcommit(), and tpabort(). As a result, the transaction can be completed before a call is issued to tpreturn().

In this situation, the following facts apply:

 


Oracle TUXEDO System-supplied Subroutines

The Oracle Tuxedo system-supplied subroutines, tpsvrinit(), tpsvrdone() tpsvrthrinit (3c), and tpsvrthrdone (3c), must follow certain rules when used in transactions.

Note: tpsvrthrinit (3c) and tpsvrthrdone (3c) can be specified for multithreaded applications only. tpsvrinit() and tpsvrdone() can be specified for both threaded and non-threaded applications.

The Oracle Tuxedo system server calls tpsvrinit() or tpsvrthrinit (3c) during initialization. Specifically, tpsvrinit() or tpsvrthrinit (3c) is called after the calling process becomes a server but before it starts handling service requests. If tpsvrinit() or tpsvrthrinit (3c) performs any asynchronous communication, all replies must be retrieved before the function returns; otherwise, the system ignores all pending replies and the server exits. If tpsvrinit() or tpsvrthrinit (3c) defines any transactions, they must be completed with all asynchronous replies retrieved before the function returns; otherwise, the system aborts the transaction and ignores all outstanding replies. In this case, the server exits gracefully.

The Oracle Tuxedo system server abstraction calls tpsvrdone() or tpsvrthrdone (3c) after it finishes processing service requests but before it exits. At this point, the server’s services are no longer advertised, but the server has not yet left the application. If tpsvrdone() or tpsvrthrdone (3c) initiates communication, it must retrieve all outstanding replies before it returns; otherwise, pending replies are ignored by the system and the server exits. If a transaction is started within tpsvrdone() or tpsvrthrdone (3c), it must be completed with all replies retrieved; otherwise, the system aborts the transaction and ignores the replies. In this case, too, the server exits.

 


Central Event Log

The central event log is a record of significant events in your Oracle Tuxedo application. Messages about these events are sent to the log by your application clients and services via the userlog (3c) function.

Any analysis of the central event log must be provided by the application. You should establish strict guidelines for the events that are to be recorded in the userlog (3c). Application debugging can be simplified by eliminating trivial messages.

For information on configuring the central event log on the Windows 2003 platform, refer to Using Oralce Tuxedo ATMI on Windows.

Log Name

The application administrator defines (in the configuration file) the absolute pathname that is used as the prefix of the name of the userlog (3c) error message file on each machine. The userlog (3c) function creates a date—in the form mmddyy, representing the month, day, and year—and adds this date to the pathname prefix, forming the full filename of the central event log. A new file is created daily. Thus, if a process sends messages to the central event log on succeeding days, the messages are written into different files.

Log Entry Format

Entries in the log consist of the following components:

For example, suppose that a security program executes the following call at 4:22:14pm on a UNIX machine called mach1 (as returned by the uname command):

userlog("Unknown User ’%s’ \n", usrnm);

The resulting log entry appears as follows:

162214.mach1!security.23451: Unknown User ’abc’

In this example, the process ID for security is 23451, and the variable usrnm contains the value abc.

If the preceding message was generated by the Oracle Tuxedo system (rather than by the application), it might appear as follows:

162214.mach1!security.23451: LIBSEC_CAT: 999: Unknown User ’abc’

In this case, the message catalog name is LIBSEC_CAT and the message number is 999.

If the message is sent to the central event log while the process is in transaction mode, other components are added to the tag in the user log entry. These components consist of the literal string gtrid followed by three long hexadecimal integers. The integers uniquely identify the global transaction and make up what is referred to as the global transaction identifier, that is, the gtrid. This identifier is used mainly for administrative purposes, but it also appears in the tag that prefixes the messages in the central event log. If the system writes the message to the central event log in transaction mode, the resulting log entry appears as follows:

162214.mach1!security.23451: gtrid x2 x24e1b803 x239:
Unknown User ’abc’

Writing to the Event Log

To write a message to the event log, you must perform the following steps:

In this example, the message is sent to the central event log if tpopen (3c) returns -1.

The userlog (3c) signature is similar to that of the UNIX System printf(3S) function. The format portion of both functions can contain literal strings and/or conversion specifications for a variable number of arguments.

 


Debugging Application Processes

Although you can use userlog (3c)statements to debug application software, it is sometimes necessary to use a debugger command for more complex problem solving.

The following sections describe how to debug an application on UNIX and Windows 2003 platforms.

Debugging Application Processes on UNIX Platforms

The standard UNIX system debugging command is dbx(1). For complete information about this tool, refer to dbx(1) in a UNIX system reference manual. If you use the -g option to compile client processes, you can debug those processes using the procedures described on the dbx(1) reference page.

To run the dbx command, enter the following:

dbx client

To execute a client process:

  1. Set any desired breakpoints in the code.
  2. Enter the dbx command.
  3. At the dbx prompt (*), type the run subcommand (r) and any options you want to pass to the client program’s main().

The task of debugging server programs is more complicated. Normally a server is started using the tmboot command, which starts the server on the correct machine with the correct options. When using dbx, it is necessary to run a server directly rather than through the tmboot command. To run a server directly, enter the r (short for run) subcommand after the prompt displayed by the dbx command.

The Oracle Tuxedo tmboot(1) command passes undocumented command-line options to the server’s predefined main(). To run a server directly, you must pass these options, manually, to the r subcommand. To find out which options need to be specified, run tmboot with the -n and -d 1 options. The -n option instructs tmboot not to execute a boot; -d 1 instructs it to display level 1 debugging statements. By default, the -d 1 option returns information about all processes. If you want information about only one process, you can specify your request accordingly with additional options. For more information, refer to the Oracle Tuxedo Command Reference.

The output of tmboot -n -d 1 includes a list of the command-line options passed by tmboot to the server’s main(), as shown in the following example:

exec server -g 1 -i 1 -u sfmax -U /tuxdir/appdir/ULOG -m 0 -A

Once you have the list of required command-line options, you are ready to run the server program directly, with the r subcommand of dbx(1). The following command line is an example:

*r -g 1 -i 1 -u sfmax -U /tuxdir/appdir/ULOG -m 0 -A 

You may not use dbx(1) to run a server that is already running as part of the configuration. If you try to do so, the server exits gracefully, indicating a duplicate server in the central event log.

Debugging Application Processes on Windows 2003 Platforms

On a Windows 2003 platform, a graphical debugger is provided as part of the Microsoft Visual C++ environment. For complete information about this tool, refer to the Microsoft Visual C++ reference manual.

To invoke the Microsoft Visual C++ debugger, enter the start command as follows:

start msdev -p process_ID
Note: For versions of the Microsoft Visual C++ debugger that are earlier than 5.0, enter the start command as follows:
Note: start msdev -p process_id

To invoke the debugger and automatically enter a process, specify the process name and arguments on the start command line, as follows:

start msdev filename argument

For example, to invoke the debugger and enter the simpcl.exe process with the ConvertThisString argument, enter the following command:

start msdev simpcl.exe ConvertThisString

When a user-mode exception occurs, you are prompted to invoke the default system debugger to examine the location of the program failure and the state of the registers, stacks, and so on. By default, Dr. Watson is used in the Windows 2003 environment uses as the default debugger for user-mode exception failures, while the kernel debugger is used in the Win32 SDK environment.

To modify the default debugger used by the Windows 2003 system for user-mode exception failures, perform the following steps:

  1. Run regedit or regedt32.
  2. Within the HKEY_LOCAL_MACHINE subtree, navigate to \SOFTWARE\Microsoft\Windows\CurrentVersion\AeDebug
  3. Double-click on the Debugger key to advance into the registry string editor.
  4. Modify the existing string to specify the debugger of your choice.
  5. For example, to request the debugger supplied with the Microsoft Visual C++ environment, enter the following command:

    msdev.exe -p %ld -e %ld
Note: For versions of the Microsoft Visual C++ debugger that are earlier than 5.0, enter the following command:
Note: msvc.exe -p %ld -e %ld

 


Comprehensive Example

Transaction integrity, message communication, and resource access are the major requirements of an Online-Transaction-Processing (OLTP) application.

This section provides a code sample that illustrates the ATMI transaction, buffer management, and communication routines operating together with SQL statements that access a resource manager. The example is borrowed from the ACCT server that is part of the Oracle Tuxedo banking application (bankapp) and illustrates the CLOSE_ACCT service.

The example shows how the set transaction statement (line 49) is used to set the consistency level and access mode of the transaction before the first SQL statement that accesses the database. (When read/write access is specified, the consistency level defaults to high consistency.) The SQL query determines the amount to be withdrawn in order to close the account based on the value of the ACCOUNT_ID (lines 50-58).

tpalloc() allocates a buffer for the request message to the WITHDRAWAL service, and the ACCOUNT_ID and the amount to be withdrawn are placed in the buffer (lines 62-74). Next, a request is sent to the WITHDRAWAL service via a tpcall() call (line 79). An SQL delete statement then updates the database by removing the account in question (line 86).

If all is successful, the buffer allocated in the service is freed (line 98) and the TPSVCINFO data buffer that was sent to the service is updated to indicate the successful completion of the transaction (line 99). Then, if the service was the initiator, the transaction is automatically committed. tpreturn() returns TPSUCCESS, along with the updated buffer, to the client process that requested the closing of the account. Finally, the successful completion of the requested service is reported on the status line of the form.

After each function call, success or failure is determined. If a failure occurs, the buffer allocated in the service is freed, any transaction begun in the service is aborted, and the TPSVCINFO buffer is updated to show the cause of failure (lines 80-83). Finally, tpreturn() returns TPFAIL and the message in the updated buffer is reported on the status line of the form.

Note: When specifying the consistency level of a global transaction in a service routine, take care to define the level in the same way for all service routines that may participate in the same transaction.
Listing 11-3 ACCT Server
001   #include <stdio.h>              /* UNIX */
002 #include <string.h> /* UNIX */
003 #include <fml.h> /* BEA Tuxedo System */
004 #include <atmi.h> /* BEA Tuxedo System */
005 #include <Usysflds.h> /* BEA Tuxedo System */
006 #include <sqlcode.h> /* BEA Tuxedo System */
007 #include <userlog.h> /* BEA Tuxedo System */
008 #include "bank.h" /* BANKING #defines */
009 #include "bank.flds.h" /* bankdb fields */
010 #include "event.flds.h" /* event fields */
011
012
013 EXEC SQL begin declare section;
014 static long account_id; /* account id */
015 static long branch_id; /* branch id */
016 static float bal, tlr_bal; /* BALANCE */
017 static char acct_type; /* account type*/
018 static char last_name[20], first_name[20]; /* last name, first name */
019 static char mid_init; /* middle initial */
020 static char address[60]; /* address */
021 static char phone[14]; /* telephone */
022 static long last_acct; /* last account branch gave */
023 EXEC SQL end declare section;

024 static FBFR *reqfb; /* fielded buffer for request message */
025 static long reqlen; /* length of request buffer */
026 static char amts[BALSTR]; /* string representation of float */

027 code for OPEN_ACCT service

028 /*
029 * Service to close an account
030 */

031 void
032 #ifdef __STDC__
033 LOSE_ACCT(TPSVCINFO *transb)

034 #else

035 CLOSE_ACCT(transb)
036 TPSVCINFO *transb;
037 #endif

038 {
039 FBFR *transf; /* fielded buffer of decoded message */

040 /* set pointer to TPSVCINFO data buffer */
041 transf = (FBFR *)transb->data;

042 /* must have valid account number */
043 if (((account_id = Fvall(transf, ACCOUNT_ID, 0)) < MINACCT) ||
044 (account_id > MAXACCT)) {
045 (void)Fchg(transf, STATLIN, 0, "Invalid account number", (FLDLEN)0);
046 tpreturn(TPFAIL, 0, transb->data, 0L, 0);
047 }

048 /* Set transaction level */
049 EXEC SQL set transaction read write;

050 /* Retrieve AMOUNT to be deleted */
051 EXEC SQL declare ccur cursor for
052 select BALANCE from ACCOUNT where ACCOUNT_ID = :account_id;
053 EXEC SQL open ccur;
054 EXEC SQL fetch ccur into :bal;
055 if (SQLCODE != SQL_OK) { /* nothing found */
056 (void)Fchg(transf, STATLIN, 0, getstr("account",SQLCODE), (FLDLEN)0);
057 EXEC SQL close ccur;
058 tpreturn(TPFAIL, 0, transb->data, 0L, 0);
059 }

060 /* Do final withdrawal */

061 /* make withdraw request buffer */
062 if ((reqfb = (FBFR *)tpalloc("FML",NULL,transb->len)) == (FBFR *)NULL) {
063 (void)userlog("tpalloc failed in close_acct\n");
064 (void)Fchg(transf, STATLIN, 0,
065 "Unable to allocate request buffer", (FLDLEN)0);
066 tpreturn(TPFAIL, 0, transb->data, 0L, 0);
067 }
068 reqlen = Fsizeof(reqfb);
069 (void)Finit(reqfb,reqlen);

070 /* put ID in request buffer */
071 (void)Fchg(reqfb,ACCOUNT_ID,0,(char *)&account_id, (FLDLEN)0);

072 /* put amount into request buffer */
073 (void)sprintf(amts,"%.2f",bal);
074 (void)Fchg(reqfb,SAMOUNT,0,amts, (FLDLEN)0);

075 /* increase the priority of this withdraw */
076 if (tpsprio(PRIORITY, 0L) == -1)
077 (void)userlog("Unable to increase priority of withdraw");

078 /* tpcall to withdraw service to remove remaining balance */
079 if (tpcall("WITHDRAWAL", (char *)reqfb, 0L, (char **)&reqfb,
080 (long *)&reqlen,TPSIGRSTRT) == -1) {
081 (void)Fchg(transf, STATLIN, 0,"Cannot make withdrawal", (FLDLEN)0);
082 tpfree((char *)reqfb);
083 tpreturn(TPFAIL, 0,transb->data, 0L, 0);
084 }

085 /* Delete account record */

086 EXEC SQL delete from ACCOUNT where current of ccur;
087 if (SQLCODE != SQL_OK) { /* Failure to delete */
088 (void)Fchg(transf, STATLIN, 0,"Cannot close account", (FLDLEN)0);
089 EXEC SQL close ccur;
090 tpfree((char *)reqfb);
091 tpreturn(TPFAIL, 0, transb->data, 0L, 0);
092 }
093 EXEC SQL close ccur;

094 /* prepare buffer for successful return */
095 (void)Fchg(transf, SBALANCE, 0, Fvals(reqfb,SAMOUNT,0), (FLDLEN)0);
096 (void)Fchg(transf, FORMNAM, 0, "CCLOSE", (FLDLEN)0);
097 (void)Fchg(transf, STATLIN, 0, " ", (FLDLEN)0);
098 tpfree((char *)reqfb);
099 tpreturn(TPSUCCESS, 0, transb->data, 0L, 0);
100 }

  Back to Top       Previous  Next