Previous | Contents | Index |
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.
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.
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.
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.
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).
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.
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.
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:
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).
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:
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:
|
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.
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.
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.
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 |
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.
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:
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:
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 |