hp Reliable Transaction Router
Application Design Guide


Previous Contents Index

Starting a Transaction

There are two ways to start a transaction:

Using the rtr_start_tx Call

Use the rtr_start_tx call when the application must set a client-side transaction timeout to ensure that both client and server do not wait too long for a message. When a transaction is started with rtr_send_to_server , no timeout is specified.

For example,


 rtr_start_tx(&Channel, 
        RTR_NO_FLAGS, 
        RTR_NO_TIMOUTMS, 
        RTR_NO_JOINCHAN); //or NULL 

Using the rtr_send_to_server Call

The rtr_send_to_server call sends a message as part of a transaction from a client. If there is no transaction currently active on the channel, a new one is started. The transaction accept can be bundled with the last message. A client has two options for message delivery after a failure:

Using the rtr_reply_to_client Call

The rtr_reply_to_client call sends a reply message from a server to the client. The reply message is part of the transaction initiated by the client. For example,


status = rtr_reply_to_client (&Channel, 
                              RTR_NO_FLAGS, 
                              MsgBuffer, 
                              DataLen, 
                              RTR_NO_MSGFMT); 

The reply message format can be of any form as designed by the application. For example,


     struct acct_inq_msg_t { 
                            char reply_text[80]; 
                           } acct_reply_msg; 

Identifying a Transaction

When an application receives a message with the rtr_receive_message call, the message status block ( MsgStatusBlock ) contains the transaction identifier. For example,


status = rtr_receive_message (&Channel, 
                              RTR_NO_FLAGS, 
                              RTR_ANYCHAN, 
                              MsgBuffer, 
                              DataLen, 
                              RTR_NO_TIMOUTMS, 
                              &MsgStatusBlock); 

The pointer &MsgStatusBlock points to the message status block that describes the received message. For example,


 typedef struct {rtr_msg_type_t         msgtype; 
                 rtr_usrhdl_t           usrhdl; 
                 rtr_msglen_t           msglen; 
                 rtr_tid_t              tid;     
/*If a transactional message, the transaction ID or tid, msgsb.tid */ 
                 rtr_evtnum_t           evtnum; 
                } rtr_msgsb_t; 

Use the rtr_get_tid call to obtain the RTR transaction identifier for the current transaction. The TID (transaction identifier) is a unique number generated by RTR for each transaction. The application can use the TID if the client needs to know the TID to take some action before receiving a response.

Use the rtr_set_user_handle call to set a user handle on a transaction instead of using a channel. A client application with multiple transactions outstanding can match a reply or completion status with the appropriate transaction by establishing a new user handle each time a transaction is started.

Committing a Transaction

A server application ends a transaction by accepting or rejecting it. A transaction is accepted explicitly with the rtr_accept_tx call, and rejected explicitly with the rtr_reject_tx call. RTR can reject a transaction at any time once the transaction is started, but before it is committed. If RTR cannot deliver a transaction to its destination, it rejects the transaction explicitly and delivers the reject completion status to all participants.

A transaction participant can specify a reason for an accept or reject on the rtr_accept_tx and rtr_reject_tx call. If more than one transaction participant specifies a reason, RTR uses the OR operator to combine the reason values together. For example, with two servers A and B, each providing a reason code of 1 and 2, respectively, the client receives the result of the OR operation, reason code 3, in its message buffer.


Server A                      Server B 
 rtr_reason_t                 rtr_reason_t 
  reason = 1 ;                reason=2 ; 
  rtr_reject_tx (             rtr_reject_tx ( 
  channel,                    channel, 
  flags,                      flags, 
  reason );                   reason ); 
typedef struct { 
  rtr_status_t status; 
  rtr_reason_t reason; 
} rtr_status_data_t; 
 

The client receives the results of the OR operation in its message buffer:


rtr_status_data_t 
    msgbuf; 
    msgbuf.reason = 3; 

A transaction is done once a client or server application receives a completion message, either an rtr_mt_closed, rtr_mt_accepted , or rtr_mt_rejected message from RTR. An application no longer receives messages related to a transaction after receiving a completion message or if the application calls rtr_reject_tx . A client or server can also specify RTR_F_ACC_FORGET on the rtr_accept_tx call to signal its acceptance and end its involvement in a transaction early. RTR returns no more messages (including completion messages) associated with the transaction; any such messages received will be returned to the caller.

When issuing the rtr_accept_tx call with RTR_NO_FLAGS on the call, the caller expresses its request for successful completion of the transaction, and may give an accept reason that is passed on to all participants in the transaction. The accept is final; the caller cannot reject the transaction later. The caller cannot send any more messages for this transaction.

A client can accept a transaction in one of two ways: with the rtr_accept_tx call or by using the RTR_F_SEN_ACCEPT flag on the rtr_send_to_server call.

When the client sets RTR_F_SEN_ACCEPT on the rtr_send_to_server call, this removes the need to issue an rtr_accept_tx call and can help optimization of client traffic. Merging the data and accept messages in one call puts them in a single network packet. This can make better use of network resources and improve throughput.

The rtr_reject_tx call rejects a transaction. Any participant in a transaction can call rtr_reject_tx . The reject is final; the caller cannot accept the transaction later. The caller can specify a reject reason that is passed to all accepting participants of the transaction. Once the transaction has been rejected, the caller receives no more messages for this transaction.

The server can set the retry flag RTR_F_REJ_RETRY to have RTR redeliver the transaction beginning with msg1 without aborting the transaction for other participants. Issuing an rtr_reject_tx call with this flag can let another transaction proceed if locks held by this transaction cause a database deadlock.

Uncertain Transactions

If there is a crash before the rtr_accept_tx statement is executed, on recovery, the transaction is replayed as rtr_mt_msg1 because the database will have rolled back the prior transaction instance. However, if there is a crash after the rtr_accept_tx statement is executed, on recovery, the transaction is replayed as rtr_mt_msg1_uncertain because RTR does not know the status of the prior transaction instance. Your application must understand the implications of such failures and deal with them appropriately.

Administering Transaction Timeouts

RTR provides a relatively simple way to administer a transaction timeout in the server. Use of timeout values on the rtr_receive_message function lets a server application specify how long it is prepared to wait for the next message. (Of course, the server should be prepared to wait forever to get a new transaction or for the result of an already-voted transaction.)

One way to achieve this would be to have a channel-specific global variable, say, called SERVER_INACTIVITY_TIMEOUT , which is set to the desired value (in milliseconds-that is, use a value of 5000 to set a 5 second timeout). Note that this timeout value should be used after receiving the first message of the transaction. The value should be reset to RTR_NO_TIMOUTMS after receiving the rtr_mt_prepare message. Whenever the rtr_receive_message completes with a RTR_STS_TIMOUT , the server calls the rtr_reject_tx function on that channel to abort the partially- processed transaction. This would prevent transactions from occupying the server process beyond a reasonable time.

Two-Phase Commit

The two-phase commit process includes prepare and commit phases. A transaction is tentatively accepted or rejected during the prepare phase.

Prepare Phase

To initiate the prepare phase, the server application specifies the RTR_F_OPE_EXPLICIT_PREPARE flag when opening the channel, and can use the message rtr_mt_prepare to check commit status. The message indicates to the server application that it is time to prepare any updates for a later commit or rollback operation. RTR lets the server application explicitly accept a transaction using the RTR_F_OPE_EXPLICIT_ACCEPT flag on the rtr_open_channel call. Alternatively, RTR implicitly accepts the transaction after receiving the rtr_mt_accepted message when the server issues its next rtr_receive_message call.

The commit process is initiated by the client application when it issues a call to RTR indicating that the client "accepts" the transaction. This does not mean that the transaction is fully accepted, only that the client is prepared to accept it. RTR then asks the server applications participating in the transaction if they are prepared to accept the transaction. A server application that is prepared to accept the transaction votes its intention by issuing the rtr_accept_tx call, an "accept" vote. A server application that is not prepared to accept the transaction issues the rtr_reject_tx call, a "not accept" vote. Issuing all votes concludes the prepare phase.

Commit Phase

When RTR has collected all votes from all participating server applications, it determines if the transaction is to be committed. If all collected votes are "accept," the transaction is committed; RTR informs all participating channels. If any vote is "not accept," the transaction is not committed. A server application can expose the prepare phase of two-phase commit by using the rtr_mt_prepare message type with the RTR_F_OPE_EXPLICIT_PREPARE flag. If the application's rtr_open_channel call sets neither the RTR_F_OPE_EXPLICIT_ACCEPT nor RTR_F_OPE_EXPLICIT_PREPARE flag, both prepare and accept processing are implicit.

The server application can participate in the two-phase commit process fully, somewhat, a little, or not at all. To participate fully, the server does an explicit prepare and an explicit accept of the transaction. To participate somewhat, the server does an explicit prepare and an implicit accept of the transaction. To participate a little, the server does an explicit accept of the transaction. To participate not at all, the server does an implicit accept of the transaction. Table 5-5 summarizes the level of server participation:

Table 5-5 Server Participation
Commit Phase Full Somewhat Little Not at all
Explicit prepare yes yes    
Explicit accept yes   yes  
Implicit accept   yes   yes

Your application can use the level of participation that makes the most sense for your business and operations needs.

Explicit Accept, Explicit Prepare

To request an explicit accept and explicit prepare of transactions, the server channel is opened with the RTR_F_OPE_EXPLICIT_PREPARE and RTR_F_OPE_EXPLICIT_ACCEPT flags. These specify that the channel will receive both prepare and accept messages. The server then explicitly accepts or rejects a transaction when it receives the prepare message. The transaction sequence for an explicit prepare and explicit accept is as follows:
Client RTR Server
rtr_start_tx    
rtr_send_to_server -> rtr_mt_msg1 -> rtr_receive_message
rtr_accept_tx -> rtr_mt_prepare -> rtr_receive_message
    <- rtr_accept_tx
rtr_receive_message <- rtr_mt_accepted <- rtr_receive_message

With explicit transaction handling, the following steps occur:

  1. The server application waits for a message from the client application.
  2. The server application receives the rtr_mt_prepare request message from RTR.
  3. The server application issues the accept or reject.

A participant can reject the transaction up to the time RTR has sent the rtr_mt_prepare message type to the server in the rtr_accept_tx call. A participant can reject the transaction up to the time the rtr_accept_tx call is executed. Once the client application has called rtr_accept_tx , the result cannot be changed.

Implicit Prepare, Explicit Accept

The sequence for an implicit prepare and explicit accept is as follows:
Client RTR Server
rtr_start_tx    
rtr_send_to_server -> rtr_mt_msg1 -> rtr_receive_message
rtr_accept_tx -> <- rtr_accept_tx
rtr_receive_message <- rtr_mt_accepted <- rtr_receive_message

In certain database applications, where the database manager does not let an application explicitly prepare the database, transactions can simply be accepted or rejected. Server applications that do not specify the RTR_F_EXPLICIT_ACCEPT flag in their rtr_open_channel call implicitly accept the in-progress transaction when an rtr_receive_message call is issued after the last message has been received for the transaction. This call returns the final status for the transaction, rtr_mt_accepted or rtr_mt_rejected . If neither the RTR_F_OPE_EXPLICIT_ACCEPT nor the RTR_F_OPE_EXPLICT_PREPARE flags are set in the rtr_open_channel call, then both prepare and accept processing will be implicit.

For server optimization, the server can signal its acceptance of a transaction with either rtr_reply_to_client , using the RTR_F_REP_ACCEPT flag, or with the client issuing the rtr_send_to_server call, using the RTR_F_SEN_ACCEPT flag. This helps to minimize network traffic for transactions by increasing the likelihood that the data message and the RTR accept message will be sent in the same network packet.

Transaction Recovery

When a transaction fails in progress, RTR provides recovery support using RTR replay technology. RTR, as a distributed transaction manager, communicates with a database resource manager in directing the two-phase commit process. When using the XA protocol, the application does not need to process rtr_mt_uncertain messages (see the section Using XA, for more details on using XA).

The typical server application transaction sequence for committing a transaction to the database is as follows:


rtr_receive_message (rtr_mt_msg1) 
SQL update 
rtr_accept_tx 
rtr_receive_message (rtr_mt_accepted) 
SQL commit 
rtr_receive_message [wait for next transaction] 

This sequence is also illustrated in Figure 2-7, CSN Vote Window for the C API.

A failure can occur at any step in this sequence; the impact of a failure depends on when (at which step) it occurs, and on the server configuration.

Failure before rtr_accept_tx

If a failure occurs before the rtr_accept_tx call is issued, RTR causes the following to occur:

Failure after rtr_accept_tx

If a failure occurs after the rtr_accept_tx call is issued but before the rtr_receive_message , the transaction is replayed. The type of the first message is then rtr_mt_uncertain when the server is restarted. In this case, servers should check to see if the transaction has already been executed in a previous presentation. If not, it is safe to re-execute the transaction because the database operation never occurred. After the failure, the following occurs:

If a failure occurs after the SQL commit but before receipt of a message starting the next transaction, RTR does not know the difference.

If a failure occurs after an rtr_receive_message call is made to begin a new transaction, RTR assumes a successful commit if a server calls rtr_receive_message after receiving the rtr_mt_accepted message and will forget the transaction. There is no replay following these events.


Previous Next Contents Index