Subject | Re: isc_reconnect_transaction ??? |
---|---|
Author | Ann W. Harrison |
Post date | 2002-10-16T18:15:55Z |
At 03:17 PM 10/16/2002 +0200, Erik Kunze wrote:
a look at some of the code - I really don't remember
the details all that well...
Unless A is between phases of a two-phase commit - that is
isc_prepare_transaction has been called but isc_commit_transaction
has not returned success - losing a connection to any database
will cause all children to rollback.
There are two ways to handle a multi-database transactions.
One is to start it with a "start_multiple" call. In that
case, all you do is call commit. It notices that the
transaction is multi-database, and calls prepare on each
of the child transactions, creating an RDB$TRANSACTIONS
messages for each. GFIX (tdr.c) reads the messages and
fixes up the transaction states.
You can also start separate transactions from the client
application and when you're ready to commit, call prepare
on each one passing your own message. The system will
store a record in rdb$transactions for each database
containing your message. Then call commit on each one.
As the commit succeeds, it deletes the associated rdb$transactions
record. Should your program A discover an error, it should
attempt to start a new transaction on each client and read
the messages it left for itself.
Modules to look at include jrd/tra.c, alice/tdr.c, alice/met.e,
jrd why.c
Regards,
Ann
www.ibphoenix.com
We have answers.
This is the interesting code from why.c
static STATUS prepare (
STATUS *status,
TRA transaction)
{
/**************************************
*
* p r e p a r e
*
**************************************
*
* Functional description
* Perform the first phase of a two-phase commit
* for a multi-database transaction.
*
**************************************/
TRA sub;
UCHAR *p, *description;
#ifdef PC_PLATFORM
UCHAR tdr_buffer[256];
#else
UCHAR tdr_buffer[1024];
#endif
USHORT length = 0;
for (sub = transaction->next; sub; sub = sub->next)
length += 256;
description = (length > sizeof (tdr_buffer)) ? (UCHAR*) gds__alloc ((SLONG)
length) : tdr_buffer;
/* build a transaction description record containing
the host site and database/transaction
information for the target databases. */
if (!(p = description))
{
status [0] = isc_arg_gds;
status [1] = isc_virmemexh;
status [2] = isc_arg_end;
CHECK_STATUS (status);
return status [1];
}
*p++ = TDR_VERSION;
ISC_get_host (p + 2, length - 16);
*p++ = TDR_HOST_SITE;
*p = (UCHAR) strlen ((SCHAR*) p + 1);
while (*++p)
;
/* Get database and transaction stuff for each sub-transaction */
for (sub = transaction->next; sub; sub = sub->next)
{
get_database_info (status, sub, &p);
get_transaction_info (status, sub, &p);
}
/* So far so good -- prepare each sub-transaction */
length = p - description;
for (sub = transaction->next; sub; sub = sub->next)
if (CALL (PROC_PREPARE, sub->implementation)(
status,
&sub->handle,
length,
description))
{
if (description != tdr_buffer)
free_block (description);
CHECK_STATUS (status);
return status [1];
}
if (description != tdr_buffer)
free_block (description);
CHECK_STATUS_SUCCESS (status);
return SUCCESS;
}
>Unfortunately ist not as simple as you may think.Oh, I assure you I don't think it's simple. I've had
a look at some of the code - I really don't remember
the details all that well...
Unless A is between phases of a two-phase commit - that is
isc_prepare_transaction has been called but isc_commit_transaction
has not returned success - losing a connection to any database
will cause all children to rollback.
There are two ways to handle a multi-database transactions.
One is to start it with a "start_multiple" call. In that
case, all you do is call commit. It notices that the
transaction is multi-database, and calls prepare on each
of the child transactions, creating an RDB$TRANSACTIONS
messages for each. GFIX (tdr.c) reads the messages and
fixes up the transaction states.
You can also start separate transactions from the client
application and when you're ready to commit, call prepare
on each one passing your own message. The system will
store a record in rdb$transactions for each database
containing your message. Then call commit on each one.
As the commit succeeds, it deletes the associated rdb$transactions
record. Should your program A discover an error, it should
attempt to start a new transaction on each client and read
the messages it left for itself.
Modules to look at include jrd/tra.c, alice/tdr.c, alice/met.e,
jrd why.c
Regards,
Ann
www.ibphoenix.com
We have answers.
This is the interesting code from why.c
static STATUS prepare (
STATUS *status,
TRA transaction)
{
/**************************************
*
* p r e p a r e
*
**************************************
*
* Functional description
* Perform the first phase of a two-phase commit
* for a multi-database transaction.
*
**************************************/
TRA sub;
UCHAR *p, *description;
#ifdef PC_PLATFORM
UCHAR tdr_buffer[256];
#else
UCHAR tdr_buffer[1024];
#endif
USHORT length = 0;
for (sub = transaction->next; sub; sub = sub->next)
length += 256;
description = (length > sizeof (tdr_buffer)) ? (UCHAR*) gds__alloc ((SLONG)
length) : tdr_buffer;
/* build a transaction description record containing
the host site and database/transaction
information for the target databases. */
if (!(p = description))
{
status [0] = isc_arg_gds;
status [1] = isc_virmemexh;
status [2] = isc_arg_end;
CHECK_STATUS (status);
return status [1];
}
*p++ = TDR_VERSION;
ISC_get_host (p + 2, length - 16);
*p++ = TDR_HOST_SITE;
*p = (UCHAR) strlen ((SCHAR*) p + 1);
while (*++p)
;
/* Get database and transaction stuff for each sub-transaction */
for (sub = transaction->next; sub; sub = sub->next)
{
get_database_info (status, sub, &p);
get_transaction_info (status, sub, &p);
}
/* So far so good -- prepare each sub-transaction */
length = p - description;
for (sub = transaction->next; sub; sub = sub->next)
if (CALL (PROC_PREPARE, sub->implementation)(
status,
&sub->handle,
length,
description))
{
if (description != tdr_buffer)
free_block (description);
CHECK_STATUS (status);
return status [1];
}
if (description != tdr_buffer)
free_block (description);
CHECK_STATUS_SUCCESS (status);
return SUCCESS;
}