fis-gtm/sr_port/mur_forward_play_cur_jrec.c

380 lines
14 KiB
C

/****************************************************************
* *
* Copyright 2010, 2012 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 <stddef.h> /* for offsetof() macro */
#include "gtm_time.h"
#include "gtm_string.h"
#include "min_max.h"
#ifdef VMS
#include <rms.h>
#include <devdef.h>
#include <ssdef.h>
#endif
#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 "mur_jnl_ext.h"
#include "iosp.h"
#include "gtmmsg.h"
#include "op.h"
#include "mu_gv_stack_init.h"
#include "targ_alloc.h"
#include "tp_change_reg.h"
#include "gvcst_protos.h" /* for gvcst_root_search prototype */
#include "tp_set_sgm.h"
#include "tp_frame.h"
#ifdef GTM_CRYPT
#include "gtmcrypt.h"
#endif
GBLREF gv_key *gv_currkey;
GBLREF gv_namehead *gv_target;
GBLREF gd_region *gv_cur_region;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF mur_gbls_t murgbl;
GBLREF mur_opt_struct mur_options;
GBLREF uint4 dollar_tlevel;
#ifdef DEBUG
GBLREF jnl_gbls_t jgbl;
#endif
error_def(ERR_DUPTN);
error_def(ERR_JNLTPNEST);
static void (* const extraction_routine[])() =
{
#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) extract_rtn,
#include "jnl_rec_table.h"
#undef JNL_TABLE_ENTRY
};
uint4 mur_forward_play_cur_jrec(reg_ctl_list *rctl)
{
boolean_t process_losttn;
boolean_t is_set_kill_zkill_ztrig_ztworm, is_set_kill_zkill_ztrig, added;
trans_num curr_tn;
enum jnl_record_type rectype;
enum rec_fence_type rec_fence;
enum broken_type recstat;
jnl_tm_t rec_time;
int4 rec_image_count = 0; /* This is a dummy variable for UNIX */
uint4 status;
mval mv;
seq_num rec_token_seq, rec_strm_seqno, resync_strm_seqno;
jnl_record *rec;
jnl_string *keystr;
multi_struct *multi;
jnl_ctl_list *jctl;
ht_ent_mname *tabent;
mname_entry gvent;
gvnh_reg_t *gvnh_reg;
GTMCRYPT_ONLY(
int4 crypt_status;
)
forw_multi_struct *forw_multi;
# if (defined(DEBUG) && defined(UNIX))
int4 strm_idx;
# endif
assert(!rctl->forw_eof_seen);
jctl = rctl->jctl;
/* Ensure we never DOUBLE process the same journal record in the forward phase */
assert((jctl != rctl->last_processed_jctl) || (jctl->rec_offset != rctl->last_processed_rec_offset));
DEBUG_ONLY(
rctl->last_processed_jctl = jctl;
rctl->last_processed_rec_offset = jctl->rec_offset;
)
rec = rctl->mur_desc->jnlrec;
rectype = (enum jnl_record_type)rec->prefix.jrec_type;
rec_time = rec->prefix.time;
assert(rec_time <= mur_options.before_time);
assert(rec_time >= mur_options.after_time);
assert((0 == mur_options.after_time) || mur_options.forward && !rctl->db_updated);
is_set_kill_zkill_ztrig_ztworm = (boolean_t)(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype));
if (is_set_kill_zkill_ztrig_ztworm)
{
keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node;
# ifdef GTM_CRYPT
if (jctl->jfh->is_encrypted)
{
DECODE_SET_KILL_ZKILL_ZTRIG(keystr, rec->prefix.forwptr, jctl->encr_key_handle, crypt_status);
if (0 != crypt_status)
{
GC_GTM_PUTMSG(crypt_status, NULL);
return crypt_status;
}
}
# endif
}
if (mur_options.selection && !mur_select_rec(jctl))
return SS_NORMAL;
rec_token_seq = (REC_HAS_TOKEN_SEQ(rectype)) ? GET_JNL_SEQNO(rec) : 0;
process_losttn = rctl->process_losttn;
if (!process_losttn && mur_options.rollback)
{
if (rec_token_seq >= murgbl.losttn_seqno)
process_losttn = rctl->process_losttn = TRUE;
# if (defined(UNIX) && defined(DEBUG))
if ((rec_token_seq < murgbl.losttn_seqno) && murgbl.resync_strm_seqno_nonzero && IS_REPLICATED(rectype))
{
assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype) || IS_COM(rectype) || (JRT_NULL == (rectype)));
assert(&rec->jrec_set_kill.strm_seqno == &rec->jrec_null.strm_seqno);
assert(&rec->jrec_set_kill.strm_seqno == &rec->jrec_tcom.strm_seqno);
rec_strm_seqno = GET_STRM_SEQNO(rec);
if (rec_strm_seqno)
{
strm_idx = GET_STRM_INDEX(rec_strm_seqno);
rec_strm_seqno = GET_STRM_SEQ60(rec_strm_seqno);
resync_strm_seqno = murgbl.resync_strm_seqno[strm_idx];
assert(!resync_strm_seqno || (rec_strm_seqno < resync_strm_seqno));
}
}
# endif
}
/* Note: Broken transaction determination is done below only based on the records that got selected as
* part of the mur_options.selection criteria. Therefore depending on whether a broken transaction gets
* selected or not, future complete transactions might either go to the lost transaction or extract file.
*/
recstat = process_losttn ? LOST_TN : GOOD_TN;
status = SS_NORMAL;
if (FENCE_NONE != mur_options.fences)
{
if (IS_FENCED(rectype))
{
assert(rec_token_seq);
DEBUG_ONLY(
/* assert that all TP records before min_broken_time are not broken */
if (IS_TP(rectype) &&
((!mur_options.rollback && rec_time < murgbl.min_broken_time) ||
(mur_options.rollback && rec_token_seq < murgbl.min_broken_seqno)))
{
VMS_ONLY(
MUR_GET_IMAGE_COUNT(jctl, rec, rec_image_count, status);
assert(SS_NORMAL == status);
)
rec_fence = GET_REC_FENCE_TYPE(rectype);
if (NULL != (multi = MUR_TOKEN_LOOKUP(rec_token_seq,
rec_image_count, rec_time, rec_fence)))
{
assert(0 == multi->partner);
assert(FALSE == multi->this_is_broken);
}
}
)
/* In most cases, the fact whether a TP tn is broken or not would have been determined already in
* mur_forward. In this case, rctl->forw_multi would be set appropriately. So use that to get to
* "multi" and avoid a hashtable lookup. If forw_multi is NULL (e.g. for ZTP or single-region TP),
* the hash-table lookup cannot be avoided.
*/
multi = NULL;
forw_multi = rctl->forw_multi;
if (NULL != forw_multi)
{
multi = forw_multi->multi;
/* Always honor the "recstat" from the forw_multi since that has been determined taking into
* consideration the BROKEN_TN status of ALL participating regions.
*/
assert((GOOD_TN != forw_multi->recstat) || (GOOD_TN == recstat));
recstat = forw_multi->recstat;
} else if (IS_REC_POSSIBLY_BROKEN(rec_time, rec_token_seq))
{
assert(!mur_options.rollback || process_losttn);
VMS_ONLY(
MUR_GET_IMAGE_COUNT(jctl, rec, rec_image_count, status);
if (SS_NORMAL != status)
return status;
)
rec_fence = GET_REC_FENCE_TYPE(rectype);
assert(rec_token_seq == ((struct_jrec_upd *)rec)->token_seq.token);
multi = MUR_TOKEN_LOOKUP(rec_token_seq, rec_image_count, rec_time, rec_fence);
if ((NULL != multi) && (0 < multi->partner))
{
process_losttn = rctl->process_losttn = TRUE;
recstat = BROKEN_TN;
}
}
/* Check that if the hashtable reports a tn as GOOD, it better have had the same
* # of participants in the TCOM records across all the participating regions.
*/
assert((NULL == multi) || (BROKEN_TN == recstat) || (FALSE == multi->this_is_broken));
} else if ((FENCE_ALWAYS == mur_options.fences) && is_set_kill_zkill_ztrig_ztworm)
{
process_losttn = rctl->process_losttn = TRUE;
recstat = BROKEN_TN;
}
} else
forw_multi = NULL;
if (mur_options.show)
{
assert(SS_NORMAL == status);
if (BROKEN_TN != recstat)
{
if (JRT_PFIN == rectype)
status = mur_pini_state(jctl, rec->prefix.pini_addr, FINISHED_PROC);
else if ((JRT_EOF != rectype)
&& ((JRT_ALIGN != rectype) || (JNL_HDR_LEN != rec->prefix.pini_addr)))
{ /* Note that it is possible that we have a PINI record followed by a PFIN record
* and later an ALIGN record with the pini_addr pointing to the original PINI
* record (see comment in jnl_write.c where pini_addr gets assigned to JNL_HDR_LEN)
* In this case we do not want the ALIGN record to cause the process to become
* ACTIVE although it has written a PFIN record. Hence the check above.
*/
status = mur_pini_state(jctl, rec->prefix.pini_addr, ACTIVE_PROC);
}
} else
status = mur_pini_state(jctl, rec->prefix.pini_addr, BROKEN_PROC);
if (SS_NORMAL != status)
return status; /* "mur_pini_state" failed due to bad pini_addr */
++jctl->jnlrec_cnt[rectype]; /* for -show=STATISTICS */
}
if (!mur_options.update && !mur_options.extr[GOOD_TN])
return SS_NORMAL;
if (murgbl.ok_to_update_db && IS_TUPD(rectype) && (GOOD_TN == recstat))
{ /* Even for FENCE_NONE we apply fences. Otherwise a TUPD becomes UPD etc.
* If forw_multi is non-NULL, a multi-region TP transaction is being played as a SINGLE
* TP transaction across all the involved regions. Therefore only ONE op_tstart is done
* even though more than one TSET might be encountered. In this case, do not issue JNLTPNEST error.
*/
if (dollar_tlevel && (NULL == forw_multi))
{
assert(FALSE);
murgbl.wrn_count++;
gtm_putmsg(VARLSTCNT(6) ERR_JNLTPNEST, 4, jctl->jnl_fn_len,
jctl->jnl_fn, jctl->rec_offset, &rec->prefix.tn);
OP_TROLLBACK(0);
}
if (!dollar_tlevel)
{ /* Note: op_tstart resets gv_currkey. So set gv_currkey later. */
/* mv is used to determine transaction id. But it is ignored by recover/rollback */
mv.mvtype = MV_STR;
mv.str.len = 0;
mv.str.addr = NULL;
op_tstart(IMPLICIT_TSTART, TRUE, &mv, -1);
DEBUG_ONLY(jgbl.max_tp_ztp_jnl_upd_num = 0;)
}
tp_set_sgm(); /* needed to set "sgm_info_ptr" to correspond to "rctl" */
}
/* For extract, if database was present we would have done gvcst_init().
* For recover/rollback gvcst_init() should definitely have been done.
* In both cases rctl->gd->open will be non-NULL. Note that rctl->csa could be non-NULL
* (set in mur_forward) even if rctl->gd->open is non-NULL. So dont use that.
* Only then can we call gvcst_root_search() to find out collation set up for this global.
*/
assert(gv_cur_region == rctl->gd);
assert(!mur_options.update || (gv_cur_region->open && (NULL != rctl->csa)));
is_set_kill_zkill_ztrig = (boolean_t)(IS_SET_KILL_ZKILL_ZTRIG(rectype));
if (is_set_kill_zkill_ztrig)
{
assert(NULL != keystr);
memcpy(gv_currkey->base, &keystr->text[0], keystr->length);
gv_currkey->base[keystr->length] = '\0';
gv_currkey->end = keystr->length;
if (gv_cur_region->open)
{/* find out collation of key in the jnl-record from the database corresponding to the jnl file */
gvent.var_name.addr = (char *)gv_currkey->base;
gvent.var_name.len = STRLEN((char *)gv_currkey->base);
COMPUTE_HASH_MNAME(&gvent);
if ((NULL != (tabent = lookup_hashtab_mname(&rctl->gvntab, &gvent)))
&& (NULL != (gvnh_reg = (gvnh_reg_t *)tabent->value)))
{
gv_target = gvnh_reg->gvt;
gv_cur_region = gvnh_reg->gd_reg;
assert(gv_cur_region->open);
} else
{
assert(gv_cur_region->max_key_size <= MAX_KEY_SZ);
gv_target = (gv_namehead *)targ_alloc(gv_cur_region->max_key_size,
&gvent, gv_cur_region);
gvnh_reg = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t));
gvnh_reg->gvt = gv_target;
gvnh_reg->gd_reg = gv_cur_region;
if (NULL != tabent)
{ /* Since the global name was found but gv_target was null and
* now we created a new gv_target, the hash table key must point
* to the newly created gv_target->gvname. */
tabent->key = gv_target->gvname;
tabent->value = (char *)gvnh_reg;
} else
{
added = add_hashtab_mname(&rctl->gvntab, &gv_target->gvname,
gvnh_reg, &tabent);
assert(added);
}
}
GVCST_ROOT_SEARCH;
}
}
if (GOOD_TN == recstat)
{
if ((is_set_kill_zkill_ztrig_ztworm && !IS_TP(rectype)) || JRT_TCOM == rectype)
{
/* Do forward journaling, detecting operations with duplicate transaction numbers.
* While doing journaling on a database, a process may be killed immediately after
* updating (or partially updating) the journal file, but before the database gets
* updated. Since the transaction was never fully committed, the database
* transaction number has not been updated, and the last journal record does not
* reflect the actual state of the database. The next process to update the
* database writes a journal record with the same transaction number as the
* previous record. While processing the journal file, we must recognize this and
* issue a DUPTN warning so the user knows this was encountered during the recovery.
*/
curr_tn = rec->prefix.tn;
if (rctl->last_tn == curr_tn)
{
assert(FALSE); /* We want to debug this */
murgbl.wrn_count++;
gtm_putmsg(VARLSTCNT(6) ERR_DUPTN, 4, &curr_tn, jctl->rec_offset, jctl->jnl_fn_len, jctl->jnl_fn);
if (dollar_tlevel)
{
assert(murgbl.ok_to_update_db);
OP_TROLLBACK(0);
}
}
rctl->last_tn = curr_tn;
}
if (murgbl.ok_to_update_db)
{
assert(!mur_options.rollback || (rec_token_seq < murgbl.losttn_seqno));
if (SS_NORMAL != (status = mur_output_record(rctl))) /* updates murgbl.consist_jnl_seqno */
return status;
assert(!mur_options.rollback || (murgbl.consist_jnl_seqno <= murgbl.losttn_seqno));
}
}
if (GOOD_TN != recstat || mur_options.extr[GOOD_TN])
{
if (murgbl.extr_file_create[recstat])
{
if (SS_NORMAL != (status = mur_cre_file_extfmt(jctl, recstat)))
return status;
murgbl.extr_file_create[recstat] = FALSE;
}
/* extract "rec" using routine "extraction_routine[rectype]" into broken transaction file */
EXTRACT_JNLREC(jctl, rec, extraction_routine[rectype], murgbl.file_info[recstat], status);
if (SS_NORMAL != status)
return status;
}
return SS_NORMAL;
}