fis-gtm/sr_port/mur_forward_play_multireg_tp.c

202 lines
7.4 KiB
C

/****************************************************************
* *
* Copyright 2010, 2011 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_time.h"
#include "gtm_string.h"
#include "gdsroot.h"
#include "gdsbt.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "buddy_list.h"
#include "hashtab_int4.h" /* needed for muprec.h */
#include "hashtab_int8.h" /* needed for muprec.h */
#include "hashtab_mname.h" /* needed for muprec.h */
#include "muprec.h"
#include "gdskill.h" /* needed for tp.h */
#include "gdscc.h" /* needed for tp.h */
#include "tp.h"
#include "iosp.h"
#include "tp_change_reg.h"
#include "op_tcommit.h"
GBLREF mur_gbls_t murgbl;
GBLREF mur_opt_struct mur_options;
GBLREF uint4 dollar_tlevel;
GBLREF struct_jrec_tcom tcom_record;
GBLREF jnl_gbls_t jgbl;
GBLREF jnl_fence_control jnl_fence_ctl;
/* Now that we are ready to apply the multi-region TP transaction, check the most recent "forw_multi->recstat" status
* and propagate that to all the participating "rctl"s in the first_tp_rctl list. Use the following macro to implement that.
*/
#define SET_RCTL_PROCESS_LOSTTN_IF_NEEDED(FORW_MULTI, RCTL) \
{ \
assert(!rctl->forw_eof_seen); \
if (GOOD_TN != FORW_MULTI->recstat) \
RCTL->process_losttn = TRUE; \
}
uint4 mur_forward_play_multireg_tp(forw_multi_struct *forw_multi, reg_ctl_list *rctl)
{
enum jnl_record_type rectype;
enum broken_type recstat;
jnl_tm_t rec_time;
uint4 status;
seq_num rec_token_seq;
jnl_record *rec;
jnl_ctl_list *jctl;
reg_ctl_list *save_rctl, *next_rctl;
uint4 num_tcoms, num_participants;
boolean_t tcom_played, first_tcom, deleted;
ht_ent_int8 *tabent;
forw_multi_struct *cur_forw_multi, *prev_forw_multi;
error_def(ERR_JNLREADEOF);
save_rctl = rctl; /* save input "rctl" (needed at end) */
assert(!save_rctl->forw_eof_seen);
assert(1 < forw_multi->num_reg_total); /* should not have come here for single-region TP transactions */
rec_token_seq = forw_multi->token;
recstat = forw_multi->recstat;
if (mur_options.rollback && (rec_token_seq >= murgbl.losttn_seqno) && (GOOD_TN == recstat))
recstat = forw_multi->recstat = LOST_TN;
next_rctl = forw_multi->first_tp_rctl;
assert(NULL != next_rctl);
num_tcoms = 0;
first_tcom = TRUE;
assert(!dollar_tlevel);
DEBUG_ONLY(num_participants = (uint4)-1;) /* a very high value to indicate uninitialized state */
do
{
rctl = next_rctl;
next_rctl = rctl->next_tp_rctl;
assert(NULL != next_rctl);
SET_RCTL_PROCESS_LOSTTN_IF_NEEDED(forw_multi, rctl);
assert(!rctl->forw_eof_seen);
assert(num_tcoms < num_participants);
MUR_CHANGE_REG(rctl);
jctl = rctl->jctl;
tcom_played = FALSE;
for (status = SS_NORMAL; SS_NORMAL == status; status = mur_next_rec(&jctl))
{
if (tcom_played)
break;
rec = rctl->mur_desc->jnlrec;
rectype = (enum jnl_record_type)rec->prefix.jrec_type;
rec_time = rec->prefix.time;
if ((BROKEN_TN == recstat) && (JRT_ALIGN != rectype))
{ /* Check if current record is not a TP transaction (only exception is an ALIGN record which
* could show up in the MIDDLE of a TP transaction) or has a <token,time> that is different.
* If so we have come to beginning of NEXT transaction. Break in this case (this function
* should play only records of the current multi-region TP transaction. In case of GOOD_TN
* recstat, we are guaranteed to see a TCOM record before seeing the NEXT transaction.
*/
if (!IS_TP(rectype) || (rec_time != forw_multi->time) || (rec_token_seq != GET_JNL_SEQNO(rec)))
break;
}
assert(IS_TP(rectype) || (JRT_ALIGN == rectype));
# ifdef DEBUG
if (IS_TP(rectype))
{
assert(REC_HAS_TOKEN_SEQ(rectype));
assert(rec_token_seq == GET_JNL_SEQNO(rec));
}
# endif
status = mur_forward_play_cur_jrec(rctl);
if (SS_NORMAL != status)
return status;
assert(!murgbl.ok_to_update_db || dollar_tlevel || (GOOD_TN != recstat));
assert(!murgbl.ok_to_update_db || !dollar_tlevel || (GOOD_TN == recstat));
if (IS_COM(rectype))
{
if (first_tcom)
{
num_participants = rec->jrec_tcom.num_participants;
first_tcom = FALSE;
}
assert(rec->jrec_tcom.num_participants == num_participants);
num_tcoms++;
if ((num_tcoms == num_participants) && murgbl.ok_to_update_db && (GOOD_TN == recstat))
{ /* TCOM record of LAST region. Do actual transaction commit */
jnl_fence_ctl.token = rec_token_seq;
jgbl.mur_jrec_participants = rec->jrec_tcom.num_participants;
memcpy(tcom_record.jnl_tid, rec->jrec_tcom.jnl_tid, TID_STR_SIZE);
assert(jnl_fence_ctl.token == rec->jrec_tcom.token_seq.token);
assert(dollar_tlevel);
op_tcommit();
assert(!dollar_tlevel);
}
tcom_played = TRUE;
MUR_FORW_TOKEN_REMOVE(rctl);
}
}
CHECK_IF_EOF_REACHED(rctl, status); /* sets rctl->forw_eof_seen if needed; resets "status" to SS_NORMAL */
if (SS_NORMAL != status)
return status;
if (rctl->forw_eof_seen)
{
DELETE_RCTL_FROM_UNPROCESSED_LIST(rctl);
if (NULL != rctl->forw_multi)
{ /* Possible if we did not see TCOM. But has to be a BROKEN tn in that case.
* Treat this region as having completed token processing.
*/
assert(BROKEN_TN == recstat);
MUR_FORW_TOKEN_REMOVE(rctl);
}
}
} while (next_rctl != rctl);
assert((num_tcoms == num_participants) || (BROKEN_TN == recstat));
assert(!dollar_tlevel);
/* Now that the multi-region "forw_multi" structure is processed, it can be freed. Along with it, the corresponding
* hashtable entry can be freed as well as long as there are no other same-token "forw_multi" structures.
*/
tabent = forw_multi->u.tabent;
assert(NULL != tabent);
/* "tabent" should have been set before coming into this function. But make sure it is still correct
* (i.e. no other hash table expansions occurred in between).
*/
assert(tabent == lookup_hashtab_int8(&murgbl.forw_token_table, (gtm_uint64_t *)&rec_token_seq));
if ((tabent->value == forw_multi) && (NULL == forw_multi->next))
{ /* forw_multi is the ONLY element in the linked list so it is safe to delete the hashtable entry itself */
deleted = delete_hashtab_int8(&murgbl.forw_token_table, &forw_multi->token);
assert(deleted);
} else
{ /* delete "forw_multi" from the singly linked list */
prev_forw_multi = NULL;
cur_forw_multi = tabent->value;
for ( ; (NULL != cur_forw_multi); prev_forw_multi = cur_forw_multi, cur_forw_multi = cur_forw_multi->next)
{
if (cur_forw_multi == forw_multi)
{
assert(prev_forw_multi != forw_multi);
if (NULL == prev_forw_multi)
tabent->value = cur_forw_multi->next;
else
prev_forw_multi->next = cur_forw_multi->next;
break;
}
}
assert(NULL != cur_forw_multi);
}
free_element(murgbl.forw_multi_list, (char *)forw_multi);
MUR_CHANGE_REG(save_rctl); /* switch to region corresponding to input "rctl" before returning as caller relies on this */
assert(!dollar_tlevel);
assert(!save_rctl->forw_eof_seen || save_rctl->deleted_from_unprocessed_list);
return (!save_rctl->forw_eof_seen ? SS_NORMAL : ERR_JNLREADEOF);
}