hp Reliable Transaction Router
Application Design Guide


Previous Contents Index

RTR Events

RTR delivers status information to which client and server applications can subscribe. Status information is delivered as messages, where the type of each message is an RTR event.

RTR events are numbered. The base value for RTR events is defined by the symbol RTR_EVTNUM_RTRBASE ; its maximum value is defined by the symbol RTR_EVTNUM_RTRMAX . RTR events and event numbers are listed in the Reliable Transaction Router API manuals and in the RTR header files rtr.h and rtrapi.h.

An application can subscribe to RTR events to receive notification of external events that are of interest to the application. For example, a shadow server may need to know if it is a primary or a secondary server to perform certain work, such as uploading information to a central database, that is done at only one site.

To subscribe to all RTR events, use the range indicators RTR_EVTNUM_RTRBASE and RTR_EVTNUM_RTRMAX . RTR events are delivered as messages of type rtr_mt_rtr_event .

In application design, consider creating separate facilities for sending broadcasts. By separating broadcast notification from transactional traffic, performance improvements can be substantial. Facilities can further be reconfigured to place the RTR routers strategically to minimize wide-area traffic.

A server application can expect to see a primary or secondary event delivered only in certain transaction states. For more detail, see the state diagrams in Appendix C, Server States.

Location Transparency

With location transparency, applications do not need to be modified when the hardware configuration is altered, whether changes are made to systems running RTR services or to the network topology. Client and server applications do not know the location of one another so services can be started anywhere in the network. Actual configuration binding is a system management operation at run time, through the assignment of roles (frontend/backend/router) within a given facility to the participant nodes.

For RTR to automatically take care of failover, server applications need to specify certain availability attributes for the partition.

Because RTR automatically takes care of failover, applications need not be concerned with specifying the location of server resources.

Handling Error Conditions

RTR can provide information to an application with the RTRMessage and RTREvent classes (for the C++ API). Certain inherited methods within these classes translate RTR internal error message values to informational text meaningful to the reader. For the C API, this is done with the rtr_error_text call.

If an application encounters an error, it should log the error message received. Error messages are more fully described in rtrapi.h for the C++ API and in rtr.h for the C API, where each error code is explained.

For example, the following short program uses the standard C library output function to display the text of an error status code.


Program "prog": 
#include "rtr.h" 

or


#include <rtr.h> 
main() { 
 printf("%s", 
  rtr_error_text(RTR_STS_NOLICENSE)); 
} 

When this program is run, it produces the following output:


$run prog 
 No license installed 

The several hundred error or status codes reside in the rtr.h header file; status codes can come from any RTR subsystem. A few codes that an application is likely to encounter are described in Table 3-3.

Table 3-3 RTR Error Codes
Status Code Meaning
RTR_STS_COMSTAUNO Commitment status unobtainable. The fate of the transaction currently being committed is unobtainable; this may be due to a hardware failure.
RTR_STS_DLKTXRES The transaction being processed was aborted due to deadlock with other transactions using the same servers. RTR will replay the transaction after the deadlock has been resolved and cleared.
RTR_STS_FELINLOS Frontend link lost; probably due to a network failure.
RTR_STS_INVFLAGS Invalid flags.
RTR_STS_NODSTFND No destination found; no server had declared itself to handle the key value specified in the sent message. Probably a server down or disconnected.
RTR_STS_REPLYDIFF Two servers respond with different information during a replay; transaction aborted.
RTR_STS_TIMOUT Timeout expired; transaction aborted.
RTR_STS_SRVDIED Probably a server image exited, for example because a node is down.
RTR_STS_SRVDIEDVOT A server exited before committing a transaction.
RTR_STS_SRVDIEDCOM A server exited after being told to commit a transaction.

RTR can abort a transaction at any time, so the application must be prepared to deal with such aborted transactions. Server applications are expected to roll back transactions as the need arises, and must be built to take the correct action, and subsequently carry on to deal with new transactions that are received.

A client application can also get a reject and must also be built to deal with the likely cases it will encounter. The application must be built to decide on the correct course of action in the event of a transaction abort.

Using Locks

When using a database system with RTR, an application designer must be aware of how the database system works and how it handles database locks. Because Oracle is a frequently used database system, this section provides a short summary of Oracle locking methods. The application designer must use Oracle documentation to supplement this brief description. This material is fully discussed in the Oracle8 and Oracle8i Application Developer's Guides, specifically in the chapters on Processing SQL Statements, Explicit Data Locking, Explicitly Acquiring Row Locks, Serializable and Row Locking Parameters, User Locks, Non-Default Locking, and Concurrency Control Using Serializable Transactions. Oracle database operations are performed using Structured Query Language (SQL).

Oracle Locking

Privileges Required

In its own schema, an application can automatically acquire any type of table locks. However, to acquire a table lock on a table in another schema, the application must have the LOCK ANY TABLE system privilege or an object privilege such as SELECT or UPDATE for the table.

Overriding Default Locking

By default, Oracle locks data structures automatically. However, an application can request specific data locks on rows or tables when it needs to override default locking. Explicit locking lets an application share or deny access to a table for the duration of a transaction.

An application can explicitly lock entire tables using the LOCK TABLE statement, but locking a table means that no other transaction, user, or application can access it. This can cause performance problems.

With the SELECT FOR UPDATE statement, an application explicitly locks specific rows of a table to ensure the rows do not change before an update or a delete. Oracle automatically obtains row-level locks at update or delete time, so use the FOR UPDATE clause only to lock the rows before the update or delete.

A SELECT statement with Oracle does not acquire any locks, but a SELECT ... FOR UPDATE does. For example, the following is a typical SELECT ... FOR UPDATE statement:


 
 SELECT partno FROM parts FOR UPDATE OF price 

This statement starts a transaction to update the parts table with a price change for a specific part.

Oracle Explicit Data Locking

To ensure data concurrency, integrity, and statement-level read consistency, Oracle always performs necessary locks. However, an application can override default locks. This can be useful when:

Overrides to Oracle locks can be done at two levels:

At transaction level: Transactions override Oracle default locks with the following SQL statements:

At system level : Oracle can start an instance with non-default locking by adjusting the following initialization parameters:

If an application overrides any Oracle default locks, the application itself must:

Table Locks

When a LOCK TABLE statement executes, it overrides default locking, and a transaction explicitly acquires the specified table locks. A LOCK TABLE statement on a view locks the underlying base tables (see Table 3-4).

Table 3-4 LOCK TABLE Statements
Statement Meaning
LOCK TABLE tablename IN EXCLUSIVE MODE [NOWAIT]; Acquires exclusive table locks. Locks all rows of the table. No other user can modify the table. With NOWAIT, the application acquires the table lock only if the lock is immediately available, and Oracle issues an error if not. Without NOWAIT, the transaction does not proceed until the requested table lock is acquired. If the wait for a table lock reaches the limit set by the initialization parameter DISTRIBUTED_LOCK_TIMEOUT, a distributed transaction can time out. As no data will have been modified due to the timeout, the application can proceed as if it has encountered a deadlock.
LOCK TABLE tablename IN ROW SHARE MODE;
LOCK TABLE tablename IN ROW EXCLUSIVE MODE;
These offer the highest degree of concurrency. Consider if the transaction must prevent another transaction from acquiring an intervening share, share row, or exclusive table lock for a table before the table can be updated in the transaction. If another transaction acquires an intervening share, share row, or exclusive table lock, no other transactions can update the table until the locking transaction commits or rolls back.
LOCK TABLE tablename IN SHARE MODE; Consider this share table lock if:
  • Your transaction only queries the table and requires a consistent set of table data for the duration of the transaction (requires transaction-level read consistency for the locked table).
  • Other transactions to update the locked table concurrently can be made to wait until all transactions with the share table locks commit or roll back.
  • Other transactions can acquire concurrent share table locks on the same table, providing them transaction-level read consistency.

Note: If multiple transactions concurrently hold share table locks for the same table, NO transaction can update the table. Thus if share table locks on the same table are common, deadlocks will be frequent and updates will not proceed. For such a case, use share row exclusive or exclusive table locks.
LOCK TABLE tablename IN SHARE ROW EXCLUSIVE MODE; Acquire a share row exclusive table lock when:
  • Your transaction requires both transaction-level read consistency for the specified table, and the ability to update the locked table.
  • Other transactions can obtain explicit row locks.
  • The application needs only a single transaction to have this behavior.

Acquiring Row Locks

The SELECT ... FOR UPDATE statement acquires exclusive row locks of selected rows. The statement can be used to lock a row without changing the row. Acquiring row locks can also be used to ensure that only a single interactive application user updates rows at a given time. For information on using this statement with cursors or triggers, see the Oracle8 or Oracle8i documentation. To acquire a row lock only when it is immediately available, include NOWAIT in the statement.

Each row in the return set of a SELECT ... FOR UPDATE statement is individually locked. The statement waits until a previous transaction releases the lock. If a SELECT ... FOR UPDATE statement locks many rows in a table, and the table is subject to moderately frequent updates, it may improve performance to acquire an exclusive table lock rather than using row locks.

If the wait for a row lock reaches the limit set by the initialization parameter DISTRIBUTED_LOCK_TIMEOUT, a distributed transaction can time out. As no data will have been modified, the application can proceed as if it has encountered a deadlock.

Setting SERIALIZABLE and ROW_LOCKING Parameters

How an instance handles locking is determined by the SERIALIZABLE option on the SET TRANSACTION or ALTER SESSION command, and the initialization parameter ROW_LOCKING. By default, SERIALIZABLE is set to false and ROW_LOCKING is set to always.

Normally these parameters should never be changed. However they may be used for compatibility with applications that run with earlier versions of Oracle, or for sites that must run in ANSI/ISO-compatible mode. Performance will usually suffer with non-default locking.

Using the LOCK TABLE Statement

The application uses the LOCK TABLE statement to lock entire database tables in a specified lock mode to share or deny access to them. For example, the statement below locks the parts table in row-share mode. Row-share locks allow concurrent access to a table; they prevent other users from locking the entire table for exclusive use. Table locks are released when your transaction issues a commit or rollback.


    LOCK TABLE parts IN ROW SHARE MODE NOWAIT; 

The lock mode determines which other locks can be placed on the table. For example, many users can acquire row-share locks on a table at the same time, but only one user at a time can acquire an exclusive lock. While one user has an exclusive lock on a table, no other users can insert, delete, or update rows in that table. For more information about lock modes, see the Oracle8 or Oracle8i Server Application Developer's Guide.

A table lock never keeps other users from querying a table, and a query never acquires a table lock. Only if two different transactions try to modify the same row will one transaction wait for the other to complete.

If your program includes SQL locking statements, make sure the Oracle users requesting the locks have the privileges needed to obtain the locks.

Table 3-5 Summary of Locking Options
Case Description SERIALIZABLE ROW_LOCKING
0 Default settings FALSE ALWAYS
1 As Oracle Version 5 and earlier (no concurrent inserts, updates or deletes in a table) FALSE (disabled) INTENT
2 ANSI compatible Enabled ALWAYS
3 ASNI compatible with table-level locking (no concurrent inserts, updates or deletes in a table) Enabled INTENT

Table 3-6 Non-Default Locking Behavior
Statement   Case 0 Case 1   Case 2   Case 3  
SERIALIZABLE FALSE (disabled) Disabled   Enabled   Enabled  
ROW_LOCKING ALWAYS INTENT   ALWAYS   INTENT  
      Row Table Row Table Row Table
SELECT   - - - S - S
INSERT   X SRX X RX X SRX
UPDATE   X SRX X SRX X SRX
DELETE   X SRX X SRX X SRX
SELECT ... FOR UPDATE   X RS X S X S
LOCK TABLE ... IN..            
 
  ROW SHARE MODE RS RS RS RS RS RS
  ROW EXCLUSIVE MODE RX RX RX RX RX RX
  SHARE MODE S S S S S S
  SHARE ROW EXCLUSIVE MODE SRX SRX SRX SRX SRX SRX
  EXCLUSIVE MODE X X X X X X
DDL Statements   - X - X - X
Modes: X = EXLUSIVE
  RS = ROW SHARE
  RX = ROW EXCLUSIVE
  S = SHARE
  SRX = SHARE ROW EXCLUSIVE

The information in this table comes from the Oracle 8i Application Developer's Guide.

Distributed Deadlocks

A deadlock or deadly embrace can occur when transactions lock data items in a database. The typical scenario is with two transactions txn1 and txn2 executing concurrently with the following sequence of events:

  1. txn1 write-locks data item A.
  2. txn2 write-locks data item B.
  3. txn1 requests a lock on data item B but must wait because txn2 still has a lock on data item B.
  4. txn2 requests a lock on data item A but must wait because txn1 still has a lock on data item A.

Neither txn1 nor txn2 can proceed; they are in a deadly embrace. Figure 3-7 illustrates a deadly embrace.

Figure 3-7 Deadly Embrace


With RTR, to avoid such deadlocks, follow these guidelines:

  1. Always engage servers in the same order, and wait for the reply before each send.
  2. Provide several concurrent servers to minimize contention. Estimate the number of concurrent servers needed by determining the volume of transactions the servers must support, considering periods of maximum activity, and allowing for growth. The larger the volume on your servers, the more likely it is that your application will benefit from using concurrent servers.

RTR attempts to resolve deadlocks by aborting one deadlocked transaction path with error code RTR_STS_DLKTXRES and replaying the transaction. Other paths are not affected. Server applications should be written to handle this status appropriately.

The RTR status code RTR_STS_DLKTXRES can occur under several environmental conditions that RTR detects and works around. The application need not take any explicit action other than releasing the resources connected with the active transaction such as doing a rollback on the database transaction.

For example, RTR may issue an RTR_STS_DLKTXRES status when:

As an example of the first case, consider clients A and B both performing transactions TCA and TCB, where both TCA and TCB include a message to both server X and server Y followed by an ACCEPT. There is only one instance of Server X and Server Y available, and due to the quirks of distributed processing, only Server X receives the message belonging to TCA and only Server Y receives the message belonging to TCB. Figure 3-8 reflects this scenario. Because Server Y has no chance of accepting TCA until TCB is processed to completion and Server X has no chance of accepting TCB until TCA is processed to completion, Server X and Y are in a distributed deadlock. In such a case, RTR selects TCA or TCB to abort with DLKTXRES and replays it in a different order.

Figure 3-8 Scenario for Distributed Deadlock


Sometimes RTR needs to abort a transaction and reschedule it. For example, it can happen that a state change is needed after the primary server started to process a transaction but RTR had to change its role to secondary before the transaction was completed. Thus the transaction would be executed on the other node as primary and later played to this server as secondary. RTR uses the same status code RTR_STS_DLKTXRES when aborting the transaction.


Previous Next Contents Index