fis-gtm/sr_unix/gtmrecv_process.c

3583 lines
164 KiB
C
Raw Normal View History

/****************************************************************
* *
* Copyright 2006, 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. *
* *
****************************************************************/
#if defined(__MVS__) && !defined(_ISOC99_SOURCE)
#define _ISOC99_SOURCE
#endif
#include "mdef.h"
#include "gtm_socket.h"
#include "gtm_inet.h"
#include "gtm_time.h"
#include "gtm_fcntl.h"
#include "gtm_unistd.h"
#include "gtm_string.h"
#include "gtm_stdio.h" /* for FILE * in repl_comm.h */
#include <sys/time.h>
#include <errno.h>
#ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT
#include <sys/poll.h>
#endif
#ifdef VMS
#include <descrip.h> /* Required for gtmrecv.h */
#endif
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gtmrecv.h"
#include "repl_comm.h"
#include "repl_msg.h"
#include "repl_dbg.h"
#include "repl_errno.h"
#include "iosp.h"
#include "gtm_event_log.h"
#include "eintr_wrappers.h"
#include "jnl.h"
#include "repl_sp.h"
#include "repl_filter.h"
#include "repl_log.h"
#include "gtmsource.h"
#include "sgtm_putmsg.h"
#include "gt_timer.h"
#include "min_max.h"
#include "error.h"
#include "copy.h"
#include "repl_instance.h"
#include "ftok_sems.h"
#include "buddy_list.h" /* needed for muprec.h */
#include "hashtab_mname.h" /* needed for muprec.h */
#include "hashtab_int4.h" /* needed for muprec.h */
#include "hashtab_int8.h" /* needed for muprec.h */
#include "muprec.h"
#include "gtmmsg.h"
#include "is_proc_alive.h"
#include "jnl_typedef.h"
#include "iotcpdef.h"
#include "memcoherency.h"
#include "have_crit.h" /* needed for ZLIB_UNCOMPRESS */
#include "deferred_signal_handler.h" /* needed for ZLIB_UNCOMPRESS */
#include "gtm_zlib.h"
#include "wbox_test_init.h"
#ifdef GTM_TRIGGER
#include "repl_sort_tr_buff.h"
#endif
#include "replgbl.h"
#include "gtmio.h"
#include "repl_inst_dump.h" /* for "repl_dump_histinfo" prototype */
#include "gv_trigger_common.h"
#include "anticipatory_freeze.h"
#define GTM_ZLIB_UNCMP_ERR_STR "error from zlib uncompress function "
#define GTM_ZLIB_Z_MEM_ERROR_STR "Out-of-memory " GTM_ZLIB_UNCMP_ERR_STR
#define GTM_ZLIB_Z_BUF_ERROR_STR "Insufficient output buffer " GTM_ZLIB_UNCMP_ERR_STR
#define GTM_ZLIB_Z_DATA_ERROR_STR "Input-data-incomplete-or-corrupt " GTM_ZLIB_UNCMP_ERR_STR
#define GTM_ZLIB_UNCMPLEN_ERROR_STR "Decompressed message data length %d is not equal to precompressed length %d "
#define GTM_ZLIB_UNCMP_ERR_SEQNO_STR "at seqno "INT8_FMT" "INT8_FMTX"\n"
#define GTM_ZLIB_UNCMP_ERR_SOLVE_STR "before sending REPL_CMP_SOLVE message\n"
#define GTM_ZLIB_UNCMPTRANSITION_STR "Defaulting to NO decompression\n"
#define RECVBUFF_REPLMSGLEN_FACTOR 8
#define GTMRECV_POLL_INTERVAL (1000000 - 1)/* micro sec, almost 1 sec */
#define MAX_GTMRECV_POLL_INTERVAL 1000000 /* 1 sec in micro sec */
#define GTMRECV_WAIT_FOR_STARTJNLSEQNO 100 /* ms */
#define GTMRECV_WAIT_FOR_UPD_PROGRESS 100 /* ms */
#define GTMRECV_WAIT_FOR_UPD_PROGRESS_US (GTMRECV_WAIT_FOR_UPD_PROGRESS * 1000) /* micro sec */
/* By having different high and low watermarks, we can reduce the # of XOFF/XON exchanges */
#define RECVPOOL_HIGH_WATERMARK_PCTG 90 /* Send XOFF when %age of receive pool space occupied goes beyond this */
#define RECVPOOL_LOW_WATERMARK_PCTG 80 /* Send XON when %age of receive pool space occupied falls below this */
#define RECVPOOL_XON_TRIGGER_SIZE (1 * 1024 * 1024) /* Keep the low water mark within this amount of high water mark
* so that we don't wait too long to send XON */
#define GTMRECV_XOFF_LOG_CNT 100
#define GTMRECV_HEARTBEAT_PERIOD 10 /* seconds, timer that goes off every this period is the time keeper for
* receiver server; used to reduce calls to time related systemc calls */
#define ONLN_RLBK_CMD_MAXLEN 1024
#define MUPIP_DIST_STR "$gtm_dist/mupip "
#define ONLN_RLBK_CMD "journal "
#define ONLN_RLBK_VERBOSE "-verbose "
#define ONLN_RLBK_QUALIFIERS "-online -rollback -backward \"*\" -fetchresync=" /* port# will be filled later */
GBLDEF repl_msg_ptr_t gtmrecv_msgp;
GBLDEF int gtmrecv_max_repl_msglen;
GBLDEF struct timeval gtmrecv_poll_interval, gtmrecv_poll_immediate;
GBLDEF int gtmrecv_sock_fd = FD_INVALID;
GBLDEF boolean_t repl_connection_reset = TRUE;
GBLDEF boolean_t gtmrecv_wait_for_jnl_seqno = FALSE;
GBLDEF boolean_t gtmrecv_bad_trans_sent = FALSE;
GBLDEF boolean_t gtmrecv_send_cmp2uncmp = FALSE;
GBLDEF struct sockaddr_in primary_addr;
GBLDEF qw_num repl_recv_data_recvd = 0;
GBLDEF qw_num repl_recv_data_processed = 0;
GBLDEF qw_num repl_recv_postfltr_data_procd = 0;
GBLDEF qw_num repl_recv_lastlog_data_recvd = 0;
GBLDEF qw_num repl_recv_lastlog_data_procd = 0;
GBLDEF time_t repl_recv_prev_log_time;
GBLDEF time_t repl_recv_this_log_time;
GBLDEF volatile time_t gtmrecv_now = 0;
STATICDEF uchar_ptr_t gtmrecv_cmpmsgp;
STATICDEF int gtmrecv_cur_cmpmsglen;
STATICDEF int gtmrecv_max_repl_cmpmsglen;
STATICDEF uchar_ptr_t gtmrecv_uncmpmsgp;
STATICDEF int gtmrecv_max_repl_uncmpmsglen;
STATICDEF int gtmrecv_repl_cmpmsglen;
STATICDEF int gtmrecv_repl_uncmpmsglen;
STATICFNDCL void gtmrecv_repl_send_loop_error(int status, char *msgtypestr);
STATICFNDCL int repl_tr_endian_convert(unsigned char remote_jnl_ver, uchar_ptr_t jnl_buff, uint4 jnl_len);
STATICFNDCL void do_flow_control(uint4 write_pos);
STATICFNDCL int gtmrecv_est_conn(void);
STATICFNDCL int gtmrecv_start_onln_rlbk(void);
STATICFNDCL void prepare_recvpool_for_write(int datalen, int pre_filter_write_len);
STATICFNDCL void copy_to_recvpool(uchar_ptr_t databuff, int datalen);
STATICFNDCL void wait_for_updproc_to_clear_backlog(void);
STATICFNDCL void process_tr_buff(int msg_type);
STATICFNDCL void gtmrecv_updresync_histinfo_find_seqno(seq_num input_seqno, int4 strm_num, repl_histinfo *histinfo);
STATICFNDCL void gtmrecv_updresync_histinfo_get(int4 index, repl_histinfo *histinfo);
STATICFNDCL void gtmrecv_process_need_strminfo_msg(repl_needstrminfo_msg_ptr_t need_strminfo_msg);
STATICFNDCL void gtmrecv_process_need_histinfo_msg(repl_needhistinfo_msg_ptr_t need_histinfo_msg, repl_histinfo *histinfo);
STATICFNDCL void do_main_loop(boolean_t crash_restart);
STATICFNDCL void gtmrecv_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr);
STATICFNDCL void gtmrecv_main_loop(boolean_t crash_restart);
GBLREF gtmrecv_options_t gtmrecv_options;
GBLREF int gtmrecv_listen_sock_fd;
GBLREF recvpool_addrs recvpool;
GBLREF boolean_t gtmrecv_logstats;
GBLREF int gtmrecv_filter;
GBLREF int gtmrecv_log_fd;
GBLREF FILE *gtmrecv_log_fp;
GBLREF seq_num seq_num_zero, seq_num_one, seq_num_minus_one;
GBLREF unsigned char *repl_filter_buff;
GBLREF int repl_filter_bufsiz;
GBLREF unsigned int jnl_source_datalen, jnl_dest_maxdatalen;
GBLREF unsigned char jnl_source_rectype, jnl_dest_maxrectype;
GBLREF int repl_max_send_buffsize, repl_max_recv_buffsize;
GBLREF seq_num lastlog_seqno;
GBLREF uint4 log_interval;
GBLREF qw_num trans_recvd_cnt, last_log_tr_recvd_cnt;
GBLREF jnlpool_addrs jnlpool;
GBLREF jnlpool_ctl_ptr_t jnlpool_ctl;
GBLREF jnl_gbls_t jgbl;
GBLREF mur_opt_struct mur_options;
GBLREF mur_gbls_t murgbl;
GBLREF repl_conn_info_t *this_side, *remote_side;
GBLREF int4 strm_index;
error_def(ERR_INSNOTJOINED);
error_def(ERR_INSROLECHANGE);
error_def(ERR_INSUNKNOWN);
error_def(ERR_JNLNEWREC);
error_def(ERR_JNLRECFMT);
error_def(ERR_JNLSETDATA2LONG);
error_def(ERR_NOSUPPLSUPPL);
error_def(ERR_PRIMARYNOTROOT);
error_def(ERR_RCVRMANYSTRMS);
error_def(ERR_REPL2OLD);
error_def(ERR_REPLCOMM);
error_def(ERR_REPLGBL2LONG);
error_def(ERR_REPLINSTNOHIST);
error_def(ERR_REPLINSTREAD);
error_def(ERR_REPLTRANS2BIG);
error_def(ERR_REPLXENDIANFAIL);
error_def(ERR_RESUMESTRMNUM);
error_def(ERR_REUSEINSTNAME);
error_def(ERR_SECNODZTRIGINTP);
error_def(ERR_SECONDAHEAD);
error_def(ERR_STRMNUMIS);
error_def(ERR_SUPRCVRNEEDSSUPSRC);
error_def(ERR_TEXT);
error_def(ERR_UNIMPLOP);
error_def(ERR_UPDSYNCINSTFILE);
typedef enum
{
GTM_RECV_POOL,
GTM_RECV_CMPBUFF
} gtmrecv_buff_t;
static unsigned char *buffp, *buff_start, *msgbuff, *filterbuff;
static int buff_unprocessed;
static int buffered_data_len;
static int max_recv_bufsiz;
static int data_len;
static int exp_data_len;
static boolean_t xoff_sent;
static repl_msg_t xon_msg, xoff_msg;
static int xoff_msg_log_cnt = 0;
static long recvpool_high_watermark, recvpool_low_watermark;
static uint4 write_loc, write_wrap;
static uint4 write_off;
static double time_elapsed;
static int recvpool_size;
static int heartbeat_period;
#ifdef REPL_CMP_SOLVE_TESTING
static boolean_t repl_cmp_solve_timer_set;
#endif
#define GTMRECV_EXPAND_CMPBUFF_IF_NEEDED(cmpmsglen) \
{ \
int lclcmpmsglen; \
\
lclcmpmsglen = ROUND_UP2(cmpmsglen, REPL_MSG_ALIGN); \
if (lclcmpmsglen > gtmrecv_max_repl_cmpmsglen) \
{ \
if (NULL != gtmrecv_cmpmsgp) \
free(gtmrecv_cmpmsgp); \
gtmrecv_cmpmsgp = (uchar_ptr_t)malloc(lclcmpmsglen); \
gtmrecv_max_repl_cmpmsglen = (lclcmpmsglen); \
} \
assert(0 == (gtmrecv_max_repl_cmpmsglen % REPL_MSG_ALIGN)); \
}
#define GTMRECV_EXPAND_UNCMPBUFF_IF_NEEDED(uncmpmsglen) \
{ \
if (uncmpmsglen > gtmrecv_max_repl_uncmpmsglen) \
{ \
if (NULL != gtmrecv_uncmpmsgp) \
free(gtmrecv_uncmpmsgp); \
gtmrecv_uncmpmsgp = (uchar_ptr_t)malloc(uncmpmsglen); \
gtmrecv_max_repl_uncmpmsglen = (uncmpmsglen); \
} \
assert(0 == (gtmrecv_max_repl_uncmpmsglen % REPL_MSG_ALIGN)); \
}
/* Set an alternate buffer as the target to hold the incoming compressed journal records.
* This will then be uncompressed into yet another buffer from where the records will be
* transferred to the receive pool one transaction at a time.
*/
#define GTMRECV_SET_BUFF_TARGET_CMPBUFF(cmplen, uncmplen, curcmplen) \
{ \
GTMRECV_EXPAND_CMPBUFF_IF_NEEDED(cmplen); \
GTMRECV_EXPAND_UNCMPBUFF_IF_NEEDED(uncmplen); \
curcmplen = 0; \
}
/* Wrap the "prepare_recvpool_for_write", "copy_to_recvpool", "do_flow_control" and "gtmrecv_poll_actions" functions in macros.
* This is needed because every invocation of these functions (except for a few gtmrecv_poll_actions invocations) should check
* a few global variables to determine if there was an error and in that case return from the caller.
* All callers of these functions should use the macro and not invoke the function directly.
* This avoids duplication of the error check logic.
*/
#define PREPARE_RECVPOOL_FOR_WRITE(curdatalen, prefilterdatalen) \
{ \
prepare_recvpool_for_write(curdatalen, prefilterdatalen); \
/* could update "recvpool_ctl->write" and "write_loc" */ \
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \
return; \
}
#define COPY_TO_RECVPOOL(ptr, datalen) \
{ \
copy_to_recvpool(ptr, datalen); /* uses and updates "write_loc" */ \
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \
return; \
}
#define DO_FLOW_CONTROL(write_loc) \
{ \
do_flow_control(write_loc); \
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \
return; \
}
#define GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp) \
{ \
gtmrecv_poll_actions(data_len, buff_unprocessed, buffp); \
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \
return; \
}
/* The below macro is used (within this module) to check for errors (and issue appropriate message) after REPL_SEND_LOOP
* returns.
* NOTE: This macro, in its current form, cannot be used in all REPL_SEND_LOOP usages due to specific post-error processing
* done in a few places. There is scope to:
* (a) Improve the macro to adjust to post-error processing
* (b) Provide similar macro to be used for REPL_RECV_LOOP usages
*/
#define CHECK_REPL_SEND_LOOP_ERROR(status, msgstr) \
{ \
if (SS_NORMAL != status) \
{ \
gtmrecv_repl_send_loop_error(status, msgstr); \
if (repl_connection_reset) \
return; \
} \
}
#define GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED \
{ \
sgmnt_addrs *repl_csa; \
\
repl_csa = &FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs; \
assert(repl_csa->now_crit); \
assert(jnlpool.jnlpool_ctl == jnlpool_ctl); \
if (repl_csa->onln_rlbk_cycle != jnlpool_ctl->onln_rlbk_cycle) \
{ \
SYNC_ONLN_RLBK_CYCLES; \
rel_lock(jnlpool.jnlpool_dummy_reg); \
gtmrecv_onln_rlbk_clnup(); \
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno) \
return; \
} \
}
/* For cross-endian conversion to happen on the receiving side, the receiver must understand the layout of the journal
* records. To keep the endian conversion logic on both primary and secondary simple, the following scheme is used:
* (a) If primary < secondary, endian conversion will happen on primary.
* (b) If primary >= secondary, primary will apply internal filters to convert the records to secondary's format. The
* secondary on receiving them will do the necessary endian conversion before letting the update process see them.
*
* However, the above logic will cause the older versions (< V5.4-002) to NOT replicate to V5.4-002 as the endian-conversion
* by-primary is introduced only from V5.4-002 and above. Hence, allow secondary to do endian conversion for this special
* case when the primary is a GT.M version running V5.3-003 (V18_JNL_VER) to V5.4-001 (V20_JNL_VER). The lower limit is
* chosen to be V5.3-003 since that was the first version where cross-endian conversion was supported.
*
* There is one other exception. V5.5 source server (V22_JNL_VER) had a bug wherein a history record is endian-converted
* when the replication is NOT cross-endian and vice versa. In either case, do an endian conversion of the history record.
*
* The below macro takes all the above conditions into consideration to determine if the receiver server needs to do endian
* converison or not.
*/
#define ENDIAN_CONVERSION_NEEDED(IS_NEW_HISTREC, THIS_JNL_VER, REMOTE_JNL_VER, X_ENDIAN) \
((IS_NEW_HISTREC && (V22_JNL_VER == REMOTE_JNL_VER)) \
|| (X_ENDIAN && ((REMOTE_JNL_VER >= THIS_JNL_VER) || (V21_JNL_VER > REMOTE_JNL_VER))))
STATICFNDEF void gtmrecv_repl_send_loop_error(int status, char *msgtypestr)
{
char print_msg[1024];
assert((EREPL_SEND == repl_errno) || (EREPL_SELECT == repl_errno));
if (REPL_CONN_RESET(status) && EREPL_SEND == repl_errno)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection got reset while sending %s message. Status = %d ; %s\n",
msgtypestr, status, STRERROR(status));
repl_connection_reset = TRUE;
repl_close(&gtmrecv_sock_fd);
SNPRINTF(print_msg, SIZEOF(print_msg), "Closing connection on receiver side\n");
repl_log(gtmrecv_log_fp, TRUE, TRUE, print_msg);
gtm_event_log(GTM_EVENT_LOG_ARGC, "MUPIP", "ERR_REPLWARN", print_msg);
return;
} else if (EREPL_SEND == repl_errno)
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error sending %s message. Error in send : %s",
msgtypestr, STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
} else if (EREPL_SELECT == repl_errno)
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error sending %s message. Error in select : %s",
msgtypestr, STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
/* convert endianness of transaction */
STATICFNDEF int repl_tr_endian_convert(unsigned char remote_jnl_ver, uchar_ptr_t jnl_buff, uint4 jnl_len)
{
unsigned char *jb, *jstart, *ptr;
enum jnl_record_type rectype;
int status, reclen;
uint4 jlen;
jrec_prefix *prefix;
jnl_record *rec;
jnl_string *keystr;
mstr_len_t *vallen_ptr;
mstr_len_t temp_val;
repl_old_triple_jnl_ptr_t oldtriple;
repl_histinfo *histinfo;
jnl_str_len_t nodeflags_keylen;
uint4 update_num, num_participants_4bytes;
unsigned short num_participants_2bytes;
# ifdef DEBUG
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
# endif
assert((remote_jnl_ver >= this_side->jnl_ver) || (V21_JNL_VER > remote_jnl_ver) || (V22_JNL_VER == remote_jnl_ver));
jb = jnl_buff;
status = SS_NORMAL;
jlen = jnl_len;
assert(0 == ((UINTPTR_T)jb % SIZEOF(UINTPTR_T)));
while (JREC_PREFIX_SIZE <= jlen)
{
assert(0 == ((UINTPTR_T)jb % SIZEOF(UINTPTR_T)));
rec = (jnl_record *) jb;
rectype = (enum jnl_record_type)rec->prefix.jrec_type;
reclen = rec->prefix.forwptr = GTM_BYTESWAP_24(rec->prefix.forwptr);
if (!IS_REPLICATED(rectype) || (0 == reclen) || (reclen > jlen))
{ /* Bad OR Incomplete record */
assert(FALSE);
status = -1;
break;
}
assert(!IS_ZTP(rectype));
assert((JRT_HISTREC == rectype) || (JRT_TRIPLE == rectype) || IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)
|| (JRT_TCOM == rectype) || (JRT_NULL == rectype));
DEBUG_ONLY(jstart = jb;)
if (JRT_HISTREC == rectype)
{
histinfo = &(((repl_histrec_jnl_ptr_t)rec)->histcontent);
ENDIAN_CONVERT_REPL_HISTINFO(histinfo);
} else if (JRT_TRIPLE == rectype)
{
oldtriple = (repl_old_triple_jnl_ptr_t)rec;
oldtriple->cycle = GTM_BYTESWAP_32(oldtriple->cycle);
oldtriple->start_seqno = GTM_BYTESWAP_64(oldtriple->start_seqno);
} else
{ /* pini_addr, time, checksum and tn field of the journal records created by the source server are
* irrelevant to the receiver server and hence no point doing the endian conversion for them.
*/
((jrec_suffix *)((unsigned char *)rec + reclen - JREC_SUFFIX_SIZE))->backptr = reclen;
rec->jrec_null.jnl_seqno = GTM_BYTESWAP_64(rec->jrec_null.jnl_seqno);
/* Starting jnl ver V22, we have a "strm_seqno" field in the journal record so endian convert that */
if (V22_JNL_VER <= remote_jnl_ver)
{ /* At this point, we could have a TCOM or NULL or SET/KILL/ZKILL/ZTRIG type of record.
* Assert that all of them have "strm_seqno" at the exact same offset so we can avoid
* an if/then/else check on the record types in order to endian convert "strm_seqno".
*/
assert(&rec->jrec_null.strm_seqno == &rec->jrec_set_kill.strm_seqno);
assert(&rec->jrec_null.strm_seqno == &rec->jrec_tcom.strm_seqno);
rec->jrec_null.strm_seqno = GTM_BYTESWAP_64(rec->jrec_null.strm_seqno);
}
if (IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype))
{
/* This code will need changes in case the jnl-ver changes from V23 to V24 so add an assert to
* alert to that possibility. Once the code is fixed for the new jnl format, change the assert
* to reflect the new latest jnl-ver.
*/
assert(JNL_VER_THIS == V23_JNL_VER);
/* To better understand the logic below (particularly the use of hardcoded offsets), see comment
* in repl_filter.c (search for "struct_jrec_upd layout" for the various jnl versions we support).
*/
if (V22_JNL_VER <= remote_jnl_ver)
{ /* byte-swap update_num */
assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num);
rec->jrec_set_kill.update_num = GTM_BYTESWAP_32(rec->jrec_set_kill.update_num);
/* No need to byte-swap num_participants as it is not used by the update process */
/* Get pointer to mumps_node */
keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node;
assert(keystr == (jnl_string *)&rec->jrec_ztworm.ztworm_str);
} else if (V19_JNL_VER <= remote_jnl_ver)
{ /* byte-swap update_num */
ptr = (unsigned char *)rec + 32; /* is offset of update_num in V19 struct_jrec_upd */
update_num = *(uint4 *)ptr;
*(uint4 *)ptr = GTM_BYTESWAP_32(update_num);
/* No need to byte-swap num_participants as it is not used by the update process */
/* Get pointer to mumps_node */
keystr = (jnl_string *)((unsigned char *)rec + 40); /* is offset of mumps_node */
} else
{
assert(V17_JNL_VER <= remote_jnl_ver);
/* Note: In V17, there is no update_num or num_participants like V19 so no endian convert */
/* Get pointer to mumps_node */
keystr = (jnl_string *)((unsigned char *)rec + 32); /* is offset of mumps_node */
}
/* In V18, the jnl_string contained a 32 bit length field followed by mumps_node
* In V19, the "length" field is divided into 8 bit "nodeflags" and 24 bit "length" fields.
* Byteswap the entire 32 bit value
*/
nodeflags_keylen = *(jnl_str_len_t *)keystr;
*(jnl_str_len_t *)keystr = GTM_BYTESWAP_32(nodeflags_keylen);
if (IS_SET(rectype))
{
assert(!IS_ZTWORM(rectype));
/* SET records have a 'value' part which needs to be endian converted */
vallen_ptr = (mstr_len_t *)&keystr->text[keystr->length];
GET_MSTR_LEN(temp_val, vallen_ptr);
temp_val = GTM_BYTESWAP_32(temp_val);
PUT_MSTR_LEN(vallen_ptr, temp_val);
/* The actual 'value' itself is a character array and hence needs no endian conversion */
}
} else if (JRT_TCOM == rectype)
{ /* byte-swap num_participants as this is relied upon by "repl_sort_tr_buff".
* The offset and size of this field are different for older versions. Endian convert accordingly.
* V22 struct_jrec_tcom layout is as follows.
* offset = 0042 [0x002a] size = 0002 [0x0002] ----> num_participants
* V19/V20/V21 struct_jrec_tcom layout is as follows.
* offset = 0034 [0x0022] size = 0002 [0x0002] ----> num_participants
* V17/V18 struct_jrec_tcom layout is as follows.
* offset = 0040 [0x0028] size = 0004 [0x0004] ----> participants
*/
if (V22_JNL_VER <= remote_jnl_ver)
{
assert(42 == ((INTPTR_T)&rec->jrec_tcom.num_participants - (INTPTR_T)rec));
assert(SIZEOF(num_participants_2bytes) == SIZEOF(rec->jrec_tcom.num_participants));
num_participants_2bytes = rec->jrec_tcom.num_participants;
rec->jrec_tcom.num_participants = GTM_BYTESWAP_16(num_participants_2bytes);
} else if (V19_JNL_VER <= remote_jnl_ver)
{
ptr = (unsigned char *)rec + 34; /* is offset of update_num in V19 struct_jrec_upd */
assert(SIZEOF(num_participants_2bytes) == SIZEOF(unsigned short));
num_participants_2bytes = *(unsigned short *)ptr;
rec->jrec_tcom.num_participants = GTM_BYTESWAP_16(num_participants_2bytes);
} else
{
assert(V17_JNL_VER <= remote_jnl_ver);
ptr = (unsigned char *)rec + 40; /* is offset of update_num in V19 struct_jrec_upd */
assert(SIZEOF(num_participants_4bytes) == SIZEOF(uint4));
num_participants_4bytes = *(uint4 *)ptr;
rec->jrec_tcom.num_participants = GTM_BYTESWAP_32(num_participants_4bytes);
}
assert(rec->jrec_tcom.num_participants);
/* token_seq.jnl_seqno & strm_seqno have already been endian converted. */
}
/* else if (JRT_NULL == rectype)
* token_seq.jnl_seqno & strm_seqno have already been endian converted.
*/
assert(reclen == REC_LEN_FROM_SUFFIX(jb, reclen));
}
jb = jb + reclen;
assert(jb == jstart + reclen);
jlen -= reclen;
}
if ((-1 != status) && (0 != jlen))
{ /* Incomplete record */
assert(FALSE);
status = -1;
}
return status;
}
STATICFNDEF void do_flow_control(uint4 write_pos)
{
/* Check for overflow before writing */
recvpool_ctl_ptr_t recvpool_ctl;
upd_proc_local_ptr_t upd_proc_local;
gtmrecv_local_ptr_t gtmrecv_local;
long space_used;
unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */
int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */
int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */
int status; /* needed for REPL_{SEND,RECV}_LOOP */
int read_pos;
seq_num temp_seq_num;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
gtmrecv_local = recvpool.gtmrecv_local;
space_used = 0;
if (recvpool_ctl->wrapped)
space_used = write_pos + recvpool_size - (read_pos = upd_proc_local->read);
if (!recvpool_ctl->wrapped || space_used > recvpool_size)
space_used = write_pos - (read_pos = upd_proc_local->read);
assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */
if (space_used >= recvpool_high_watermark && !xoff_sent)
{ /* Send XOFF message */
if (!remote_side->cross_endian)
{
xoff_msg.type = REPL_XOFF;
memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&upd_proc_local->read_jnl_seqno, SIZEOF(seq_num));
xoff_msg.len = MIN_REPL_MSGLEN;
} else
{
xoff_msg.type = GTM_BYTESWAP_32(REPL_XOFF);
temp_seq_num = GTM_BYTESWAP_64(upd_proc_local->read_jnl_seqno);
memcpy((uchar_ptr_t)&xoff_msg.msg[0], (uchar_ptr_t)&temp_seq_num, SIZEOF(seq_num));
xoff_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN);
}
REPL_SEND_LOOP(gtmrecv_sock_fd, &xoff_msg, MIN_REPL_MSGLEN, FALSE, &gtmrecv_poll_immediate)
{
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_XOFF");
if (gtmrecv_logstats)
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Space used = %ld, High water mark = %d Low water mark = %d, "
"Updproc Read = %d, Recv Write = %d, Sent XOFF\n", space_used, recvpool_high_watermark,
recvpool_low_watermark, read_pos, write_pos);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_XOFF sent as receive pool has %ld bytes transaction data yet to be "
"processed\n", space_used);
xoff_sent = TRUE;
xoff_msg_log_cnt = 1;
assert(GTMRECV_WAIT_FOR_UPD_PROGRESS_US < MAX_GTMRECV_POLL_INTERVAL);
gtmrecv_poll_interval.tv_sec = 0;
gtmrecv_poll_interval.tv_usec = GTMRECV_WAIT_FOR_UPD_PROGRESS_US;
} else if (space_used < recvpool_low_watermark && xoff_sent)
{
if (!remote_side->cross_endian)
{
xon_msg.type = REPL_XON;
memcpy((uchar_ptr_t)&xon_msg.msg[0], (uchar_ptr_t)&upd_proc_local->read_jnl_seqno, SIZEOF(seq_num));
xon_msg.len = MIN_REPL_MSGLEN;
} else
{
xon_msg.type = GTM_BYTESWAP_32(REPL_XON);
temp_seq_num = GTM_BYTESWAP_64(upd_proc_local->read_jnl_seqno);
memcpy((uchar_ptr_t)&xon_msg.msg[0], (uchar_ptr_t)&temp_seq_num, SIZEOF(seq_num));
xon_msg.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN);
}
REPL_SEND_LOOP(gtmrecv_sock_fd, &xon_msg, MIN_REPL_MSGLEN, FALSE, &gtmrecv_poll_immediate)
{
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_XON");
if (gtmrecv_logstats)
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Space used now = %ld, High water mark = %d, "
"Low water mark = %d, Updproc Read = %d, Recv Write = %d, Sent XON\n", space_used,
recvpool_high_watermark, recvpool_low_watermark, read_pos, write_pos);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL_XON sent as receive pool has %ld bytes free space to buffer transaction "
"data\n", recvpool_size - space_used);
xoff_sent = FALSE;
xoff_msg_log_cnt = 0;
gtmrecv_poll_interval.tv_sec = 0;
gtmrecv_poll_interval.tv_usec = GTMRECV_POLL_INTERVAL;
}
return;
}
STATICFNDEF int gtmrecv_est_conn(void)
{
recvpool_ctl_ptr_t recvpool_ctl;
upd_proc_local_ptr_t upd_proc_local;
gtmrecv_local_ptr_t gtmrecv_local;
GTM_SOCKLEN_TYPE primary_addr_len;
fd_set input_fds;
int status;
const int disable_keepalive = 0;
struct linger disable_linger = {0, 0};
struct timeval save_gtmrecv_poll_interval, sel_timeout_val;
char print_msg[1024];
int send_buffsize, recv_buffsize, tcp_r_bufsize;
short retry_num;
#ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT
long poll_timeout;
unsigned long poll_nfds;
struct pollfd poll_fdlist[1];
#endif
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
/* Wait for a connection from a Source Server.
* The Receiver Server is an iterative server.
*/
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
gtmrecv_local = recvpool.gtmrecv_local;
gtmrecv_comm_init((in_port_t)gtmrecv_local->listen_port);
primary_addr_len = SIZEOF(primary_addr);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for a connection...\n");
/* Null initialize fields that need to be initialized only after connecting to the primary.
* It is ok not to hold a lock on the journal pool while updating jnlpool_ctl fields since this will be the only
* process updating those fields.
*/
assert(remote_side == &gtmrecv_local->remote_side);
remote_side->proto_ver = REPL_PROTO_VER_UNINITIALIZED;
jnlpool_ctl->primary_instname[0] = '\0';
#ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT
FD_ZERO(&input_fds);
FD_SET(gtmrecv_listen_sock_fd, &input_fds);
save_gtmrecv_poll_interval = gtmrecv_poll_interval;
#else
poll_fdlist[0].fd = gtmrecv_listen_sock_fd;
poll_fdlist[0].events = POLLIN;
poll_nfds = 1;
poll_timeout = gtmrecv_poll_interval.tv_usec / 1000; /* convert to millisecs */
#endif
/*
* Note - the following while loop checks for EINTR on the select. The
* SELECT macro is not used because the FD_SET is redone before the new
* call to select (after the continue).
*/
while (0 >=
#ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT
(status = select(gtmrecv_listen_sock_fd + 1, &input_fds, NULL, NULL, &save_gtmrecv_poll_interval))
#else
(status = poll(&poll_fdlist[0], poll_nfds, poll_timeout))
#endif
)
{
#ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT
save_gtmrecv_poll_interval = gtmrecv_poll_interval;
FD_SET(gtmrecv_listen_sock_fd, &input_fds);
#endif
if (0 == status)
gtmrecv_poll_actions(0, 0, NULL);
else if (EINTR == errno || EAGAIN == errno)
continue;
else
{
status = ERRNO;
SNPRINTF(print_msg, SIZEOF(print_msg), "Error in select on listen socket : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
ACCEPT_SOCKET(gtmrecv_listen_sock_fd, (struct sockaddr *)&primary_addr,
(GTM_SOCKLEN_TYPE *)&primary_addr_len, gtmrecv_sock_fd);
if (FD_INVALID == gtmrecv_sock_fd)
{
status = ERRNO;
#ifdef __hpux
/* ENOBUFS will normally signify a transient state. Hence retry before issuing an error*/
if (ENOBUFS == status)
{
retry_num = 0;
while (HPUX_MAX_RETRIES > retry_num)
{
/*In case of succeeding with select in first go, accept will still get 5ms time difference*/
SHORT_SLEEP(5);
for ( ; HPUX_MAX_RETRIES > retry_num; retry_num++)
{
#ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT
sel_timeout_val.tv_sec = 0;
sel_timeout_val.tv_usec = HPUX_SEL_TIMEOUT;
FD_ZERO(&input_fds);
FD_SET(gtmrecv_listen_sock_fd, &input_fds);
status = select(gtmrecv_listen_sock_fd + 1, &input_fds, NULL, NULL, &sel_timeout_val);
#else
poll_fdlist[0].fd = gtmrecv_listen_sock_fd;
poll_fdlist[0].events = POLLIN;
poll_nfds = 1;
poll_timeout = HPUX_SEL_TIMEOUT / 1000; /* convert to millisecs */
status = poll(&poll_fdlist[0], poll_nfds, poll_timeout);
#endif
if (0 == status)
gtmrecv_poll_actions(0, 0, NULL);
if (0 < status)
break;
else
SHORT_SLEEP(5);
}
if (0 > status)
{
status = ERRNO;
SNPRINTF(print_msg, SIZEOF(print_msg),"Error in select on listen socket after ENOBUFS : %s",
STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
ACCEPT_SOCKET(gtmrecv_listen_sock_fd, (struct sockaddr *)&primary_addr,
(GTM_SOCKLEN_TYPE *)&primary_addr_len, gtmrecv_sock_fd);
status = ERRNO;
if ((FD_INVALID == gtmrecv_sock_fd) && (ENOBUFS == status))
retry_num++;
else
break;
}
}
if (FD_INVALID == gtmrecv_sock_fd)
#endif
{
status = ERRNO;
SNPRINTF(print_msg, SIZEOF(print_msg), "Error accepting connection from Source Server : %s",
STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
/* Connection established */
repl_close(&gtmrecv_listen_sock_fd); /* Close the listener socket */
repl_connection_reset = FALSE;
if (-1 == setsockopt(gtmrecv_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger)))
{
status = ERRNO;
SNPRINTF(print_msg, SIZEOF(print_msg), "Error with receiver server socket disable linger : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
#ifdef REPL_DISABLE_KEEPALIVE
if (-1 == setsockopt(gtmrecv_sock_fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&disable_keepalive,
SIZEOF(disable_keepalive)))
{ /* Till SIGPIPE is handled properly */
status = ERRNO;
SNPRINTF(print_msg, SIZEOF(print_msg), "Error with receiver server socket disable keepalive : %s",
STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
#endif
if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &send_buffsize)))
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error getting socket send buffsize : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
if (send_buffsize < GTMRECV_TCP_SEND_BUFSIZE)
{
if (0 != (status = set_send_sock_buff_size(gtmrecv_sock_fd, GTMRECV_TCP_SEND_BUFSIZE)))
{
if (send_buffsize < GTMRECV_MIN_TCP_SEND_BUFSIZE)
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Could not set TCP send buffer size to %d : %s",
GTMRECV_MIN_TCP_SEND_BUFSIZE, STRERROR(status));
rts_error(VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
}
if (0 != (status = get_send_sock_buff_size(gtmrecv_sock_fd, &repl_max_send_buffsize))) /* may have changed */
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error getting socket send buffsize : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
if (0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &recv_buffsize)))
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error getting socket recv buffsize : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
if (recv_buffsize < GTMRECV_TCP_RECV_BUFSIZE)
{
for (tcp_r_bufsize = GTMRECV_TCP_RECV_BUFSIZE;
tcp_r_bufsize >= MAX(recv_buffsize, GTMRECV_MIN_TCP_RECV_BUFSIZE)
&& 0 != (status = set_recv_sock_buff_size(gtmrecv_sock_fd, tcp_r_bufsize));
tcp_r_bufsize -= GTMRECV_TCP_RECV_BUFSIZE_INCR)
;
if (tcp_r_bufsize < GTMRECV_MIN_TCP_RECV_BUFSIZE)
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Could not set TCP receive buffer size in range [%d, %d], last "
"known error : %s", GTMRECV_MIN_TCP_RECV_BUFSIZE, GTMRECV_TCP_RECV_BUFSIZE,
STRERROR(status));
rts_error(VARLSTCNT(6) MAKE_MSG_INFO(ERR_REPLCOMM), 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
if (0 != (status = get_recv_sock_buff_size(gtmrecv_sock_fd, &repl_max_recv_buffsize))) /* may have changed */
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error getting socket recv buffsize : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection established, using TCP send buffer size %d receive buffer size %d\n",
repl_max_send_buffsize, repl_max_recv_buffsize);
repl_log_conn_info(gtmrecv_sock_fd, gtmrecv_log_fp);
/* re-determine endianness of other side */
remote_side->endianness_known = FALSE;
/* re-determine journal format of other side */
remote_side->jnl_ver = 0;
/* re-determine compression level on the replication pipe after every connection establishment */
repl_zlib_cmp_level = ZLIB_CMPLVL_NONE;
/* Reset prior connection related state variables (see <C9J02_003091_receiver_server_assert_due_to_lingering_XOFF>) */
xoff_sent = FALSE;
xoff_msg_log_cnt = 0;
/* Note that even though we are reopening a fresh connection, we should NOT reset the cached information
* last_rcvd_histinfo, last_valid_histinfo etc. in this case as we might just resume processing from where
* the previous connection left off in which case all the cached information is still valid. If we dont
* resume from where we left off, the receiver will anyways error out asking for a rollback to be done so
* the cached information never gets used if it is invalid.
*/
return (SS_NORMAL);
}
int gtmrecv_alloc_filter_buff(int bufsiz)
{
unsigned char *old_filter_buff, *free_filter_buff;
bufsiz = ROUND_UP2(bufsiz, OS_PAGE_SIZE);
if ((NO_FILTER != gtmrecv_filter) && (repl_filter_bufsiz < bufsiz))
{
REPL_DPRINT3("Expanding filter buff from %d to %d\n", repl_filter_bufsiz, bufsiz);
free_filter_buff = filterbuff;
old_filter_buff = repl_filter_buff;
filterbuff = (unsigned char *)malloc(bufsiz + OS_PAGE_SIZE);
repl_filter_buff = (uchar_ptr_t)ROUND_UP2((unsigned long)filterbuff, OS_PAGE_SIZE);
if (NULL != free_filter_buff)
{
assert(NULL != old_filter_buff);
memcpy(repl_filter_buff, old_filter_buff, repl_filter_bufsiz);
free(free_filter_buff);
}
repl_filter_bufsiz = bufsiz;
}
return (SS_NORMAL);
}
void gtmrecv_free_filter_buff(void)
{
if (NULL != filterbuff)
{
assert(NULL != repl_filter_buff);
free(filterbuff);
filterbuff = repl_filter_buff = NULL;
repl_filter_bufsiz = 0;
}
}
int gtmrecv_alloc_msgbuff(void)
{
gtmrecv_max_repl_msglen = MAX_REPL_MSGLEN + SIZEOF(gtmrecv_msgp->type); /* add SIZEOF(...) for alignment */
assert(NULL == gtmrecv_msgp); /* first time initialization. The receiver server doesn't need to re-allocate */
msgbuff = (unsigned char *)malloc(gtmrecv_max_repl_msglen + OS_PAGE_SIZE);
gtmrecv_msgp = (repl_msg_ptr_t)ROUND_UP2((unsigned long)msgbuff, OS_PAGE_SIZE);
gtmrecv_alloc_filter_buff(gtmrecv_max_repl_msglen);
return (SS_NORMAL);
}
void gtmrecv_free_msgbuff(void)
{
if (NULL != msgbuff)
{
assert(NULL != gtmrecv_msgp);
free(msgbuff);
msgbuff = NULL;
gtmrecv_msgp = NULL;
}
}
/* This function can be used to only send fixed-size message types across the replication pipe.
* This in turn uses REPL_SEND* macros but also does error checks and sets the global variables
* "repl_connection_reset" or "gtmrecv_wait_for_jnl_seqno" accordingly.
* It also does the endian conversion of the 'type' and 'len' fields of the repl_msg_t structure being sent.
*
* msg = Pointer to the message buffer to send
* type = One of the various message types listed in repl_msg.h
* len = Length of the message to be sent
* msgtypestr = Message name as a string to display meaningful error messages
* optional_seqno = Optional seqno that needs to be printed along with the message name
*/
void gtmrecv_repl_send(repl_msg_ptr_t msgp, int4 type, int4 len, char *msgtypestr, seq_num optional_seqno)
{
unsigned char *msg_ptr; /* needed for REPL_SEND_LOOP */
int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */
int status; /* needed for REPL_SEND_LOOP */
FILE *log_fp;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(!mur_options.rollback || (NULL == recvpool.gtmrecv_local));
assert(mur_options.rollback || (NULL != recvpool.gtmrecv_local));
assert((REPL_MULTISITE_MSG_START > type) || (REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver));
log_fp = (NULL == gtmrecv_log_fp) ? stdout : gtmrecv_log_fp;
if (MAX_SEQNO != optional_seqno)
{
repl_log(log_fp, TRUE, TRUE, "Sending %s message with seqno "INT8_FMT" "INT8_FMTX"\n", msgtypestr,
optional_seqno, optional_seqno);
} else
repl_log(log_fp, TRUE, TRUE, "Sending %s message\n", msgtypestr);
/* Assert that if we dont know the endianness of the remote side, we assume it is the same endianness */
assert(remote_side->endianness_known || !remote_side->cross_endian);
if (!remote_side->cross_endian)
{
msgp->type = type;
msgp->len = len;
} else
{
msgp->type = GTM_BYTESWAP_32(type);
msgp->len = GTM_BYTESWAP_32(len);
}
REPL_SEND_LOOP(gtmrecv_sock_fd, msgp, len, FALSE, &gtmrecv_poll_immediate)
{
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
CHECK_REPL_SEND_LOOP_ERROR(status, msgtypestr);
assert(SS_NORMAL == status);
}
/* This function is invoked on receipt of a REPL_NEED_INSTINFO message.
* After doing some checks, it sends back a REPL_INSTINFO message containing the instance information.
* If any of the checks fail it issues the appropriate error right away.
* There can be TWO callers. One is the receiver server and another fetchresync rollback.
* is_rcvr_srvr is 1 for the former and 0 for the latter.
*/
void gtmrecv_check_and_send_instinfo(repl_needinst_msg_ptr_t need_instinfo_msg, boolean_t is_rcvr_srvr)
{
boolean_t remote_side_is_supplementary, grab_lock_needed;
repl_inst_hdr_ptr_t inst_hdr;
repl_instinfo_msg_t instinfo_msg;
repl_inst_uuid *strm_start, *strm_top, *strm_info;
FILE *log_fp;
int reuse_slot, first_usable_slot;
seq_num strm_jnl_seqno;
sgmnt_addrs *repl_csa;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
repl_csa = &FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs;
grab_lock_needed = is_rcvr_srvr || ((NULL != jnlpool_ctl) && !repl_csa->hold_onto_crit);
remote_side_is_supplementary = need_instinfo_msg->is_supplementary;
remote_side->is_supplementary = remote_side_is_supplementary;
assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */
if (remote_side->cross_endian)
ENDIAN_CONVERT_REPL_INST_UUID(&need_instinfo_msg->lms_group_info);
assert(is_rcvr_srvr && (NULL != gtmrecv_log_fp) || !is_rcvr_srvr && (NULL == gtmrecv_log_fp));
assert(!is_rcvr_srvr || !repl_csa->hold_onto_crit);
assert(is_rcvr_srvr || !jgbl.onlnrlbk || repl_csa->hold_onto_crit);
log_fp = !is_rcvr_srvr ? stdout : gtmrecv_log_fp;
repl_log(log_fp, TRUE, TRUE, "Received REPL_NEED_INSTINFO message from primary instance [%s]\n",
need_instinfo_msg->instname);
inst_hdr = jnlpool.repl_inst_filehdr;
assert(NULL != inst_hdr);
/* The fact that we came here (REPL_NEED_INSTINFO message) implies the source server understands the
* supplementary protocol. In that case, make sure -UPDATERESYNC if specified at receiver server startup
* had a value as well. If not, issue error.
*/
if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && (FD_INVALID == recvpool.gtmrecv_local->updresync_instfile_fd))
{
rts_error(VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Source side is >= V5.5-000 implies -UPDATERESYNC needs a value specified"));
assert(FALSE); /* we dont expect the rts_error to return control */
}
/* We usually expect the LMS group info to be non-NULL on both primary and secondary. An exception is if
* both of them are being brought up for the first time using a GT.M version that supports supplementary instances.
* In this case, if the primary was brought up as a root primary, then it should still have the group info filled.
* Assert that.
*/
assert(inst_hdr->lms_group_info.created_time
|| need_instinfo_msg->lms_group_info.created_time || !need_instinfo_msg->is_rootprimary);
assert((is_rcvr_srvr && (NULL != jnlpool_ctl)) || (!is_rcvr_srvr && (NULL == jnlpool_ctl)) || jgbl.onlnrlbk
|| (jgbl.mur_rollback && ANTICIPATORY_FREEZE_AVAILABLE));
/* If this instance is supplementary and the journal pool exists (to indicate whether updates are enabled or not
* which in turn helps us know whether this is an originating instance or not) do some additional checks.
*/
if (is_rcvr_srvr && inst_hdr->is_supplementary)
{
assert(NULL != jnlpool_ctl);
if (!jnlpool_ctl->upd_disabled)
{ /* this supplementary instance was started with -UPDOK. Issue error if source is also supplementary */
if (need_instinfo_msg->is_supplementary)
{
rts_error(VARLSTCNT(6) ERR_NOSUPPLSUPPL, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname),
LEN_AND_STR((char *)need_instinfo_msg->instname));
assert(FALSE); /* we dont expect the rts_error to return control */
}
} else
{ /* this supplementary instance was started with -UPDNOTOK. Issue error if source is not supplementary */
if (!need_instinfo_msg->is_supplementary)
{
rts_error(VARLSTCNT(6) ERR_SUPRCVRNEEDSSUPSRC, 4,
LEN_AND_STR((char *)inst_hdr->inst_info.this_instname),
LEN_AND_STR((char *)need_instinfo_msg->instname));
assert(FALSE); /* we dont expect the rts_error to return control */
}
}
}
/* Assert that if the receiver side is a root primary (i.e. has updates enabled), it better be a
* supplementary instance and have the LMS group info filled in. Exception is if this a FETCHRESYNC
* ROLLBACK run on an instance which was once primary (not necessarily a supplementary one).
*/
assert((NULL == jnlpool_ctl) || jnlpool_ctl->upd_disabled
|| inst_hdr->is_supplementary && IS_REPL_INST_UUID_NON_NULL(inst_hdr->lms_group_info)
|| jgbl.onlnrlbk || (jgbl.mur_rollback && ANTICIPATORY_FREEZE_AVAILABLE));
/* Check if primary and secondary are in same LMS group. Otherwise issue error. An exception is if the group info has
* not yet been filled in after instance file creation. In that case, copy the info from primary and skip the error check.
*/
if (IS_REPL_INST_UUID_NON_NULL(inst_hdr->lms_group_info))
{ /* LMS Group info has been initialized. Compare with that of the source side */
if (memcmp(&need_instinfo_msg->lms_group_info, &inst_hdr->lms_group_info, SIZEOF(inst_hdr->lms_group_info)))
{ /* Source and Receiver are part of DIFFERENT LMS Groups. If this instance is supplementary and remote
* side is not supplementary then we expect them to be different. Otherwise issue error.
*/
if (!inst_hdr->is_supplementary || remote_side_is_supplementary)
{
rts_error(VARLSTCNT(6) ERR_INSNOTJOINED, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname),
LEN_AND_STR((char *)need_instinfo_msg->instname));
assert(FALSE); /* we dont expect the rts_error to return control */
}
} else
{ /* Primary and Secondary are part of SAME LMS Group. If this instance is supplementary and remote
* side is not supplementary then we expect them to be different. Issue error in that case.
*/
if (inst_hdr->is_supplementary && !remote_side_is_supplementary)
{
assert(!need_instinfo_msg->is_supplementary); /* else NOSUPPLSUPPL error must have been issued */
rts_error(VARLSTCNT(6) ERR_INSROLECHANGE, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname),
LEN_AND_STR((char *)need_instinfo_msg->instname));
assert(FALSE); /* we dont expect the rts_error to return control */
}
}
if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync
&& (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd))
{ /* Caller is receiver server and -UPDATERESYNC=<INSTFILENAME> was specified. Check if the lms_group_info
* of that file matches that of the source side. If not issue error.
*/
if (memcmp(&recvpool.gtmrecv_local->updresync_lms_group,
&need_instinfo_msg->lms_group_info, SIZEOF(inst_hdr->lms_group_info)))
{
rts_error(VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Specified input instance file does not have same "
"LMS Group information as source server instance"));
}
}
} else if (IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info))
{ /* LMS Group info is NULL in instance file header. Initialize it in journal pool from the value
* on the primary side AND flush the changes to disk. Get lock before manipulating it.
* If caller is rollback, no other process can be touching the instance file until we are done so
* no need of the lock in that case.
*/
if (grab_lock_needed)
{
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
if (is_rcvr_srvr)
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
}
inst_hdr->lms_group_info = need_instinfo_msg->lms_group_info;
repl_inst_flush_filehdr();
if (grab_lock_needed)
rel_lock(jnlpool.jnlpool_dummy_reg);
}
/* If this instance is supplementary and remote side is not, then find out which stream # the non-supplementary source
* corresponds to. Issue error if the source LMS group is unknown in the instance file. If this is non-supplementary,
* the stream index is 0.
*/
if (inst_hdr->is_supplementary && !remote_side_is_supplementary)
{
assert(ARRAYSIZE(inst_hdr->strm_group_info) == (MAX_SUPPL_STRMS - 1));
assert(SIZEOF(repl_inst_uuid) == SIZEOF(inst_hdr->strm_group_info[0]));
strm_start = &inst_hdr->strm_group_info[0];
strm_top = strm_start + ARRAYSIZE(inst_hdr->strm_group_info);
/* Do this check under a lock as another receiver server started with -UPDATERESYNC=<INSTFILENAME> (not supported
* now but will be in the future) could be changing the "strm_group_info" array concurrently.
* If a matching stream is found, update gtmrecv_local->strm_index to reflect this while still holding lock.
* This field is checked by the -UPDATERESYNC to see if a receiver has a given stream # actively in use.
*/
if (grab_lock_needed)
{
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
if (is_rcvr_srvr)
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
}
reuse_slot = 0;
if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync && gtmrecv_options.reuse_specified)
{ /* If -REUSE was specified, check if instance name specified matches any existing slot.
* If slot has already been found, dont search any more.
*/
for (strm_info = strm_start; strm_info < strm_top; strm_info++)
{
if (IS_REPL_INST_UUID_NULL(*strm_info))
continue;
if (!STRCMP(gtmrecv_options.reuse_instname, strm_info->this_instname))
{
reuse_slot = (strm_info - strm_start) + 1;
break;
}
}
if (strm_info == strm_top)
{ /* -REUSE specified an instance name that is not present in any of the 15 strm_group slots */
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(6) ERR_REUSEINSTNAME, 0, ERR_TEXT, 2,
LEN_AND_LIT("Instance name in REUSE does not match any of 15 slots in instance file"));
}
assert(reuse_slot);
}
first_usable_slot = 0;
strm_index = 0;
for (strm_info = strm_start; strm_info < strm_top; strm_info++)
{
if (IS_REPL_INST_UUID_NULL(*strm_info))
{
if (!first_usable_slot)
first_usable_slot = (strm_info - strm_start) + 1;
continue;
}
if (!memcmp(&need_instinfo_msg->lms_group_info, strm_info, SIZEOF(repl_inst_uuid)))
{ /* Found the stream corresponding to the source side */
strm_index = (strm_info - strm_start) + 1;
break;
}
}
if (strm_info == strm_top)
{ /* Non-supplementary source is unknown to this supplementary instance */
if (reuse_slot) /* -REUSE specified and did locate a reusable slot. Use it. */
{
assert(is_rcvr_srvr);
strm_index = reuse_slot;
} else if (gtmrecv_options.resume_specified)
{
assert(is_rcvr_srvr);
strm_index = gtmrecv_options.resume_strm_num;
} else if (is_rcvr_srvr && recvpool.gtmrecv_local->updateresync)
{
if (first_usable_slot)
strm_index = first_usable_slot;
else
{
if (grab_lock_needed)
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2,
LEN_AND_LIT("No empty slot found. Specify REUSE to choose one for reuse"));
}
} else
{
if (grab_lock_needed)
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(6) ERR_INSUNKNOWN, 4, LEN_AND_STR((char *)inst_hdr->inst_info.this_instname),
LEN_AND_STR((char *)need_instinfo_msg->instname));
assert(FALSE); /* we dont expect the rts_error to return control */
}
/* Since we did not find the stream in the existing instance file but did find a slot, fill that slot
* while we have the lock on the instance file. This way another -updateresync startup of a receiver
* server (when we have multiple receiver server support) will see this slot taken when it gets the lock.
*/
assert(is_rcvr_srvr && recvpool.gtmrecv_local->updateresync
&& (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd));
assert(!memcmp(&recvpool.gtmrecv_local->updresync_lms_group,
&need_instinfo_msg->lms_group_info, SIZEOF(inst_hdr->lms_group_info)));
assert(0 < strm_index);
strm_info = &strm_start[strm_index - 1];
assert(strm_info < strm_top);
assert(IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info));
*strm_info = need_instinfo_msg->lms_group_info;
repl_inst_flush_filehdr();
} else if ((gtmrecv_options.resume_specified) && (gtmrecv_options.resume_strm_num != strm_index))
{ /* If -RESUME was specified, then the slot it matched must be same as slot found without its use */
assert(is_rcvr_srvr);
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(6) ERR_RESUMESTRMNUM, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side LMS "
"group is found in instance file but RESUME specifies different stream number"));
} else if (reuse_slot && (reuse_slot != strm_index))
{ /* If -REUSE was specified, then the slot it matched must be same as slot found without its use */
assert(is_rcvr_srvr);
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(6) ERR_REUSEINSTNAME, 0, ERR_TEXT, 2, LEN_AND_LIT("Source side LMS "
"group is found in instance file but REUSE specifies different instance name"));
}
assert(INVALID_SUPPL_STRM != strm_index);
assert(0 < strm_index);
assert(MAX_SUPPL_STRMS > strm_index);
/* Maintain stream slot # in shared memory as well so another -UPDATERESYNC=<INSTFILENAME>
* can see which stream# is actively in use by this receiver server. */
if (is_rcvr_srvr)
{
if (recvpool.gtmrecv_local->strm_index && (strm_index != recvpool.gtmrecv_local->strm_index))
{ /* This receiver server has already connected to a source server with a different stream #.
* Since mixing of multiple stream journal records in the same receive pool confuses the
* update process, issue error. Note: This limitation "might" be removed in the future.
*/
rel_lock(jnlpool.jnlpool_dummy_reg);
rts_error(VARLSTCNT(4) ERR_RCVRMANYSTRMS, 2, strm_index, recvpool.gtmrecv_local->strm_index);
}
recvpool.gtmrecv_local->strm_index = strm_index;
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Determined non-supplementary source Stream # = %d\n", strm_index);
assert(IS_REPL_INST_UUID_NON_NULL(need_instinfo_msg->lms_group_info));
recvpool.gtmrecv_local->remote_lms_group = need_instinfo_msg->lms_group_info;
rel_lock(jnlpool.jnlpool_dummy_reg);
} else
repl_log(stdout, TRUE, TRUE, "Determined non-supplementary source Stream # = %d\n", strm_index);
/* Compute non-zero strm_jnl_seqno to send across in the REPL_INSTINFO message.
* For receiver server
* If updateresync startup and this is the first connection to a source server
* If -RESUME is not specified :
* --> gtmrecv_local->updresync_jnl_seqno
* If -RESUME is specified :
* --> jnlpool_ctl->strm_seqno[gtmrecv_options.resume_strm_num]
* If updateresync startup and this is not the first connection to a source server
* --> recvpool_ctl->jnl_seqno
* If normal startup and this is the first connection to a source server
* --> inst_hdr->strm_seqno[strm_index] where strm_index > 0
* If this is not the first connection to a source server
* --> recvpool_ctl->jnl_seqno
* In case of instance crash
* --> No need to worry about this case as we would have otherwise issued a
* --> REPLREQROLLBACK error when the source server for this instance started up after the crash.
* For rollback
* If normal startup after a clean shutdown of the instance file
* --> inst_hdr->strm_seqno[strm_index] where strm_index > 0
* In case of crash shutdown of instance file
* --> Take max of cs_data->strm_seqno[strm_index] across all databases and use this (strm_index > 0)
*/
if (is_rcvr_srvr)
{
if (recvpool.upd_proc_local->read_jnl_seqno)
strm_jnl_seqno = recvpool.recvpool_ctl->jnl_seqno;
else
{
if (recvpool.gtmrecv_local->updateresync)
{
assert(FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd);
if (!gtmrecv_options.resume_specified)
strm_jnl_seqno = recvpool.gtmrecv_local->updresync_jnl_seqno;
else
{
strm_jnl_seqno = jnlpool_ctl->strm_seqno[gtmrecv_options.resume_strm_num];
/* It is possible for the strm_seqno to be 0. This implies the stream has
* had no updates. In that case, ideally this value should have been 1.
* But because we want to differentiate a stream that has had updates from
* stream numbers where there is no interest, we follow this convention.
* Therefore, in this case, reset the 0 back to 1 so we never send a zero
* seqno to the other side.
*/
if (!strm_jnl_seqno)
strm_jnl_seqno = 1;
}
} else
strm_jnl_seqno = jnlpool_ctl->strm_seqno[strm_index];
assert(0 == GET_STRM_INDEX(strm_jnl_seqno));
}
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Sending Stream Seqno = "INT8_FMT" "INT8_FMTX"\n",
strm_jnl_seqno, strm_jnl_seqno);
} else
{
if (jnlpool.repl_inst_filehdr->crash)
strm_jnl_seqno = mur_get_max_strm_reg_seqno(strm_index);
else
{
assert((NULL == jnlpool_ctl) || jgbl.onlnrlbk);
assert((NULL == jnlpool_ctl)
|| (jnlpool_ctl->strm_seqno[strm_index] == inst_hdr->strm_seqno[strm_index]));
strm_jnl_seqno = jnlpool.repl_inst_filehdr->strm_seqno[strm_index];
}
repl_log(stdout, TRUE, TRUE, "Sending Stream Seqno = "INT8_FMT" "INT8_FMTX"\n",
strm_jnl_seqno, strm_jnl_seqno);
}
} else
{
strm_jnl_seqno = 0; /* actually no need to initialize this since source server will not look at this
* field in this case but still be safe */
if (inst_hdr->is_supplementary)
{ /* In case caller is receiver server, "strm_index" would have been already set to 0 in jnlpool_init.c.
* But in case caller is rollback, "strm_index" would still be set to -1. In this case, set it to 0.
*/
assert(!is_rcvr_srvr || (0 == strm_index));
strm_index = 0;
}
DEBUG_ONLY(
if (is_rcvr_srvr)
NULL_INITIALIZE_REPL_INST_UUID(recvpool.gtmrecv_local->remote_lms_group);
)
}
assert(!is_rcvr_srvr || (INVALID_SUPPL_STRM == recvpool.gtmrecv_local->strm_index)
|| ((0 <= recvpool.gtmrecv_local->strm_index) && (MAX_SUPPL_STRMS > recvpool.gtmrecv_local->strm_index)));
/* Initialize the remote side protocol version from "proto_ver" field of this msg */
assert(REPL_PROTO_VER_SUPPLEMENTARY <= need_instinfo_msg->proto_ver);
remote_side->proto_ver = need_instinfo_msg->proto_ver;
/*************** Send REPL_INSTINFO message ***************/
memset(&instinfo_msg, 0, SIZEOF(instinfo_msg));
memcpy(instinfo_msg.instname, inst_hdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1);
if (grab_lock_needed)
{
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
if (is_rcvr_srvr)
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
}
instinfo_msg.was_rootprimary = (unsigned char)repl_inst_was_rootprimary();
if (grab_lock_needed)
rel_lock(jnlpool.jnlpool_dummy_reg);
if (!is_rcvr_srvr)
murgbl.was_rootprimary = instinfo_msg.was_rootprimary;
instinfo_msg.strm_jnl_seqno = strm_jnl_seqno;
/* strm_jnl_seqno is not expected to be zero unless this is a non-supplementary instance (like A->B) in which case
* strm_seqno is not maintained OR the remote side is supplementary (like P->Q) in which case the two instances do
* not communicate in-terms of strm_seqno (once the handshake is established)
*/
assert(strm_jnl_seqno || !inst_hdr->is_supplementary || remote_side_is_supplementary);
instinfo_msg.lms_group_info = inst_hdr->lms_group_info;
assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */
if (remote_side->cross_endian)
{
ENDIAN_CONVERT_REPL_INST_UUID(&instinfo_msg.lms_group_info);
instinfo_msg.strm_jnl_seqno = GTM_BYTESWAP_64(instinfo_msg.strm_jnl_seqno);
}
gtmrecv_repl_send((repl_msg_ptr_t)&instinfo_msg, REPL_INSTINFO, SIZEOF(repl_instinfo_msg_t), "REPL_INSTINFO", MAX_SEQNO);
if (repl_connection_reset || is_rcvr_srvr && gtmrecv_wait_for_jnl_seqno)
return;
/* Do not allow an instance which was formerly a root primary or which still
* has a non-zero value of "zqgblmod_seqno" to start up as a tertiary. The only exception is
* if this is P (supplementary instance) receiving from a non-supplementary instance.
* In that case, P can never be a root primary of the non-supplementary group and therefore
* cannot be affected by lost transactions being applied from the non-supplementary group.
*/
if ((!inst_hdr->is_supplementary || remote_side_is_supplementary) && !need_instinfo_msg->is_rootprimary
&& (instinfo_msg.was_rootprimary || (is_rcvr_srvr && jnlpool_ctl->max_zqgblmod_seqno)))
{
if (is_rcvr_srvr)
{
gtm_putmsg(VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *) need_instinfo_msg->instname));
gtmrecv_autoshutdown(); /* should not return */
} else
rts_error(VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2, LEN_AND_STR((char *) need_instinfo_msg->instname));
assert(FALSE);
}
if (is_rcvr_srvr)
memcpy(jnlpool_ctl->primary_instname, need_instinfo_msg->instname, MAX_INSTNAME_LEN - 1);
}
STATICFNDEF int gtmrecv_start_onln_rlbk(void)
{
char command[ONLN_RLBK_CMD_MAXLEN], *errptr;
int status, save_errno, cmdlen;
gtmrecv_local_ptr_t gtmrecv_local;
assert(!have_crit(CRIT_HAVE_ANY_REG));
gtmrecv_local = recvpool.gtmrecv_local;
MEMCPY_LIT(command, MUPIP_DIST_STR);
cmdlen = STR_LIT_LEN(MUPIP_DIST_STR);
MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_CMD);
cmdlen += STR_LIT_LEN(ONLN_RLBK_CMD);
if (gtmrecv_options.autorollback_verbose)
{
MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_VERBOSE);
cmdlen += STR_LIT_LEN(ONLN_RLBK_VERBOSE);
}
MEMCPY_LIT(&command[cmdlen], ONLN_RLBK_QUALIFIERS);
cmdlen += STR_LIT_LEN(ONLN_RLBK_QUALIFIERS);
assert(0 < gtmrecv_local->listen_port);
SNPRINTF(&command[cmdlen], ONLN_RLBK_CMD_MAXLEN, "%d", gtmrecv_local->listen_port); /* will add '\0' at the end */
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Executing %s\n", command);
if (0 != (status = SYSTEM((char *)command)))
{
if (-1 == status)
{
save_errno = errno;
errptr = (char *)STRERROR(save_errno);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "SYSTEM command failed: %s\n", errptr);
} else
{ /* ONLINE ROLLBACK returned a non-zero status */
repl_log(gtmrecv_log_fp, TRUE, TRUE, "ONLINE FETCHRESYNC ROLLBACK exited with code - %d\n", status);
}
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Could not complete ONLINE FETCHRESYNC ROLLBACK due to the above errors\n");
} else
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "ONLINE FETCHRESYNC ROLLBACK completed successfully\n");
}
return status;
}
/* We are here because a grab_lock or grab_crit saw a concurrent online rollback and we need to break the connection and
* re-establish with the new sequence number (the rolled back one). Wait for the update process to let us know the new sequence
* number from where we should start requesting transactions from.
* Ideally this function should have been a part of onln_rlbk.c. However, the function calls into gtmrecv_poll_actions which
* is strictly in the libmupip archive. Moving the function into onln_rlbk.c means it will try to pull in gtmrecv_poll_actions
* and all the modules that it calls which is something that we don't desire. So, keep this function isolated from onln_rlbk.c
*/
void gtmrecv_onln_rlbk_clnup(void)
{
boolean_t connection_already_reset;
assert(NULL != gtmrecv_log_fp);
assert(jnlpool.jnlpool_ctl == jnlpool_ctl);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "---> ONLINE ROLLBACK. Current Jnlpool Seqno : "INT8_FMT"\n", jnlpool_ctl->jnl_seqno);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for update process to set recvpool_ctl->onln_rlbk_flag\n");
connection_already_reset = repl_connection_reset;
assert(!gtmrecv_wait_for_jnl_seqno);
while (TRUE)
{
SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS);
gtmrecv_poll_actions(data_len, buff_unprocessed, buffp);
/* If connection was already closed before we came here (possible if receiver server closed the connection in
* response to a REPL_ROLLBACK_FIRST message) after spawning off an online rollback, then we should NOT check for
* repl_connection_reset. This way, we avoid breaking prematurely from this loop without waiting for the update
* process to acknowledge the online rollback. In this case, wait for gtmrecv_wait_for_jnl_seqno to be set to TRUE
* by gtmrecv_poll_actions.
*/
if ((!connection_already_reset && repl_connection_reset) || gtmrecv_wait_for_jnl_seqno)
break;
}
return;
}
/* This function is invoked on receipt of a REPL_NEED_HISTINFO message.
* This in turn sends a REPL_HISTINFO message containing the history information.
*/
void gtmrecv_send_histinfo(repl_histinfo *cur_histinfo)
{
repl_histinfo1_msg_t histinfo1_msg;
repl_histinfo2_msg_t histinfo2_msg;
repl_histinfo_msg_t histinfo_msg;
FILE *log_fp;
char remote_proto_ver;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
/* If sending history from a supplementary to a non-supplementary version, assert that the history record
* particularly the "strm_seqno" is 0 as a non-zero value is not understood by a non-supplementary instance.
*/
assert((NULL == this_side) || (this_side->is_supplementary == jnlpool.repl_inst_filehdr->is_supplementary));
assert(!jnlpool.repl_inst_filehdr->is_supplementary || remote_side->is_supplementary || !cur_histinfo->strm_seqno);
remote_proto_ver = remote_side->proto_ver;
assert(REPL_PROTO_VER_MULTISITE <= remote_proto_ver);
assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */
if (REPL_PROTO_VER_SUPPLEMENTARY > remote_proto_ver)
{ /* Remote side does not support supplementary protocol. Send OLDER histinfo messages */
/*************** Send REPL_OLD_TRIPLEINFO1 message ***************/
histinfo1_msg.start_seqno = !remote_side->cross_endian ?
cur_histinfo->start_seqno : GTM_BYTESWAP_64(cur_histinfo->start_seqno);
memcpy(histinfo1_msg.instname, cur_histinfo->root_primary_instname, MAX_INSTNAME_LEN - 1);
histinfo1_msg.instname[MAX_INSTNAME_LEN - 1] = '\0';
gtmrecv_repl_send((repl_msg_ptr_t)&histinfo1_msg, REPL_OLD_TRIPLEINFO1, MIN_REPL_MSGLEN,
"REPL_OLD_TRIPLEINFO1", cur_histinfo->start_seqno);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
/*************** Send REPL_OLD_TRIPLEINFO2 message ***************/
if (!remote_side->cross_endian)
{
histinfo2_msg.start_seqno = cur_histinfo->start_seqno;
histinfo2_msg.cycle = cur_histinfo->root_primary_cycle;
histinfo2_msg.histinfo_num = cur_histinfo->histinfo_num;
} else
{
histinfo2_msg.start_seqno = GTM_BYTESWAP_64(cur_histinfo->start_seqno);
histinfo2_msg.cycle = GTM_BYTESWAP_32(cur_histinfo->root_primary_cycle);
histinfo2_msg.histinfo_num = GTM_BYTESWAP_32(cur_histinfo->histinfo_num);
}
gtmrecv_repl_send((repl_msg_ptr_t)&histinfo2_msg, REPL_OLD_TRIPLEINFO2, MIN_REPL_MSGLEN,
"REPL_OLD_TRIPLEINFO2", cur_histinfo->start_seqno);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
} else
{ /* Remote side does support supplementary protocol. Send NEWER histinfo message. */
histinfo_msg.history = *cur_histinfo;
histinfo_msg.history.root_primary_instname[MAX_INSTNAME_LEN - 1] = '\0'; /* just in case */
if (remote_side->cross_endian)
ENDIAN_CONVERT_REPL_HISTINFO(&histinfo_msg.history);
gtmrecv_repl_send((repl_msg_ptr_t)&histinfo_msg, REPL_HISTINFO, SIZEOF(repl_histinfo_msg_t),
"REPL_HISTINFO", cur_histinfo->start_seqno);
}
log_fp = (NULL == gtmrecv_log_fp) ? stdout : gtmrecv_log_fp;
repl_dump_histinfo(log_fp, TRUE, FALSE, "History sent", cur_histinfo);
}
STATICFNDEF void prepare_recvpool_for_write(int datalen, int pre_filter_write_len)
{
recvpool_ctl_ptr_t recvpool_ctl;
recvpool_ctl = recvpool.recvpool_ctl;
if (datalen > recvpool_size)
{ /* Too large a transaction to be accommodated in the Receive Pool */
rts_error(VARLSTCNT(7) ERR_REPLTRANS2BIG, 5, &recvpool_ctl->jnl_seqno, datalen, pre_filter_write_len,
RTS_ERROR_LITERAL("Receive"));
}
if ((write_loc + datalen) > recvpool_size)
{
REPL_DEBUG_ONLY(
if (recvpool_ctl->wrapped)
REPL_DPRINT1("Update Process too slow. Waiting for it to free up space and wrap\n");
)
while (recvpool_ctl->wrapped)
{ /* Wait till the updproc wraps */
SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS);
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
assert(recvpool_ctl->wrapped == FALSE);
recvpool_ctl->write_wrap = write_wrap = write_loc;
/* The update process reads (a) "recvpool_ctl->write" first. If "write" is not equal to
* "upd_proc_local->read", it then reads (b) "recvpool_ctl->write_wrap" and assumes that
* "write_wrap" holds a non-stale value. This is in turn used to compare "temp_read" and
* "write_wrap" to determine how much of unprocessed data there is in the receive pool. If
* it so happens that the receiver server sets "write_wrap" in the above line to a value
* that is lesser than its previous value (possible if in the previous wrap of the pool,
* transactions used more portions of the pool than in the current wrap), it is important
* that the update process sees the updated value of "write_wrap" as long as it sees the
* corresponding update to "write". This is because it will otherwise end up processing
* the tail section of the receive pool (starting from the uptodate value of "write" to the
* stale value of "write_wrap") that does not contain valid journal data. For this read order
* dependency to hold good, the receiver server needs to do a write memory barrier
* after updating "write_wrap" but before updating "write". The update process
* will do a read memory barrier after reading "wrapped" but before reading "write".
*/
SHM_WRITE_MEMORY_BARRIER;
/* The update process reads (a) "recvpool_ctl->wrapped" first and then reads (b)
* "recvpool_ctl->write". If "wrapped" is TRUE, it assumes that "write" will never hold a stale
* value that reflects a corresponding previous state of "wrapped" (i.e. "write" will point to
* the beginning of the receive pool, either 0 or a small non-zero value instead of pointing
* to the end of the receive pool). For this to hold good, the receiver server needs to do
* a write memory barrier after updating "write" but before updating "wrapped". The update
* process will do a read memory barrier after reading "wrapped" but before reading "write".
*/
recvpool_ctl->write = write_loc = 0;
SHM_WRITE_MEMORY_BARRIER;
recvpool_ctl->wrapped = TRUE;
}
assert(buffered_data_len <= recvpool_size);
DO_FLOW_CONTROL(write_loc);
}
STATICFNDEF void copy_to_recvpool(uchar_ptr_t databuff, int datalen)
{
uint4 upd_read;
uint4 future_write;
upd_proc_local_ptr_t upd_proc_local;
recvpool_ctl_ptr_t recvpool_ctl;
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
future_write = write_loc + datalen;
upd_read = upd_proc_local->read;
REPL_DEBUG_ONLY(
if (recvpool_ctl->wrapped && (upd_read <= future_write))
{
REPL_DPRINT1("Update Process too slow. Waiting for it to free up space\n");
}
)
while (recvpool_ctl->wrapped && (upd_read <= future_write))
{ /* Write will cause overflow. Wait till there is more space available */
SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS);
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
upd_read = upd_proc_local->read;
}
memcpy(recvpool.recvdata_base + write_loc, databuff, datalen);
write_loc = future_write;
if (write_loc > write_wrap)
write_wrap = write_loc;
}
STATICFNDEF void wait_for_updproc_to_clear_backlog(void)
{
upd_proc_local_ptr_t upd_proc_local;
recvpool_ctl_ptr_t recvpool_ctl;
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
while (upd_proc_local->read != recvpool_ctl->write)
{
SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_PROGRESS);
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
}
STATICFNDEF void process_tr_buff(int msg_type)
{
recvpool_ctl_ptr_t recvpool_ctl;
seq_num log_seqno, recv_jnl_seqno, upd_seqno, diff_seqno;
uint4 in_size, out_size, out_bufsiz, tot_out_size, upd_read, max_strm_histinfo;
boolean_t filter_pass = FALSE, is_new_histrec, is_repl_cmpc;
uchar_ptr_t save_buffp, save_filter_buff, in_buff, out_buff;
int idx, status, num_strm_histinfo;
qw_num msg_total;
repl_old_triple_jnl_t old_triple_content;
uLongf destlen;
int cmpret, cur_data_len, rc;
repl_msg_ptr_t msgp, msgp_top;
int4 histinfo_strm_num;
uint4 write_len, pre_filter_write_len, pre_filter_write;
boolean_t uncmpfail;
repl_histinfo *cur_histinfo, *pool_histinfo, tmp_histinfo;
repl_histrec_jnl_t *pool_histrec, tmp_histjrec, rcvd_strm_histjrec[MAX_SUPPL_STRMS];
static boolean_t first_histrec = TRUE;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
recvpool_ctl = recvpool.recvpool_ctl;
is_repl_cmpc = ((REPL_TR_CMP_JNL_RECS == msg_type) || (REPL_TR_CMP_JNL_RECS2 == msg_type));
is_new_histrec = ((REPL_OLD_TRIPLE == msg_type) || (REPL_HISTREC == msg_type));
assert(!is_repl_cmpc || !is_new_histrec); /* HISTINFO records should not be compressed in the pipe */
if (is_repl_cmpc)
{
assert(gtmrecv_max_repl_uncmpmsglen);
destlen = gtmrecv_max_repl_uncmpmsglen;
if (ZLIB_CMPLVL_NONE == gtm_zlib_cmp_level)
{ /* Receiver does not have compression enabled in the first place but yet source server has sent
* compressed records. Stop source server from sending compressed records.
*/
uncmpfail = TRUE;
} else
{
ZLIB_UNCOMPRESS(gtmrecv_uncmpmsgp, destlen, gtmrecv_cmpmsgp, gtmrecv_repl_cmpmsglen, cmpret);
GTM_WHITE_BOX_TEST(WBTEST_REPL_TR_UNCMP_ERROR, cmpret, Z_DATA_ERROR);
recv_jnl_seqno = recvpool_ctl->jnl_seqno;
switch(cmpret)
{
case Z_MEM_ERROR:
assert(FALSE);
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_MEM_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno);
break;
case Z_BUF_ERROR:
assert(FALSE);
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_BUF_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno);
break;
case Z_DATA_ERROR:
assert(gtm_white_box_test_case_enabled
&& (WBTEST_REPL_TR_UNCMP_ERROR == gtm_white_box_test_case_number));
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_Z_DATA_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SEQNO_STR, recv_jnl_seqno, recv_jnl_seqno);
break;
}
uncmpfail = (Z_OK != cmpret);
if (!uncmpfail)
{
GTM_WHITE_BOX_TEST(WBTEST_REPL_TR_UNCMP_ERROR, destlen, gtmrecv_repl_uncmpmsglen - 1);
if (destlen != gtmrecv_repl_uncmpmsglen)
{ /* decompression did not yield precompressed data length */
assert(gtm_white_box_test_case_enabled
&& (WBTEST_REPL_TR_UNCMP_ERROR == gtm_white_box_test_case_number));
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPLEN_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SEQNO_STR, destlen, gtmrecv_repl_uncmpmsglen,
recv_jnl_seqno, recv_jnl_seqno);
uncmpfail = TRUE;
}
}
}
if (uncmpfail)
{ /* Since uncompression failed, default to NO compression. Send a REPL_CMP2UNCMP message accordingly */
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPTRANSITION_STR);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for update process to clear the backlog first\n");
wait_for_updproc_to_clear_backlog();
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Update process has successfully cleared the backlog\n");
gtmrecv_send_cmp2uncmp = TRUE; /* trigger REPL_CMP2UNCMP message processing */
gtmrecv_poll_actions(data_len, buff_unprocessed, buffp);
assert(!gtmrecv_send_cmp2uncmp);
assert(gtmrecv_wait_for_jnl_seqno);
return;
}
assert(0 == destlen % REPL_MSG_ALIGN);
msgp = (repl_msg_ptr_t)gtmrecv_uncmpmsgp;
msgp_top = (repl_msg_ptr_t)(gtmrecv_uncmpmsgp + destlen);
}
do
{
assert(remote_side->endianness_known); /* only then is remote_side->cross_endian reliable */
if (is_repl_cmpc)
{
if (msgp >= msgp_top)
{
assert(msgp == msgp_top);
break;
}
/* If primary is of different endianness, endian convert the UNCOMPRESSED message header
* before using the type and len fields (the compressed message header was already endian
* converted as part of receiving the message in do_main_loop())
*/
if (remote_side->cross_endian)
{
msgp->type = GTM_BYTESWAP_32(msgp->type);
msgp->len = GTM_BYTESWAP_32(msgp->len);
}
assert(REPL_TR_JNL_RECS == msgp->type);
cur_data_len = msgp->len - REPL_MSG_HDRLEN;
assert(0 < cur_data_len);
assert(0 == (cur_data_len % REPL_MSG_ALIGN));
PREPARE_RECVPOOL_FOR_WRITE(cur_data_len, 0); /* could update "recvpool_ctl->write" and "write_loc" */
COPY_TO_RECVPOOL((uchar_ptr_t)msgp + REPL_MSG_HDRLEN, cur_data_len);/* uses and updates "write_loc" */
msgp = (repl_msg_ptr_t)((uchar_ptr_t)msgp + cur_data_len + REPL_MSG_HDRLEN);
}
write_off = recvpool_ctl->write;
write_len = (write_loc - write_off);
assert((write_off != write_wrap) || (0 == write_off));
assert(remote_side->jnl_ver);
assert(!remote_side->cross_endian || (V18_JNL_VER <= remote_side->jnl_ver));
if (ENDIAN_CONVERSION_NEEDED(is_new_histrec, this_side->jnl_ver, remote_side->jnl_ver, remote_side->cross_endian))
{
if (SS_NORMAL != (status = repl_tr_endian_convert(remote_side->jnl_ver,
recvpool.recvdata_base + write_off, write_len)))
rts_error(VARLSTCNT(5) ERR_REPLXENDIANFAIL, 3, LEN_AND_LIT("Replicating"),
&recvpool.upd_proc_local->read_jnl_seqno);
}
if (!is_new_histrec)
{
if (NO_FILTER != gtmrecv_filter)
{ /* Need to pass through filter */
pre_filter_write = write_off;
pre_filter_write_len = write_len;
if (gtmrecv_filter & INTERNAL_FILTER)
{
in_buff = recvpool.recvdata_base + write_off;
in_size = write_len;
out_buff = repl_filter_buff;
out_bufsiz = repl_filter_bufsiz;
tot_out_size = 0;
while (SS_NORMAL != (status =
repl_filter_old2cur[remote_side->jnl_ver - JNL_VER_EARLIEST_REPL](
in_buff, &in_size, out_buff, &out_size, out_bufsiz))
&& (EREPL_INTLFILTER_NOSPC == repl_errno))
{
save_filter_buff = repl_filter_buff;
gtmrecv_alloc_filter_buff(repl_filter_bufsiz + (repl_filter_bufsiz >> 1));
in_buff += in_size;
in_size = (uint4)(pre_filter_write_len -
(in_buff - recvpool.recvdata_base - write_off));
out_bufsiz = (uint4)(repl_filter_bufsiz - (out_buff - save_filter_buff) - out_size);
out_buff = repl_filter_buff + (out_buff - save_filter_buff) + out_size;
tot_out_size += out_size;
}
if (SS_NORMAL == status)
write_len = tot_out_size + out_size;
else
{
assert(EREPL_INTLFILTER_SECNODZTRIGINTP == repl_errno);
if (EREPL_INTLFILTER_BADREC == repl_errno)
rts_error(VARLSTCNT(1) ERR_JNLRECFMT);
else if (EREPL_INTLFILTER_DATA2LONG == repl_errno)
rts_error(VARLSTCNT(4) ERR_JNLSETDATA2LONG, 2, jnl_source_datalen,
jnl_dest_maxdatalen);
else if (EREPL_INTLFILTER_NEWREC == repl_errno)
rts_error(VARLSTCNT(4) ERR_JNLNEWREC, 2, (unsigned int)jnl_source_rectype,
(unsigned int)jnl_dest_maxrectype);
else if (EREPL_INTLFILTER_REPLGBL2LONG == repl_errno)
rts_error(VARLSTCNT(1) ERR_REPLGBL2LONG);
else if (EREPL_INTLFILTER_SECNODZTRIGINTP == repl_errno)
rts_error(VARLSTCNT(3) ERR_SECNODZTRIGINTP, 1, &recvpool_ctl->jnl_seqno);
else /* (EREPL_INTLFILTER_INCMPLREC == repl_errno) */
GTMASSERT;
}
} else
{
if (write_len > repl_filter_bufsiz)
gtmrecv_alloc_filter_buff(write_len);
memcpy(repl_filter_buff, recvpool.recvdata_base + write_off, write_len);
}
assert(write_len <= repl_filter_bufsiz);
GTMTRIG_ONLY(
if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver)
{
repl_sort_tr_buff(repl_filter_buff, write_len);
DBG_VERIFY_TR_BUFF_SORTED(repl_filter_buff, write_len);
}
)
if ((gtmrecv_filter & EXTERNAL_FILTER) &&
(SS_NORMAL != (status = repl_filter(recvpool_ctl->jnl_seqno, repl_filter_buff, (int*)&write_len,
repl_filter_bufsiz))))
repl_filter_error(recvpool_ctl->jnl_seqno, status);
GTMTRIG_ONLY(
/* Ensure that the external filter has not disturbed the sorted sequence of the
* update_num
*/
DEBUG_ONLY(
if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver)
DBG_VERIFY_TR_BUFF_SORTED(repl_filter_buff, write_len);
)
)
assert(write_len <= repl_filter_bufsiz);
write_loc = write_off; /* reset "write_loc" */
PREPARE_RECVPOOL_FOR_WRITE(write_len, pre_filter_write_len); /* could update "->write"
* and "write_loc" */
COPY_TO_RECVPOOL((uchar_ptr_t)repl_filter_buff, write_len);/* uses and updates "write_loc" */
write_off = recvpool_ctl->write;
repl_recv_postfltr_data_procd += (qw_num)write_len;
filter_pass = TRUE;
} else
{
GTMTRIG_ONLY(
if ((unsigned char)V19_JNL_VER <= remote_side->jnl_ver)
{
repl_sort_tr_buff((uchar_ptr_t)(recvpool.recvdata_base + write_off), write_len);
DBG_VERIFY_TR_BUFF_SORTED((recvpool.recvdata_base + write_off), write_len);
}
)
}
}
if (recvpool_ctl->jnl_seqno - lastlog_seqno >= log_interval)
{
log_seqno = recvpool_ctl->jnl_seqno;
upd_seqno = recvpool.upd_proc_local->read_jnl_seqno;
assert(log_seqno >= upd_seqno);
diff_seqno = (log_seqno - upd_seqno);
trans_recvd_cnt += (log_seqno - lastlog_seqno);
msg_total = repl_recv_data_recvd - buff_unprocessed;
/* Don't include data not yet processed, we'll include that count in a later log */
if (NO_FILTER == gtmrecv_filter)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Seqno : "INT8_FMT" "INT8_FMTX
" Jnl Total : "INT8_FMT" "INT8_FMTX" Msg Total : "INT8_FMT" "INT8_FMTX
" Current backlog : "INT8_FMT" "INT8_FMTX"\n",
log_seqno, log_seqno, repl_recv_data_processed, repl_recv_data_processed,
msg_total, msg_total, diff_seqno, diff_seqno);
} else
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO - Seqno : "INT8_FMT" "INT8_FMTX
" Pre filter total : "INT8_FMT" "INT8_FMTX" Post filter total : "
INT8_FMT" "INT8_FMTX" Msg Total : "INT8_FMT" "INT8_FMTX
" Current backlog : "INT8_FMT" "INT8_FMTX"\n",
log_seqno, log_seqno,
repl_recv_data_processed, repl_recv_data_processed,
repl_recv_postfltr_data_procd, repl_recv_postfltr_data_procd,
msg_total, msg_total, diff_seqno, diff_seqno);
}
/* Approximate time with an error not more than GTMRECV_HEARTBEAT_PERIOD. We use this
* instead of calling time(), and expensive system call, especially on VMS. The
* consequence of this choice is that we may defer logging when we may have logged. We
* can live with that. Currently, the logging interval is not changeable by users.
* When/if we provide means of choosing log interval, this code may have to be re-examined.
* - Vinaya 2003/09/08.
*/
assert(0 != gtmrecv_now);
repl_recv_this_log_time = gtmrecv_now;
assert(repl_recv_this_log_time >= repl_recv_prev_log_time);
time_elapsed = difftime(repl_recv_this_log_time, repl_recv_prev_log_time);
if ((double)GTMRECV_LOGSTATS_INTERVAL <= time_elapsed)
{
repl_log(gtmrecv_log_fp, TRUE, FALSE, "REPL INFO since last log : Time elapsed : "
"%00.f Tr recvd : "INT8_FMT" Tr bytes : "INT8_FMT" Msg bytes : "INT8_FMT"\n",
time_elapsed, trans_recvd_cnt - last_log_tr_recvd_cnt,
repl_recv_data_processed - repl_recv_lastlog_data_procd,
msg_total - repl_recv_lastlog_data_recvd);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "REPL INFO since last log : Time elapsed : "
"%00.f Tr recvd/s : %f Tr bytes/s : %f Msg bytes/s : %f\n", time_elapsed,
(float)(trans_recvd_cnt - last_log_tr_recvd_cnt)/time_elapsed,
(float)(repl_recv_data_processed - repl_recv_lastlog_data_procd)/time_elapsed,
(float)(msg_total - repl_recv_lastlog_data_recvd)/time_elapsed);
repl_recv_lastlog_data_procd = repl_recv_data_processed;
repl_recv_lastlog_data_recvd = msg_total;
last_log_tr_recvd_cnt = trans_recvd_cnt;
repl_recv_prev_log_time = repl_recv_this_log_time;
}
lastlog_seqno = log_seqno;
}
if (gtmrecv_logstats)
{
if (!filter_pass)
{
repl_log(gtmrecv_log_fp, FALSE, FALSE, "Tr : "INT8_FMT" Size : %d Write : %d "
"Total : "INT8_FMT"\n", recvpool_ctl->jnl_seqno, write_len,
write_off, repl_recv_data_processed);
} else
{
assert(!is_new_histrec);
repl_log(gtmrecv_log_fp, FALSE, FALSE, "Tr : "INT8_FMT" Pre filter Size : %d "
"Post filter Size : %d Pre filter Write : %d Post filter Write : %d "
"Pre filter Total : "INT8_FMT" Post filter Total : "INT8_FMT"\n",
recvpool_ctl->jnl_seqno, pre_filter_write_len, write_len,
pre_filter_write, write_off, repl_recv_data_processed,
repl_recv_postfltr_data_procd);
}
}
recvpool_ctl->write_wrap = write_wrap;
if (recvpool.gtmrecv_local->noresync)
{ /* With -NORESYNC, we could take the strm_seqno further back with multiple connects of the same receiver
* server with different source servers at different seqnos. So avoid any confusion with asserts
* that check for increasing seqnos using last_valid_histinfo and last_rcvd_histinfo.
*/
memset(&recvpool.recvpool_ctl->last_valid_histinfo, 0, SIZEOF(recvpool.recvpool_ctl->last_valid_histinfo));
memset(&recvpool.recvpool_ctl->last_rcvd_histinfo, 0, SIZEOF(recvpool.recvpool_ctl->last_rcvd_histinfo));
/* In the case of -NORESYNC, we are a root primary supplementary instance (not a propagating primary)
* and therefore recvpool.recvpool_ctl->last_valid_strm_histinfo[] and
* recvpool.recvpool_ctl->last_rcvd_strm_histinfo[] are unused and hence no need to reset them.
*/
assert(!remote_side->is_supplementary); /* assert that we are not a propagating primary supplementary */
assert(jnlpool.repl_inst_filehdr->is_supplementary && !jnlpool_ctl->upd_disabled);
}
if (is_new_histrec)
{
/* Note: The REPL_OLD_TRIPLE or REPL_HISTREC messages are endian converted by the source server
* in case the receiver is running with a this_side->jnl_ver higher than the source. If not, the call to
* function "repl_tr_endian_convert" (done already in the current function) will take care of
* the endian conversion. So no more endian conversion needed at this point.
*/
if (REPL_OLD_TRIPLE == msg_type)
{
assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver);
assert(REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver);
assert(SIZEOF(old_triple_content) == write_len);
memcpy((sm_uc_ptr_t)&old_triple_content, (recvpool.recvdata_base + write_off),
SIZEOF(old_triple_content));
assert(JRT_TRIPLE == old_triple_content.jrec_type);
assert(old_triple_content.forwptr == SIZEOF(old_triple_content));
assert(old_triple_content.start_seqno == recvpool_ctl->jnl_seqno);
assert(old_triple_content.start_seqno >= recvpool.upd_proc_local->read_jnl_seqno);
assert((old_triple_content.start_seqno > recvpool_ctl->last_valid_histinfo.start_seqno)
|| ((old_triple_content.start_seqno == recvpool_ctl->last_valid_histinfo.start_seqno)
&& gtm_white_box_test_case_enabled
&& (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number)));
cur_histinfo = &tmp_histjrec.histcontent;
memcpy(cur_histinfo->root_primary_instname, old_triple_content.instname, MAX_INSTNAME_LEN - 1);
cur_histinfo->root_primary_instname[MAX_INSTNAME_LEN - 1] = '\0';
cur_histinfo->start_seqno = old_triple_content.start_seqno;
cur_histinfo->strm_seqno = 0;
cur_histinfo->root_primary_cycle = old_triple_content.cycle;
cur_histinfo->creator_pid = 0;
cur_histinfo->created_time = 0;
/* No need to initialize the following fields as they are reinitialized later
* when the history record gets added to the instance file (in "repl_inst_histinfo_add").
* cur_histinfo->histinfo_num = INVALID_HISTINFO_NUM;
* cur_histinfo->prev_histinfo_num = INVALID_HISTINFO_NUM;
* cur_histinfo->last_histinfo_num[] = INVALID_HISTINFO_NUM;
*/
cur_histinfo->strm_index = 0;
cur_histinfo->history_type = HISTINFO_TYPE_NORMAL;
NULL_INITIALIZE_REPL_INST_UUID(cur_histinfo->lms_group);
tmp_histjrec.jrec_type = JRT_HISTREC;
tmp_histjrec.forwptr = SIZEOF(repl_histrec_jnl_t);
/* We now have to upgrade a REPL_OLD_TRIPLE format history record to a REPL_HISTREC record.
* The latter is a much bigger record so make room for this in the receive pool.
*/
write_loc = write_off; /* reset "write_loc" */
write_len = tmp_histjrec.forwptr;
PREPARE_RECVPOOL_FOR_WRITE(write_len, SIZEOF(old_triple_content));
/* above macro could update "recvpool_ctl->write" and "write_loc" */
COPY_TO_RECVPOOL((uchar_ptr_t)&tmp_histjrec, write_len);/* uses and updates "write_loc" */
write_off = recvpool_ctl->write;
/* Copy relevant fields from received histinfo message to "last_rcvd_histinfo" if applicable */
cur_histinfo = &recvpool_ctl->last_rcvd_histinfo;
assert(old_triple_content.start_seqno >= cur_histinfo->start_seqno);
*cur_histinfo = tmp_histjrec.histcontent;
assert(!remote_side->is_supplementary); /* so no need to maintain last_rcvd_strm_histinfo[] */
} else
{
assert(REPL_HISTREC == msg_type);
assert(REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver);
assert(SIZEOF(repl_histrec_jnl_t) == write_len);
pool_histrec = (repl_histrec_jnl_t *)((sm_uc_ptr_t)recvpool.recvdata_base + write_off);
assert(JRT_HISTREC == pool_histrec->jrec_type);
assert(pool_histrec->forwptr == SIZEOF(repl_histrec_jnl_t));
pool_histinfo = &pool_histrec->histcontent;
assert(pool_histinfo->start_seqno == recvpool_ctl->jnl_seqno);
assert(pool_histinfo->start_seqno >= recvpool.upd_proc_local->read_jnl_seqno);
assert(jnlpool.jnlpool_ctl == jnlpool_ctl);
if (jnlpool.repl_inst_filehdr->is_supplementary && !jnlpool_ctl->upd_disabled)
{ /* Modify the history record to reflect the stream # */
assert((0 <= recvpool.gtmrecv_local->strm_index)
&& (MAX_SUPPL_STRMS > recvpool.gtmrecv_local->strm_index));
assert(strm_index == recvpool.gtmrecv_local->strm_index);
pool_histinfo->strm_index = recvpool.gtmrecv_local->strm_index;
assert(0 == pool_histinfo->strm_seqno);
assert(IS_REPL_INST_UUID_NON_NULL(recvpool.gtmrecv_local->remote_lms_group));
pool_histinfo->lms_group = recvpool.gtmrecv_local->remote_lms_group;
if (recvpool.gtmrecv_local->updateresync)
{
assert(FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd);
/* Make it known that this is an updateresync type history record */
pool_histinfo->history_type = HISTINFO_TYPE_UPDRESYNC;
}
/* We want to do a similar check for -noresync but we cannot use gtmrecv_local->noresync
* since that is reset the moment a REPL_WILL_RESTART_WITH_INFO message is seen (much
* before coming here). So use a static variable to determine if this is the first
* history record that is being received by this receiver server and if -noresync was
* specified in the command line (indicated by gtmrecv_options.noresync), set the
* history_type of the incoming history record to reflect the noresync type.
*/
assert(!recvpool.gtmrecv_local->noresync);
if (first_histrec)
{
if (gtmrecv_options.noresync)
pool_histinfo->history_type = HISTINFO_TYPE_NORESYNC;
first_histrec = FALSE;
}
}
assert((INVALID_SUPPL_STRM != strm_index) || (0 == pool_histinfo->strm_index));
if ((INVALID_SUPPL_STRM == strm_index) || (strm_index == pool_histinfo->strm_index))
{
assert((pool_histinfo->start_seqno > recvpool_ctl->last_valid_histinfo.start_seqno)
|| ((pool_histinfo->start_seqno == recvpool_ctl->last_valid_histinfo.start_seqno)
&& gtm_white_box_test_case_enabled
&& (WBTEST_UPD_PROCESS_ERROR == gtm_white_box_test_case_number)));
cur_histinfo = &recvpool_ctl->last_rcvd_histinfo;
assert(pool_histinfo->start_seqno >= cur_histinfo->start_seqno);
*cur_histinfo = *pool_histinfo;
} else
{
assert(!recvpool_ctl->insert_strm_histinfo);
cur_histinfo = pool_histinfo;
}
}
/* If supplementary instance with updates disabled, initialize last_rcvd_strm_histinfo as well */
if (remote_side->is_supplementary)
{ /* Check if "insert_strm_histinfo" is set. If so and if REPL_STRMINFO messages were
* exchanged between the source and receiver, we need to potentially insert history
* records for valid stream #s > 0. This is to ensure that even in case of a
* -updateresync startup, we record knowledge of all streams that exist on the source side
* (which we previously received as part of the same connection) on the receiver side as well.
* Examples of when REPL_STRMINFO messages are not exchanged are if receiver side is at
* jnl_seqno = 1, or if the receiver side is at the exact same jnl_seqno as the source side
* and no prior history is available.
*/
if (recvpool_ctl->insert_strm_histinfo
&& (max_strm_histinfo = recvpool_ctl->max_strm_histinfo)) /* caution: assignment */
{ /* Make room for the new records in the receive pool first.
* At this point "cur_histinfo" holds the first received history record.
* Add the other stream history records onto it.
*/
assert(0 == cur_histinfo->strm_index);
/* Determine how many history records need to be added */
num_strm_histinfo = 0;
/* Copy 0th stream history record into receive pool first */
rcvd_strm_histjrec[num_strm_histinfo].jrec_type = JRT_HISTREC;
rcvd_strm_histjrec[num_strm_histinfo].forwptr = SIZEOF(repl_histrec_jnl_t);
cur_histinfo->strm_seqno = jnlpool_ctl->strm_seqno[0];
rcvd_strm_histjrec[num_strm_histinfo++].histcontent = *cur_histinfo;
/* Copy non-zero stream history records if they exist */
for (idx = 1; idx < max_strm_histinfo; idx++)
{
if (recvpool_ctl->is_valid_strm_histinfo[idx])
{
DEBUG_ONLY(tmp_histinfo = recvpool_ctl->last_rcvd_strm_histinfo[idx]);
assert(IS_REPL_INST_UUID_NON_NULL(tmp_histinfo.lms_group));
rcvd_strm_histjrec[num_strm_histinfo].jrec_type = JRT_HISTREC;
rcvd_strm_histjrec[num_strm_histinfo].forwptr = SIZEOF(repl_histrec_jnl_t);
rcvd_strm_histjrec[num_strm_histinfo].histcontent
= recvpool_ctl->last_rcvd_strm_histinfo[idx];
/* Fix the "start_seqno" & "strm_seqno" fields of the history record */
rcvd_strm_histjrec[num_strm_histinfo].histcontent.start_seqno
= cur_histinfo->start_seqno;
/* Note that jnlpool_ctl->strm_seqno[idx] could be 0 if we have not yet
* seen any updates in this stream. But since we do have a history record
* for this stream and its strm_seqno cannot be 0 (has to be non-zero),
* set it to 1 in that case.
*/
rcvd_strm_histjrec[num_strm_histinfo].histcontent.strm_seqno
= jnlpool_ctl->strm_seqno[idx] ? jnlpool_ctl->strm_seqno[idx] : 1;
num_strm_histinfo++;
/* Do not reset recvpool_ctl->is_valid_strm_histinfo[idx] to FALSE
* as this reflects reality and staying this way helps later
* communication with the source in cases of reconnects/pipe-drains.
*/
}
}
write_loc = write_off; /* reset "write_loc" */
write_len = num_strm_histinfo * SIZEOF(repl_histrec_jnl_t);
PREPARE_RECVPOOL_FOR_WRITE(write_len, 0);
/* could update "recvpool_ctl->write" * and "write_loc" */
COPY_TO_RECVPOOL((uchar_ptr_t)rcvd_strm_histjrec, write_len);
/* uses and updates "write_loc" */
write_off = recvpool_ctl->write;
/* Note: "last_rcvd_strm_histinfo" is automatically maintained in this case */
/* Now that we have inserted the stream specific history records, reset flag */
recvpool_ctl->insert_strm_histinfo = FALSE;
} else
{ /* Maintain last_rcvd_strm_histinfo[] as well.
* In addition, if insert_strm_histinfo is TRUE, it means no REPL_STRMINFO messages
* were exchanged at startup. In that case, no need of inserting stream specific
* history information in the receive pool so reset variable to FALSE.
*/
if (recvpool_ctl->insert_strm_histinfo)
recvpool_ctl->insert_strm_histinfo = FALSE;
histinfo_strm_num = cur_histinfo->strm_index;
assert((0 <= histinfo_strm_num) && (MAX_SUPPL_STRMS > histinfo_strm_num));
memcpy(&recvpool_ctl->last_rcvd_strm_histinfo[histinfo_strm_num],
cur_histinfo, SIZEOF(repl_histinfo));
recvpool_ctl->is_valid_strm_histinfo[histinfo_strm_num] = TRUE;
if (recvpool_ctl->max_strm_histinfo <= histinfo_strm_num)
recvpool_ctl->max_strm_histinfo = histinfo_strm_num + 1;
}
}
repl_dump_histinfo(gtmrecv_log_fp, TRUE, TRUE, "New History Content", cur_histinfo);
} else
{ /* Note: In case of a propagating primary supplementary instance, the below if implies that we will
* change last_rcvd_histinfo to last_valid_histinfo even if the logical update corresponded to a
* different stream than 0 (the stream corresponding to the history of interest on this receiver).
* But there should not be any issues due to this as it is an update nevertheless and is going to
* bump up the recvpool_ctl->jnl_seqno to one more than last_rcvd_histinfo.start_seqno.
*/
if (recvpool_ctl->jnl_seqno == recvpool_ctl->last_rcvd_histinfo.start_seqno)
{ /* Move over stuff from "last_rcvd_histinfo" to "last_valid_histinfo" */
memcpy(&recvpool_ctl->last_valid_histinfo,
&recvpool_ctl->last_rcvd_histinfo, SIZEOF(repl_histinfo));
if (remote_side->is_supplementary)
{ /* Propagating primary supplementary instance. Maintain last_valid_strm_histinfo too. */
max_strm_histinfo = recvpool_ctl->max_strm_histinfo;
assert(max_strm_histinfo);
for (idx = 0; idx < max_strm_histinfo; idx++)
{
if (recvpool_ctl->is_valid_strm_histinfo[idx])
{
DEBUG_ONLY(tmp_histinfo = recvpool_ctl->last_rcvd_strm_histinfo[idx]);
assert((0 == idx) || IS_REPL_INST_UUID_NON_NULL(tmp_histinfo.lms_group));
assert((0 != idx) || IS_REPL_INST_UUID_NULL(tmp_histinfo.lms_group));
memcpy(&recvpool_ctl->last_valid_strm_histinfo[idx],
&recvpool_ctl->last_rcvd_strm_histinfo[idx], SIZEOF(repl_histinfo));
recvpool_ctl->is_valid_strm_histinfo[idx] = FALSE;
}
}
recvpool_ctl->max_strm_histinfo = 0;
/* Now that last_valid_strm_histinfo[0] is initialized, it is safe to reset the below */
if (recvpool_ctl->insert_strm_histinfo)
recvpool_ctl->insert_strm_histinfo = FALSE;
}
/* Now that at least one history record has been written into the receive pool and is guaranteed
* to be written to the instance file (when this gets processed by the update process), dont use
* -updateresync or -noresync for future handshakes in case the current connection gets reset.
*/
if (recvpool.gtmrecv_local->updateresync)
{
recvpool.gtmrecv_local->updateresync = FALSE;
/* Close fd of the input instance file name used for the -updateresync */
if (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd)
CLOSEFILE_RESET(recvpool.gtmrecv_local->updresync_instfile_fd, rc);
}
assert(!recvpool.gtmrecv_local->noresync); /* should have been reset already */
}
QWINCRBYDW(recvpool_ctl->jnl_seqno, 1);
assert(recvpool_ctl->last_valid_histinfo.start_seqno < recvpool_ctl->jnl_seqno);
}
/* The update process looks at "recvpool_ctl->write" first and then reads (a) "recvpool_ctl->write_wrap"
* AND (b) all journal data in the receive pool upto this offset. It assumes that (a) and (b) will never
* hold stale values corresponding to a previous state of "recvpool_ctl->write". In order for this
* assumption to hold good, the receiver server needs to do a write memory barrier after updating the
* receive pool data and "write_wrap" but before updating "write". The update process will do a read
* memory barrier after reading "write" but before reading "write_wrap" or the receive pool data. Not
* enforcing the read order will result in the update process attempting to read/process invalid data
* from the receive pool (which could end up in db out of sync situation between primary and secondary).
*/
SHM_WRITE_MEMORY_BARRIER;
recvpool_ctl->write = write_loc;
} while (is_repl_cmpc);
return;
}
/* Retrieve the history record corresponding to "input_seqno" in the instance file used with the -UPDATERESYNC qualifier.
* Do special processing in case of P->Q type of connection (where primary and secondary are both supplementary instances)
* which is to search only in the 0th stream.
*
* Note: This function is similar to "repl_inst_histinfo_find_seqno" except that this operates on the input instance file
* provided with the -updateresync qualifier. Two reasons why we need this code duplication is
* a) The "repl_inst_histinfo_find_seqno" function currently is coded to use only the replication instance file.
* b) The input instance file for -updateresync could be cross endian.
* If "repl_inst_histinfo_find_seqno" is enhanced to fix these limitations, then we can avoid this code duplication.
*/
STATICFNDEF void gtmrecv_updresync_histinfo_find_seqno(seq_num input_seqno, int4 strm_num, repl_histinfo *histinfo)
{
char print_msg[1024];
int fd, status;
int4 histinfo_num;
off_t offset;
fd = recvpool.gtmrecv_local->updresync_instfile_fd;
assert(FD_INVALID != fd);
/* If remote side is non-supplementary then its instance file (which is given as input to the -updateresync
* command) knows only strem 0, so reset strm_num to 0 unconditionally.
*/
if (!remote_side->is_supplementary)
strm_num = 0;
if (INVALID_SUPPL_STRM == strm_num)
histinfo_num = recvpool.gtmrecv_local->updresync_num_histinfo;
else
histinfo_num = recvpool.gtmrecv_local->updresync_num_histinfo_strm[strm_num];
if (INVALID_HISTINFO_NUM == histinfo_num)
{ /* The instance file cannot be used for updateresync if it has NO history records. */
rts_error(VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Input instance file has NO history records"));
}
assert(0 <= histinfo_num);
if (!recvpool.gtmrecv_local->updresync_jnl_seqno)
{ /* The instance file cannot be used for updateresync if it has a ZERO seqno */
rts_error(VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num,
ERR_TEXT, 2, RTS_ERROR_LITERAL("Input instance file has jnl_seqno of 0"));
}
if (input_seqno > recvpool.gtmrecv_local->updresync_jnl_seqno)
{ /* Input seqno is greater than the max seqno in the updateresync input instance file. So can never be found. */
SNPRINTF(print_msg, SIZEOF(print_msg), "Seqno "INT8_FMT" "INT8_FMTX" cannot be found in input instance file "
" which has a max seqno of "INT8_FMT" "INT8_FMTX"\n", input_seqno, input_seqno,
recvpool.gtmrecv_local->updresync_jnl_seqno, recvpool.gtmrecv_local->updresync_jnl_seqno);
rts_error(VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_STR(print_msg));
}
histinfo->start_seqno = 0;
do
{
offset = REPL_INST_HISTINFO_START + ((histinfo_num) * SIZEOF(repl_histinfo));
LSEEKREAD(fd, offset, histinfo, SIZEOF(repl_histinfo), status);
if (0 != status)
{ /* At this point, we dont have the name of the input instance file used in the -updateresync qualifier.
* So we use a value of "" instead. The fact that the REPLINSTREAD message is preceded by a UPDSYNCINSTFILE
* error indicates to the user it is the -updateresync qualifier where the issue is so it is not a big loss.
*/
if (-1 == status)
rts_error(VARLSTCNT(15) ERR_UPDSYNCINSTFILE, 0,
ERR_STRMNUMIS, 1, strm_num,
ERR_TEXT, 2, RTS_ERROR_LITERAL("Error reading history record"),
ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""));
else
rts_error(VARLSTCNT(16) ERR_UPDSYNCINSTFILE, 0,
ERR_STRMNUMIS, 1, strm_num,
ERR_TEXT, 2, RTS_ERROR_LITERAL("Error reading history record"),
ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""), status);
}
if (recvpool.gtmrecv_local->updresync_cross_endian)
ENDIAN_CONVERT_REPL_HISTINFO(histinfo);
if (input_seqno > histinfo->start_seqno)
return; /* found history record corresponding to input_seqno. return right away */
histinfo_num = (INVALID_SUPPL_STRM == strm_num) ? (histinfo_num - 1) : histinfo->prev_histinfo_num;
} while (INVALID_HISTINFO_NUM != histinfo_num);
/* Could not find history record in -updateresync= input instance file */
SNPRINTF(print_msg, SIZEOF(print_msg), "Receiver side instance seqno "INT8_FMT" "INT8_FMTX" is less than"
" any history record found in instance file", input_seqno, input_seqno);
rts_error(VARLSTCNT(9) ERR_UPDSYNCINSTFILE, 0, ERR_STRMNUMIS, 1, strm_num, ERR_TEXT, 2, LEN_AND_STR(print_msg));
}
/* Retrieve the "index"'th history record in the instance file specified using the -UPDATERESYNC qualifier.
*
* This function is similar to "repl_inst_histinfo_get" except that this operates on the input instance file provided
* with the -updateresync qualifier. Two reasons why we need this code duplication is
* a) The "repl_inst_histinfo_get" function currently is coded to use only the replication instance file.
* b) The input instance file for -updateresync could be cross endian.
* If "repl_inst_histinfo_get" is enhanced to fix these limitations, then we can avoid this code duplication.
*/
STATICFNDEF void gtmrecv_updresync_histinfo_get(int4 index, repl_histinfo *histinfo)
{
char print_msg[1024];
int fd, status;
off_t offset;
fd = recvpool.gtmrecv_local->updresync_instfile_fd;
assert(FD_INVALID != fd);
assert(0 <= index);
offset = REPL_INST_HISTINFO_START + ((index) * SIZEOF(repl_histinfo));
LSEEKREAD(fd, offset, histinfo, SIZEOF(repl_histinfo), status);
if (0 != status)
{ /* At this point, we dont have the name of the input instance file used in the -updateresync qualifier.
* So we use a value of "" instead. The fact that the REPLINSTREAD message is preceded by a UPDSYNCINSTFILE
* error indicates to the user it is the -updateresync qualifier where the issue is so it is not a big loss.
*/
if (-1 == status)
rts_error(VARLSTCNT(12) ERR_UPDSYNCINSTFILE, 0,
ERR_TEXT, 2, RTS_ERROR_LITERAL("Error reading history record"),
ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""));
else
rts_error(VARLSTCNT(13) ERR_UPDSYNCINSTFILE, 0,
ERR_TEXT, 2, RTS_ERROR_LITERAL("Error reading history record"),
ERR_REPLINSTREAD, 4, SIZEOF(repl_histinfo), (qw_off_t *)&offset, LEN_AND_LIT(""), status);
}
if (recvpool.gtmrecv_local->updresync_cross_endian)
ENDIAN_CONVERT_REPL_HISTINFO(histinfo);
}
/* This function is invoked on receipt of a REPL_NEED_STRMINFO message. In case of an error, it returns if either
* repl_connection_reset OR gtmrecv_wait_for_jnl_seqno are set so the caller should check for this and return as well.
*/
STATICFNDEF void gtmrecv_process_need_strminfo_msg(repl_needstrminfo_msg_ptr_t need_strminfo_msg)
{
int4 status;
int idx;
repl_histinfo histinfo, *previously_rcvd_histinfo;
repl_strminfo_msg_t strminfo_msg;
seq_num need_strminfo_seqno, last_valid_histinfo_seqno;
assert(remote_side->is_supplementary); /* STRMINFO messages are sent only in case source and receiver are supplementary */
assert(0 == strm_index);
assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */
if (!remote_side->cross_endian)
need_strminfo_seqno = need_strminfo_msg->seqno;
else
need_strminfo_seqno = GTM_BYTESWAP_64(need_strminfo_msg->seqno);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_STRMINFO message for seqno "INT8_FMT" "INT8_FMTX"\n",
need_strminfo_seqno, need_strminfo_seqno);
if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd))
{ /* The stream information that is being requested needs to be found in the -updateresync input instance file
* (not the receiver side instance file).
*/
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired stream info in -updateresync input instance file\n");
gtmrecv_updresync_histinfo_find_seqno(need_strminfo_seqno, INVALID_SUPPL_STRM, &histinfo);
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
strminfo_msg.last_histinfo_num[idx] = histinfo.last_histinfo_num[idx];
assert(recvpool.recvpool_ctl->insert_strm_histinfo);
} else
{ /* The history record needs to be found in the receiver side instance file or in the receive pool.
* If last_valid_strm_histinfo[0] has non-default content, then because this is a supplementary instance
* and a propagating primary, we can rest assured that last_valid_strm_histinfo[1] thru [15] reflect
* the latest history records for each stream if the stream exists on this instance. And so no need
* to go to the instance file at all. If [0] does not have any content, then it means the cached history
* is empty for not just the 0th stream but for every other stream as well i.e. the receive pool is empty
* and the receiver is connecting with a source for the first time. So go to the instance file in that case.
*/
last_valid_histinfo_seqno = recvpool.recvpool_ctl->last_valid_strm_histinfo[0].start_seqno;
if (last_valid_histinfo_seqno)
{
assert(need_strminfo_seqno > last_valid_histinfo_seqno);
assert(!recvpool.gtmrecv_local->updateresync);
assert(!recvpool.gtmrecv_local->noresync);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the receive pool\n");
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
{
previously_rcvd_histinfo = &recvpool.recvpool_ctl->last_valid_strm_histinfo[idx];
assert((0 != idx) || IS_REPL_INST_UUID_NULL(previously_rcvd_histinfo->lms_group));
if ((0 == idx) || IS_REPL_INST_UUID_NON_NULL(previously_rcvd_histinfo->lms_group))
strminfo_msg.last_histinfo_num[idx] = UNKNOWN_HISTINFO_NUM;
else
{
assert(0 != idx);
strminfo_msg.last_histinfo_num[idx] = INVALID_HISTINFO_NUM;
}
}
assert(!recvpool.recvpool_ctl->insert_strm_histinfo);
histinfo.strm_index = 0; /* so "0 < histinfo.strm_index" if block is skipped below for this case */
} else
{
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Searching for the desired history in the replication instance file\n");
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
/* above macro will "return" if repl_connection_reset OR gtmrecv_wait_for_jnl_seqno is set */
status = repl_inst_wrapper_histinfo_find_seqno(need_strminfo_seqno, INVALID_SUPPL_STRM, &histinfo);
rel_lock(jnlpool.jnlpool_dummy_reg);
if (0 != status)
{ /* Close the connection */
assert(ERR_REPLINSTNOHIST == status);
assert(FALSE);
gtmrecv_autoshutdown(); /* should not return */
}
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
strminfo_msg.last_histinfo_num[idx] = histinfo.last_histinfo_num[idx];
}
}
if (0 < histinfo.strm_index)
{
assert(histinfo.last_histinfo_num[histinfo.strm_index] < histinfo.histinfo_num);
strminfo_msg.last_histinfo_num[histinfo.strm_index] = histinfo.histinfo_num;
}
if (remote_side->cross_endian)
{
for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
{
assert(4 == SIZEOF(strminfo_msg.last_histinfo_num[idx])); /* so GTM_BYTESWAP_32 can be used below */
strminfo_msg.last_histinfo_num[idx] = GTM_BYTESWAP_32(strminfo_msg.last_histinfo_num[idx]);
}
}
gtmrecv_repl_send((repl_msg_ptr_t)&strminfo_msg, REPL_STRMINFO, SIZEOF(repl_strminfo_msg_t), "REPL_STRMINFO", MAX_SEQNO);
return;
}
/* This function is invoked on receipt of a REPL_NEED_HISTINFO message. In case of an error, it returns if either
* repl_connection_reset OR gtmrecv_wait_for_jnl_seqno are set so the caller should check for this and return as well.
*/
STATICFNDEF void gtmrecv_process_need_histinfo_msg(repl_needhistinfo_msg_ptr_t need_histinfo_msg, repl_histinfo *histinfo)
{
boolean_t maintain_rcvd_strm_histinfo, suppl_propagate_primary;
int4 need_histinfo_num, need_histinfo_strm_num, status;
recvpool_ctl_ptr_t recvpool_ctl;
seq_num first_unprocessed_seqno, last_unprocessed_histinfo_seqno;
seq_num need_histinfo_seqno, last_valid_histinfo_seqno;
assert(remote_side->endianness_known); /* ensure remote_side->cross_endian is reliable */
if (!remote_side->cross_endian)
{
need_histinfo_seqno = need_histinfo_msg->seqno;
need_histinfo_num = need_histinfo_msg->histinfo_num;
need_histinfo_strm_num = need_histinfo_msg->strm_num;
} else
{
need_histinfo_seqno = GTM_BYTESWAP_64(need_histinfo_msg->seqno);
need_histinfo_num = GTM_BYTESWAP_32(need_histinfo_msg->histinfo_num);
need_histinfo_strm_num = GTM_BYTESWAP_32(need_histinfo_msg->strm_num);
}
assert((INVALID_SUPPL_STRM == strm_index) || ((0 <= strm_index) && (MAX_SUPPL_STRMS > strm_index)));
if (INVALID_SUPPL_STRM == strm_index)
{ /* Both receiver and source sides are non-supplementary instances */
if (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver)
{ /* needhistinfo_msg.strm_num & histinfo_num are uninitialized in this case. Fix it */
need_histinfo_strm_num = INVALID_SUPPL_STRM;
need_histinfo_num = INVALID_HISTINFO_NUM;
} else
{
assert(INVALID_SUPPL_STRM == need_histinfo_strm_num);
assert(INVALID_HISTINFO_NUM == need_histinfo_num);
}
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for seqno "INT8_FMT" "INT8_FMTX"\n",
need_histinfo_seqno, need_histinfo_seqno);
} else if (0 < strm_index)
{ /* Receiver side is supplementary but Source side is a non-supplementary instance */
assert(INVALID_SUPPL_STRM == need_histinfo_strm_num);
assert(INVALID_HISTINFO_NUM == need_histinfo_num);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for Stream %d : Seqno "
INT8_FMT" "INT8_FMTX"\n", strm_index, need_histinfo_seqno, need_histinfo_seqno);
need_histinfo_strm_num = strm_index;
} else
{ /* Both receiver and source sides are supplementary instances */
/* strm_index is 0 at this point (already asserted above) */
assert(INVALID_SUPPL_STRM != need_histinfo_strm_num);
if (INVALID_HISTINFO_NUM == need_histinfo_num)
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for Stream %d : "
"Seqno "INT8_FMT" "INT8_FMTX"\n", need_histinfo_strm_num, need_histinfo_seqno, need_histinfo_seqno);
else
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_NEED_HISTINFO message for History Number %d\n",
need_histinfo_num);
}
/* The only two histinfo_num values that have special meaning are negative. So we can check for a valid value
* by checking for positive. Assert that below before doing the positive check.
*/
assert((0 > INVALID_HISTINFO_NUM) && (0 > UNKNOWN_HISTINFO_NUM));
recvpool_ctl = recvpool.recvpool_ctl;
if (0 <= need_histinfo_num)
{ /* Handle simplest case first. Get the "need_histinfo_num"'th history record directly from the instance file */
if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd))
{ /* The history record that is being requested needs to be found in the -updateresync input instance file
* (not the receiver side instance file).
*/
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Searching for desired history in the -updateresync input instance file\n");
gtmrecv_updresync_histinfo_get(need_histinfo_num, histinfo);
} else
{ /* The history record needs to be found in the receiver side instance file */
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Searching for the desired history in the replication instance file\n");
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
/* above macro will "return" if repl_connection_reset OR gtmrecv_wait_for_jnl_seqno is set */
status = repl_inst_histinfo_get(need_histinfo_num, histinfo);
rel_lock(jnlpool.jnlpool_dummy_reg);
if (0 != status)
{ /* Close the connection */
assert(ERR_REPLINSTNOHIST == status);
assert(FALSE);
gtmrecv_autoshutdown(); /* should not return */
}
}
maintain_rcvd_strm_histinfo = TRUE;
} else if (UNKNOWN_HISTINFO_NUM == need_histinfo_num)
{ /* This value was sent in a previous REPL_STRMINFO message for a particular non-zero stream # because
* we had not yet played this history record in the instance file. Return history record directly from
* where the receiver server saved a copy of the last unprocessed history record for this stream number.
*/
assert(need_histinfo_strm_num);
assert(remote_side->is_supplementary);
assert((0 < need_histinfo_strm_num) && (MAX_SUPPL_STRMS > need_histinfo_strm_num));
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for desired history in the receive pool\n");
*histinfo = recvpool_ctl->last_valid_strm_histinfo[need_histinfo_strm_num];
maintain_rcvd_strm_histinfo = FALSE;
} else
{
first_unprocessed_seqno = recvpool.upd_proc_local->read_jnl_seqno;
repl_log(gtmrecv_log_fp, TRUE, FALSE, "Update process has processed upto seqno "INT8_FMT" "INT8_FMTX"\n",
first_unprocessed_seqno, first_unprocessed_seqno);
suppl_propagate_primary = remote_side->is_supplementary;
if (!suppl_propagate_primary)
last_valid_histinfo_seqno = recvpool_ctl->last_valid_histinfo.start_seqno;
else
last_valid_histinfo_seqno = recvpool_ctl->last_valid_strm_histinfo[0].start_seqno;
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Starting seqno of the last valid history in the receive pool is "INT8_FMT" "INT8_FMTX"\n",
last_valid_histinfo_seqno, last_valid_histinfo_seqno);
if (last_valid_histinfo_seqno >= first_unprocessed_seqno)
last_unprocessed_histinfo_seqno = last_valid_histinfo_seqno;
else
last_unprocessed_histinfo_seqno = MAX_SEQNO;
if (last_unprocessed_histinfo_seqno && (need_histinfo_seqno > last_unprocessed_histinfo_seqno))
{
/* NOTE0: The source server is requesting histinfo information for a seqno whose corresponding
* histinfo has also not yet been processed by the update process (and hence not present in the
* instance file). Find latest histinfo information that is stored in receive pool.
* NOTE1: Even though there could be more than one unprocessed history record in the receive pool,
* the source should request only the last one for comparison. If the last history record on the
* receiver matches on the source side too, replication can resume from there. If not, the receiver
* side should do a rollback (REPL_ROLLBACK_FIRST message). That is why it is enough to maintain
* recvpool_ctl->last_valid_histinfo and not pointers to all the unprocessed histories.
* NOTE2: There is one exception to this and that is if the receiver server had already connected
* to a source server and placed records in the receiver pool and then lost the connection and
* reestablished connection (with the same or a different source server) AND had been started with
* the -noresync option. In this case, it will not see a REPL_ROLLBACK_FIRST message but instead
* will go back in history to find the first matching history. But in this case, the -noresync is
* valid only for the FIRST connection and is cleared for future connections. So reconnections would
* assume as if -noresync was not specified and hence will fall into the same REPL_ROLLBACK_FIRST
* category as described in NOTE0. Assert it below.
* NOTE3: In the case of a propagating primary supplementary instance, we need to not just store the
* last received history record but also one for each stream possible.
*/
assert(!recvpool.gtmrecv_local->updateresync);
assert(!recvpool.gtmrecv_local->noresync);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Searching for the desired history in the receive pool\n");
if (!suppl_propagate_primary)
memcpy(histinfo, &recvpool_ctl->last_valid_histinfo, SIZEOF(repl_histinfo));
else
{
assert(!need_histinfo_strm_num);
memcpy(histinfo, &recvpool_ctl->last_valid_strm_histinfo[0], SIZEOF(repl_histinfo));
}
assert(!recvpool_ctl->insert_strm_histinfo);
} else if (recvpool.gtmrecv_local->updateresync && (FD_INVALID != recvpool.gtmrecv_local->updresync_instfile_fd))
{ /* The receiver was started with -UPDATERESYNC=<INSTFILENAME>. The history record that is being requested
* needs to be found in the input instance file (not the receiver side instance file).
* Read the history record corresponding to need_histinfo_seqno.
*/
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Searching for desired history in the -updateresync input instance file\n");
gtmrecv_updresync_histinfo_find_seqno(need_histinfo_seqno, need_histinfo_strm_num, histinfo);
assert(histinfo->start_seqno);
} else
{ /* The seqno has been processed by the update process. Hence the histinfo
* for this will be found in the instance file. Search there.
*/
assert(NULL != jnlpool.jnlpool_dummy_reg);
repl_log(gtmrecv_log_fp, TRUE, TRUE,
"Searching for the desired history in the replication instance file\n");
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
status = repl_inst_wrapper_histinfo_find_seqno(need_histinfo_seqno, need_histinfo_strm_num, histinfo);
rel_lock(jnlpool.jnlpool_dummy_reg);
if (0 != status)
{ /* Close the connection */
assert(ERR_REPLINSTNOHIST == status);
gtmrecv_autoshutdown(); /* should not return */
}
assert((histinfo->histinfo_num != (jnlpool.repl_inst_filehdr->num_histinfo - 1))
|| (histinfo->start_seqno == jnlpool_ctl->last_histinfo_seqno));
if (0 < need_histinfo_strm_num)
{ /* About to send to a non-supplementary instance. It does not understand strm_seqnos.
* So convert it back to a format it understands.
*/
CONVERT_SUPPL2NONSUPPL_HISTINFO(*histinfo);
}
assert(histinfo->start_seqno);
assert(histinfo->start_seqno < need_histinfo_seqno);
}
maintain_rcvd_strm_histinfo = (0 == histinfo->strm_index);
}
if (maintain_rcvd_strm_histinfo && recvpool_ctl->insert_strm_histinfo)
{
assert(remote_side->is_supplementary);
need_histinfo_strm_num = histinfo->strm_index;
assert((0 <= need_histinfo_strm_num) && (MAX_SUPPL_STRMS > need_histinfo_strm_num));
assert(IS_REPL_INST_UUID_NULL(recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num].lms_group));
assert((FALSE == recvpool_ctl->is_valid_strm_histinfo[need_histinfo_strm_num])
|| !memcmp(&recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num],
histinfo, SIZEOF(repl_histinfo)));
memcpy(&recvpool_ctl->last_rcvd_strm_histinfo[need_histinfo_strm_num], histinfo, SIZEOF(repl_histinfo));
recvpool_ctl->is_valid_strm_histinfo[need_histinfo_strm_num] = TRUE;
if (recvpool_ctl->max_strm_histinfo <= need_histinfo_strm_num)
recvpool_ctl->max_strm_histinfo = need_histinfo_strm_num + 1;
}
return;
}
STATICFNDEF void do_main_loop(boolean_t crash_restart)
{
/* The work-horse of the Receiver Server */
boolean_t dont_reply_to_heartbeat = FALSE, is_repl_cmpc;
boolean_t uncmpfail, send_cross_endian;
char print_msg[1024];
gtmrecv_local_ptr_t gtmrecv_local;
gtm_time4_t ack_time;
int4 msghdrlen, strm_num;
int4 need_histinfo_num;
int cmpret;
int msg_type, msg_len;
int status; /* needed for REPL_{SEND,RECV}_LOOP */
int torecv_len, recvd_len, recvd_this_iter; /* needed for REPL_RECV_LOOP */
int tosend_len, sent_len, sent_this_iter; /* needed for REPL_SEND_LOOP */
recvpool_ctl_ptr_t recvpool_ctl;
repl_cmpinfo_msg_ptr_t cmptest_msg;
repl_cmpinfo_msg_t cmpsolve_msg;
repl_cmpmsg_ptr_t cmpmsgp;
repl_heartbeat_msg_t heartbeat;
repl_histinfo histinfo;
repl_needhistinfo_msg_ptr_t need_histinfo_msg;
repl_needinst_msg_ptr_t need_instinfo_msg;
repl_needstrminfo_msg_ptr_t need_strminfo_msg;
repl_old_instinfo_msg_t old_instinfo_msg;
repl_old_needinst_msg_ptr_t old_need_instinfo_msg;
repl_start_msg_ptr_t msgp;
repl_start_reply_msg_t *start_msg;
seq_num ack_seqno, temp_ack_seqno;
seq_num request_from, recvd_jnl_seqno;
sgmnt_addrs *repl_csa;
uchar_ptr_t old_buffp;
uint4 recvd_start_flags;
uLong cmplen;
uLongf destlen;
unsigned char *msg_ptr; /* needed for REPL_{SEND,RECV}_LOOP */
unsigned char remote_jnl_ver;
upd_proc_local_ptr_t upd_proc_local;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
gtmrecv_local = recvpool.gtmrecv_local;
gtmrecv_wait_for_jnl_seqno = FALSE;
assert((NULL != jnlpool.jnlpool_dummy_reg) && jnlpool.jnlpool_dummy_reg->open);
repl_csa = &FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs;
DEBUG_ONLY(
assert(!repl_csa->hold_onto_crit);
ASSERT_VALID_JNLPOOL(repl_csa);
)
/* If BAD_TRANS was written by the update process, it would have updated recvpool_ctl->jnl_seqno accordingly.
* Only otherwise, do we need to wait for it to write "recvpool_ctl->jnl_seqno".
*/
if (!gtmrecv_bad_trans_sent)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for Update Process to write jnl_seqno\n");
while (QWEQ(recvpool_ctl->jnl_seqno, seq_num_zero))
{
SHORT_SLEEP(GTMRECV_WAIT_FOR_STARTJNLSEQNO);
gtmrecv_poll_actions(0, 0, NULL);
if (repl_connection_reset)
return;
}
/* The call to "gtmrecv_poll_actions" above might have set the variable "gtmrecv_wait_for_jnl_seqno" to TRUE.
* In that case, we need to reset it to FALSE here as we are now going to wait for the jnl_seqno below.
* Not doing so will cause us to wait for jnl_seqno TWICE (once now and once when we later enter this function).
*/
gtmrecv_wait_for_jnl_seqno = FALSE;
/* Since remote primary is multisite capable (otherwise we would have issued an error), we need to send the
* journal seqno of this instance for comparison. If the receiver has received more seqnos than have been
* processed by the update process, we should be sending the last received seqno across to avoid receiving
* duplicate and out-of-order seqnos. This is maintained in "recvpool_ctl->jnl_seqno" and is guaranteed to
* be greater than or equal to the journal seqno of this instance.
*/
request_from = recvpool_ctl->jnl_seqno;
/* If this is the first time the update process initialized "recvpool_ctl->jnl_seqno", it should be
* equal to "jnlpool_ctl->jnl_seqno". But if the receiver had already connected and received a bunch
* of seqnos and if the update process did not process all of them and if the receiver disconnects
* and re-establishes the connection, the value of "recvpool_ctl->jnl_seqno" could be greater than
* "jnlpool_ctl->jnl_seqno" if there is non-zero backlog on the secondary. Assert accordingly.
* There is one exception to this and that is if this is a root primary supplementary instance.
* In that case, the receive pool talks about the non-supplementary instance stream jnl_seqnos whereas
* the jnlpool talks about the merged stream of jnl_seqnos (including any local updates). Therefore we
* cannot compare the two at all.
*/
assert((!jnlpool_ctl->upd_disabled && jnlpool.repl_inst_filehdr->is_supplementary)
|| (recvpool_ctl->jnl_seqno >= jnlpool_ctl->jnl_seqno));
assert(request_from);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Requesting transactions from JNL_SEQNO "INT8_FMT" "INT8_FMTX"\n",
request_from, request_from);
/* Send (re)start JNL_SEQNO to Source Server.
* Note that even though we might know the endianness of the source side at this time, we still send this
* message in the native endian format. This keeps the logic on the source server side simple. This is the only
* exception to the general rule that the receiver server does all endian conversion for messages except the
* first one in the connection handshake. The source side knows to endian convert this (instead of expecting it
* to be in the source endian format) if this EPL_START_JNL_SEQNO message is not the first one in the connection.
* The only exception is if the source side is pre-V55000 AND we have already determined the endianness of the
* source side (i.e. the REPL_START_JNL_SEQNO message about to be sent is not the first one for this connection)
* AND the source is cross-endian. In that case, the pre-V55000 source does not know to handle a
* receiver-side-native-endian format message. So endian convert the message only in this case.
*/
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Sending REPL_START_JNL_SEQNO message with seqno "INT8_FMT" "INT8_FMTX"\n",
request_from, request_from);
send_cross_endian = (remote_side->endianness_known && remote_side->cross_endian
&& (REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver));
msgp = (repl_start_msg_ptr_t)gtmrecv_msgp;
memset(msgp, 0, SIZEOF(*msgp));
/* Since REPL_START_JNL_SEQNO is 0, there is no endian conversion necessary but for completeness we do it */
msgp->type = send_cross_endian ? GTM_BYTESWAP_32(REPL_START_JNL_SEQNO) : REPL_START_JNL_SEQNO;
if (send_cross_endian)
*((seq_num *)msgp->start_seqno) = GTM_BYTESWAP_64(request_from);
else
*((seq_num *)msgp->start_seqno) = request_from;
msgp->start_flags = START_FLAG_NONE;
msgp->start_flags |= (gtmrecv_options.stopsourcefilter ? START_FLAG_STOPSRCFILTER : 0);
/* If -UPDATERESYNC is specified then let pre-V55000 source server know so it does not ask for history record
* exchange. In case of post-V55000 source server, we expect a value to the -updateresync qualifier (the instance
* file name) and that is used for the history record exchange. In that case the source server ignores the
* START_FLAG_UPDATERESYNC bit and requests a history exchange anyways.
*/
msgp->start_flags |= (gtmrecv_local->updateresync ? START_FLAG_UPDATERESYNC : 0);
/* Let source server know if -NORESYNC was specified in receiver server startup */
msgp->start_flags |= (gtmrecv_local->noresync ? START_FLAG_NORESYNC : 0);
msgp->start_flags |= START_FLAG_HASINFO;
if (this_side->is_std_null_coll)
msgp->start_flags |= START_FLAG_COLL_M;
msgp->start_flags |= START_FLAG_VERSION_INFO;
GTMTRIG_ONLY(msgp->start_flags |= START_FLAG_TRIGGER_SUPPORT;)
if (send_cross_endian)
msgp->start_flags = GTM_BYTESWAP_32(msgp->start_flags);
msgp->jnl_ver = this_side->jnl_ver;
msgp->proto_ver = REPL_PROTO_VER_THIS;
msgp->node_endianness = NODE_ENDIANNESS;
msgp->is_supplementary = jnlpool.repl_inst_filehdr->is_supplementary;
msgp->len = send_cross_endian ? GTM_BYTESWAP_32(MIN_REPL_MSGLEN) : MIN_REPL_MSGLEN;
msg_len = MIN_REPL_MSGLEN;
REPL_SEND_LOOP(gtmrecv_sock_fd, msgp, msg_len, FALSE, &gtmrecv_poll_immediate)
{
GTMRECV_POLL_ACTIONS(0, 0, NULL);
}
CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_START_JNL_SEQNO");
}
gtmrecv_bad_trans_sent = FALSE;
request_from = recvpool_ctl->jnl_seqno;
assert(request_from >= seq_num_one);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Waiting for REPL_WILL_RESTART_WITH_INFO or REPL_ROLLBACK_FIRST message\n");
/* Receive journal data and place it in the Receive Pool */
buff_start = (unsigned char *)gtmrecv_msgp;
buffp = buff_start;
buff_unprocessed = 0;
data_len = 0;
write_loc = recvpool_ctl->write;
write_wrap = recvpool_ctl->write_wrap;
repl_recv_data_recvd = 0;
repl_recv_data_processed = 0;
repl_recv_postfltr_data_procd = 0;
repl_recv_lastlog_data_recvd = 0;
repl_recv_lastlog_data_procd = 0;
msghdrlen = REPL_MSG_HDRLEN;
while (TRUE)
{
recvd_len = gtmrecv_max_repl_msglen - buff_unprocessed;
while ((SS_NORMAL == (status = repl_recv(gtmrecv_sock_fd,
(buffp + buff_unprocessed), &recvd_len, FALSE, &gtmrecv_poll_interval)))
&& (0 == recvd_len))
{
recvd_len = gtmrecv_max_repl_msglen - buff_unprocessed;
if (xoff_sent)
{
DO_FLOW_CONTROL(write_loc);
}
if (xoff_sent && GTMRECV_XOFF_LOG_CNT <= xoff_msg_log_cnt)
{ /* update process is still running slow, gtmrecv_poll_interval is now 0.
* Force wait before logging any message.
*/
SHORT_SLEEP(GTMRECV_POLL_INTERVAL >> 10); /* approximate in ms */
REPL_DPRINT1("Waiting for Update Process to clear recvpool space\n");
xoff_msg_log_cnt = 0;
} else if (xoff_sent)
xoff_msg_log_cnt++;
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
if (SS_NORMAL != status)
{
if (EREPL_RECV == repl_errno)
{
if (REPL_CONN_RESET(status))
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Connection reset. Status = %d ; %s\n",
status, STRERROR(status));
repl_connection_reset = TRUE;
repl_close(&gtmrecv_sock_fd);
return;
} else
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error in receiving from source. "
"Error in recv : %s", STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
} else if (EREPL_SELECT == repl_errno)
{
SNPRINTF(print_msg, SIZEOF(print_msg), "Error in receiving from source. Error in select : %s",
STRERROR(status));
rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(print_msg));
}
}
if (repl_connection_reset)
return;
# ifdef REPL_CMP_SOLVE_TESTING
/* Received communication from the source server, so we can cancel the timer */
if (TREF(gtm_environment_init) && repl_cmp_solve_timer_set)
{
cancel_timer((TID)repl_cmp_solve_rcv_timeout);
repl_cmp_solve_timer_set = FALSE;
}
# endif
/* Something on the replication pipe - read it */
REPL_DPRINT3("Pending data len : %d Prev buff unprocessed : %d\n", data_len, buff_unprocessed);
buff_unprocessed += recvd_len;
repl_recv_data_recvd += (qw_num)recvd_len;
if (gtmrecv_logstats)
repl_log(gtmrecv_log_fp, FALSE, FALSE, "Recvd : %d Total : %d\n", recvd_len, repl_recv_data_recvd);
while (msghdrlen <= buff_unprocessed)
{
if (0 == data_len)
{
assert(0 == ((unsigned long)buffp % REPL_MSG_ALIGN));
if (!remote_side->endianness_known)
{
remote_side->endianness_known = TRUE;
msg_type = ((repl_msg_ptr_t)buffp)->type;
assert((REPL_MSGTYPE_LAST > msg_type) || (REPL_MSGTYPE_LAST > GTM_BYTESWAP_32(msg_type)));
if ((REPL_MSGTYPE_LAST < msg_type) && (REPL_MSGTYPE_LAST > GTM_BYTESWAP_32(msg_type)))
{
remote_side->cross_endian = TRUE;
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Source and Receiver sides have opposite "
"endianness\n");
} else
{
remote_side->cross_endian = FALSE;
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Source and Receiver sides have same "
"endianness\n");
}
}
if (remote_side->cross_endian)
{
((repl_msg_ptr_t)buffp)->type = GTM_BYTESWAP_32(((repl_msg_ptr_t)buffp)->type);
((repl_msg_ptr_t)buffp)->len = GTM_BYTESWAP_32(((repl_msg_ptr_t)buffp)->len);
}
msg_type = (((repl_msg_ptr_t)buffp)->type & REPL_TR_CMP_MSG_TYPE_MASK);
if (REPL_TR_CMP_JNL_RECS == msg_type)
{
msg_len = ((repl_msg_ptr_t)buffp)->len - REPL_MSG_HDRLEN;
gtmrecv_repl_cmpmsglen = msg_len;
gtmrecv_repl_uncmpmsglen = (((repl_msg_ptr_t)buffp)->type >> REPL_TR_CMP_MSG_TYPE_BITS);
assert(0 < gtmrecv_repl_uncmpmsglen);
assert(REPL_TR_CMP_THRESHOLD > gtmrecv_repl_uncmpmsglen);
/* Since msg_len is compressed length, it need not be 8-byte aligned. Make it so
* since 8-byte aligned length would have been sent by the source server anyways.
*/
msg_len = ROUND_UP(msg_len, REPL_MSG_ALIGN);
buffp += REPL_MSG_HDRLEN;
exp_data_len = gtmrecv_repl_uncmpmsglen;
buff_unprocessed -= REPL_MSG_HDRLEN;
GTMRECV_SET_BUFF_TARGET_CMPBUFF(gtmrecv_repl_cmpmsglen, gtmrecv_repl_uncmpmsglen,
gtmrecv_cur_cmpmsglen);
} else if (REPL_TR_CMP_JNL_RECS2 == msg_type)
{ /* A REPL_TR_CMP_JNL_RECS2 message is special in that it has a bigger message header.
* So check if unprocessed length is greater than the header. If not need to read more.
*/
msghdrlen = REPL_MSG_HDRLEN2;
if (msghdrlen > buff_unprocessed) /* Did not receive the full-header. */
break; /* Break out of here and read more data first. */
msghdrlen = REPL_MSG_HDRLEN; /* reset to regular msg hdr length for future messages */
cmpmsgp = (repl_cmpmsg_ptr_t)buffp;
if (remote_side->cross_endian)
{
cmpmsgp->cmplen = GTM_BYTESWAP_32(cmpmsgp->cmplen);
cmpmsgp->uncmplen = GTM_BYTESWAP_32(cmpmsgp->uncmplen);
}
gtmrecv_repl_cmpmsglen = cmpmsgp->cmplen;
gtmrecv_repl_uncmpmsglen = cmpmsgp->uncmplen;
assert(0 < gtmrecv_repl_uncmpmsglen);
assert(REPL_TR_CMP_THRESHOLD <= gtmrecv_repl_uncmpmsglen);
msg_len = ((repl_msg_ptr_t)buffp)->len - REPL_MSG_HDRLEN2;
/* Unlike REPL_TR_CMP_JNL_RECS message, msg_len is guaranteed to be 8-byte aligned here */
buffp += REPL_MSG_HDRLEN2;
exp_data_len = gtmrecv_repl_uncmpmsglen;
buff_unprocessed -= REPL_MSG_HDRLEN2;
GTMRECV_SET_BUFF_TARGET_CMPBUFF(gtmrecv_repl_cmpmsglen, gtmrecv_repl_uncmpmsglen,
gtmrecv_cur_cmpmsglen);
} else
{
msg_len = ((repl_msg_ptr_t)buffp)->len - REPL_MSG_HDRLEN;
buffp += REPL_MSG_HDRLEN;
exp_data_len = msg_len;
buff_unprocessed -= REPL_MSG_HDRLEN;
/* If the target buffer is the receive pool, prepare the receive pool for the write.
* In addition, check if transaction will fit in.
* If the target buffer is not the receive pool, we will do this check after uncompression.
*/
PREPARE_RECVPOOL_FOR_WRITE(exp_data_len, 0);
}
assert(0 <= buff_unprocessed);
assert(0 == (msg_len % REPL_MSG_ALIGN));
data_len = msg_len;
assert(0 == (exp_data_len % REPL_MSG_ALIGN));
}
assert(0 == (data_len % REPL_MSG_ALIGN));
buffered_data_len = ((data_len <= buff_unprocessed) ? data_len : buff_unprocessed);
buffered_data_len = ROUND_DOWN2(buffered_data_len, REPL_MSG_ALIGN);
old_buffp = buffp;
buffp += buffered_data_len;
buff_unprocessed -= buffered_data_len;
data_len -= buffered_data_len;
switch(msg_type)
{
case REPL_TR_JNL_RECS:
case REPL_TR_CMP_JNL_RECS:
case REPL_TR_CMP_JNL_RECS2:
case REPL_OLD_TRIPLE:
case REPL_HISTREC:
is_repl_cmpc = ((REPL_TR_CMP_JNL_RECS == msg_type) || (REPL_TR_CMP_JNL_RECS2 == msg_type));
if (!is_repl_cmpc)
{
COPY_TO_RECVPOOL(old_buffp, buffered_data_len); /* uses and updates "write_loc" */
} else
{
memcpy(gtmrecv_cmpmsgp + gtmrecv_cur_cmpmsglen, old_buffp, buffered_data_len);
gtmrecv_cur_cmpmsglen += buffered_data_len;
assert(gtmrecv_cur_cmpmsglen <= gtmrecv_max_repl_cmpmsglen);
}
repl_recv_data_processed += (qw_num)buffered_data_len;
if (0 == data_len)
{
process_tr_buff(msg_type);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
}
break;
case REPL_LOSTTNCOMPLETE:
if (0 == data_len)
{
assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_LOSTTNCOMPLETE message\n");
if (SS_NORMAL != (status = repl_inst_reset_zqgblmod_seqno_and_tn()))
{ /* only reason we know currently for the above function to fail is due
* to a concurrent online rollback. In this case, we cannot continue
* and need to start afresh.
*/
assert(-1 == status);
gtmrecv_onln_rlbk_clnup();
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
}
}
break;
case REPL_HEARTBEAT:
if (0 == data_len)
{ /* Heartbeat msg contents start from buffp - msg_len */
GTM_WHITE_BOX_TEST(WBTEST_REPL_HEARTBEAT_NO_ACK, dont_reply_to_heartbeat, TRUE);
if (dont_reply_to_heartbeat)
{
dont_reply_to_heartbeat = FALSE;
break;
}
memcpy(heartbeat.ack_seqno, buffp - msg_len, msg_len);
assert(remote_side->endianness_known); /* only then is remote_side->cross_endian
* reliable */
if (!remote_side->cross_endian)
{
ack_time = *(gtm_time4_t *)&heartbeat.ack_time[0];
memcpy((uchar_ptr_t)&ack_seqno,
(uchar_ptr_t)&heartbeat.ack_seqno[0], SIZEOF(seq_num));
} else
{
ack_time = GTM_BYTESWAP_32(*(gtm_time4_t *)&heartbeat.ack_time[0]);
memcpy((uchar_ptr_t)&temp_ack_seqno,
(uchar_ptr_t)&heartbeat.ack_seqno[0], SIZEOF(seq_num));
ack_seqno = GTM_BYTESWAP_64(temp_ack_seqno);
}
REPL_DPRINT4("HEARTBEAT received with time %ld SEQNO "INT8_FMT" at %ld\n",
ack_time, ack_seqno, time(NULL));
ack_seqno = upd_proc_local->read_jnl_seqno;
if (!remote_side->cross_endian)
{
heartbeat.type = REPL_HEARTBEAT;
heartbeat.len = MIN_REPL_MSGLEN;
memcpy((uchar_ptr_t)&heartbeat.ack_seqno[0],
(uchar_ptr_t)&ack_seqno, SIZEOF(seq_num));
} else
{
heartbeat.type = GTM_BYTESWAP_32(REPL_HEARTBEAT);
heartbeat.len = GTM_BYTESWAP_32(MIN_REPL_MSGLEN);
temp_ack_seqno = GTM_BYTESWAP_64(ack_seqno);
memcpy((uchar_ptr_t)&heartbeat.ack_seqno[0],
(uchar_ptr_t)&temp_ack_seqno, SIZEOF(seq_num));
}
REPL_SEND_LOOP(gtmrecv_sock_fd, &heartbeat, MIN_REPL_MSGLEN,
FALSE, &gtmrecv_poll_immediate)
{
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
CHECK_REPL_SEND_LOOP_ERROR(status, "REPL_HEARTBEAT");
REPL_DPRINT4("HEARTBEAT sent with time %ld SEQNO "INT8_FMT" at %ld\n",
ack_time, ack_seqno, time(NULL));
}
break;
case REPL_OLD_NEED_INSTANCE_INFO:
if (0 == data_len)
{
assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN);
old_need_instinfo_msg = (repl_old_needinst_msg_ptr_t)(buffp -
msg_len - REPL_MSG_HDRLEN);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_OLD_NEED_INSTANCE_INFO message"
" from primary instance [%s]\n", old_need_instinfo_msg->instname);
if (jnlpool.repl_inst_filehdr->is_supplementary)
{ /* Issue REPL2OLD error because this is a supplementary instance and remote
* side runs a GT.M version that does not know the supplementary protocol */
rts_error(VARLSTCNT(6) ERR_REPL2OLD, 4,
LEN_AND_STR(old_need_instinfo_msg->instname),
LEN_AND_STR(jnlpool.repl_inst_filehdr->inst_info.this_instname));
}
/* The source server does not understand the supplementary protocol.
* So make sure -UPDATERESYNC if specified at receiver server startup
* had no value. If not, issue error.
*/
if (gtmrecv_local->updateresync
&& (FD_INVALID != gtmrecv_local->updresync_instfile_fd))
{
rts_error(VARLSTCNT(6) ERR_UPDSYNCINSTFILE, 0, ERR_TEXT, 2,
RTS_ERROR_LITERAL("Source side is non-supplementary implies "
"-UPDATERESYNC needs no value specified"));
}
/* Initialize the remote side protocol version from "proto_ver" field of this msg */
remote_side->proto_ver = old_need_instinfo_msg->proto_ver;
assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver);
assert(REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver);
/*************** Send REPL_OLD_INSTANCE_INFO message ***************/
memset(&old_instinfo_msg, 0, SIZEOF(old_instinfo_msg));
memcpy(old_instinfo_msg.instname,
jnlpool.repl_inst_filehdr->inst_info.this_instname, MAX_INSTNAME_LEN - 1);
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
old_instinfo_msg.was_rootprimary = (unsigned char)repl_inst_was_rootprimary();
rel_lock(jnlpool.jnlpool_dummy_reg);
gtmrecv_repl_send((repl_msg_ptr_t)&old_instinfo_msg, REPL_OLD_INSTANCE_INFO,
MIN_REPL_MSGLEN, "REPL_OLD_INSTANCE_INFO", MAX_SEQNO);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
/* Do not allow an instance which was formerly a root primary or which still
* has a non-zero value of "zqgblmod_seqno" to start up as a tertiary.
*/
assert(jnlpool.jnlpool_ctl == jnlpool_ctl);
if ((old_instinfo_msg.was_rootprimary || jnlpool_ctl->max_zqgblmod_seqno)
&& !old_need_instinfo_msg->is_rootprimary)
{
gtm_putmsg(VARLSTCNT(4) ERR_PRIMARYNOTROOT, 2,
LEN_AND_STR((char *) old_need_instinfo_msg->instname));
gtmrecv_autoshutdown(); /* should not return */
assert(FALSE);
}
memcpy(jnlpool_ctl->primary_instname, old_need_instinfo_msg->instname,
MAX_INSTNAME_LEN - 1);
}
break;
case REPL_NEED_INSTINFO:
if (0 == data_len)
{
assert(msg_len == SIZEOF(repl_needinst_msg_t) - REPL_MSG_HDRLEN);
need_instinfo_msg = (repl_needinst_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN);
gtmrecv_check_and_send_instinfo(need_instinfo_msg, IS_RCVR_SRVR_TRUE);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
}
break;
case REPL_CMP_TEST:
if (0 == data_len)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_CMP_TEST message\n");
uncmpfail = FALSE;
if (ZLIB_CMPLVL_NONE == gtm_zlib_cmp_level)
{ /* Receiver does not have compression enabled in the first place.
* Send dummy REPL_CMP_SOLVE response message.
*/
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Environment variable "
"gtm_zlib_cmp_level specifies NO decompression (set to %d)\n",
gtm_zlib_cmp_level);
uncmpfail = TRUE;
}
assert(remote_side->endianness_known); /* ensure remote_side->cross_endian
* is reliable */
if (!uncmpfail)
{
assert(msg_len == REPL_MSG_CMPINFOLEN - REPL_MSG_HDRLEN);
cmptest_msg = (repl_cmpinfo_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN);
if (!remote_side->cross_endian)
cmplen = cmptest_msg->datalen;
else
cmplen = GTM_BYTESWAP_32(cmptest_msg->datalen);
if (REPL_MSG_CMPEXPDATALEN < cmplen)
{
assert(FALSE); /* since src srvr should not have sent such a msg */
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Compression test message "
"has compressed data length (%d) greater than receiver "
"allocated length (%d)\n", (int)cmplen,
REPL_MSG_CMPEXPDATALEN);
uncmpfail = TRUE;
}
}
if (!uncmpfail)
{
destlen = REPL_MSG_CMPEXPDATALEN; /* initialize available
* decompressed buffer space */
ZLIB_UNCOMPRESS(&cmpsolve_msg.data[0], destlen, &cmptest_msg->data[0],
cmplen, cmpret);
GTM_WHITE_BOX_TEST(WBTEST_REPL_TEST_UNCMP_ERROR, cmpret, Z_DATA_ERROR);
switch(cmpret)
{
case Z_MEM_ERROR:
assert(FALSE);
repl_log(gtmrecv_log_fp, TRUE, TRUE,
GTM_ZLIB_Z_MEM_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SOLVE_STR);
break;
case Z_BUF_ERROR:
assert(FALSE);
repl_log(gtmrecv_log_fp, TRUE, TRUE,
GTM_ZLIB_Z_BUF_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SOLVE_STR);
break;
case Z_DATA_ERROR:
assert(gtm_white_box_test_case_enabled
&& (WBTEST_REPL_TEST_UNCMP_ERROR
== gtm_white_box_test_case_number));
repl_log(gtmrecv_log_fp, TRUE, TRUE,
GTM_ZLIB_Z_DATA_ERROR_STR
GTM_ZLIB_UNCMP_ERR_SOLVE_STR);
break;
}
if (Z_OK != cmpret)
uncmpfail = TRUE;
}
if (!uncmpfail)
{
cmpsolve_msg.datalen = (int4)destlen;
GTM_WHITE_BOX_TEST(WBTEST_REPL_TEST_UNCMP_ERROR, cmpsolve_msg.datalen,
REPL_MSG_CMPDATALEN - 1);
if (REPL_MSG_CMPDATALEN != cmpsolve_msg.datalen)
{ /* decompression did not yield precompressed data length */
assert(gtm_white_box_test_case_enabled
&& (WBTEST_REPL_TEST_UNCMP_ERROR
== gtm_white_box_test_case_number));
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPLEN_ERROR_STR
"\n", cmpsolve_msg.datalen, REPL_MSG_CMPDATALEN);
uncmpfail = TRUE;
}
}
if (uncmpfail)
{
cmpsolve_msg.datalen = REPL_RCVR_CMP_TEST_FAIL;
repl_log(gtmrecv_log_fp, TRUE, TRUE, GTM_ZLIB_UNCMPTRANSITION_STR);
}
if (remote_side->cross_endian)
cmpsolve_msg.datalen = GTM_BYTESWAP_32(cmpsolve_msg.datalen);
cmpsolve_msg.proto_ver = REPL_PROTO_VER_THIS;
# ifdef REPL_CMP_SOLVE_TESTING
if (TREF(gtm_environment_init))
{
start_timer((TID)repl_cmp_solve_rcv_timeout, 15 * 60 * 1000,
repl_cmp_solve_rcv_timeout, 0, NULL);
repl_cmp_solve_timer_set = TRUE;
}
# endif
gtmrecv_repl_send((repl_msg_ptr_t)&cmpsolve_msg, REPL_CMP_SOLVE,
REPL_MSG_CMPINFOLEN, "REPL_CMP_SOLVE", MAX_SEQNO);
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
if (!uncmpfail)
repl_zlib_cmp_level = gtm_zlib_cmp_level;
}
break;
case REPL_NEED_STRMINFO:
if (0 == data_len)
{
assert(REPL_PROTO_VER_SUPPLEMENTARY <= remote_side->proto_ver);
assert(remote_side->is_supplementary);
assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN);
need_strminfo_msg = (repl_needstrminfo_msg_ptr_t)(buffp - msg_len
- REPL_MSG_HDRLEN);
gtmrecv_process_need_strminfo_msg(need_strminfo_msg);
/* Check for error return from above function call */
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
}
break;
case REPL_NEED_HISTINFO:
/* case REPL_NEED_TRIPLE_INFO: too but that message has been renamed to REPL_NEED_HISTINFO */
if (0 == data_len)
{
assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver);
assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN);
need_histinfo_msg = (repl_needhistinfo_msg_ptr_t)(buffp - msg_len
- REPL_MSG_HDRLEN);
gtmrecv_process_need_histinfo_msg(need_histinfo_msg, &histinfo);
/* Check for error return from above function call */
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
/* Send the histinfo */
gtmrecv_send_histinfo(&histinfo);
/* Check for error return from above function call as well */
if (repl_connection_reset || gtmrecv_wait_for_jnl_seqno)
return;
}
break;
case REPL_WILL_RESTART_WITH_INFO:
case REPL_ROLLBACK_FIRST:
if (0 != data_len)
break;
/* Have received a REPL_WILL_RESTART_WITH_INFO or REPL_ROLLBACK_FIRST message. If
* have not yet received a REPL_OLD_NEED_INSTANCE_INFO or REPL_NEED_INSTINFO message
* (which would have initialized "remote_side->proto_ver"), it
* means the remote side does not understand multi-site replication communication
* protocol. Note that down.
*/
if (REPL_PROTO_VER_UNINITIALIZED == remote_side->proto_ver)
{ /* Issue REPL2OLD error because primary is dual-site */
rts_error(VARLSTCNT(6) ERR_REPL2OLD, 4, LEN_AND_STR(UNKNOWN_INSTNAME),
LEN_AND_STR(jnlpool.repl_inst_filehdr->inst_info.this_instname));
}
/* Assert that endianness_known and cross_endian have already been initialized.
* This ensures that remote_side->cross_endian is reliable */
assert(remote_side->endianness_known);
if (jnlpool.repl_inst_filehdr->was_rootprimary)
{ /* This is the first time an instance that was formerly a root primary
* is brought up as an immediate secondary of the new root primary. Once
* fetchresync rollback has happened and the receiver and source server
* have communicated successfully, the instance file header field that
* indicates this was a root primary can be reset to FALSE as the zero
* or non-zeroness of the "zqgblmod_seqno" field in the respective
* database file headers henceforth controls whether this instance can
* be brought up as a tertiary or not. Flush changes to file on disk.
*/
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
jnlpool.repl_inst_filehdr->was_rootprimary = FALSE;
repl_inst_flush_filehdr();
rel_lock(jnlpool.jnlpool_dummy_reg);
}
assert(REPL_PROTO_VER_MULTISITE <= remote_side->proto_ver);
assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN);
start_msg = (repl_start_reply_msg_ptr_t)(buffp - msg_len - REPL_MSG_HDRLEN);
assert((unsigned long)start_msg % SIZEOF(seq_num) == 0); /* alignment check */
memcpy((uchar_ptr_t)&recvd_jnl_seqno,
(uchar_ptr_t)start_msg->start_seqno, SIZEOF(seq_num));
/* Assert that "node_endianness" field reflects our cross-endian understanding */
BIGENDIAN_ONLY(assert((REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver)
|| (remote_side->cross_endian
&& (LITTLE_ENDIAN_MARKER == start_msg->node_endianness))
|| (!remote_side->cross_endian
&& (BIG_ENDIAN_MARKER == start_msg->node_endianness))));
LITTLEENDIAN_ONLY(assert((REPL_PROTO_VER_SUPPLEMENTARY > remote_side->proto_ver)
|| (remote_side->cross_endian
&& (BIG_ENDIAN_MARKER == start_msg->node_endianness))
|| (!remote_side->cross_endian
&& (LITTLE_ENDIAN_MARKER == start_msg->node_endianness))));
if (remote_side->cross_endian)
recvd_jnl_seqno = GTM_BYTESWAP_64(recvd_jnl_seqno);
/* Handle REPL_ROLLBACK_FIRST case (easy one) first */
if (REPL_ROLLBACK_FIRST == msg_type)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_ROLLBACK_FIRST message. "
"Secondary is out of sync with the primary. "
"Secondary at "INT8_FMT" "INT8_FMTX", Primary at "INT8_FMT" "INT8_FMTX". "
"Do ROLLBACK FIRST\n",
request_from, request_from, recvd_jnl_seqno, recvd_jnl_seqno);
if (gtmrecv_options.autorollback)
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Receiver was started with "
"-AUTOROLLBACK. Initiating automatic ONLINE FETCHRESYNC "
"ROLLBACK\n");
repl_connection_reset = TRUE;
repl_close(&gtmrecv_sock_fd);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Closing connection before starting "
"ROLLBACK\n");
if (SS_NORMAL != gtmrecv_start_onln_rlbk())
{ /* gtmrecv_start_onln_rlbk() would have issued the appropriate
* error message.
*/
assert(FALSE);
gtmrecv_autoshutdown(); /* should not return */
assert(FALSE);
}
grab_lock(jnlpool.jnlpool_dummy_reg, GRAB_LOCK_ONLY);
GTMRECV_ONLN_RLBK_CLNUP_IF_NEEDED;
/* The ONLINE ROLLBACK did not change the physical or the logical state as
* otherwise the above macro would have returned to the caller. But, since
* we have already disconnected the connection by now, we cannot resume the
* flow from this point on. So, go ahead and release the lock and shutdown
* the Receiver Server.
*/
rel_lock(jnlpool.jnlpool_dummy_reg);
} else
{
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Receiver was not started with "
"-AUTOROLLBACK. Manual ROLLBACK required. Shutting down\n");
}
gtmrecv_autoshutdown(); /* should not return */
assert(FALSE);
break;
}
/* Handle REPL_WILL_RESTART_WITH_INFO case now */
assert(REPL_WILL_RESTART_WITH_INFO == msg_type);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received REPL_WILL_RESTART_WITH_INFO message"
" with seqno "INT8_FMT" "INT8_FMTX"\n", recvd_jnl_seqno, recvd_jnl_seqno);
remote_side->jnl_ver = start_msg->jnl_ver;
remote_jnl_ver = remote_side->jnl_ver;
REPL_DPRINT3("Local jnl ver is octal %o, remote jnl ver is octal %o\n",
this_side->jnl_ver, remote_jnl_ver);
repl_check_jnlver_compat(!remote_side->cross_endian);
/* older versions zero filler that was in place of start_msg->start_flags,
* so we are okay fetching start_msg->start_flags unconditionally.
*/
GET_ULONG(recvd_start_flags, start_msg->start_flags);
if (remote_side->cross_endian)
recvd_start_flags = GTM_BYTESWAP_32(recvd_start_flags);
assert(remote_jnl_ver > V15_JNL_VER || 0 == recvd_start_flags);
if (remote_jnl_ver <= V15_JNL_VER) /* safety in pro */
recvd_start_flags = 0;
remote_side->is_std_null_coll = (recvd_start_flags & START_FLAG_COLL_M) ? TRUE : FALSE;
if (remote_side->is_std_null_coll != this_side->is_std_null_coll)
remote_side->null_subs_xform = (remote_side->is_std_null_coll
? STDNULL_TO_GTMNULL_COLL : GTMNULL_TO_STDNULL_COLL);
else
remote_side->null_subs_xform = FALSE;
/* this sets null_subs_xform regardless of remote_jnl_ver */
remote_side->is_supplementary = start_msg->is_supplementary;
remote_side->trigger_supported = (recvd_start_flags & START_FLAG_TRIGGER_SUPPORT)
? TRUE : FALSE;
assert((remote_jnl_ver - JNL_VER_EARLIEST_REPL) < ARRAYSIZE(repl_filter_old2cur));
assert((remote_jnl_ver - JNL_VER_EARLIEST_REPL) < ARRAYSIZE(repl_filter_cur2old));
if ((this_side->jnl_ver > remote_jnl_ver)
&& (IF_NONE != repl_filter_old2cur[remote_jnl_ver - JNL_VER_EARLIEST_REPL]))
{
assert(IF_INVALID != repl_filter_old2cur[remote_jnl_ver - JNL_VER_EARLIEST_REPL]);
/* reverse transformation should exist */
assert(IF_INVALID != repl_filter_cur2old[remote_jnl_ver - JNL_VER_EARLIEST_REPL]);
assert(IF_NONE != repl_filter_cur2old[remote_jnl_ver - JNL_VER_EARLIEST_REPL]);
gtmrecv_filter |= INTERNAL_FILTER;
/* Any time the ^#t global format version is bumped, the below
* assert will trip. This way, anyone who bumps the trigger label
* ensures that the internal filter routines in repl_filter.c are
* accordingly changed to upgrade triggers before applying them
* on the current database which supports the latest ^#t format.
*/
assert(0 == MEMCMP_LIT(HASHT_GBL_CURLABEL, "2"));
gtmrecv_alloc_filter_buff(gtmrecv_max_repl_msglen);
} else
{
gtmrecv_filter &= ~INTERNAL_FILTER;
if (NO_FILTER == gtmrecv_filter)
gtmrecv_free_filter_buff();
}
/* Don't send any more stopsourcefilter message */
gtmrecv_options.stopsourcefilter = FALSE;
assert(jnlpool.jnlpool_ctl == jnlpool_ctl);
assert(QWEQ(recvd_jnl_seqno, request_from)
|| (jnlpool.repl_inst_filehdr->is_supplementary
&& !jnlpool_ctl->upd_disabled && strm_index));
assert(this_side->is_supplementary == jnlpool.repl_inst_filehdr->is_supplementary);
if (this_side->is_supplementary && !remote_side->is_supplementary)
{
/* For the non-supplementary -> supplementary replication connection that happens
* for the very first time during the lifetime of this receiver server,
* (i.e. when upd_proc_local->read_jnl_seqno is 0), until this point in time,
* we cannot be sure what the starting point of the transmission is (partly due
* to -UPDATERESYNC but primarily due to -NORESYNC). Update process will be
* waiting for receiver to set read_jnl_seqno to a non-zero value. Finish it now.
* In case this is a reconnect, recvpool.upd_proc_local->read_jnl_seqno already
* reflects how much the update process has processed so that should be untouched
* by the receiver server as it will otherwise confuse the concurrently running
* update process (see <C9J02_003091_updproc_assert_fail_due_to_seqno_gap>).
* In addition do not touch recvpool_ctl->jnl_seqno in this case as we need to
* resume filling in the receive pool from where the previous connection stopped.
*/
assert(0 == GET_STRM_INDEX(recvd_jnl_seqno));
if (0 == recvpool.upd_proc_local->read_jnl_seqno)
{ /* Set read_jnl_seqno after jnl_seqno. Update process reads it in opposite
* order. Have memory barriers in between to ensure no out-of-order reads.
*/
recvpool.recvpool_ctl->jnl_seqno = recvd_jnl_seqno;
SHM_WRITE_MEMORY_BARRIER;
recvpool.upd_proc_local->read_jnl_seqno = recvd_jnl_seqno;
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Wrote upd_proc_local->read_jnl_seqno"
" : "INT8_FMT" "INT8_FMTX"\n", recvd_jnl_seqno, recvd_jnl_seqno);
/* If -NORESYNC was used in receiver startup, we dont want to use it
* anymore as future such connections could cause recvpool_ctl->jnl_seqno
* to be reset further backwards and will confuse the update process.
*/
if (recvpool.gtmrecv_local->noresync)
recvpool.gtmrecv_local->noresync = FALSE;
} else
{
assert(recvpool.recvpool_ctl->jnl_seqno
>= recvpool.upd_proc_local->read_jnl_seqno);
assert(!recvpool.gtmrecv_local->noresync);
}
}
/* Now that recvpool.recvpool_ctl->jnl_seqno has been determined for sure (for
* non-supplementary or non-root-primary instances it is determined even before but
* otherwise it takes until now). Force a log on the first recv. This function uses
* recvpool.recvpool_ctl->jnl_seqno hence the placement here.
*/
gtmrecv_reinit_logseqno();
break;
case REPL_INST_NOHIST:
if (0 == data_len)
{
assert(msg_len == MIN_REPL_MSGLEN - REPL_MSG_HDRLEN);
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Originating instance encountered a "
"REPLINSTNOHIST error. JNL_SEQNO of this replicating instance precedes"
" the current history in the originating instance file. "
"Receiver server exiting.\n");
gtmrecv_autoshutdown(); /* should not return */
assert(FALSE);
}
break;
default:
/* Discard the message */
repl_log(gtmrecv_log_fp, TRUE, TRUE, "Received UNKNOWN message (type = %d). "
"Discarding it.\n", msg_type);
assert(FALSE);
buffp += buffered_data_len;
buff_unprocessed -= buffered_data_len;
data_len -= buffered_data_len;
break;
}
if (repl_connection_reset)
return;
}
assert(0 == ((unsigned long)(buffp) % REPL_MSG_ALIGN));
if ((0 != buff_unprocessed) && (buff_start != buffp))
{
REPL_DPRINT4("Incmpl msg hdr, moving %d bytes from %lx to %lx\n", buff_unprocessed, (caddr_t)buffp,
(caddr_t)buff_start);
memmove(buff_start, buffp, buff_unprocessed);
}
buffp = buff_start;
GTMRECV_POLL_ACTIONS(data_len, buff_unprocessed, buffp);
}
}
void repl_cmp_solve_rcv_timeout(void)
{
GTMASSERT;
}
STATICFNDEF void gtmrecv_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr)
{
assert(0 != gtmrecv_now);
UNIX_ONLY(assert(*interval_ptr == heartbeat_period);) /* interval_len and interval_ptr are dummies on VMS */
gtmrecv_now += heartbeat_period;
REPL_DPRINT2("Starting heartbeat timer with %d s\n", heartbeat_period);
start_timer((TID)gtmrecv_heartbeat_timer, heartbeat_period * 1000, gtmrecv_heartbeat_timer, SIZEOF(heartbeat_period),
&heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */
}
STATICFNDEF void gtmrecv_main_loop(boolean_t crash_restart)
{
assert(FD_INVALID == gtmrecv_sock_fd);
gtmrecv_poll_actions(0, 0, NULL); /* Clear any pending bad trans */
gtmrecv_est_conn();
gtmrecv_bad_trans_sent = FALSE; /* this assignment should be after gtmrecv_est_conn since gtmrecv_est_conn can
* potentially call gtmrecv_poll_actions. If the timing is right,
* gtmrecv_poll_actions might set this variable to TRUE if the update process sets
* bad_trans in the recvpool. When we are (re)establishing connection with the
* source server, there is no point in doing bad trans processing. Also, we have
* to send START_JNL_SEQNO message to the source server. If not, there will be a
* deadlock with the source and receiver servers waiting for each other to send
* a message. */
repl_recv_prev_log_time = gtmrecv_now;
while (!repl_connection_reset)
do_main_loop(crash_restart);
return;
}
void gtmrecv_process(boolean_t crash_restart)
{
recvpool_ctl_ptr_t recvpool_ctl;
upd_proc_local_ptr_t upd_proc_local;
gtmrecv_local_ptr_t gtmrecv_local;
if (ZLIB_CMPLVL_NONE != gtm_zlib_cmp_level)
gtm_zlib_init(); /* Open zlib shared library for compression/decompression */
recvpool_ctl = recvpool.recvpool_ctl;
upd_proc_local = recvpool.upd_proc_local;
gtmrecv_local = recvpool.gtmrecv_local;
/* Check all message sizes are the same size (32 bytes = MIN_REPL_MSGLEN) except for the REPL_OLD_TRIPLE message
* (repl_histrec_msg_t structure) which is 8 bytes more. Pre-supplementary, the receiver server knew to handle
* different sized messages only for a few messages types REPL_TR_JNL_RECS, REPL_OLD_TRIPLE and REPL_CMP_SOLVE.
* But post-supplementary it knows to handle different sized messages for various additional message types
* (including REPL_NEED_INSTINFO, REPL_INSTINFO, REPL_HISTREC).
*
*/
assert(MIN_REPL_MSGLEN == SIZEOF(repl_start_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_start_reply_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_resync_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_old_needinst_msg_t));
assert(MIN_REPL_MSGLEN < SIZEOF(repl_needinst_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_needhistinfo_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_old_instinfo_msg_t));
assert(MIN_REPL_MSGLEN < SIZEOF(repl_instinfo_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_histinfo1_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_histinfo2_msg_t));
assert(MIN_REPL_MSGLEN < SIZEOF(repl_histinfo_msg_t));
assert(MIN_REPL_MSGLEN < SIZEOF(repl_old_triple_msg_t));
assert(MIN_REPL_MSGLEN < SIZEOF(repl_histrec_msg_t));
assert(MIN_REPL_MSGLEN == SIZEOF(repl_heartbeat_msg_t));
assert(GTMRECV_POLL_INTERVAL < MAX_GTMRECV_POLL_INTERVAL);
gtmrecv_poll_interval.tv_sec = 0;
gtmrecv_poll_interval.tv_usec = GTMRECV_POLL_INTERVAL;
gtmrecv_poll_immediate.tv_sec = 0;
gtmrecv_poll_immediate.tv_usec = 0;
recvpool_size = recvpool_ctl->recvpool_size;
recvpool_high_watermark = (long)((float)RECVPOOL_HIGH_WATERMARK_PCTG / 100 * recvpool_size);
recvpool_low_watermark = (long)((float)RECVPOOL_LOW_WATERMARK_PCTG / 100 * recvpool_size);
if ((long)((float)(RECVPOOL_HIGH_WATERMARK_PCTG - RECVPOOL_LOW_WATERMARK_PCTG) / 100 * recvpool_size) >=
RECVPOOL_XON_TRIGGER_SIZE)
{ /* for large receive pools, the difference between high and low watermarks as computed above may be too large that
* we may not send XON quickly enough. Limit the difference to RECVPOOL_XON_TRIGGER_SIZE */
recvpool_low_watermark = recvpool_high_watermark - RECVPOOL_XON_TRIGGER_SIZE;
}
REPL_DPRINT4("RECVPOOL HIGH WATERMARK is %ld, LOW WATERMARK is %ld, Receive pool size is %ld\n",
recvpool_high_watermark, recvpool_low_watermark, recvpool_size);
gtmrecv_alloc_msgbuff();
gtmrecv_now = time(NULL);
heartbeat_period = GTMRECV_HEARTBEAT_PERIOD; /* time keeper, well sorta */
start_timer((TID)gtmrecv_heartbeat_timer, heartbeat_period * 1000, gtmrecv_heartbeat_timer, SIZEOF(heartbeat_period),
&heartbeat_period); /* start_timer expects time interval in milli seconds, heartbeat_period is in seconds */
do
{
gtmrecv_main_loop(crash_restart);
} while (repl_connection_reset);
GTMASSERT; /* shouldn't reach here */
return;
}