In a VERSANT open transaction, each phase of a commit or rollback to a VERSANT database is controlled explicitly by a non-VERSANT Transaction Manager.
VERSANT system supports a number of different transaction protocol standards, including X/Open and its subset XA, with numerous C/VERSANT functions.
a transaction manager — a process running on the machine containing the session database,
a coordinator transaction log — a log containing coordinator transaction entries which is maintained by the session database,
participant transaction logs — a log containing participant transaction entries, which is maintained by each participant database.
Commit and Rollback
The procedures for commits and rollbacks are similar:
1. The transaction manager sends create, update, and delete information and an "are you ready?" message to the session database and to each connected database. This state is called "Phase 1".
2. When every database responds that it is ready, the transaction manager sends a "commit" message to all databases and writes to its coordinator transaction log. This is state is called "Phase 2".
3. Each database performs its part of the commit or rollback, writes to its participant transaction log, and sends a success or failure message to the transaction manager.
4. If the transaction manger receives a success message from each database, it deletes the transaction entry from its log and sends a message to each participating database to delete its transaction entry.
Recovery
If any database involved in a transaction crashes during a commit or rollback, the transaction entry will remain in the coordinator transaction log. When the crashed database restarts, the following will happen as part of the startup procedure.
1. The database will ask that the coordinator transaction log be examined.
2. For each Phase 2 entry in the transaction log, the transaction manager will attempt to finish the commit or rollback.
3. For each Phase 1 entry, the transaction manager will declare that the transaction involved is a "zombie".
VERSANT system supports a number of different transaction protocol standards, including X/Open and its subset XA, with numerous C/VERSANT functions.
The flow of control in a generic transaction protocol system is something like the following (details differ from one protocol standard to another).
1.
A Begin_Work()
function starts the transaction. It registers the transaction with a Transaction Manager and creates a unique transaction identification number.
2. The application now invokes Resource Managers, reading and writing from the client machine or from databases, and sending requests to local and remote services.
3. When the Resource Managers get their first requests from a Transaction Manager, they join the associated transaction, telling the Transaction Manager that they wants to participate.
4. After the system answers all requests, the transaction calls a Commit_Work()
function. This causes the Transaction Manager to begin a two-phase commit protocol. In Phase 1, the Transaction Manager asks all of the Resource Managers that have joined the transaction if they think the transaction is consistent and complete.
5. If all the Resource Managers respond "yes," the transaction is a correct transformation. The Resource Managers record this fact in their individual transaction logs. The Resource Managers then release the locks on the messages, finish any other necessary functioning, and the transaction is complete.
6. If any Resource Manager votes "no" the commit fails, causing the Transaction Manager to orchestrate a rollback. The Transaction Manager reads the transaction's log, and for each log record, calls the Resource Manager that wrote the record, asking the Resource Manager to undo the transformation. Once all the undos are finished, the Transaction Manager calls all the Resource Managers that had joined the transaction to tell them that the transaction was aborted.
7. The Transaction Manager also handles transaction recoveries if a site or node fails. If a site fails, the transaction protocol system restarts all the resource managers. As part of the Transaction Managers' restart code, the Transaction Manager tells Resource Managers the outcome of all transactions that were in progress at the time of the failure. The Resource Manager updates the logs and reconstructs its state.
Typically, each site has its own Transaction Manager. This allows each site to operate independently from each other. If the transaction deals with remote sites during its execution, remote Transaction Managers handle their own part of the process.
VERSANT provides the following functions that support open transactions.
o_beginxact()
|
Start a transaction and assign a transaction identifier. |
o_commitp1()
|
Commit phase 1. |
o_commitp2()
|
Commit phase 2. |
o_deleteZombieList()
|
Delete list of zombie transactions. |
o_flush()
|
Flush specified objects. |
o_getparticipants()
|
Get databases participating in transaction. |
o_getxid()
|
Get transaction identifier. |
o_getZombieCount()
|
Get zombie count. |
o_getZombieIndex()
|
Get zombie index. |
o_getZombieList()
|
Get zombie list. |
o_incZombieIndex()
|
Increment zombie index. |
o_recover()
|
Get list of zombie transactions. |
o_rollback()
|
Rollback transaction. |
o_setflushobjects()
|
Set flush options. |
o_setxid()
|
Set transaction identifier. |
o_setZombieCount()
|
Set zombie count. |
o_setZombieIndex()
|
Set zombie index. |
o_unsetxid()
|
Remove transaction identifier. |
o_xastatetransition()
|
Is XA call consistent. |
The X/Open DTP Model as shown in the XA Specification looks something like this:
Please refer to the XA Specification and the X/Open Guide for more information.
The key concept is that the XA and X/Open transaction models open up the two-phase commit process so that each phase can be controlled explicitly by an external Transaction Manager.
Resource Manager Identifier
The Resource Manager must be able to map the Transaction Identifier to the recoverable work it did for the corresponding branch. The Resource Manager may perform bitwise comparisons on the data components of an Transaction Identifier for the lengths specified in the Transaction Identifier structure. Most XA routines pass a pointer to the Transaction Identifier. These pointers are valid only for the duration of the call. If the Resource Manager needs to refer to the Transaction Identifier after it returns from the call, it must make a local copy before returning.
The xa.h
header defines a public structure called a Transaction Identifier, xid_t
, to identify a transaction branch. Resource Managers and Transaction Managers both use the Transaction Identifier structure.
Each Resource Manager must provide a switch that gives the Transaction Manager access to the Resource Manager's XA routines. This lets the administrator change the set of Resource Managers linked with an executable module without having to recompile the application.
A different set of Resource Managers and their switches may be linked into each separate application executable module in the Distributed Transaction Processing system. Several instances of a Resource Manager can share the Resource Manager's switch structure.
A Resource Manager's switch is defined as a structure called xa_switch_t
.
sprintf(sessionName, "XA.Resource Manager Identifier:%0d", rmid);
Based on the assumption that Resource Manager Identifiers are unique, the session names should be unique also.
In the multi-process model, there is not much advantage to have this Resource Manager Identifier to VERSANT session binding. It may be useful, however, in the multi-thread model.
When an external transaction terminates, all transaction states will be removed from the client even if there are objects in the cache. With this change, you must still be in a database session to be in a transaction, but you can now be in a session but not in a transaction. In the standard VERSANT transaction model, once a session started, you were always in a transaction.
To allow you to associate an external transaction with a VERSANT transaction, use the set transaction function, o_setxid()
. Once the association is done, the VERSANT transaction will be considered an external transaction, and it will give up its role as a coordinator on deciding how to terminate the transaction. In other words, it will become passive and wait for an external request before recovering zombie transactions.
Only one VERSANT transaction is allowed to associate with an external transaction. If the server find that the external transaction with the given Transaction Identifier is already associated with a VERSANT transaction, the VERSANT server will return the VERSANT transaction to the client. This allows an external transaction to attach to a VERSANT transaction multiple times providing that they are serialized (i.e., no more than one attachment at any instance).
To avoid inconsistencies, every external transaction will be allowed to use only one front end cache at all times.
xa.h
.
Data Structures |
|
vsnt_xa_switch_t
|
An instance of xa_switch_t that maps XA functions to VERSANT functions.
|
xa_switch_t
|
Information about function mapping. |
xid_t
|
Information about a transaction. |
Functions |
|
vsnt_xa_close()
|
Close a Resource Manager. |
vsnt_xa_commit()
|
Commit a transaction. |
vsnt_xa_complete()
|
Complete asynchronous operation. |
vsnt_xa_end()
|
Notify when a thread of control ends. |
vsnt_xa_forget()
|
Remove external transaction entry. |
vsnt_xa_open()
|
Open a Resource Manager. |
vsnt_xa_prepare()
|
Prepare for a commit. |
vsnt_xa_recover()
|
Get list of prepared transactions. |
vsnt_xa_rollback()
|
Rollback transactoin. |
vsnt_xa_start()
|
Notify when application is ready. |
|
|
vsnt_xa_open
|
xa_open()
|
vsnt_xa_close
|
xa_close()
|
vsnt_xa_start
|
xa_start()
|
vsnt_xa_end
|
xa_end()
|
vsnt_xa_rollback
|
xa_rollback()
|
vsnt_xa_prepare
|
xa_prepare()
|
vsnt_xa_commit
|
xa_commit()
|
vsnt_xa_recover
|
xa_recover()
|
vsnt_xa_forget
|
xa_forget()
|
vsnt_xa_complete
|
xa_complete()
|
o_err o_beginxact(
o_xidhandle xid,
o_xacthandle* xact,
o_u4b flags);
xid
parameter with it, and set xact
to the handle representing the VERSANT transaction.Parameters:
xid
xact
flags
o_err o_commitp1(o_xidhandle xid, o_u4b flags);
Perform a Phase 1 commit on the current VERSANT transaction.
If o_commitp1()
is called without first calling o_flush()
, all dirty objects in the entire cache will be flushed. However, if o_commitp1()
is called from a different process, you will be responsible for flushing the objects from the other caches.
Parameters:
xid
flags
o_err o_commitp2(o_xidhandle xid, o_u4b flags);
Perform a Phase 2 commit on the current VERSANT transaction.
Parameters:
xid
flags
O_COMMIT_ONE_PHASE
O_COMMIT_ONE_PHASE
is passed to o_commitp2()
without calling o_flush()
, all dirty objects in the entire cache will be flushed.
However, if this function is called with O_COMMIT_ONE_PHASE
by a different process, you will be responsible for flushing the objects from the other caches.
o_err o_deleteZombieList();
Delete the vstr
of zombie transaction identifiers and clear all counts and indexes associated with it.
o_err o_flush(o_vstr *failed_objects);
Flush specified dirty objects to their database(s).
o_getflushobjects()
function.
failed_objects
failed_objects
, returns the list of objects that fail. Currently, it is only used for optimistic locking sessions to return objects that fail the time stamp validation.
o_err o_getparticipants(
o_xidhandle xid,
o_list* participants,
o_u4b options);
xid
.Parameters:
xid
particpants
options
O_GETPARTS_INITIALIZE
, then this function will recalculate the participants list; otherwise it will reuse the participants list that was calculated in a previous call to this function.
o_err o_getxid(o_xidhandle *xid, o_u4b flags)
This function is used exclusively by XA to get the Transaction Identifier of the current VERSANT transaction.
The association between the external and VERSANT transaction is established on the client as well as the server side.
Parameters:
xid
flags
O_XID_COMPARE
OM_TR_INVALID_XID
is returned.
O_XID_RETRIEVE
xid
parameter.
o_u4b o_getZombieCount();
Get the number of zombie transactions.
o_u4b o_getZombieIndex();
Get the zombie index, which refers to the next xid
in the vstr
of zombie transactions that has not yet been returned to the user.
o_vstr *o_getZombieList();
Get a vstr
containing the Transaction Identifiers of all zombie transactions. This list is intialized by the o_recover()
function.
See also o_recover()
.
o_u4b o_incZombieIndex();
Increment the zombie index and return its new value.
o_err o_recover(o_vstr *xids);
Get a list of transactions that are currently in Phase 1 commit (zombie) state.
The transactions list will be returned to specified vstr of Transaction Identifiers. You are then responsible for releasing this vstr when you are done.
o_err o_rollback(o_xidhandle xid, o_u4b flags);
Roll back the work performed in the current VERSANT transaction.
Parameters:
xid
flags
o_err o_setflushobjects(o_vstr objects, o_u4b options);
Register the flush options.
o_flush()
function.
objects
NULL
, all dirty objects in the cache will be flushed.
options
O_FLUSH_RETAIN_OBJECT
O_FLUSH_RETAIN_SCHEMA_OBJECT
O_FLUSH_CLEAN_CODS
seps
) structure. The O_FLUSH_RETAIN_OBJECT
and O_FLUSH_CLEAN_CODS
options apply to the given objects if it is not NULL
; otherwise, they apply to the entire cache.When there are retained locks at the end of a VERSANT transaction, the next external transaction will also associate with this VERSANT transaction in order to inherit its locks. This support is limited to a single cache (process) model.
o_err o_setxid(o_xidhandle xid, o_4b size, o_u4b flags)
This function is used exclusively by XA to set the Transaction Identifier to the current VERSANT transaction. The association between the external and VERSANT transaction is established on the client as well as the server side.
Parameter:
xid
size
flags
O_XID_IGNORE_COUNT
O_EXTERNAL_FORGET_XACT
O_EXTERNAL_END_XACT
void o_setZombieCount();
Set the zombie count, an internal data variable, to the number of xids
in the vstr
of zombie transactions.
void o_setZombieIndex(o_u4b count);
Set the zombie index, an internal data variable pointing to the next xid
in the vstr
of zombie transaction that has not yet been processed, to the value of count
, where count
is the number of zombie transactions.
See o_getZombieCount()
.
o_err o_unsetxid(o_xidhandle xid, o_4b size, o_u4b flags)
This function is used exclusively by XA to remove the Transaction Identifier from the current VERSANT transaction. The association between the external and VERSANT transaction is established on the client as well as the server side.
xid
size
xid
parameter.
flags
O_XID_IGNORE_COUNT
O_EXTERNAL_FORGET_XACT
O_EXTERNAL_END_XACT
xa_switch_t
named vsnt_xa_switch_t.
This structure translates Transaction Manager calls to VERSANT interface functions.
struct xa_switch_t vsnt_xa_switch =
{
"VERSANT-ODBMS",
TMNOFLAGS,
0,
vsnt_xa_open,
vsnt_xa_close,
vsnt_xa_start,
vsnt_xa_end,
vsnt_xa_rollback,
vsnt_xa_prepare,
vsnt_xa_commit,
vsnt_xa_recover,
vsnt_xa_forget,
vsnt_xa_complete
};
Elements are:
|
Name of Resource Manager. |
|
Flags for Resource Manager. |
|
Version. |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
|
Map to |
struct xa_switch_t
{
char name[Resource ManagerNAMESZ];
long flags;
long version;
int (*xa_open_entry) (char*, int, long);
int (*xa_close_entry) (char*, int, long);
int (*xa_start_entry) (XID*, int, long);
int (*xa_end_entry) (XID*, int, long);
int (*xa_rollback_entry) (XID*, int, long);
int (*xa_prepare_entry) (XID*, int, long);
int (*xa_commit_entry) (XID*, int, long);
int (*xa_recover_entry) (XID*, long, int, long);
int (*xa_forget_entry) (XID*, int, long);
int (*xa_complete_entry) (int*, int*, int, long);
};
Resource Manager switch structure containing information about the Resource Manager.
Elements are:
name[Resource ManagerNAMESZ]
flags
This flag indicates whether the Resource Manager uses dynamic registration, whether the Resource Manager operates asynchronously, and whether the Resource Manager supports the migration of associations. Only the migration of associations is currently be supported.
version
0
in this release.
xa_open_entry
xa_close_entry
xa_start_entry
xa_end_entry
xa_rollback_entry
xa_prepare_entry
xa_commit_entry
xa_recover_entry
xa_forget_entry
xa_complete_entry
struct xid_t
{
long formatID;
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE];
};
An identifier containing a Global Transaction Identifier and a Branch Qualifier.
Elements are:
formatID
If formatID
is -1
, the transaction identifier is NULL
.
gtrid_length
gtrid
), starting at the first byte of the data element (data[0]
).The maximum length is 64 bytes.
bqual_length
gtrid
(data[gtrid_length]
).The maximum length is 64 bytes.
data[XIDDATASIZE]
Neither component in data[]
is null terminated.
You do not need to initialize any unused bytes.
int o_xastatetransition(
o_xidhandle xid,
o_u4b xaoperation,
o_u4b flags,
int xaer);
xid
parameter.
If the XA function was called out of sequence or if the input flags are inconsistent, then this function will return XAER_RMFAIL
.
Parameters:
xid
xaoperation
flags
xaer
static int vsnt_xa_close(char *xa_info, int rmid, long flags);
VERSANT interprets the xa_close()
call from the Transaction Manager as vsnt_xa_close().
A Transaction Manager calls xa_close()
to close a currently open Resource Manager in the calling thread of control. Once closed, the resource manager cannot participate in global transactions on behalf of the calling thread until it is re-opened. Objects in the cache, if present, will not be flushed and will be lost.
It is an error, [XAER_PROTO]
, for a Transaction Manager to call xa_close()
within a thread of control that is associated with a transaction branch (i.e., the Transaction Manager must call xa_end()
before calling xa_close()
).
Parameters:
xa_info
xa_info
points to a null-terminated character string that may contain instance-specific information for the Resource Manager. In the case of VERSANT, we require that it conform to the following format:
<database name>
The maximum length of the database name is 31 bytes.
rmid
xa_open()
and identifies the Resource Manager called from the thread of control.
flags:
flags
must be set to TMNOFLAGS
.
static int vsnt_xa_commit(XID *xid, int rmid, long flags);
VERSANT interprets the xa_commit()
call from the Transaction Manager as vsnt_xa_commit()
.
A Transaction Manager calls xa_commit()
to commit the work associated with *xid
. Any changes made to resources held during the transaction branch are made permanent.
All associations for *xid
must have been ended by using xa_end()
with TMSUCCESS
set in the flags
parameter.
Parameters:
xid
xids
. It is the Transaction Manager's responsibility to ignore Transaction Identifiers that do not belong to it.
rmid
xa_open()
, This parameter identifies the resource manager called from the thread of control.
flags
TMNOFLAGS
or TMONEPHASE
.
The options TMNOWAIT
and TMASYNC
are not supported.
static int vsnt_xa_complete(
int *handle,
int *retval,
int rmid,
long flags)
xa_complete()
call from the Transaction Manager as vsnt_xa_complete()
.
This functions always returns XAER_PROTO
, since no asynchronous operation is supported in this release.
static int vsnt_xa_end(XID *xid, int rmid, long flags);
VERSANT interprets the xa_end()
call from the Transaction Manager as vsnt_xa_end()
. A Transaction Manager calls xa_end()
when a thread of control finishes.
Parameters:
xid
xa_start()
call that established the thread's association; otherwise, an error, [XAER_NOTA]
, will be returned.
rmid
xa_open()
, identifies the Resource Manager called from the thread of control.
flags
TMSUCCESS
or TMFAIL
, TMSUSPEND
, or TMMIGRATE
. TMASYNC
is not supported. In case the flag is set to TMSUSPEND
, the front end cache will not be flushed, and we will not allow a different process to use the suspended external transaction.
static int vsnt_xa_forget(
Transaction Identifier* xid,
int rmid,
long flags);
xa_forget()
call from the Transaction Manager as vsnt_xa_forget()
.A request will be sent from the client to the server asking it to remove the external transaction entry.
static int vsnt_xa_open(char *xa_info, int rmid, long flags);
VERSANT interprets the xa_open()
call from the Transaction Manager as vsnt_xa_open().
Transaction Manager calls xa_open
to initialize a Resource Manager and prepare it for use in a distributed transaction processing environment. It must be called before any other resource manager (xa_
) calls are made. If the Resource Manager supports multiple instances, the Transaction Manager can call xa_open()
more than once for the same Resource Manager.
Parameters:
xa_info
xa_info
points to a null-terminated character string that may contain instance-specific information for the Resource Manager. In the case of VERSANT, we require that it conform to the following format:
<database name>
The maximum length of the database name is 31 bytes.
rmid
rmid
, an integer assigned by the Transaction Manager, uniquely identifies the called Resource Manager instance within the thread of control. The Resource Manager passes the rmid
on subsequent calls to XA routines to identify the Resource Manager. This identifier remains constant until the Transaction Manager in this thread closes the Resource Manager.
flags
TMNOFLAGS
. The only other value, TMASYNC
, is not supported.
static int vsnt_xa_prepare(XID *xid, int rmid, long flags);
VERSANT interprets the xa_prepare()
call from the Transaction Manager as vsnt_xa_prepare()
.
A Transaction Manager calls xa_prepare()
to request a Resource Manager to prepare for commitment any work perform on behalf of xid
. All associations for xid
must have been ended by using xa_end()
with TMSUCCESS
set in flags.
Once this function successfully returns, the Resource Manager must guarantee that the transaction branch may be either committed or rolled back regardless of failures. A resource manager cannot erase it's knowledge of a branch until the Transaction Manager calls either xa_commit()
or xa_rollback()
to complete the branch.
Parameters:
xid
rmid
xa_open()
, identifies the Resource Manager called from the thread of control.
flags
TMNOFLAGS
.
static int vsnt_xa_recover(
Transaction Identifier *xids,
long count,
int rmid,
long flags);
xa_recover()
call from the Transaction Manager as vsnt_xa_recover()
.
A Transaction Manager calls xa_recover()
during recovery to obtain a list of transactions that are currently in a prepared state. The caller points xids
to an array into which the resource manager places Transaction Identifiers for these transactions, and sets count to the maximum number of Transaction Identifiers that fit into that array.
So that all Transaction Identifiers may be returned irrespective of the size of the array xids
, one or more xa_recover()
calls may be used within a single recovery scan.
The start of a recovery scan moves a cursor to the start of a list of prepared transactions. Throughout the recovery scan the cursor marks the current position in that list. Each call advances the cursor past the set of Transaction Identifiers it returns.
Two consecutive complete recovery scans return the same list of transactions unless a Transaction Manager calls xa_commit()
, xa_forget()
, xa_prepare()
, or xa_rollback()
for that Resource Manager.
A Transaction Manager may call this function from any thread of control, but all calls in a given recovery scan must be made by the same thread.
Parameters:
flags
xid
xa_recover()
places zero or more Transaction Identifiers in the space pointed to by xids. It is the Transaction Manager's responsibility to ignore Transaction Identifiers that do not belong to it.
count
xa_recover()
again with TMENDRSCAN
set in flags.) Multiple invocations of xa_recover()
retrieve all the prepared transactions.
rmid
rmid
, the same integer that the transaction manager generated when calling xa_open()
, This parameter identifies the resource manager called from the thread of control.
flags
flags
:
TMSTARTRSCAN
xa_recover()
should start a recovery scan for the thread of control and position the cursor to the start of the list. Transaction Identifiers are returned from that point. If a recovery scan is already open, the effect is as if the recovery scan were ended and then restarted.
TMENDRSCAN
xa_recover()
should end the recovery scan after returning the Transaction Identifiers. if this flag is used in conjunction with TMSTARTRSCAN
, the single xa_recover()
call starts and then ends a scan.
TMNOFLAGS
flags
. A recovery scan must already be started. Transaction Identifiers are returned starting at the current cursor position.
static int vsnt_xa_rollback(XID *xid, int rmid, long flags);
VERSANT interprets the xa_rollback()
call from the Transaction Manager as vsnt_xa_rollback()
.
A Transaction Manager calls xa_rollback()
to roll back the work performed at a Resource Manager on behalf of the transaction branch.
Parameters:
xid
rmid
xa_open()
, identifies the Resource Manager called from the thread of control.
flags
TMNOFLAGS
.
static int vsnt_xa_start(XID *xid, int rmid, long flags);
VERSANT interprets the xa_start()
call from the Transaction Manager as vsnt_xa_start()
.
A Transaction Manager calls xa_start()
to inform a Resource Manager that an application may do work on behalf of a transaction branch.
Parameters:
xid
*xid
to a VERSANT transaction on the server side.
rmid
xa_open()
and identifies the Resource Manager called from the thread of control.
flags
TMNOFLAGS
, TMJOIN
, or TMRESUME
, TMNOWAIT
and TMASYNC
are not supported. Only the process that suspended it can resume a suspended external transaction.
This online documentation is confidential and proprietary to Versant Corporation and is licensed to you, or to your organization, pursuant to the terms of an agreement between you and Versant that restricts the use of this documentation. Please refer to the agreement for more information or call Versant at 510-789-1500 with any questions.