Previous | Contents | Index |
Register a function to be called when a message arrives and no call to rtr_receive_message() is active.
status = rtr_set_wakeup (void (*wu_rou)(void))
Argument Data Type Access status rtr_status_t write wu_rou procedure read
rtr_status_t rtr_set_wakeup (
procedure void (*wu_rou) (void)
)
void (*wu_rou) (void)
The routine to be called by RTR when no call to rtr_receive_message() is being processed and a message is waiting to be delivered.
The rtr_set_wakeup() call sets the address of a function to be called when no call to rtr_receive_message() is outstanding and a message is waiting to be delivered.Return Value A value indicating the status of the routine. Possible values are:To cancel wakeups, call the routine with an argument of null .
If a wakeup routine has been set using this call, subsequent calls to rtr_set_wakeup() should either disable the wakeup feature (with an argument of null ), or replace the current wakeup routine with another.
Note
The wakeup feature is not required in a threaded application; its functionality can be provided by a dedicated thread that receives and dispatches RTR messages.
RTR_STS_OK | Normal successful completion |
/* This client application has written a function which * RTR should call whenever there is a message for it. * The function is a `wake-up' function. */ void wake_me_up(void){ . . . status = rtr_receive_message( channel, RTR_NO_FLAGS, RTR_ANYCHAN, &receive_msg, sizeof(receive_msg), receive_time_out, &msgsb); check_status( "rtr_receive_message", status ); /* The rtr_msgsb_t tells us what type of * message we are receiving. This application is using * the message type to dispatch the message to handlers * which the programmer has written. */ select (rtr_msg_type_t){ case rtr_mt_prepare: status = prepare_function(receive_msg, msgsb); break; case rtr_mt_accept: status = accept_function(channel, receive_msg); break; . . Dispatch all possible; send needed parameter info. . } } /* In the main function, tell RTR to call the above function * when there is a message being sent to this application. */ status = rtr_set_wakeup(wake_me_up); sleep(); // Sleep until RTR sends a message.
Explicitly start a transaction on the specified channel.
status = rtr_start_tx (channel, flags, timoutms, pjointxid)
Argument Data Type Access status rtr_status_t write channel rtr_channel_t read flags rtr_sta_flag_t read timoutms rtr_timout_t read pjointxid rtr_pointer_t read
rtr_status_t rtr_start_tx (
rtr_channel_t channel ,
rtr_sta_flag_t flags ,
rtr_timout_t timoutms ,
rtr_pointer_t pjointxid
)
channel
The channel identifier returned earlier by the rtr_open_channel() call.flags
Flags that specify options for the call. Normally specify RTR_NO_FLAGS for this parameter, unless using nested transactions. Nested transaction flags, listed in Table 3-30, are only valid if the pjointxid parameter does not equal RTR_NO_JOINTXID.
Table 3-30 Nested Transaction Flags Flag Usage RTR_F_STA_TID_DDTM Only used if RTR_F_OPE_FOREIGN_TM is set in the rtr_open_channel call. Indicates that the transaction ID pointed to by the parameter pjointxid is a DECdtm transaction (16 bytes). RTR_F_STA_TID_RTR Only used if RTR_F_OPE_FOREIGN_TM is set in the rtr_open_channel call. Indicates that the transaction ID pointed to by the parameter pjointxid is an RTR transaction (28 bytes). RTR_F_STA_TID_XA Only used if RTR_F_OPE_FOREIGN_TM is set in the rtr_open_channel call. Indicates that the transaction ID pointed to by the parameter pjointxid is an XA transaction.
Note
The flags RTR_F_STA_TID_RTR, RTR_F_STA_TID_XA and RTR_F_STA_DDTM are mutually exclusive.timoutms
Transaction timeout specified in milliseconds. If the timeout time expires, RTR aborts the transaction and returns status RTR_STS_TIMOUT.If no timeout is required, specify RTR_NO_TIMOUTMS.
pjointxid
Pointer to the transaction identifier if the parent transaction.
The rtr_start_tx() call is used to start a transaction explicitly.Return Value A value indicating the status of the routine. Possible status values are:An explicit transaction start is only necessary if:
- either a join to an existing transaction is to be done
- or a transaction timeout is to be specified
Transactions are implicitly started when a message is sent on a currently inactive channel. Implicitly started transactions have no timeout and are not joined to other RTR transactions.
Nested Transaction Usage
If the RTR_F_OPE_FOREIGN_TM flag is set for a channel, then the global coordinating transaction manager for this transaction is a foreign transaction manager. In this case, the application must use the rtr_start_tx() call to start a transaction (the transaction cannot be started implicitly on a first call to rtr_send_to_server() ), and specify the pjointxid parameter.
When a nested transaction is started (pjointxid not equal to RTR_NO_JOINTXID), then that transaction is given a new RTR transaction ID (which the application can retrieve by calling rtr_get_tid()). The foreign transaction ID passed in pjointxid is used only to identify the transaction for the foreign transaction manager (for example, when the foreign transaction manager goes through recovery and requests RTR to return all transactions in prepared state).
The channel on which a nested transaction is started must be opened as a client channel, on a node defined with the frontend role, on which an RTR journal is required. If no facilities on that node have the backend role defined, so as to support a journal, RTR opens a local journal on the node.
Each nested transaction is started on a separate client channel. There is no restriction on the levels of nesting for nested transactions, or on the number of nested transactions that can be started and controlled by one global transaction manager.
rtr_xid_t xa_txn; /* This client-server pair handle transactions which contain * multiple messages within each one. Transactions are explicitly * started and prepared, as directed by this client. * * Fill in the information in the XA transaction id struct. * The information will be sent to the server to tag the transaction. */ xa_txn.formatID = RTR_XID_FORMATID_RTR_XA; xa_txn.gtrid_length = 4; xa_txn.bqual_length = 4; strcpy(xa_txn.data, "6789.0003"); /* Start the transaction; specify a timeout so we don't get * stuck waiting forever. */ status = rtr_start_tx( &channel, RTR_F_STA_TID_XA, 1000, &xa_txn ); check_status(status); // May be RTR_STS_TIMEOUT.
This appendix describes the differences between the portable RTR API
available in Reliable Transaction Router Version 3, the portable API, and the OpenVMS
API used in Reliable Transaction Router Version 2, the OpenVMS API.
A.1 Compatibility between RTR Versions
Reliable Transaction Router Version 3 interoperates with RTR Version 2.2 in a DECnet environment using DECnet Phase IV naming. (The same version of RTR must be installed on all routers and backends. See the section on Network Transports in the Reliable Transaction Router System Manager's Manual to find out how to configure your Version 3 nodes.)
Note that the size of an RTR transaction ID has been changed for
Reliable Transaction Router Version 3; it is now 28 bytes. (This ensures that the
transaction ID contains a unique node specification.)
A.2 Reasons for a Portable API
RTR was first developed for use within an OpenVMS environment. Reliable Transaction Router Version 3 extends the applicability of RTR to allow users to create fault-tolerant distributed applications running on networks of heterogeneous machines and platforms.
The OpenVMS API presented some incompatibilities when used on non-OpenVMS platforms as follows:
The benefits of using the portable API are:
The portable API has been designed to:
Table A-1 compares the OpenVMS and Portable API calls.
OpenVMS API | Portable API |
---|---|
$dcl_tx_prc() | rtr_open_channel() |
$start_tx() | rtr_start_tx() [optional] |
$commit_tx() | rtr_accept_tx() |
$abort_tx() | rtr_reject_tx() |
$vote_tx() | rtr_accept_tx()/rtr_reject_tx() |
$deq_tx() | rtr_receive_message() |
$enq_tx() | rtr_send_to_server()/ rtr_reply_to_client()/rtr_broadcast_event() |
$dcl_tx_prc() (SHUT) | rtr_close_channel() |
$get_txi() | rtr_request_info() |
$set_txi() | rtr_set_info() |
ASTPRM (on asynch calls) | rtr_set_user_handle() |
-- | rtr_error_text() |
-- | rtr_get_tid() |
-- | rtr_prepare_tx() |
-- | rtr_set_wakeup() |
Start here!
Purpose:
This tutorial goes through all of the steps needed to set up a simple
RTR-based application for a new user. The intent is to provide a
starting point for learning about RTR, and to simplify the main
concepts of RTR; you will be able to cruise through this at a more
rapid pace than you normally would with the RTR reference information.
At the end of this tutorial, you’ll find brief descriptions of some of the more complex features RTR provides, and pointers to the documentation where you can study them in detail.
Summary:
This tutorial walks you through designing, coding and setting up a
basic RTR-based client-server application. To do this, you’ll use RTR
to perform two important services for you:
In the system that you are about to develop, the client application interacts with the user to read and display data. The server application handles requests from the client, and sends replies back to it. When we refer to ‘client’ and ‘server’, we will be referring to the applications. When we refer to the computer nodes on which the client or server is executing, we will call them ‘frontend’ and ‘backend’ nodes, respectively.
In most applications, the server would probably talk to a database in order to retrieve or save data according to what a user had entered in the user-interface. In the interest of simplifying this tutorial, however, this server is only going to tell you whether it received your client’s request.
What’s different in this system from a non-RTR system is that there will be two servers: one of the servers, also known as the ‘primary server’, almost always talks with the client. In a perfect world, nothing would ever happen to this server; clients would always get the information they asked for, and all changes would be made to the database when the user updated information. Every time anyone attempted to access this server, it would always be there, ready and waiting to ‘serve’, and users could feel secure in the knowledge that the data in the database was changed exactly as they had requested.
But we’re all well aware that this is not always the case, and when servers do go down, it’s usually at the most inopportune time. So you are going to use RTR to designate a second server as a "standby" server. In this way, if a user is attempting to get some real work done, and the primary server is down, the user will never notice. The standby server will spring into action, and replace the original server by handling the user’s requests in just the same way as the primary server had been doing. And, this will be done from the same point at which the primary server had crashed!
Materials List:
In order to fully develop this system, you will need a client
application and frontend node, a server application and two backend
nodes, and a router. What are these things?
Frontend:
The frontend node is the system on which your client application is
executing. As in any client-server system, the client application
interacts with the user, then conveys the user’s requests to the
server. When developing an RTR-based client-server system, your client
will have the following characteristics:
Example code for the client application and the server application can be found in the ‘examples’ subdirectory of your RTR installation directory.
BackEnd1:
Your first backend node will be running the primary server application.
It, too, can be
on any of the above operating systems, except the Windows system must
be NT. It also must have RTR installed on it, and will contain your
server application. Your server application will use RTR to listen for
requests from the client, receive and handle those requests, and
confirm the result to the client.
BackEnd2:
This machine will run the standby server application. It will probably
also be doing
any one of a number of other things that have nothing to do with this
tutorial, or even with RTR. It most probably will be sitting on one of
your coworkers’ desks, helping him or her to earn their weekly salary
and support their family. Hopefully, you get along with this coworker
well enough that they will install RTR on their machine, so that you
may complete this tutorial.
Router:
Your router is simply RTR software which keeps track of everything that
is going on for
you when your application is running. The router can execute on a
separate machine, on a frontend machine, or on a backend machine. In
this tutorial, we will keep our router on the same machine as the
client.
Install RTR:
Your first step, once you have determined the three computers you are
going to use for this tutorial, is to be sure RTR is installed and
configured on each machine. The RTR installation is well documented and
straightforward, although slightly different for each operating system
on which the installation is being run. Refer to the section in the RTR
Installation Guide for the system on which you are installing
RTR.
For the purpose of documenting examples, the machine you have decided to use for the client application will be referred to as FE (frontend), primary server as BE1 (backend 1), secondary server as BE2 (backend 2). Remember that the router will be on the FE machine. The journal must be accessible to both backend servers.
Start RTR:
You will need to start RTR on each of the machines on which you have
installed it. You may do this from one machine. In order to be able to
issue commands to RTR on a remote
node, however, you must have an account on that node with the necessary
access privileges. The operating system’s documentation, or your system
manager, will have information on how set up privileges to enable users
to run applications over the network.
Use the command interface on your system to interact with RTR. At the command prompt, type in RTR, and press the Return or Enter key. You will then be at the RTR> prompt, and can start RTR on all of the nodes. For example, on a UNIX system, it will look like this:
% rtr RTR> start rtr/node=(FE,BE1,BE2) RTR> exit |
This command starts ‘services’ or ‘daemons’ on each of the nodes in the list. These are processes that listen for messages being sent by other RTR services or daemons over the network. After executing the command, a ‘ps’, ‘show process’ or Task Manager review of processes executing on your system should now show at least one process named ‘rtr’ or ‘rtr.exe’ on each of the machines. This process is the one that manages the communications between the nodes in the RTR-based application, and handles all transactions and recoveries.
Create a Recovery Journal:
This step holds the key to letting the second server pick up on the
work at exactly the right time; no work is lost, and the hot swap to
the standby server is
"automagic". RTR keeps track of the work being done by
writing data to this journal. If a failure
occurs, all incomplete transactions are being kept track of here, and
can be replayed by the standby server when it comes to the rescue. When
transactions have been completed, they are removed from this journal.
For this example, only your backend nodes need a recovery journal, and you must create the journal before creating your facility; you’ll learn more about facilities in the next section. You’ll now need to go to each of the backend nodes that you’ll be using and create a journal there. Log into each machine and, using the command prompt interface, run RTR and create the journal. When you specify the location of the journal, it should be the disk name or share name where the journal will be located. The journal must be accessible by both of the backend servers.
This is an example of what the command would look like on a VMS system.
$ RTR RTR> create journal user2 RTR> exit |
To allow both servers to access the journal, you have a number of options:
In any case, you should be sure the disk is not on your primary server, since this is the machine that we are protecting, in case of a crash. If the machine goes down, the standby server would not be able to access the disk.
The Database:
While we are having this discussion on sharing resources, we should
also mention how a database fits into this system, as well.
Previous | Next | Contents | Index |