openGauss-server/src/gausskernel/cbb/communication/libcomm.cpp

10252 lines
382 KiB
C++
Executable File

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* libcomm.cpp
*
* IDENTIFICATION
* src/gausskernel/cbb/communication/libcomm.cpp
*
* -------------------------------------------------------------------------
*/
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libcgroup.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <net/if.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/time.h>
#include <unistd.h>
#include "sctp_core/mc_sctp.h"
#include "sctp_core/mc_tcp.h"
#include "sctp_core/mc_poller.h"
#include "sctp_utils/sctp_platform.h"
#include "sctp_utils/sctp_thread.h"
#include "sctp_utils/sctp_lqueue.h"
#include "sctp_utils/sctp_queue.h"
#include "sctp_utils/sctp_lock_free_queue.h"
#include "distributelayer/streamCore.h"
#include "distributelayer/streamProducer.h"
#include "pgxc/poolmgr.h"
#include "libpq/auth.h"
#include "libpq/pqsignal.h"
#include "storage/ipc.h"
#include "utils/ps_status.h"
#include "utils/dynahash.h"
#include "vecexecutor/vectorbatch.h"
#include "vecexecutor/vecnodes.h"
#include "executor/execStream.h"
#include "miscadmin.h"
#include "gssignal/gs_signal.h"
#ifdef ENABLE_UT
#define static
#endif
/*
* we need to notify peer process when
* assert failed so we can get the state
* of both sender and receiver
*/
#define LIBCOMM_ASSERT(condition, nidx, sidx, node_role) gs_libcomm_handle_assert(condition, nidx, sidx, node_role)
#define IS_LOCAL_HOST(host) (strcmp(host, "*") == 0 || strcmp(host, "localhost") == 0 || strcmp(host, "0.0.0.0") == 0)
#define LIBCOMM_INTERFACE_END(wakeup_abnormal, immediate_interrupt) \
do { \
if (((wakeup_abnormal) || (immediate_interrupt)) && \
(t_thrd.postmaster_cxt.ProcessStartupPacketForLogicConn == false)) \
CHECK_FOR_INTERRUPTS(); \
t_thrd.int_cxt.ImmediateInterruptOK = (immediate_interrupt); \
} while (0)
#define IS_NOTIFY_REMOTE(reason) \
((reason) != ECOMMSCTPTCPDISCONNECT && (reason) != ECOMMSCTPREMOETECLOSE && (reason) != ECOMMSCTPREJECTSTREAM)
#define DEBUG_QUERY_ID (likely(u_sess == NULL) ? t_thrd.comm_cxt.debug_query_id : u_sess->debug_query_id)
static const int INVALID_SOCK = -1;
#define CHECKCONNSTATTIMEOUT 5
#define IPC_MSG_LEN 4
#ifndef MS_PER_S
#define MS_PER_S 1000
#endif
// the beginnig of global variable definition
static int g_print_interval_time = 60000; // 60s
static int g_ackchk_time = 2000; // default 2s
static knl_session_context g_comm_session;
// reload hba.conf
static const int RELOAD_HBA_RETRY_COUNT = 10;
static const int WAIT_SLEEP_200MS = 200000;
/* hash tables */
/* hash table to keep: ip + port -> connection status */
static HTAB* g_htab_ip_state = NULL;
pthread_mutex_t g_htab_ip_state_lock;
/* hash table to keep: nodename -> node id */
static HTAB* g_htab_nodename_node_idx = NULL;
pthread_mutex_t g_htab_nodename_node_idx_lock;
static int g_nodename_count = 0;
/* hash table to keep: fd_id -> node id */
static HTAB* g_htab_fd_id_node_idx = NULL;
pthread_mutex_t g_htab_fd_id_node_idx_lock;
/* for wake up thread */
static HTAB* g_htab_tid_poll = NULL;
pthread_mutex_t g_htab_tid_poll_lock;
/* at receiver and sender: hash table to keep: socket -> socket version number, for socket management */
static HTAB* g_htab_socket_version = NULL;
pthread_mutex_t g_htab_socket_version_lock;
static ArrayLockFreeQueue<char> g_memory_pool_queue;
// some error definition for receiver
static const int RECV_MEM_ERROR = -2;
static const int RECV_NET_ERROR = -1;
#define RECV_NEED_RETRY 0
#define THREAD_FREE_TIME_10S 10000000
#define THREAD_INTSERVAL_60S 60000000
#define THREAD_WORK_PERCENT 0.8
#define MSG_HEAD_MAGIC_NUM 0x9D
#define MSG_HEAD_MAGIC_NUM2 0x3E
#define MSG_HEAD_TEMP_CHECKSUM 0xCE3BA6CE
gsocket gs_invalid_gsock = {0, 0, 0, 0};
// if a big then b return a-b, else return 0
#define ABS_SUB(a, b) (((a) > (b)) ? ((a) - (b)) : 0)
#define STREAM_SCAN_FINISH 'F'
#define STREAM_SCAN_WAIT 'W'
#define STREAM_SCAN_DATA 'D'
// declarations of functions
static int gs_update_fd_to_htab_socket_version(struct sock_id* fd_id);
static void gs_s_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason, bool clean_epoll, int node_idx);
static void gs_r_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason);
static void gs_s_close_bad_data_socket(struct sock_id* fd_id, int close_reason, int node_idx);
static void gs_r_close_bad_data_socket(int node_idx, sock_id fd_id, bool is_lock);
static void gs_senders_struct_set();
static void gs_receivers_struct_set(int ctrl_port, int data_port);
static void gs_pmailbox_init();
static void gs_cmailbox_init();
static bool gs_mailbox_build(int idx);
static void gs_mailbox_destory(int idx);
static int gs_poll_create();
void gs_poll_close();
static int gs_poll(int time_out);
static void gs_poll_signal(binary_semaphore* semaphore);
static void gs_broadcast_poll();
static int gs_get_node_idx(char* node_name);
static void gs_s_close_logic_connection(struct p_mailbox* pmailbox, int close_reason, FCMSG_T* msg);
static void gs_r_close_logic_connection(struct c_mailbox* cmailbox, int close_reason, FCMSG_T* msg);
static int gs_s_close_stream(gsocket* gsock);
static int gs_r_close_stream(gsocket* gsock);
static void gs_libcomm_handle_assert(bool condition, int nidx, int sidx, int node_role);
static int gs_accept_data_conntion(struct iovec* iov, sock_id fd_id);
static void gs_accept_ctrl_conntion(struct sock_id* t_fd_id, struct FCMSG_T* fcmsgr);
static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version);
static int gs_r_build_reply_conntion(FCMSG_T* fcmsgr, int local_version);
static int gs_s_build_tcp_ctrl_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx, bool is_reply);
static bool gs_s_check_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx, bool is_reply, int type);
extern bool executorEarlyStop();
static void gs_online_change_capacity();
static int gs_tcp_write_noblock(int node_idx, int sock, const char* msg, int msg_len, int *send_count);
static void gs_set_reply_sock(int node_idx);
static int gs_reload_hba(int fd, sockaddr ctrl_client);
extern GlobalNodeDefinition* global_node_definition;
extern knl_instance_context g_instance;
ThreadId startCommSenderFlow(void);
ThreadId startCommReceiverFlow();
ThreadId startCommAuxiliary();
ThreadId startCommReceiver(int* tid);
void startCommReceiverWorker(ThreadId* threadid);
// function implentations
//
void gs_set_debug_mode(bool mod)
{
set_debug_mode(mod);
} // gs_set_debug_mode
void gs_set_timer_mode(bool mod)
{
set_timer_mode(mod);
} // gs_set_timer_mode
void gs_set_stat_mode(bool mod)
{
set_stat_mode(mod);
} // gs_set_stat_mode
void gs_set_no_delay(bool mod)
{
set_no_delay(mod);
} // gs_set_no_delay
void gs_set_ackchk_time(int mod)
{
g_ackchk_time = mod;
}
void gs_set_libcomm_used_rate(int rate)
{
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL) {
g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[POSTMASTER] = rate;
}
}
void init_libcomm_cpu_rate()
{
int i;
int g_recv_num = g_instance.comm_cxt.counters_cxt.g_recv_num;
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate == NULL) {
LIBCOMM_MALLOC
(g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate, (sizeof(int)*(g_recv_num + GS_RECV_LOOP)), int);
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate == NULL) {
ereport(FATAL, (errmsg("(s|libcomm_cpu_rate init)\tFailed to malloc g_libcomm_used_rate.")));
}
}
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL) {
for (i = 0; i < g_recv_num + GS_RECV_LOOP; i++) {
g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[i] = 0;
}
}
}
static void print_stream_sock_info()
{
int i;
tcp_info info;
socklen_t tcp_info_len = sizeof(info);
errno_t ss_rc = memset_s(&info, tcp_info_len, 0x0, tcp_info_len);
securec_check(ss_rc, "\0", "\0");
if (g_instance.comm_cxt.g_senders->sender_conn != NULL) {
for (i = 0; i < MAX_CN_DN_NODE_NUM; i++) {
int sock = g_instance.comm_cxt.g_senders->sender_conn[i].socket;
if (sock > 0) {
int ret = getsockopt(sock, SOL_TCP, TCP_INFO, &info, &tcp_info_len);
if (ret == 0) {
print_socket_info(sock, &info, true);
}
}
}
}
if (g_instance.comm_cxt.g_receivers->receiver_conn != NULL) {
for (i = 0; i < MAX_CN_DN_NODE_NUM; i++) {
int sock = g_instance.comm_cxt.g_receivers->receiver_conn[i].socket;
if (sock > 0) {
int ret = getsockopt(sock, SOL_TCP, TCP_INFO, &info, &tcp_info_len);
if (ret == 0) {
print_socket_info(sock, &info, false);
}
}
}
}
}
static void recv_ackchk_msg(int sock)
{
/* receive ack package */
struct pollfd input_fd;
input_fd.fd = sock;
input_fd.events = POLLIN;
input_fd.revents = 0;
if (poll(&input_fd, 1, 0) > 0) {
char buf[1024];
/* Receive ack check message, we don't care the content */
(void)recv(input_fd.fd, buf, sizeof(buf), 0);
}
}
#ifdef LIBCOMM_SPEED_TEST_ENABLE
#define LIBCOMM_PERFORMANCE_PLAN_ID 1
#define LIBCOMM_PERFORMANCE_PN_ID 0XF000
void gs_set_test_thread_num(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_thread_num = newval;
}
void gs_set_test_msg_len(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_msg_len = newval;
}
void gs_set_test_send_sleep(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_send_sleep = newval;
}
void gs_set_test_send_once(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_send_once = newval;
}
void gs_set_test_recv_sleep(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_recv_sleep = newval;
}
void gs_set_test_recv_once(int newval)
{
g_instance.comm_cxt.tests_cxt.libcomm_test_recv_once = newval;
}
#endif
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
void gs_set_fault_injection(int newval)
{
set_comm_fault_injection(newval);
}
static int g_comm_fault_injection = 0;
void set_comm_fault_injection(int type)
{
if (type <= LIBCOMM_FI_MAX) {
g_comm_fault_injection = type;
} else {
LIBCOMM_ELOG(WARNING, "[FAULT INJECTION] invalid fault injection type, %d.", type);
}
}
/*
* function name : is_comm_fault_injection
* description : Determine whether to perform fault injection
* arguments : type: the type of fault injection.
* return value : true: FI succeed
* false:
*/
bool is_comm_fault_injection(LibcommFaultInjection type)
{
// random Fault injection.
// the value from -10 to -1.
if (g_comm_fault_injection < 0) {
int prob = g_comm_fault_injection * (-10); // so the prob is [10, 20,30,,,100]
int r = 0;
// we use rand() to obtain random number.
srand((unsigned)time(0)); // set random seed
r = rand(); // obtain random number
r = (r >= 0) ? r : r * (-1); // get positive number
r = r % 100; // now, the range of r is (0,100)
if (r <= prob) { // FI has (prob/100)% chance to happen.
return true;
} else {
return false;
}
}
if (g_comm_fault_injection == type) {
return true;
} else {
return false;
}
}
#endif
/*
* function name : gs_change_capacity
* description : If GUC parameter "comm_max_datanode" changed this function will be called.
* notice : Only for postmaster thread.
* arguments :
* __in newval: new value (sum of CN and DN).
*/
void gs_change_capacity(int new_node_num)
{
/* Only postmaster thread can change the g_expect_node_num,
* "g_cur_node_num==0" means postmaster doesn't finish initialization.
*/
if ((t_thrd.proc_cxt.MyProcPid != PostmasterPid) ||
(new_node_num == g_instance.comm_cxt.counters_cxt.g_cur_node_num) ||
(g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0)) {
return;
}
/* range for node_num (2,4096) */
if ((new_node_num > MAX_CN_DN_NODE_NUM) || (new_node_num < MIN_CN_DN_NODE_NUM)) {
LIBCOMM_ELOG(WARNING, "(pm|change capacity)\tInvalidate node num: %d.", new_node_num);
return;
}
g_instance.comm_cxt.counters_cxt.g_expect_node_num = new_node_num;
g_instance.comm_cxt.quota_cxt.g_quota_changing->post();
LIBCOMM_ELOG(LOG,
"(pm|change capacity)\tg_cur_node_num [%d], g_expect_node_num [%d].",
g_instance.comm_cxt.counters_cxt.g_cur_node_num,
g_instance.comm_cxt.counters_cxt.g_expect_node_num);
}
/*
* function name : gs_online_change_capacity
* description : For database expansion or shrinkage.
* notice : !!Curently, we only support datanode expansion.
* arguments :
* __in expected_node_num: Nodes number(CN+DN) that user wants to change to.
*/
static void gs_online_change_capacity()
{
int cur_node_idx = -1;
int cur_num = g_instance.comm_cxt.counters_cxt.g_cur_node_num;
int expect_num = g_instance.comm_cxt.counters_cxt.g_expect_node_num;
if (expect_num == cur_num) {
return;
}
if (expect_num < cur_num) {
LIBCOMM_ELOG(WARNING, "(auxiliary)\tNot support change %d datanodes reduce to %d.", cur_num, expect_num);
atomic_set(&g_instance.comm_cxt.counters_cxt.g_expect_node_num, cur_num);
return;
}
for (cur_node_idx = cur_num; cur_node_idx < expect_num; cur_node_idx++) {
if (false == gs_mailbox_build(cur_node_idx)) {
goto cleanup_capacity;
}
}
LIBCOMM_ELOG(NOTICE, "(auxiliary)\tChange %d datanodes capacity to %d successfully.", cur_num, expect_num);
atomic_set(&g_instance.comm_cxt.counters_cxt.g_cur_node_num, expect_num);
return;
cleanup_capacity:
/* if failed, we need to release the memory, from cur_node_idx-1 to cur_num */
for (cur_node_idx -= 1; cur_node_idx >= cur_num; cur_node_idx--) {
gs_mailbox_destory(cur_node_idx);
}
LIBCOMM_ELOG(NOTICE, "(auxiliary)\tFailed to change %d datanodes capacity to %d.", cur_num, expect_num);
return;
}
int gs_get_cur_node()
{
return (g_instance.comm_cxt.counters_cxt.g_cur_node_num != 0)
? g_instance.comm_cxt.counters_cxt.g_cur_node_num
: (g_instance.attr.attr_network.MaxCoords + u_sess->attr.attr_network.comm_max_datanode);
}
static void gs_set_local_host(const char* host)
{
uint32 cpylen;
errno_t ss_rc;
const char* real_host = NULL;
if (IS_LOCAL_HOST(host)) {
real_host = "127.0.0.1";
} else {
real_host = host;
}
cpylen = comm_get_cpylen(real_host, HOST_ADDRSTRLEN);
ss_rc = memset_s(g_instance.comm_cxt.localinfo_cxt.g_local_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(g_instance.comm_cxt.localinfo_cxt.g_local_host, HOST_ADDRSTRLEN, real_host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.localinfo_cxt.g_local_host[cpylen] = '\0';
} // gs_set_local_host
void gs_update_recv_ready_time()
{
g_instance.comm_cxt.localinfo_cxt.g_r_first_recv_time = time(NULL);
}
uint64 gs_get_recv_ready_time()
{
return g_instance.comm_cxt.localinfo_cxt.g_r_first_recv_time;
}
static void gs_set_quota(unsigned long quota, int quotanotify_ratio)
{
if (quota == 0) { // do not using quota mechanism
g_instance.comm_cxt.quota_cxt.g_having_quota = false;
g_instance.comm_cxt.quota_cxt.g_quota = DEFULTMSGLEN;
} else {
g_instance.comm_cxt.quota_cxt.g_having_quota = true;
g_instance.comm_cxt.quota_cxt.g_quota = quota * 1024; // Byte
/* Minimum value is DEFULTMSGLEN */
if (g_instance.comm_cxt.quota_cxt.g_quota < DEFULTMSGLEN) {
g_instance.comm_cxt.quota_cxt.g_quota = DEFULTMSGLEN;
}
}
g_instance.comm_cxt.quota_cxt.g_quotanofify_ratio = quotanotify_ratio;
} // gs_set_quota
static void gs_receivers_struct_set(int ctrl_port, int data_port)
{
g_instance.comm_cxt.g_receivers->server_ctrl_tcp_port = ctrl_port;
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.port = data_port;
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.ss_len = 0;
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket = -1;
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket_id = -1;
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.assoc_id = 0;
for (int i = 0; i < MAX_CN_DN_NODE_NUM; i++) {
LIBCOMM_PTHREAD_RWLOCK_INIT(&g_instance.comm_cxt.g_receivers->receiver_conn[i].rwlock, NULL);
g_instance.comm_cxt.g_receivers->receiver_conn[i].port = 0;
g_instance.comm_cxt.g_receivers->receiver_conn[i].ss_len = 0;
g_instance.comm_cxt.g_receivers->receiver_conn[i].socket = -1;
g_instance.comm_cxt.g_receivers->receiver_conn[i].socket_id = -1;
g_instance.comm_cxt.g_receivers->receiver_conn[i].assoc_id = 0;
g_instance.comm_cxt.g_receivers->receiver_conn[i].comm_bytes = 0;
g_instance.comm_cxt.g_receivers->receiver_conn[i].comm_count = 0;
g_instance.comm_cxt.g_r_node_sock[i].init();
}
return;
}
/*
* function name : gs_init_receivers
* description : init g_receivers
* arguments :
* __in ctrl_tcp_port:
* __in base_sctp_port:
*/
static void gs_receivers_struct_init(int ctrl_port, int data_port)
{
LIBCOMM_MALLOC(g_instance.comm_cxt.g_r_node_sock, (MAX_CN_DN_NODE_NUM * sizeof(struct node_sock)), node_sock);
if (g_instance.comm_cxt.g_r_node_sock == NULL) {
ereport(FATAL, (errmsg("(r|receivers init)\tFailed to malloc g_r_node_sock[%d].", MAX_CN_DN_NODE_NUM)));
}
LIBCOMM_MALLOC(g_instance.comm_cxt.g_receivers->receiver_conn,
(MAX_CN_DN_NODE_NUM * sizeof(struct node_connection)),
node_connection);
if (g_instance.comm_cxt.g_receivers->receiver_conn == NULL) {
ereport(FATAL, (errmsg("(r|receivers init)\tFailed to malloc g_receivers[%d].", MAX_CN_DN_NODE_NUM)));
}
/* initialize g_receivers */
gs_receivers_struct_set(ctrl_port, data_port);
/* initialize cmailbox */
gs_cmailbox_init();
return;
}
static void gs_senders_struct_init()
{
LIBCOMM_MALLOC(g_instance.comm_cxt.g_s_node_sock, (MAX_CN_DN_NODE_NUM * sizeof(struct node_sock)), node_sock);
if (g_instance.comm_cxt.g_s_node_sock == NULL) {
ereport(FATAL, (errmsg("(s|sender init)\tFailed to malloc g_s_node_sock[%d].", MAX_CN_DN_NODE_NUM)));
}
LIBCOMM_MALLOC(
g_instance.comm_cxt.g_delay_info, (MAX_CN_DN_NODE_NUM * sizeof(struct libcomm_delay_info)), libcomm_delay_info);
if (g_instance.comm_cxt.g_delay_info == NULL) {
ereport(FATAL, (errmsg("(s|sender init)\tFailed to malloc g_delay_info[%d].", MAX_CN_DN_NODE_NUM)));
}
LIBCOMM_MALLOC(g_instance.comm_cxt.g_senders->sender_conn,
(MAX_CN_DN_NODE_NUM * sizeof(struct node_connection)),
node_connection);
if (g_instance.comm_cxt.g_senders->sender_conn == NULL) {
ereport(FATAL, (errmsg("(s|sender init)\tFailed to malloc g_senders[%d].", MAX_CN_DN_NODE_NUM)));
}
// init g_s_node_sock and g_senders
gs_senders_struct_set();
// init pmailbox
gs_pmailbox_init();
return;
} // gs_init_senders
/*
* @Description : get kerberos keyfile path from GUC parameter pg_krb_server_keyfile.
* @out : gs_krb_keyfile: save kerberos keyfile path.
*/
static void gs_set_kerberos_keyfile()
{
int path_len = 0;
if (u_sess->attr.attr_security.pg_krb_server_keyfile == NULL) {
return;
}
path_len = strlen(u_sess->attr.attr_security.pg_krb_server_keyfile);
if (path_len <= 0 || path_len >= (INT_MAX - 1)) {
return;
}
LIBCOMM_MALLOC(g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile, (path_len + 1), char);
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL) {
errno_t ss_rc = strcpy_s(g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile,
path_len + 1,
u_sess->attr.attr_security.pg_krb_server_keyfile);
securec_check(ss_rc, "\0", "\0");
}
}
#if ENABLE_MULTIPLE_NODES
/* sender initialize sctp socket for sending data */
static int gs_s_sender_sock_init(int node_idx, libcommaddrinfo* libcomm_addrinfo)
{
struct sock_id fd_id = {-1, -1};
// close old socket, if not, fd and port may be leaked
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket >= 0) {
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
LIBCOMM_ELOG(WARNING,
"(s|sender socket init)\tClose old socket[%d,%d].",
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id);
}
if (gs_s_sender_conn_init(node_idx, libcomm_addrinfo) < 0) {
return -1;
}
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = 0;
if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) {
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
LIBCOMM_ELOG(WARNING, "(s|sender socket init)\tFailed to save socket[%d,%d], close it.", fd_id.fd, fd_id.id);
return -1;
}
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id = fd_id.id;
if (gs_map_sock_id_to_node_idx(fd_id, node_idx) < 0) {
LIBCOMM_ELOG(WARNING, "(s|sender socket init)\tFailed to save sock and sockid.");
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
return -1;
}
/* set reply socket for g_r_node_sock */
gs_set_reply_sock(node_idx);
return 0;
}
int libcomm_sctp_listen()
{
return mc_sctp_server_init(&(g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.ss),
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.ss_len);
return 0;
}
static int libcomm_sctp_send(LibcommSendInfo* send_info)
{
int sock = send_info->socket;
int sock_id = send_info->socket_id;
int version = send_info->version;
int streamid = send_info->streamid;
int node_idx = send_info->node_idx;
int msg_len = send_info->msg_len;
char* msg = send_info->msg;
int flag = MSG_NOSIGNAL;
const int order = 1;
int error = -1;
LIBCOMM_PTHREAD_RWLOCK_RDLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
/* if socket saved before mismatch the socket in global variable
* thsi socket may has been closed by other thread and reused
* return -1 in this case to prvent send msg to wrong node
*/
if ((sock != g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket) ||
(sock_id != g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id)) {
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
}
for (;;) {
error = mc_sctp_send(sock, streamid, order, 0, flag, NULL, msg, version, msg_len);
if (error > 0) {
g_instance.comm_cxt.g_senders->sender_conn[node_idx].comm_bytes += error;
}
g_instance.comm_cxt.g_senders->sender_conn[node_idx].comm_count += 1;
if (error == 0) {
(void)usleep(100);
} else {
break;
}
}
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return error;
}
static int libcomm_sctp_send_block_mode(LibcommSendInfo* send_info)
{
int sock = send_info->socket;
int node_idx = send_info->node_idx;
int msg_len = send_info->msg_len;
char* msg = send_info->msg;
int error = -1;
LIBCOMM_PTHREAD_RWLOCK_RDLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
error = mc_sctp_send_block_mode(sock, msg, msg_len);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return error;
}
/*
* function name : libcomm_build_sctp_connection
* description : sender check and build sctp connection to receiver
* notice : we must get g_instance.comm_cxt.g_senders->sender_conn lock before!
* arguments :
* _in_ sctp_addrinfo: remote infomation.
* _in_ node_idx: remote node index.
* return value :
* -1: failed.
* 0: succeed.
*/
static int libcomm_build_sctp_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx)
{
struct sock_id fd_id = {-1, -1};
ip_key addr;
int msg_len = 0;
int error = -1;
int try_times = REPEAT;
errno_t ss_rc = 0;
uint32 cpylen;
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
// step1: check if the data connection is ok
//
// the connection is built already
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id != 0) {
// check if the connection is ok
// if not, close the bad socket, then continue to build a new connection
// if ok, just return
//
if (mc_sctp_check_socket(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket) != 0) {
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
LIBCOMM_ELOG(WARNING,
"(s|build sctp connection)\tFailed to check sctp socket "
"node[%d]:%s, errno[%d]:%s, close socket[%d,%d].",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
errno,
mc_strerror(errno),
fd_id.fd,
fd_id.id);
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
} else {
COMM_DEBUG_LOG("(s|build sctp connection)\tAlready has sctp connection for node[%d]:%s.",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return 0;
}
}
// step 2: initialize local variables
//
struct libcomm_connect_package connect_package;
connect_package.type = SCTP_PKG_TYPE_CONNECT;
connect_package.magic_num = MSG_HEAD_MAGIC_NUM2;
ss_rc = strcpy_s(connect_package.node_name, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename);
securec_check(ss_rc, "\0", "\0");
ss_rc = strcpy_s(connect_package.host, HOST_ADDRSTRLEN, g_instance.comm_cxt.localinfo_cxt.g_local_host);
securec_check(ss_rc, "\0", "\0");
msg_len = sizeof(struct libcomm_connect_package);
// initialize remote sctp address
int ss_len = 0;
struct sockaddr_storage to_ss;
ss_rc = memset_s(&to_ss, sizeof(sockaddr_storage), 0x0, sizeof(to_ss));
securec_check(ss_rc, "\0", "\0");
(void)mc_sctp_addr_init(libcomm_addrinfo->host, libcomm_addrinfo->sctp_port, &to_ss, &ss_len);
do {
// we init socket here
if (gs_s_sender_sock_init(node_idx, libcomm_addrinfo) != 0) {
errno = ECOMMSCTPSCTPADRINIT;
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
}
// step 3: connect to destination
//
error = mc_sctp_connect(
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket, &to_ss, sizeof(struct sockaddr));
if (error != 0) {
LIBCOMM_ELOG(WARNING,
"(s|build sctp connection)\tFailed to build sctp connection "
"to %s:%d for node[%d]:%s, error[%d:%d]:%s.",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
error,
errno,
mc_strerror(errno));
continue;
}
// step 4: send node name to remote
//
error = mc_sctp_send_block_mode(
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket, (char*)&connect_package, msg_len);
if (error <= 0) {
LIBCOMM_ELOG(WARNING,
"(s|build sctp connection)\tFailed to send assoc id to %s:%d "
"for node[%d]:%s on socket[%d].",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket);
continue;
}
// step 5: receive the ack message
//
// block mode
struct libcomm_accept_package ack_msg;
error = mc_sctp_recv_block_mode(
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket, (char*)&ack_msg, sizeof(ack_msg));
// if failed, we close the bad one and retry for 10 times
if (error != 0 || ack_msg.result != 1 || ack_msg.type != SCTP_PKG_TYPE_ACCEPT) {
LIBCOMM_ELOG(WARNING,
"(s|build sctp connection)\tFailed to recv assoc id from %s:%d "
"for node[%d]:%s on socket[%d].",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket);
continue;
}
/* Client side gss kerberos authentication for sctp connection. */
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL &&
GssClientAuth(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket, libcomm_addrinfo->host) < 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tData channel GSS authentication failed, "
"remote:%s[%s:%d]:%s.",
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
mc_strerror(errno));
errno = ECOMMSCTPGSSAUTHFAIL;
break;
} else {
COMM_DEBUG_LOG("(s|connect)\tData channel GSS authentication SUCC, "
"remote:%s[%s:%d].",
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port);
}
// step 6: if everything is ok, we label the connection is built successfully
//
g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 1;
LIBCOMM_ELOG(LOG,
"(s|build sctp connection)\tSucceed to connect with socket[%d:%d] %s:%d for node[%d]:%s.",
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id,
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(addr.ip, HOST_LEN_OF_HTAB, libcomm_addrinfo->host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
addr.port = libcomm_addrinfo->sctp_port;
/* update connection state to succeed when connect succeed */
gs_update_connection_state(addr, CONNSTATESUCCEED, true, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return 0;
} while (try_times-- > 0);
// Failed to build sctp connection
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
} // gs_s_build_sctp_connection
static int mc_sctp_handle_notification(struct msghdr* inmessage)
{
int re = 0;
union comm_notification* sn = (union comm_notification*)inmessage->msg_iov[0].iov_base;
struct iovec* iov = inmessage->msg_iov;
int msg_len = iov->iov_len;
switch (sn->sn_header.sn_type) {
case COMM_SHUTDOWN_EVENT:
mc_sctp_print_message(inmessage, msg_len);
break;
case COMM_ADDR_CHANGE:
switch (sn->conn_change.conn_state) {
case COMM_LOST:
case COMM_SHUTDOWN:
mc_sctp_print_message(inmessage, msg_len);
re = -1;
break;
case COMM_RESTART:
mc_sctp_print_message(inmessage, msg_len);
break;
default:
break;
}
break;
default:
break;
}
COMM_DEBUG_CALL(mc_sctp_print_message(inmessage, msg_len));
return re;
}
static int libcomm_sctp_recv(LibcommRecvInfo* recv_info)
{
int sock = recv_info->socket;
int flags = MSG_WAITALL;
int error = -1;
errno_t rc = 0;
struct mc_lqueue_item* iov_item = NULL;
struct iovec* iov = NULL;
struct msghdr inmessage;
char incmsg[CMSG_SPACE(sizeof(struct comm_buff_info))];
rc = memset_s(&inmessage, sizeof(inmessage), 0x0, sizeof(struct msghdr));
securec_check(rc, "\0", "\0");
rc = memset_s(incmsg, CMSG_SPACE(sizeof(struct comm_buff_info)), 0x0, CMSG_SPACE(sizeof(struct comm_buff_info)));
securec_check(rc, "\0", "\0");
if (0 != libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE)) {
return RECV_MEM_ERROR;
}
iov = iov_item->element.data;
iov->iov_len = IOV_DATA_SIZE;
inmessage.msg_iov = iov;
inmessage.msg_iovlen = 1;
inmessage.msg_control = incmsg;
inmessage.msg_controllen = sizeof(incmsg);
error = mc_sctp_recv(sock, &inmessage, flags); // do receive
if (error > 0) {
g_instance.comm_cxt.g_receivers->receiver_conn[recv_info->node_idx].comm_bytes += error;
}
g_instance.comm_cxt.g_receivers->receiver_conn[recv_info->node_idx].comm_count += 1;
// not real network error, it can be resolved by trying again
if ((error == -1) && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) {
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return RECV_NEED_RETRY;
}
// real network errors, we should report it
// errno is not EAGAIN/EWOULDBLOCK/EINTR
if (error <= 0) {
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return RECV_NET_ERROR;
}
iov->iov_len = error;
if (MSG_NOTIFICATION & inmessage.msg_flags) {
error = mc_sctp_handle_notification(&inmessage);
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
if (error != 0) {
return RECV_NET_ERROR;
} else {
return RECV_NEED_RETRY;
}
}
recv_info->iov_item = iov_item;
recv_info->streamid = mc_sctp_get_sid(&inmessage);
recv_info->version = mc_sctp_get_ppid(&inmessage);
// iov point must return and save to cmailbox, no need free
return iov->iov_len;
}
/* sender initialize connection for sending data */
static int gs_s_sender_conn_init(int node_idx, libcommaddrinfo* libcomm_addrinfo)
{
errno_t ss_rc;
uint32 cpylen;
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_ADDRSTRLEN);
ss_rc = memset_s(
g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host,
HOST_ADDRSTRLEN,
libcomm_addrinfo->host,
cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host[cpylen] = '\0';
g_instance.comm_cxt.g_senders->sender_conn[node_idx].port = libcomm_addrinfo->sctp_port;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 0;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket =
mc_sctp_client_init(&(g_instance.comm_cxt.g_senders->sender_conn[node_idx].ss),
g_instance.comm_cxt.g_senders->sender_conn[node_idx].ss_len);
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket < 0) {
LIBCOMM_ELOG(WARNING,
"(s|sender socket init)\tFailed to init socket for node[%d]:%s.",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
errno = ECOMMSCTPSCTPFDINVAL;
return -1;
}
return 0;
}
#endif
static int libcomm_tcp_listen()
{
return mc_tcp_listen(g_instance.comm_cxt.localinfo_cxt.g_local_host,
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.port,
NULL);
}
/*
* function name : libcomm_malloc_iov_item
* description : malloc iov item
* arguments : iov_item: the pointer of malloc memory.
size: size of iov->iov_base
* return value : 0: malloc succeed
* -1: malloc failed
*/
static int libcomm_malloc_iov_item(struct mc_lqueue_item** iov_item, int size)
{
struct mc_lqueue_item* item = NULL;
struct iovec* iov = NULL;
/* get iov_item from memory pool */
*iov_item = (struct mc_lqueue_item*)g_memory_pool_queue.pop((char*)iov);
if (*iov_item != NULL) {
return 0;
}
/*
* IF libcomm_used_memory + size is more than g_total_usable_memory
* then errno = ECOMMSCTPMEMALLOC and return -1
*/
/* if memory pool is empty, malloc iov_item */
LIBCOMM_MALLOC(iov, sizeof(struct iovec), iovec);
if (iov == NULL) {
errno = ECOMMSCTPMEMALLOC;
return -1;
}
LIBCOMM_MALLOC(iov->iov_base, size, void);
if (iov->iov_base == NULL) {
LIBCOMM_FREE(iov, sizeof(struct iovec));
errno = ECOMMSCTPMEMALLOC;
return -1;
}
iov->iov_len = 0;
LIBCOMM_MALLOC(item, sizeof(struct mc_lqueue_item), mc_lqueue_item);
if (item == NULL) {
LIBCOMM_FREE(iov->iov_base, size);
LIBCOMM_FREE(iov, sizeof(struct iovec));
errno = ECOMMSCTPMEMALLOC;
return -1;
}
item->element.add(iov);
*iov_item = item;
return 0;
}
/*
* function name : libcomm_free_iov_item
* description : free iov item
* arguments : iov_item: the pointer of free memory.
* size: size of iov->iov_base
*/
void libcomm_free_iov_item(struct mc_lqueue_item** iov_item, int size)
{
struct mc_lqueue_item* item = *iov_item;
struct iovec* iov = NULL;
bool rc = false;
if (unlikely(item == NULL)) {
return;
}
iov = item->element.data;
Assert(iov != NULL && iov->iov_base != NULL);
iov->iov_len = 0;
/* push pointer to memory pool */
rc = g_memory_pool_queue.push((char*)item);
/* if memory pool is full, free iov_item */
if (!rc) {
LIBCOMM_FREE(iov->iov_base, size);
LIBCOMM_FREE(iov, sizeof(struct iovec));
LIBCOMM_FREE(item, sizeof(struct mc_lqueue_item));
}
*iov_item = NULL;
return;
}
/*
* function name : gs_tcp_write_noblock
* description : loop send msg by tcp with noblock mode
* arguments : node_idx: sender node id.
* sock: socket
* msg: send message content
* msg_len: msg length
* return value : length of msg had be sent
*/
static int gs_tcp_write_noblock(int node_idx, int sock, const char* msg, int msg_len, int *send_count)
{
uint64 time_enter, time_now;
int send_bytes = 0;
int error = -1;
time_enter = mc_timers_ms();
do {
/*
* we send data in non-block mode,
* but we will assure the data will
* be sent out if the network is ok
*/
error = mc_tcp_write_noblock(sock, msg + send_bytes, msg_len - send_bytes);
if (error < 0) {
errno = ECOMMSCTPSCTPDISCONNECT;
break;
}
if (send_count != NULL) {
(*send_count)++;
}
/*
* when primary and the standby is switchover,
* the old connection is broken
*/
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].ip_changed == true) {
errno = ECOMMSCTPPEERCHANGED;
break;
}
time_now = mc_timers_ms();
if (((time_now - time_enter) >
((uint64)g_instance.comm_cxt.counters_cxt.g_comm_send_timeout * SEC_TO_MICRO_SEC)) &&
(time_now > time_enter)) {
errno = ECOMMSCTPSENDTIMEOUT;
break;
}
send_bytes += error;
} while (send_bytes != msg_len);
return send_bytes;
}
static int libcomm_tcp_send(LibcommSendInfo* send_info)
{
int sock = send_info->socket;
int sock_id = send_info->socket_id;
int version = send_info->version;
int streamid = send_info->streamid;
int node_idx = send_info->node_idx;
int msg_len = send_info->msg_len;
char* msg = send_info->msg;
int error = -1;
int send_bytes;
int send_count = 0;
struct sock_id fd_id = {0, 0};
MsgHead msg_head;
msg_head.type = 'D';
msg_head.magic_num = MSG_HEAD_MAGIC_NUM;
msg_head.version = version;
msg_head.logic_id = streamid;
msg_head.msg_len = msg_len;
msg_head.checksum = MSG_HEAD_TEMP_CHECKSUM;
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
/* check socket version saved before, to prevent send msg to wrong remote node */
if ((sock != g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket) ||
(sock_id != g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id)) {
COMM_DEBUG_LOG("(s|send)\tsocket version of node%d:%s mismatch old[%d,%d], new[%d,%d].",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
sock,
sock_id,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
}
send_bytes = gs_tcp_write_noblock(node_idx, sock, (char*)&msg_head, sizeof(MsgHead), NULL);
if (send_bytes != sizeof(MsgHead)) {
/* close the bad socket when send failed */
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
LIBCOMM_ELOG(WARNING,
"(s|send)\tsend msghead failed send_bytes[%d] errno[%d:%s].",
send_bytes,
errno,
mc_strerror(errno));
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
}
COMM_DEBUG_LOG("(s|send)\tsend to dn[%d]:%s head[%d, %d] on socket[%d].",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
(int)sizeof(MsgHead),
error,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket);
send_bytes = gs_tcp_write_noblock(node_idx, sock, msg, msg_len, &send_count);
if (send_bytes > 0) {
g_instance.comm_cxt.g_senders->sender_conn[node_idx].comm_bytes += send_bytes;
}
g_instance.comm_cxt.g_senders->sender_conn[node_idx].comm_count += send_count;
COMM_DEBUG_LOG("(s|send)\tsend to dn[%d]:%s data[%d, %d] on socket[%d].",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
msg_len,
error,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket);
if (send_bytes != msg_len) {
/* close the bad socket when send failed */
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
gs_s_close_bad_data_socket(&fd_id, errno, node_idx);
LIBCOMM_ELOG(WARNING,
"(s|send)\tsend length mismatch send_bytes[%d] msg_len[%d] errno[%d:%s].",
send_bytes,
msg_len,
errno,
mc_strerror(errno));
send_bytes = -1;
}
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return send_bytes;
}
static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
{
int sock = recv_info->socket;
MsgHead msg_head = {0};
struct mc_lqueue_item* iov_item = NULL;
struct iovec* iov = NULL;
int error = -1;
// malloc 64 bytes for recv when node_idx < 0, need free
if (0 != libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE)) {
return RECV_MEM_ERROR;
}
iov = iov_item->element.data;
// recv poll event, recv msg head in block mode
error = mc_tcp_read_block(sock, &msg_head, sizeof(MsgHead), 0);
// must be a connect msg when node_idx < 0
if (error < 0 || msg_head.type != 'C' || msg_head.magic_num != MSG_HEAD_MAGIC_NUM ||
msg_head.checksum != MSG_HEAD_TEMP_CHECKSUM || msg_head.msg_len > IOV_DATA_SIZE) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tReceiver error msg head[%d] "
"from socket[%d] lid:%d type[%d], magic_num[%d], len[%u].",
error,
sock,
msg_head.logic_id,
msg_head.type,
msg_head.magic_num,
msg_head.msg_len);
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return RECV_NET_ERROR;
}
// recv msg head finish
error = mc_tcp_read_block(sock, iov->iov_base, msg_head.msg_len, 0);
if (error <= 0) {
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return RECV_NET_ERROR;
}
iov->iov_len = error;
recv_info->iov_item = iov_item;
recv_info->streamid = msg_head.logic_id;
recv_info->version = msg_head.version;
return error;
}
static int libcomm_tcp_recv(LibcommRecvInfo* recv_info)
{
MsgHead* msg_head = NULL;
struct iovec* iov = NULL;
struct mc_lqueue_item* iov_item = NULL;
int sock = recv_info->socket;
int node_idx = recv_info->node_idx;
int recv_bytes = -1;
int* head_read_cursor = 0;
int unread_head_len = 0;
int unread_body_len = 0;
// first READY message, no have idx, can not buffer, block recv
if (node_idx < 0) {
return libcomm_tcp_recv_noidx(recv_info);
}
msg_head = &g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].msg_head;
iov_item = g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].iov_item;
head_read_cursor = &(g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].head_read_cursor);
unread_head_len = sizeof(MsgHead) - *head_read_cursor;
if (unread_head_len > 0) {
// recv poll event, recv msg head in block mode
recv_bytes = mc_tcp_read_nonblock(sock, (char*)msg_head + *head_read_cursor, unread_head_len, 0);
if (recv_bytes < 0) {
return RECV_NET_ERROR;
}
if (recv_bytes == 0) {
return RECV_NEED_RETRY;
}
*head_read_cursor = *head_read_cursor + recv_bytes;
/* msg head not received complete, return to epoll_wait */
if (recv_bytes < unread_head_len) {
return RECV_NEED_RETRY;
}
}
/* recv msg head finish */
Assert(*head_read_cursor == sizeof(MsgHead));
if (msg_head->magic_num != MSG_HEAD_MAGIC_NUM || msg_head->checksum != MSG_HEAD_TEMP_CHECKSUM ||
msg_head->msg_len > IOV_DATA_SIZE) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tReceiver error msg head[%d] "
"from socket[%d] node[%d]:%s lid:%d len=%u, magic_num[%d].",
recv_bytes,
sock,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
msg_head->logic_id,
msg_head->msg_len,
msg_head->magic_num);
return RECV_NET_ERROR;
}
if (iov_item == NULL) {
if (0 != libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE)) {
return RECV_MEM_ERROR;
}
// save new malloc iov point
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].iov_item = iov_item;
}
iov = iov_item->element.data;
if (msg_head->msg_len <= iov->iov_len) {
Assert(msg_head->msg_len > iov->iov_len);
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tReceiver error msg_len %u iov_len %lu.",
msg_head->msg_len, iov->iov_len);
return RECV_NET_ERROR;
}
unread_body_len = msg_head->msg_len - iov->iov_len;
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if ((is_comm_fault_injection(LIBCOMM_FI_R_PACKAGE_SPLIT))) {
if (iov->iov_len == 0) {
unread_body_len = unread_body_len / 2;
}
}
#endif
recv_bytes = mc_tcp_read_nonblock(sock, (char*)iov->iov_base + iov->iov_len, unread_body_len, 0);
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].comm_bytes += recv_bytes;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].comm_count += 1;
// real network errors, we should report it
// errno is not EAGAIN/EWOULDBLOCK/EINTR
if (recv_bytes < 0) {
return RECV_NET_ERROR;
}
if (recv_bytes == 0) {
return RECV_NEED_RETRY;
}
iov->iov_len += recv_bytes;
/* msg body not received complete, return to epoll_wait */
if (iov->iov_len < msg_head->msg_len) {
return RECV_NEED_RETRY;
}
/* recv msg body finish */
Assert(iov->iov_len == msg_head->msg_len);
recv_info->iov_item = iov_item;
recv_info->streamid = msg_head->logic_id;
recv_info->version = msg_head->version;
COMM_DEBUG_LOG("(r|inner recv)\tReceiver msg head[%d] "
"from socket[%d] node[%d]:%s logic id:%d len=%u.",
recv_bytes,
sock,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
msg_head->logic_id,
msg_head->msg_len);
// give up iov point that g_receivers had
// iov point must return and save to cmailbox
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].iov_item = NULL;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].head_read_cursor = 0;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].msg_head.type = MSG_NULL;
if (g_ackchk_time) {
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].last_rcv_time = mc_timers_ms();
}
return iov->iov_len;
}
static int libcomm_build_tcp_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx);
static LibcommAdaptLayer g_libcomm_adapt;
static void gs_init_adapt_layer()
{
if (g_instance.attr.attr_network.comm_tcp_mode) {
g_libcomm_adapt.recv_data = libcomm_tcp_recv;
g_libcomm_adapt.send_data = libcomm_tcp_send;
g_libcomm_adapt.connect = libcomm_build_tcp_connection;
g_libcomm_adapt.accept = mc_tcp_accept;
g_libcomm_adapt.listen = libcomm_tcp_listen;
g_libcomm_adapt.block_send = libcomm_tcp_send;
g_libcomm_adapt.send_ack = mc_tcp_write_block;
g_libcomm_adapt.check_socket = mc_tcp_check_socket;
/*
* Historical residual problem!
* comm_control_port and comm_sctp_port is the same,
* it is well on sctp mode, because we use two protocol.
* and it is conflict when we only use tcp protocol on tcp mode.
* so we use sctp_port+1 for data connection for tcp mode.
*/
} else {
#ifdef ENABLE_MULTIPLE_NODES
g_libcomm_adapt.recv_data = libcomm_sctp_recv;
g_libcomm_adapt.send_data = libcomm_sctp_send;
g_libcomm_adapt.connect = libcomm_build_sctp_connection;
g_libcomm_adapt.accept = mc_sctp_accept;
g_libcomm_adapt.listen = libcomm_sctp_listen;
g_libcomm_adapt.block_send = libcomm_sctp_send_block_mode;
g_libcomm_adapt.send_ack = mc_sctp_send_block_mode;
g_libcomm_adapt.check_socket = mc_sctp_check_socket;
#endif
}
}
static inline bool is_tcp_mode()
{
return g_libcomm_adapt.recv_data == libcomm_tcp_recv;
}
static void gs_set_comm_session()
{
g_comm_session.status = KNL_SESS_FAKE;
g_comm_session.debug_query_id = 0;
g_comm_session.session_id = 0;
return;
}
void comm_fill_hash_ctl(HASHCTL* ctl, Size k_size, Size e_size)
{
ctl->keysize = k_size;
ctl->entrysize = e_size;
ctl->hash = tag_hash;
ctl->hcxt = g_instance.comm_cxt.comm_global_mem_cxt;
return;
}
void gs_init_hash_table()
{
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
HASHCTL tid_ctl, sock_ver_ctl, sock_id_ctl, nodename_ctl, ipstat_ctl;
int flags, rc;
/* init g_htab_tid_poll */
rc = memset_s(&tid_ctl, sizeof(tid_ctl), 0, sizeof(HASHCTL));
securec_check(rc, "\0", "\0");
comm_fill_hash_ctl(&tid_ctl, sizeof(int), sizeof(tid_entry));
flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX;
g_htab_tid_poll = hash_create("libcomm tid lookup hash", 65535, &tid_ctl, flags);
LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_tid_poll_lock, 0);
/* init g_htab_socket_version */
rc = memset_s(&sock_ver_ctl, sizeof(sock_ver_ctl), 0, sizeof(HASHCTL));
securec_check(rc, "\0", "\0");
comm_fill_hash_ctl(&sock_ver_ctl, sizeof(int), sizeof(sock_ver_entry));
flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX;
g_htab_socket_version = hash_create("libcomm socket version lookup hash", 65535, &sock_ver_ctl, flags);
LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_socket_version_lock, 0);
/* init g_htab_socket_version */
rc = memset_s(&sock_id_ctl, sizeof(sock_id_ctl), 0, sizeof(HASHCTL));
securec_check(rc, "\0", "\0");
comm_fill_hash_ctl(&sock_id_ctl, sizeof(sock_id), sizeof(sock_id_entry));
flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX;
g_htab_fd_id_node_idx = hash_create("libcomm socket & node_idx lookup hash", 65535, &sock_id_ctl, flags);
LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_fd_id_node_idx_lock, 0);
/* init g_htab_nodename_node_idx */
rc = memset_s(&nodename_ctl, sizeof(nodename_ctl), 0, sizeof(HASHCTL));
securec_check(rc, "\0", "\0");
comm_fill_hash_ctl(&nodename_ctl, sizeof(char_key), sizeof(nodename_entry));
flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX;
g_htab_nodename_node_idx = hash_create("libcomm nodename & node_idx lookup hash", 65535, &nodename_ctl, flags);
LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_nodename_node_idx_lock, 0);
/* init g_htab_socket_version */
rc = memset_s(&ipstat_ctl, sizeof(ipstat_ctl), 0, sizeof(HASHCTL));
securec_check(rc, "\0", "\0");
comm_fill_hash_ctl(&ipstat_ctl, sizeof(ip_key), sizeof(ip_state_entry));
flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX;
g_htab_ip_state = hash_create("libcomm ip & status lookup hash", 65535, &ipstat_ctl, flags);
LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_ip_state_lock, 0);
}
// set basic infomation for communication layer
//
int gs_set_basic_info(const char* local_host, // ip of local host
const char* local_node_name, // local node name of the datanode, like PGXCNodeName
int cur_node_num, // number of node
char* sock_path) // unix domain path
{
LIBCOMM_ELOG(LOG,
"Initialize Communication Layer : node[%d] stream[%d], "
"receiver[%d], quota[%dKB], total memory[%dKB], "
"control port[%d], data port[%d], local_host[%s], local_node_name[%s], is_tcp_mode[%d], "
"sock_path[%s], cn_dn_conn_type[%d].",
cur_node_num,
g_instance.attr.attr_network.comm_max_stream,
g_instance.attr.attr_network.comm_max_receiver,
g_instance.attr.attr_network.comm_quota_size,
g_instance.attr.attr_network.comm_usable_memory,
g_instance.attr.attr_network.comm_control_port,
g_instance.attr.attr_network.comm_sctp_port,
local_host,
local_node_name,
g_instance.attr.attr_network.comm_tcp_mode,
sock_path,
g_instance.attr.attr_storage.comm_cn_dn_logic_conn);
errno_t ss_rc;
uint32 cpylen;
g_instance.comm_cxt.g_receivers = (struct local_receivers*)palloc0(sizeof(struct local_receivers));
g_instance.comm_cxt.g_senders = (struct local_senders*)palloc0(sizeof(struct local_senders));
g_instance.comm_cxt.quota_cxt.g_quota_changing = (struct binary_semaphore*)palloc0(sizeof(struct binary_semaphore));
g_instance.comm_cxt.localinfo_cxt.g_local_host = (char*)palloc0(HOST_ADDRSTRLEN * sizeof(char));
g_instance.comm_cxt.localinfo_cxt.g_self_nodename = (char*)palloc0(NAMEDATALEN * sizeof(char));
g_instance.comm_cxt.g_unix_path = (char*)palloc0(MAXPGPATH * sizeof(char));
g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list =
(mc_poller_hndl_list*)palloc0(MAX_RECV_NUM * sizeof(mc_poller_hndl_list));
g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock = (pthread_mutex_t*)palloc0(sizeof(pthread_mutex_t));
g_instance.comm_cxt.pollers_cxt.g_r_poller_list = (mc_poller_hndl_list*)palloc0(sizeof(mc_poller_hndl_list));
g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock = (pthread_mutex_t*)palloc0(sizeof(pthread_mutex_t));
g_instance.comm_cxt.pollers_cxt.g_s_poller_list = (mc_poller_hndl_list*)palloc0(sizeof(mc_poller_hndl_list));
g_instance.comm_cxt.pollers_cxt.g_s_poller_list_lock = (pthread_mutex_t*)palloc0(sizeof(pthread_mutex_t));
gs_set_usable_memory((long)g_instance.attr.attr_network.comm_usable_memory);
gs_set_memory_pool_size((long)g_instance.attr.attr_network.comm_memory_pool);
while (g_memory_pool_queue.initialize(
(uint32)(g_instance.comm_cxt.commutil_cxt.g_memory_pool_size / IOV_ITEM_SIZE)) != 0) {
LIBCOMM_ELOG(LOG, "g_memory_pool_queue initialization failed.");
}
g_instance.comm_cxt.libcomm_log_timezone = log_timezone;
g_instance.comm_cxt.g_comm_tcp_mode = g_instance.attr.attr_network.comm_tcp_mode;
g_instance.comm_cxt.counters_cxt.g_comm_send_timeout = u_sess->attr.attr_network.PoolerTimeout;
g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested = false;
// set the number of recv loop thread
g_instance.comm_cxt.counters_cxt.g_recv_num = g_instance.attr.attr_network.comm_max_receiver;
// set the number of stream number
g_instance.comm_cxt.counters_cxt.g_max_stream_num = g_instance.attr.attr_network.comm_max_stream;
// set expect and current node num
g_instance.comm_cxt.counters_cxt.g_expect_node_num = cur_node_num;
g_instance.comm_cxt.counters_cxt.g_cur_node_num = cur_node_num;
init_libcomm_cpu_rate();
// save the node name
cpylen = comm_get_cpylen(local_node_name, NAMEDATALEN);
ss_rc = memset_s(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN, local_node_name, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.localinfo_cxt.g_self_nodename[cpylen] = '\0';
// set the global variable
gs_set_local_host(local_host);
cpylen = comm_get_cpylen(sock_path, strlen(sock_path) + 1);
ss_rc = memset_s(g_instance.comm_cxt.g_unix_path, MAXPGPATH, 0x0, strlen(sock_path) + 1);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(g_instance.comm_cxt.g_unix_path, MAXPGPATH, sock_path, strlen(sock_path) + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_unix_path[cpylen] = '\0';
// 5 used for g_quotanofify_ratio, quota ratio for dynamic quota changing
gs_set_quota(g_instance.attr.attr_network.comm_quota_size, 5);
mc_tcp_set_timeout_param(u_sess->attr.attr_network.PoolerConnectTimeout, u_sess->attr.attr_network.PoolerTimeout);
// set keepalive parameter
mc_tcp_set_keepalive_param(u_sess->attr.attr_common.tcp_keepalives_idle,
u_sess->attr.attr_common.tcp_keepalives_interval,
u_sess->attr.attr_common.tcp_keepalives_count);
// set kerberos parameter
gs_set_kerberos_keyfile();
gs_init_adapt_layer();
// receiver structure setting
gs_receivers_struct_init(
g_instance.attr.attr_network.comm_control_port, g_instance.attr.attr_network.comm_sctp_port);
// sender structure setting
gs_senders_struct_init();
// init p-, c-mailbox and g_usable_streamid one by one, if failed, return FATAL.
for (int i = 0; i < g_instance.comm_cxt.counters_cxt.g_expect_node_num; i++) {
if (false == gs_mailbox_build(i)) {
ereport(FATAL, (errmsg("Failed to build mailbox[%d].", i)));
}
}
LIBCOMM_ELOG(LOG,
"(mailbox build)\tSuccess to build p&cmailbox from [0] to [%d].",
g_instance.comm_cxt.counters_cxt.g_expect_node_num - 1);
gs_set_comm_session();
g_instance.pid_cxt.CommSenderFlowPID = startCommSenderFlow();
g_instance.pid_cxt.CommReceiverFlowPID = startCommReceiverFlow();
g_instance.pid_cxt.CommAuxiliaryPID = startCommAuxiliary();
g_instance.pid_cxt.CommReceiverPIDS =
(ThreadId*)palloc0(g_instance.attr.attr_network.comm_max_receiver * sizeof(ThreadId));
if (g_instance.pid_cxt.CommReceiverPIDS == NULL) {
ereport(FATAL, (errmsg("communicator palloc CommReceiverPIDS mempry failed")));
}
startCommReceiverWorker(g_instance.pid_cxt.CommReceiverPIDS);
return 0;
} // gs_set_basic_info
void gs_set_hs_shm_data(HaShmemData* ha_shm_data)
{
(void)atomic_set(&g_instance.comm_cxt.g_ha_shm_data, ha_shm_data);
}
int gs_get_stream_num(void)
{
return g_instance.comm_cxt.counters_cxt.g_max_stream_num;
} // gs_get_stream_num
/*
* function name : gs_map_sock_id_to_node_idx
* description : save the key of fd_id and value of node idx into g_htab_fd_id_node_idx
* arguments :
* fd_id: struct of socket and socket id.
* idx: node idx.
* return value :
* 0: succeed.
* -1: save to htab failed.
*/
int gs_map_sock_id_to_node_idx(const sock_id fd_id, int idx)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_SOCKID_NODEIDX_FAILED)) {
errno = ECOMMSCTPMEMALLOC;
LIBCOMM_ELOG(WARNING,
"(sock_id_to_node_idx)\t[FAULT INJECTION]Failed to save socket[%d,%d] for node[%d].",
fd_id.fd,
fd_id.id,
idx);
return -1;
}
#endif
bool found = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
struct sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_ENTER, &found);
entry_id->entry.val = idx;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
return 0;
}
/*
* function name : gs_check_stream_key
* description : Check mailbox version, nonequal meas mailbox has been changed by other thread
* notice : we must get the mailbox lock before!
* arguments :
* _in_ version1: the version1 in mailbox.
* _in_ version2: the version2 application used.
* return value :
* true: version1 is equal to version2.
* false: version1 is not equal to version2.
*/
static inline bool gs_check_mailbox(uint16 version1, uint16 version2)
{
return (version1 == version2);
} // gs_check_mailbox
/*
* function name: gs_clean_cmailbox
* description: clean cmailbox for pooler reuse
* arguments: gs_sock: logic conn addr.
*/
void gs_clean_cmailbox(const gsocket gs_sock)
{
if (gs_sock.type == GSOCK_INVALID) {
return;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
struct c_mailbox* cmailbox = &C_MAILBOX(gs_sock.idx, gs_sock.sid);
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (cmailbox->buff_q->is_empty != 1) {
// clear the data buffer list
cmailbox->buff_q = mc_lqueue_clear(cmailbox->buff_q);
cmailbox->query_id = 0;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
t_thrd.int_cxt.ImmediateInterruptOK = TempImmediateInterruptOK;
return;
} // gs_clean_cmailbox
/*
* function name : gs_test_libcomm_conn
* description : we need to check the logic conn state when we reuse it in poller.
* moreover, reset the local tid here, cause logic conn from pooler
* may belong to other thread before.
* as pooler will only provide one logic conn to a specific thread.
* so lock is unnecessary here.
* arguments:
* gs_sock: logic conn addr.
* return value :
* true: both pmailbox and cmailbox state is ok.
* false: one of them is changed by other thread.
*/
bool gs_test_libcomm_conn(gsocket* gs_sock)
{
struct c_mailbox* cmailbox = &C_MAILBOX(gs_sock->idx, gs_sock->sid);
if (cmailbox->local_version != gs_sock->ver) {
LIBCOMM_ELOG(LOG,
"cmailbox version mismatch for node[nid:%d,sid:%d], mailbox_ver:%d gs_sock_ver:%d.",
gs_sock->idx,
gs_sock->sid,
cmailbox->local_version,
gs_sock->ver);
return false;
} else {
cmailbox->local_thread_id = 0;
}
struct p_mailbox* pmailbox = &P_MAILBOX(gs_sock->idx, gs_sock->sid);
if (pmailbox->local_version != gs_sock->ver) {
LIBCOMM_ELOG(LOG,
"pmailbox version mismatch for node[nid:%d,sid:%d], mailbox_ver:%d gs_sock_ver:%d.",
gs_sock->idx,
gs_sock->sid,
pmailbox->local_version,
gs_sock->ver);
return false;
} else {
pmailbox->local_thread_id = 0;
}
if (cmailbox->buff_q->is_empty == 1) {
return true;
}
LIBCOMM_ELOG(LOG,
"unexpected data on connection to node [nid:%d,sid:%d,ver:%d].",
gs_sock->idx,
gs_sock->sid,
cmailbox->local_version);
return false;
} // gs_test_libcomm_conn
static void gs_r_reset_cmailbox(struct c_mailbox* cmailbox, int close_reason)
{
if (cmailbox == NULL || cmailbox->state == MAIL_CLOSED) {
return;
}
int node_idx = cmailbox->idx;
errno_t ss_rc;
printf_cmailbox_statistic(cmailbox, g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename);
cmailbox->local_version++;
if (cmailbox->local_version >= MAX_MAILBOX_VERSION) {
cmailbox->local_version = 0;
}
cmailbox->state = MAIL_CLOSED;
cmailbox->close_reason = close_reason;
cmailbox->ctrl_tcp_sock = -1;
cmailbox->is_producer = 0;
cmailbox->bufCAP = 0;
// clear the data buffer list
cmailbox->buff_q = mc_lqueue_clear(cmailbox->buff_q);
cmailbox->query_id = 0;
cmailbox->local_thread_id = 0;
cmailbox->peer_thread_id = 0;
cmailbox->remote_version = 0;
cmailbox->semaphore = NULL;
if (cmailbox->statistic != NULL) {
ss_rc = memset_s(cmailbox->statistic, sizeof(cmailbox_statistic), 0, sizeof(cmailbox_statistic));
securec_check(ss_rc, "\0", "\0");
if (!g_instance.comm_cxt.commutil_cxt.g_stat_mode) {
LIBCOMM_FREE(cmailbox->statistic, sizeof(struct cmailbox_statistic));
}
}
#ifdef SCTP_BUFFER_DEBUG
cmailbox->buff_q_tmp = mc_lqueue_clear(cmailbox->buff_q_tmp);
#endif
} // gs_r_reset_cmailbox
static void gs_s_reset_pmailbox(struct p_mailbox* pmailbox, int close_reason)
{
if (pmailbox == NULL || pmailbox->state == MAIL_CLOSED) {
return;
}
int node_idx = pmailbox->idx;
int streamid = pmailbox->streamid;
errno_t ss_rc;
printf_pmailbox_statistic(pmailbox, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
pmailbox->local_version++;
if (pmailbox->local_version >= MAX_MAILBOX_VERSION) {
pmailbox->local_version = 0;
}
pmailbox->state = MAIL_CLOSED;
pmailbox->close_reason = close_reason;
pmailbox->ctrl_tcp_sock = -1;
pmailbox->is_producer = 1;
pmailbox->bufCAP = 0;
pmailbox->query_id = 0;
pmailbox->local_thread_id = 0;
pmailbox->peer_thread_id = 0;
pmailbox->remote_version = 0;
pmailbox->semaphore = NULL;
if (pmailbox->statistic != NULL) {
ss_rc = memset_s(pmailbox->statistic, sizeof(pmailbox_statistic), 0, sizeof(pmailbox_statistic));
securec_check(ss_rc, "\0", "\0");
if (!g_instance.comm_cxt.commutil_cxt.g_stat_mode) {
LIBCOMM_FREE(pmailbox->statistic, sizeof(struct pmailbox_statistic));
}
}
// push the stream index into the avaliable stream index queue
// stream 0 is reserved
if (streamid > 0) {
(void)g_instance.comm_cxt.g_usable_streamid[node_idx].push(
g_instance.comm_cxt.g_usable_streamid + node_idx, streamid);
}
} // gs_s_reset_pmailbox
// initialize cmailbox for Consumer working threads
//
static void gs_cmailbox_init()
{
LIBCOMM_MALLOC(g_instance.comm_cxt.g_c_mailbox, MAX_CN_DN_NODE_NUM * sizeof(struct c_mailbox*), c_mailbox*);
if (g_instance.comm_cxt.g_c_mailbox == NULL) {
ereport(FATAL, (errmsg("(r|cmailbox init)\tFailed to init cmailbox.")));
}
return;
} // gs_r_cmailbox_init
static void gs_mailbox_destory(int idx)
{
p_mailbox* pmailbox = NULL;
c_mailbox* cmailbox = NULL;
int sid = -1;
mc_queue_destroy(g_instance.comm_cxt.g_usable_streamid + idx);
if (g_instance.comm_cxt.g_p_mailbox[idx] != NULL) {
pmailbox = &P_MAILBOX(idx, sid);
LIBCOMM_PTHREAD_MUTEX_DESTORY(&(pmailbox->sinfo_lock));
LIBCOMM_FREE(g_instance.comm_cxt.g_p_mailbox[idx],
g_instance.comm_cxt.counters_cxt.g_max_stream_num * sizeof(struct p_mailbox));
}
if (g_instance.comm_cxt.g_c_mailbox[idx] != NULL) {
for (sid = 0; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
cmailbox = &C_MAILBOX(idx, sid);
mc_lqueue_clear(cmailbox->buff_q);
LIBCOMM_FREE(cmailbox->buff_q, sizeof(struct mc_lqueue));
LIBCOMM_PTHREAD_MUTEX_DESTORY(&(cmailbox->sinfo_lock));
}
LIBCOMM_FREE(g_instance.comm_cxt.g_c_mailbox[idx],
g_instance.comm_cxt.counters_cxt.g_max_stream_num * sizeof(struct c_mailbox));
}
}
// initialize cmailbox[idx] and pmailbox[idx]
//
static bool gs_mailbox_build(int idx)
{
int sid = -1;
/* initialize the queue for this connection */
if (mc_queue_init(g_instance.comm_cxt.g_usable_streamid + idx, g_instance.comm_cxt.counters_cxt.g_max_stream_num) ==
-1) {
LIBCOMM_ELOG(WARNING, "(mailbox build)\tFailed to initialize g_usable_streamid[%d].", idx);
goto cleanup_mailbox;
}
/* malloc pmailbox array */
LIBCOMM_MALLOC((*(g_instance.comm_cxt.g_p_mailbox + idx)),
g_instance.comm_cxt.counters_cxt.g_max_stream_num * sizeof(struct p_mailbox),
struct p_mailbox);
if ((*(g_instance.comm_cxt.g_p_mailbox + idx)) == NULL) {
LIBCOMM_ELOG(WARNING, "(mailbox build)\tFailed to malloc pmailbox[%d].", idx);
goto cleanup_mailbox;
}
/* malloc cmailbox array */
LIBCOMM_MALLOC((*(g_instance.comm_cxt.g_c_mailbox + idx)),
g_instance.comm_cxt.counters_cxt.g_max_stream_num * sizeof(struct c_mailbox),
struct c_mailbox);
if ((*(g_instance.comm_cxt.g_c_mailbox + idx)) == NULL) {
LIBCOMM_ELOG(WARNING, "(mailbox build)\tFailed to malloc cmailbox[%d].", idx);
goto cleanup_mailbox;
}
/* do initialization */
for (sid = 0; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
/* initialize pmailbox */
P_MAILBOX(idx, sid).idx = idx;
P_MAILBOX(idx, sid).streamid = sid;
/* reset pmailbox and push the stream id to g_usable_streamid */
gs_s_reset_pmailbox(&P_MAILBOX(idx, sid), 0);
LIBCOMM_PTHREAD_MUTEX_INIT(&(P_MAILBOX(idx, sid).sinfo_lock), 0);
/* initialize cmailbox */
C_MAILBOX(idx, sid).idx = idx;
C_MAILBOX(idx, sid).streamid = sid;
gs_r_reset_cmailbox(&C_MAILBOX(idx, sid), 0);
LIBCOMM_PTHREAD_MUTEX_INIT(&(C_MAILBOX(idx, sid).sinfo_lock), 0);
/* initialize buffer queue, it is just a pointer, not space */
C_MAILBOX(idx, sid).buff_q = mc_lqueue_init(g_instance.comm_cxt.quota_cxt.g_quota);
if (C_MAILBOX(idx, sid).buff_q == NULL) {
LIBCOMM_ELOG(WARNING, "(mailbox build)\tFailed to malloc buff_q from cmailbox[%d].", idx);
goto cleanup_mailbox;
}
}
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_DYNAMIC_CAPACITY_FAILED)) {
errno = ECOMMSCTPMEMALLOC;
LIBCOMM_ELOG(WARNING, "(mailbox build)\t[FAULT INJECTION]Failed to build p&cmailbox[%d].", idx);
goto cleanup_mailbox;
}
#endif
COMM_DEBUG_LOG("(mailbox build)\tSuccess to build p&cmailbox[%d].", idx);
return true;
cleanup_mailbox:
gs_mailbox_destory(idx);
return false;
}
// initialize pmailbox for Producer working threads
//
static void gs_pmailbox_init()
{
LIBCOMM_MALLOC(g_instance.comm_cxt.g_p_mailbox, MAX_CN_DN_NODE_NUM * sizeof(struct p_mailbox*), p_mailbox*);
if (g_instance.comm_cxt.g_p_mailbox == NULL) {
ereport(FATAL, (errmsg("(s|pmailbox init)\tFailed to init pmailbox.")));
}
// malloc memory for stream id queues
LIBCOMM_MALLOC(g_instance.comm_cxt.g_usable_streamid, MAX_CN_DN_NODE_NUM * sizeof(struct mc_queue), mc_queue);
if (g_instance.comm_cxt.g_usable_streamid == NULL) {
ereport(FATAL, (errmsg("(s|pmailbox init)\tFailed to init g_usable_streamid.")));
}
return;
} // gs_s_pmailbox_init
/*
* function name : gs_set_reply_sock
* description : set reply socket for g_r_node_sock by compare remote node name
* arguments : node_idx, node we want to set reply sock
*/
static void gs_set_reply_sock(int node_idx)
{
for (int recv_idx = 0; recv_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; recv_idx++) {
if (strcmp(g_instance.comm_cxt.g_r_node_sock[recv_idx].remote_nodename,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename) == 0) {
// save reply socket in g_r_node_sock
g_instance.comm_cxt.g_r_node_sock[recv_idx].lock();
g_instance.comm_cxt.g_r_node_sock[recv_idx].sctp_reply_sock =
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
g_instance.comm_cxt.g_r_node_sock[recv_idx].unlock();
break;
}
}
return;
}
/*
* function name : gs_get_stream_id
* description : initialize socket of local senders, for sending
* notice : retry 10 times inside if failure happens,
* arguments __in node_begin
* __in node_end
*/
static void gs_senders_struct_set()
{
#ifdef ENABLE_MULTIPLE_NODES
int error = 0;
int i;
// initialize sender socket and address storage
for (i = 0; i < MAX_CN_DN_NODE_NUM; i++) {
// g_s_node_sock
g_instance.comm_cxt.g_s_node_sock[i].init();
// initialize sctp address storage
// initialize socket in gs_connect
error = mc_sctp_addr_init(g_instance.comm_cxt.localinfo_cxt.g_local_host,
0,
&(g_instance.comm_cxt.g_senders->sender_conn[i].ss),
&(g_instance.comm_cxt.g_senders->sender_conn[i].ss_len));
if (error != 0) {
ereport(FATAL,
(errmsg("(s|sender init)\tFailed to init sender[%d] for %s.",
i,
g_instance.comm_cxt.localinfo_cxt.g_local_host)));
}
// set g_instance.comm_cxt.g_senders->sender_conn AND g_sender_count
LIBCOMM_PTHREAD_RWLOCK_INIT(&g_instance.comm_cxt.g_senders->sender_conn[i].rwlock, NULL);
g_instance.comm_cxt.g_senders->sender_conn[i].socket = -1;
g_instance.comm_cxt.g_senders->sender_conn[i].socket_id = -1;
g_instance.comm_cxt.g_senders->sender_conn[i].comm_bytes = 0;
g_instance.comm_cxt.g_senders->sender_conn[i].comm_count = 0;
}
return;
#endif
}
/*
* function name : gs_update_connection_state
* description : update the connections state of htab
* signal all threads block on the connections
* arguments : addr: structure of IP & PORT
* result: succeed or failed
*/
void gs_update_connection_state(ip_key addr, int result, bool is_signal, int node_idx)
{
bool found = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock);
ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_FIND, &found);
if (!found) {
LIBCOMM_ELOG(WARNING, "(s|connect)\tFail to get connection state:port[%s:%d].", addr.ip, addr.port);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
Assert(found);
return;
}
COMM_DEBUG_LOG(
"(s|gs_update_connection_state)\tchange connection [%s:%d] state to %d.", addr.ip, addr.port, result);
/* cannot update connection state when the node idx mismatch */
if (entry_poll->entry.val.node_idx != node_idx) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
return;
}
entry_poll->entry.val.conn_state = result;
if (is_signal) {
entry_poll->entry._signal_all();
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
return;
}
/*
* function name : gs_s_get_connection_state
* description : gs_s_get_connection_state gives the
* connection state of indicated IP & PORT
* arguments : addr: structure of IP & PORT
* return value :
* CONNSTATECONNECTING: need to build a new connection
* CONNSTATEFAIL: get connection state failed
* CONNSTATESUCCEED: exist a valid connection
*/
int gs_s_get_connection_state(ip_key addr, int node_idx, int type)
{
int rc = -1;
int old_slot_id = -1;
int retry_count = 0;
int state = CONNSTATEFAIL;
bool found = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock);
ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_ENTER, &found);
/* no connection exist before, add in htab and return need to create */
if (unlikely(!found)) {
COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\tno connection exist [%s:%d].", addr.ip, addr.port);
entry_poll->entry.val.conn_state = CONNSTATECONNECTING;
entry_poll->entry.val.node_idx = node_idx;
entry_poll->entry._init();
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
return CONNSTATECONNECTING;
}
switch (entry_poll->entry.val.conn_state) {
case CONNSTATECONNECTING:
/* someone is trying to make connetion, wait the result */
COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection start [%s:%d].", addr.ip, addr.port);
while (entry_poll->entry.val.conn_state == CONNSTATECONNECTING) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
rc = entry_poll->entry._timewait(CHECKCONNSTATTIMEOUT);
retry_count++;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock);
if (retry_count > (g_instance.comm_cxt.counters_cxt.g_comm_send_timeout / CHECKCONNSTATTIMEOUT)) {
errno = ECOMMSCTPCONNTIMEOUT;
break;
}
}
COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection end [%s:%d]:%d.",
addr.ip,
addr.port,
entry_poll->entry.val.conn_state);
/* connect state update became succeed, return CONNSTATESUCCEED */
if (entry_poll->entry.val.conn_state == CONNSTATESUCCEED) {
state = CONNSTATESUCCEED;
} else { /* connect state is not succeed, return CONNSTATEFAIL */
errno = (rc == ETIMEDOUT) ? ECOMMSCTPCONNTIMEOUT : ECOMMSCTPTCPCONNFAIL;
state = CONNSTATEFAIL;
}
break;
case CONNSTATEFAIL:
COMM_DEBUG_LOG(
"(s|gs_s_get_connection_state)\tconnection invalid, need to create[%s:%d].", addr.ip, addr.port);
/* connection is failed before, update state to connecting and return need to create */
entry_poll->entry.val.conn_state = CONNSTATECONNECTING;
entry_poll->entry.val.node_idx = node_idx;
state = CONNSTATECONNECTING;
break;
case CONNSTATESUCCEED:
/* when the node idx mismatch with the valid connection before
* we assume it as a new connection, update node idx and close
* the old connection
*/
if (node_idx != entry_poll->entry.val.node_idx) {
old_slot_id = entry_poll->entry.val.node_idx;
entry_poll->entry.val.conn_state = CONNSTATECONNECTING;
entry_poll->entry.val.node_idx = node_idx;
state = CONNSTATECONNECTING;
} else {
/* a valid connection in htab, return connection succeed */
state = CONNSTATESUCCEED;
}
break;
default:
/* unexpected cases */
LIBCOMM_ELOG(WARNING,
"(s|connect)\tUnexpected state in checking connection state:port[%s:%d], state:%d, node_idx:%d.",
addr.ip,
addr.port,
entry_poll->entry.val.conn_state,
entry_poll->entry.val.node_idx);
state = CONNSTATEFAIL;
break;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock);
/* close old data connection */
if (old_slot_id != -1) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tClose the old connections for node%d[%s]:port[%s:%d], type:%d.",
old_slot_id,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, old_slot_id),
addr.ip,
addr.port,
type);
if (type == DATA_CHANNEL) {
g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].ip_changed = true;
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock);
struct sock_id sctp_fd_id = {g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket,
g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket_id};
gs_s_close_bad_data_socket(&sctp_fd_id, ECOMMSCTPPEERCHANGED, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock);
} else {
g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed = true;
g_instance.comm_cxt.g_s_node_sock[node_idx].lock();
struct sock_id ctrl_fd_id = {g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock,
g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock_id};
gs_s_close_bad_ctrl_tcp_sock(&ctrl_fd_id, ECOMMSCTPPEERCHANGED, false, node_idx);
g_instance.comm_cxt.g_s_node_sock[node_idx].unlock();
}
}
return state;
}
/*
* add local thread id to g_htab_tid_poll
* then thread will call gs_poll during connecting, send and recv
*/
static int gs_poll_create()
{
struct tid_entry* entry_tid = NULL;
bool found = false;
if (t_thrd.comm_cxt.libcomm_semaphore != NULL) {
return 0;
}
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_CREATE_POLL_FAILED)) {
errno = ECOMMSCTPMEMALLOC;
LIBCOMM_ELOG(WARNING, "(poll create)\t[FAULT INJECTION]Failed to add local tid to g_htab_tid_poll.");
return -1;
}
#endif
if (t_thrd.comm_cxt.MyPid <= 0) {
t_thrd.comm_cxt.MyPid = gettid();
}
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock);
entry_tid = (tid_entry*)hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_ENTER, &found);
if (!found) {
entry_tid->entry.val = -1;
entry_tid->entry._init();
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock);
t_thrd.comm_cxt.libcomm_semaphore = &(entry_tid->entry.sem);
return 0;
}
// delete tid from tid_poll, usually called when thread exit or logic conn is closed.
// but when the thread needed to delete is calling gs_poll, just signal it instead of del it.
// because for CN, thread usually wait for multiple logic connection, so we cannot del it when
// some logic connection is close.
//
void gs_poll_close()
{
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
if (t_thrd.comm_cxt.libcomm_semaphore != NULL) {
t_thrd.comm_cxt.libcomm_semaphore = NULL;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock);
hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock);
}
return;
}
/*
* thread which call gs_poll and block in here
* until the expected event happened
* or some error happened(timeout, logic conn is closed, interruption happened).
*/
static int gs_poll(int time_out)
{
return t_thrd.comm_cxt.libcomm_semaphore->timed_wait(time_out);
}
/*
* siganl thread when the expected event happened or some error happened
*/
static void gs_poll_signal(binary_semaphore* sem)
{
if (sem != NULL) {
sem->post();
}
}
/*
* when recv some interruption, gs_auxiliary will
* signal all thread waitting in gs_poll
* and threads will check interruption.
*/
static void gs_broadcast_poll()
{
HASH_SEQ_STATUS hash_seq;
tid_entry* element = NULL;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock);
hash_seq_init(&hash_seq, g_htab_tid_poll);
while ((element = (tid_entry*)hash_seq_search(&hash_seq)) != NULL) {
element->entry._signal();
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock);
}
/*
* function name : gs_get_node_idx
* description : gs_get_node_idx gives the node idx of backend.
* the first node connected to current process is node idx 0.
* arguments :
* node_name: name of backend
* len: NAMEDATALEN
* return value : -1:failed
* >0:node id
*/
static int gs_get_node_idx(char* node_name)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_NO_NODEIDX)) {
errno = ECOMMSCTPINVALNODEID;
LIBCOMM_ELOG(WARNING, "(s|get nodeid)\t[FAULT INJECTION]Failed to obtain node id for node %s.", node_name);
return -1;
}
#endif
// get node index
struct nodename_entry* entry_name = NULL;
struct char_key ckey;
bool found = false;
errno_t ss_rc;
int ret = -1;
uint32 cpylen = comm_get_cpylen(node_name, NAMEDATALEN);
ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(ckey.name, NAMEDATALEN, node_name, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
ckey.name[cpylen] = '\0';
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock);
entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_ENTER, &found);
if (found) {
ret = entry_name->entry.val;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock);
return ret;
}
// if the node is not registed, get a node index and save node name -> node index to hash table
int node_idx = g_nodename_count + 1;
if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
hash_search(g_htab_nodename_node_idx, &ckey, HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock);
errno = ECOMMSCTPINVALNODEID;
return -1;
}
g_nodename_count++;
entry_name->entry.val = node_idx;
ret = entry_name->entry.val;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock);
LIBCOMM_ELOG(LOG, "(s|get idx)\tGenerate node idx [%d] for node:%s.", ret, node_name);
return ret;
}
/*
* function name : gs_get_stream_id
* description : producer get usable stream index for current query, which is designed by StreamKey.
* if the key is already in the hash table, return it!
* arguments :
* _in_ key_ns: sctp stream key with node index.
* return value : -1:failed
* >0:stream id
*/
static int gs_get_stream_id(int node_idx)
{
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_NO_STREAMID)) {
errno = ECOMMSCTPSTREAMIDX;
LIBCOMM_ELOG(WARNING,
"(s|get sid)\t[FAULT INJECTION]Failed to obtain sctp stream for node[%d]:%s.",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
return -1;
}
#endif
int streamid = -1;
// have no usable stream id
if (g_instance.comm_cxt.g_usable_streamid[node_idx].pop(
g_instance.comm_cxt.g_usable_streamid + node_idx, &streamid) <= 0) {
errno = ECOMMSCTPSTREAMIDX;
LIBCOMM_ELOG(WARNING,
"(s|get sid)\tFailed to obtain sctp stream for node[%d]:%s, usable:%d/%d.",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
g_instance.comm_cxt.g_usable_streamid[node_idx].count,
g_instance.comm_cxt.counters_cxt.g_max_stream_num);
return -1;
}
// succeed to return the entry in g_s_htab_nodeid_skey_to_stream
COMM_DEBUG_LOG("(s|get sid)\tObtain sctp stream[%d] for node[%d]:%s.",
streamid,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
return streamid;
} // gs_r_get_usable_streamid
static int gs_update_fd_to_htab_socket_version(struct sock_id* fd_id)
{
struct sock_ver_entry* entry_ver = NULL;
bool found = false;
int fd = fd_id->fd;
int id = fd_id->id;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock);
entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_ENTER, &found);
if (!found) {
entry_ver->entry.val = id;
} else { // if there is an entry already, we update the version(id) of the socket(fd)
entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1);
fd_id->id = entry_ver->entry.val; // set the new id into fd_id !!!
COMM_DEBUG_LOG("(add fd & version)\tSucceed to update socket[%d] version[%d].", fd, fd_id->id);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
return 0;
} // gs_update_fd_to_htab_socket_version
// send control message to remoter without_lock
// the parameter sock and the fd_id are result parameters
//
static int gs_send_ctrl_msg_without_lock(struct node_sock* ns, FCMSG_T* msg, int node_idx, int role)
{
int ctrl_sock = -1;
int ctrl_sock_id = -1;
int rc = -1;
int send_bytes = 0;
uint64 time_enter, time_now;
time_enter = mc_timers_ms();
ctrl_sock = ns->get_nl(CTRL_TCP_SOCK, &ctrl_sock_id);
if (ctrl_sock >= 0) {
for (;;) {
/* we send data in non-block mode,
* but we will assure the data will
* be sent out if the network is ok
*/
rc = mc_tcp_write_noblock(ctrl_sock, (char*)msg + send_bytes, sizeof(FCMSG_T) - send_bytes);
if (rc < 0) {
break;
}
/* check if other thread has closed current connection, for producer only */
if ((role == ROLE_PRODUCER) && (g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed == true)) {
errno = ECOMMSCTPPEERCHANGED;
rc = -1;
shutdown(ctrl_sock, SHUT_RDWR);
break;
}
send_bytes += rc;
if ((uint32)send_bytes >= sizeof(FCMSG_T)) {
break;
}
time_now = mc_timers_ms();
if (((time_now - time_enter) >
(((uint64)g_instance.comm_cxt.counters_cxt.g_comm_send_timeout) * SEC_TO_MICRO_SEC)) &&
(time_now > time_enter)) {
errno = ECOMMSCTPSENDTIMEOUT;
rc = -1;
shutdown(ctrl_sock, SHUT_RDWR);
break;
}
}
}
// if tcp send failed, close tcp connction
if (rc <= 0) {
LIBCOMM_ELOG(WARNING,
"(SendCtrlMsg)\tFailed to send type[%s] message to node:%s with socket[%d,%d]:%s.",
ctrl_msg_string(msg->type),
ns->remote_nodename,
ctrl_sock,
ctrl_sock_id,
mc_strerror(errno));
}
COMM_DEBUG_CALL(printfcmsg("SendCtrlMsg", msg));
return rc;
} // gs_r_send_ctrl_msg
// send control message to remoter
// the parameter sock and the fd_id are result parameters
//
static int gs_send_ctrl_msg(struct node_sock* ns, FCMSG_T* msg, int role)
{
int rc = -1;
if (msg->node_idx == 0 && msg->streamid == 0) {
return 0;
}
ns->lock();
rc = gs_send_ctrl_msg_without_lock(ns, msg, msg->node_idx, role);
ns->unlock();
return rc;
} // gs_r_send_ctrl_msg
// send control message to remoter
// by sokcet
//
static int gs_send_ctrl_msg_by_socket(int ctrl_sock, FCMSG_T* msg)
{
int rc = mc_tcp_write_block(ctrl_sock, (void*)msg, sizeof(FCMSG_T)); // do send message
// if tcp send failed, close tcp connction
if (rc <= 0) {
LIBCOMM_ELOG(WARNING,
"(SendCtrlMsg)\tFailed to send type[%s] message to node[%d]:%s with socket[%d]:%s.",
ctrl_msg_string(msg->type),
msg->node_idx,
msg->nodename,
ctrl_sock,
mc_strerror(errno));
}
COMM_DEBUG_CALL(printfcmsg("SendCtrlMsg", msg));
return rc;
} // gs_r_send_ctrl_msg
// receiver close all streams of a node which is designed by control tcp socket
// we call this function because of the broken control tcp connection or sctp connection,
// if it is sctp connection, we should send the close info to remote
// step 1: get node index (node_idx)
// step 2: traverse g_c_mailbox[node_idx][*]
// step 3: do notify and reset all cmailbox
//
static void gs_r_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason)
{
struct c_mailbox* cmailbox = NULL;
struct FCMSG_T fcmsgs = {0x0};
// Note: we should have locked at the caller, so we need not lock here again
//
LIBCOMM_ELOG(WARNING,
"(r|close all streams)\tTo reset all streams "
"by socket[%d] for node[%d]:%s, detail:%s.",
fd,
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
mc_strerror(close_reason));
for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) {
cmailbox = &C_MAILBOX(node_idx, j);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if ((fd == -1 || cmailbox->ctrl_tcp_sock == fd) && (cmailbox->state != MAIL_CLOSED)) {
gs_r_close_logic_connection(cmailbox, close_reason, &fcmsgs);
// reset local stream logic connection info
COMM_DEBUG_LOG("(r|close all streams)\tTo close stream[%d], "
"node[%d]:%s, query[%lu], socket[%d].",
j,
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
cmailbox->query_id,
cmailbox->ctrl_tcp_sock);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
// Send close ctrl msg to remote without cmailbox lock
if (IS_NOTIFY_REMOTE(close_reason)) {
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER);
}
} else {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
}
}
} // gs_r_reset_all_streams_by_fd_idx
// sender close all streams of a node which is designed by control tcp socket
// we call this function because of the broken control tcp connection, so we did not need to send the status to remote
// step 1: get node index (node_idx)
// step 2: traverse g_p_mailbox[node_idx][*]
// step 3: do notification and reset all pmailbox
//
static void gs_s_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason, bool with_ctrl_lock)
{
struct p_mailbox* pmailbox = NULL;
struct FCMSG_T fcmsgs = {0x0};
// Note: we should have locked at the caller, so we need not lock here again
//
LIBCOMM_ELOG(WARNING,
"(s|close all streams)\tTo reset all streams "
"by socket[%d] for node[%d]:%s, detail:%s.",
fd,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
mc_strerror(close_reason));
for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) {
pmailbox = &P_MAILBOX(node_idx, j);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (((pmailbox->ctrl_tcp_sock == -1) || (fd == -1) || (pmailbox->ctrl_tcp_sock == fd)) &&
(pmailbox->state != MAIL_CLOSED)) {
COMM_DEBUG_LOG("(s|close all streams)\tTo close stream[%d], "
"node[%d]:%s, query[%lu], socket[%d].",
j,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->query_id,
pmailbox->ctrl_tcp_sock);
gs_s_close_logic_connection(pmailbox, close_reason, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
// Send close ctrl msg to remote without cmailbox lock
if (IS_NOTIFY_REMOTE(close_reason)) {
if (with_ctrl_lock) {
(void)gs_send_ctrl_msg_without_lock(
&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER);
} else {
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER);
}
}
} else {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
}
} // gs_s_close_all_streams_by_ctrl_tcp_sock
/* To remove the closed fd in the pooler list.
* for example, if we have many events in the poll_list [poll_1, poll_2, poll_3,...], the poller at the rear may be
* closed by the front one. So we need to check and delete the closed one in the poll_list.
* Notice: if the g_libcomm_poller_list doesn't belong to the Caller, it just returns.
*/
static void gs_clean_events(struct sock_id* old_fd_id)
{
int i = 0;
int fd = -1;
int id = -1;
if (t_thrd.comm_cxt.g_libcomm_poller_list == NULL) {
return;
}
int nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents;
for (i = 0; i < nevents; i++) {
fd = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 >> MC_POLLER_FD_ID_OFFSET));
id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 & MC_POLLER_FD_ID_MASK));
if ((old_fd_id->fd == fd) && (old_fd_id->id == id)) {
COMM_DEBUG_LOG("(clean events)\tClean socket[%d,%d] in the poller list.", fd, id);
/* if the old_fd_id in the poller list, we need to remove it.
* To simplify, we just move the last one to this position.
* if "i" is the last one, i == nevents-1, it doesn't matter.
* if i<nevents-1, move the last one to i-th position.
*/
t_thrd.comm_cxt.g_libcomm_poller_list->events[i] =
t_thrd.comm_cxt.g_libcomm_poller_list->events[nevents - 1];
t_thrd.comm_cxt.g_libcomm_poller_list->nevents--;
break;
}
}
return;
}
// receiver close and clear bad tcp control socket, and related information
//
static void gs_r_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason)
{
int fd = fd_id->fd;
int id = fd_id->id;
bool found = false;
if (fd < 0 || id < 0) {
return;
}
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock);
// step1: remove the fd from the poller cabinet
//
if (g_instance.comm_cxt.pollers_cxt.g_r_poller_list->del_fd(fd_id) != 0) {
LIBCOMM_ELOG(WARNING,
" (r|close tcp socket)\tFailed to delete socket[%d,%d] from poll list:%s.",
fd,
id,
mc_strerror(errno));
}
// step2: get node index by fd
//
int node_idx = -1;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_FIND, &found);
if (found) {
node_idx = entry_id->entry.val;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
// step3: make sure the fd and the version are matched, or it has been closed already
//
struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found);
if ((!found) || (entry_ver->entry.val != fd_id->id)) {
LIBCOMM_ELOG(WARNING,
"(r|close tcp socket)\tFailed to close socket[%d,%d], maybe already reused[%d,%d].",
fd,
id,
(found) ? fd : -1,
(found) ? entry_ver->entry.val : -1);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
return;
}
LIBCOMM_ELOG(LOG, "(r|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d].", fd, id);
if (node_idx >= 0) {
// step4: close all mailbox at receiver
//
gs_r_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason);
// step5: close the socket and reset the socket infomation structure(g_r_node_sock[node_idx])
//
g_instance.comm_cxt.g_r_node_sock[node_idx].lock();
if (g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd &&
g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) {
g_instance.comm_cxt.g_r_node_sock[node_idx].close_socket_nl(CTRL_TCP_SOCK);
LIBCOMM_ELOG(WARNING,
"(r|close tcp socket)\tTCP disconnect with socket[%d,%d] "
"to host:%s, node[%d]:[%s].",
fd,
id,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host,
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename);
}
g_instance.comm_cxt.g_r_node_sock[node_idx].unlock();
gs_clean_events(fd_id);
} else {
mc_tcp_close(fd_id->fd);
}
// step6: if the fd is closed, we update the fd version
//
entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
return;
} // gs_r_close_bad_ctrl_tcp_sock
// sender close and clear bad tcp control socket, and related information
// clean_epoll is true when this function is called by sender flow ctrl thread,
// we delete fd from epoll list and close fd.
// clean_epoll is false when this function is called by producer thread,
// in this case, we cannot close fd and delete from epoll list,
// cause other thread may use this fd after close,
// while sender flow control thread still use this fd to recv.
// NOTE: fd can be closed and deleted from epoll list only under the sender flow ctrl.
static void gs_s_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason, bool clean_epoll, int node_idx)
{
int fd = fd_id->fd;
int id = fd_id->id;
ip_key addr;
bool is_addr = false;
errno_t ss_rc;
uint32 cpylen;
bool found = false;
if (fd < 0 || id < 0) {
return;
}
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock);
// step1: remove the fd from the poller cabinet
//
if (clean_epoll) {
gs_clean_events(fd_id);
if (g_instance.comm_cxt.pollers_cxt.g_s_poller_list->del_fd(fd_id) != 0) {
COMM_DEBUG_LOG("(s|cls bad tcp socket)\tFailed to remove bad socket with socket entry[%d,%d]:%s.",
fd,
id,
mc_strerror(errno));
}
}
// step2: make sure the fd and the version are matched, or it has been closed already
//
struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found);
if (!found || entry_ver->entry.val != fd_id->id) {
LIBCOMM_ELOG(WARNING,
"(s|cls bad tcp socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].",
fd,
id,
(found) ? fd : -1,
(found) ? entry_ver->entry.val : -1);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
return;
}
// step3: close all mailbox at receiver
//
if (node_idx >= 0) {
gs_s_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason, true);
}
// step4: close the socket and reset the socket infomation structure(g_s_node_sock[node_idx])
//
LIBCOMM_ELOG(LOG,
"(s|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d] : %s.",
fd,
id,
mc_strerror(close_reason));
if (node_idx >= 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
if (g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd &&
g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) {
cpylen = comm_get_cpylen(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(
addr.ip, HOST_LEN_OF_HTAB, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
addr.port = g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_port;
is_addr = true;
// producer thread detect destination ip is changed
// then notify the origination backend to close connection
if (close_reason == ECOMMSCTPPEERCHANGED) {
struct FCMSG_T fcmsgs = {0x0};
fcmsgs.type = CTRL_PEER_CHANGED;
fcmsgs.node_idx = node_idx;
fcmsgs.streamid = 1;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(
fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
(void)gs_send_ctrl_msg_without_lock(
&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER);
}
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK);
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK_ID);
LIBCOMM_ELOG(WARNING,
"(s|cls bad tcp socket)\tClose bad socket[%d,%d] "
"for host:%s, node[%d]:%s.",
fd,
id,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
}
}
// clean_epoll is true only under sender flow control thread
if (clean_epoll) {
mc_tcp_close(fd_id->fd);
// step5: if the fd is closed, we update the fd version
//
entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1);
}
// step6: update connection state in htab
//
if (is_addr) {
gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
} // gs_s_close_bad_ctrl_tcp_sock
// Calculation how many quota size need to add in mailbox
static long gs_add_quota_size(c_mailbox* cmailbox)
{
#define COMM_HIGH_MEM_USED (used_memory >= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.8))
#define COMM_LOW_MEM_USED (used_memory <= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.5))
long used_memory = gs_get_comm_used_memory();
long add_quota = 0;
long max_buff = 0; // must be equal [DEFULTMSGLEN, comm_quota_size]
long buff_used = cmailbox->buff_q->u_size; // the used buffer size in this mailbox
long old_quota = cmailbox->bufCAP; // the quota size in this mailbox
used_memory -= g_memory_pool_queue.size() * IOV_ITEM_SIZE;
// Calculate the maximum buffer size for this mailbox
if (COMM_HIGH_MEM_USED) {
max_buff = DEFULTMSGLEN;
} else if (COMM_LOW_MEM_USED) {
max_buff = (g_instance.comm_cxt.quota_cxt.g_quota > DEFULTMSGLEN) ? g_instance.comm_cxt.quota_cxt.g_quota
: DEFULTMSGLEN;
} else {
max_buff = (g_instance.comm_cxt.quota_cxt.g_quota / 8 > DEFULTMSGLEN)
? g_instance.comm_cxt.quota_cxt.g_quota / 8
: DEFULTMSGLEN;
}
// because: max_buff = buff_used + old_quota + add_quota
// so: add_quota = max_buff - buff_used - old_quota
add_quota = max_buff - buff_used - old_quota;
/*
* buff_used+old_quota is total data size that can be received when no send quota.
* if (buff_used+old_quota < g_quota/2), need send quota.
* if (buff_used+old_quota < DEFULTMSGLEN), need send quota.
*/
if ((buff_used + old_quota < (long)(g_instance.comm_cxt.quota_cxt.g_quota >> 1)) ||
(buff_used + old_quota < DEFULTMSGLEN)) {
return add_quota < 0 ? 0 : add_quota;
} else {
return 0;
}
}
// auxiliary thread use it to change the stream state and send control message to remote point (sender)
//
static bool gs_r_quota_notify(c_mailbox* cmailbox, FCMSG_T* msg)
{
errno_t ss_rc;
uint32 cpylen;
int node_idx = cmailbox->idx;
int streamid = cmailbox->streamid;
unsigned long add_quota = gs_add_quota_size(cmailbox);
if (add_quota > 0) {
// change local stream state and quota first
cmailbox->bufCAP += add_quota;
cmailbox->state = MAIL_RUN;
COMM_DEBUG_LOG("(r|quota notify)\tSend quota to node[%d]:%s on stream[%d].",
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
streamid);
// send resume message to change remote stream state and quota
msg->type = CTRL_ADD_QUOTA;
msg->node_idx = cmailbox->idx;
msg->streamid = cmailbox->streamid;
msg->streamcap = add_quota;
msg->version = cmailbox->remote_version;
msg->query_id = cmailbox->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
msg->nodename[cpylen] = '\0';
return true;
}
return false;
} // gs_r_quota_notify
// traverse all the c_mailbox(es) to find the first query who used memory,
// and make it failure to release the memory. Otherwise, the communication layer maybe hang up.
//
static void gs_r_release_comm_memory()
{
uint64 release_query_id = 0;
int nid = 0;
int sid = 1;
struct c_mailbox* cmailbox = NULL;
unsigned long buff_size = 0;
unsigned long total_buff_size = 0;
struct FCMSG_T fcmsgs = {0x0};
for (nid = 0; nid < g_instance.comm_cxt.counters_cxt.g_cur_node_num; nid++) {
for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
cmailbox = &C_MAILBOX(nid, sid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (cmailbox->buff_q->u_size <= 0) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
continue;
}
// find the first query to release memory, save query id
if (release_query_id == 0) {
release_query_id = cmailbox->query_id;
}
if (cmailbox->query_id == release_query_id) {
buff_size = cmailbox->buff_q->u_size;
total_buff_size += buff_size;
COMM_DEBUG_LOG("(r|release memory)\tReset stream[%d] on node[%d]:%s "
"for query[%lu] to release memory[%lu Byte].",
sid,
nid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, nid),
release_query_id,
buff_size);
gs_r_close_logic_connection(cmailbox, ECOMMSCTPRELEASEMEM, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nid], &fcmsgs, ROLE_CONSUMER);
} else {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
}
}
}
LIBCOMM_ELOG(WARNING,
"(r|release memory)\tReset query[%lu] "
"to release memory[%lu Byte].",
release_query_id,
total_buff_size);
} // gs_r_release_comm_memory
// if we failed to receive message from a sctp listen socket, we should do following things
// step1: reset the streams of the related node
// step2: delete it from epoll cabinet
// step3: update the socket version
// step4: delete the socket from hash table socke -> node index (g_r_htab_data_socket_node_idx)
// step5: close the old sctp socket
//
static void gs_r_close_bad_data_socket(int node_idx, sock_id fd_id, bool is_lock)
{
if (node_idx >= 0) {
gs_r_close_all_streams_by_fd_idx(-1, node_idx, ECOMMSCTPSCTPDISCONNECT);
if (is_lock) {
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock);
}
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket = -1;
if (is_lock) {
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock);
}
}
bool found = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock);
struct sock_ver_entry* entry_ver =
(sock_ver_entry*)hash_search(g_htab_socket_version, &fd_id.fd, HASH_FIND, &found);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
if (!found || entry_ver->entry.val != fd_id.id) {
LIBCOMM_ELOG(WARNING,
"(r|close bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].",
fd_id.fd,
fd_id.id,
(found) ? fd_id.fd : -1,
(found) ? entry_ver->entry.val : -1);
return;
}
bool is_delete = false;
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
/* try to delete old_fd in g_libcomm_receiver_poller_list.
* because we have several recv thread, if the old_fd_id belongs to this thread, it can delete it successfully,
* otherwise, it returns false.
*/
if (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list != NULL) {
is_delete = (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list->del_fd(&fd_id) == 0) ? true : false;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
/* del fd_id in the htab,
* next time, -1 = g_htab_fd_id_node_idx.get_value(fd_id), So we needn't to gs_r_close_all_streams again.
*/
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
if (is_delete) {
// if the old_fd_id belongs to this recv thread, we need to clean it in the poll list, and then close(fd).
gs_clean_events(&fd_id);
if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) {
LIBCOMM_ELOG(
WARNING, "(r|close bad data socket)\tFailed to update bad data socket[%d,%d].", fd_id.fd, fd_id.id);
}
mc_tcp_close(fd_id.fd);
} else {
/* if the old_fd_id belongs to other recv thread, it means, the old_fd_id isn't in this poll_list,
* So we needn't to gs_clean_events(). we just use shutdown to send notification signal.
*/
shutdown(fd_id.fd, SHUT_RDWR);
COMM_DEBUG_LOG("(r|close bad data socket)\tSend shutdown signal for [%d,%d].", fd_id.fd, fd_id.id);
}
}
// if we failed to send message to the destination, we should do following things
//
static void gs_s_close_bad_data_socket(struct sock_id* fd_id, int close_reason, int node_idx)
{
errno_t ss_rc;
uint32 cpylen;
int fd = fd_id->fd;
int id = fd_id->id;
ip_key addr;
bool is_addr = false;
bool found = false;
if ((fd_id->fd < 0) || (fd_id->id < 0)) {
return;
}
// step1: make sure the fd and the version are matched, or it has been closed already
//
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock);
struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found);
if (!found) {
mc_tcp_close(fd_id->fd);
}
if (!found || entry_ver->entry.val != fd_id->id) {
LIBCOMM_ELOG(WARNING,
"(s|cls bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].",
fd,
id,
(found) ? fd : -1,
(found) ? entry_ver->entry.val : -1);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
return;
}
// step2: close the bad socket
//
LIBCOMM_ELOG(LOG, "(s|cls bad data socket)\tClose bad socket with socket entry[%d,%d].", fd, id);
if (node_idx >= 0) {
/* reset the socket for sender, unexpected case if this condition mismatch */
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket == fd_id->fd &&
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id == fd_id->id) {
cpylen =
comm_get_cpylen(g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(addr.ip,
HOST_LEN_OF_HTAB,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host,
cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
addr.port = g_instance.comm_cxt.g_senders->sender_conn[node_idx].port;
is_addr = true;
mc_tcp_close(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket);
g_instance.comm_cxt.g_senders->sender_conn[node_idx].port = -1;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket = -1;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id = -1;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 0;
LIBCOMM_ELOG(WARNING,
"(s|cls bad data socket)\tClose bad data socket with socket entry[%d,%d] "
"to host:%s, node[%d], node name[%s]:%s.",
fd,
id,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
mc_strerror(errno));
}
} else {
mc_tcp_close(fd_id->fd);
}
// step3: update connection state in htab
//
if (is_addr) {
gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx);
}
// step4: if the bad socket is closed, we update the fd version
//
entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock);
/*
* close all p_mailbox[node_idx][*]
* without g_htab_socket_version lock
* with g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock
*/
if (node_idx >= 0) {
gs_s_close_all_streams_by_fd_idx(-1, node_idx, close_reason, false);
}
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
return;
} // gs_s_close_bad_data_socket
/*
* @Description: push the data package to cmailbox buffer.
* @IN cmailbox: point of cmailbox.
* @IN iov: data package.
* @Return: -1: push data failed.
* 0: push data succsessed.
* @See also:
*/
static int gs_push_cmailbox_buffer(c_mailbox* cmailbox, struct mc_lqueue_item* q_item, int version)
{
struct iovec* iov = q_item->element.data;
COMM_TIMER_INIT();
int sid = cmailbox->streamid;
int idx = cmailbox->idx;
uint64 signal_start = 0;
uint64 signal_end = 0;
uint64 time_now = 0;
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
// if the stream is closed or ready to close, the data should be dropped.
if (false == gs_check_mailbox(cmailbox->local_version, version)) {
COMM_DEBUG_LOG("(r|inner recv)\tStream[%d] is closed for node[%d]:%s, drop reveived message[%d].",
sid,
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
(int)iov->iov_len);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = cmailbox->close_reason;
return -1;
}
DEBUG_QUERY_ID = cmailbox->query_id;
// there is buffer/quota to process the received data
if (cmailbox->bufCAP >= (unsigned long)(iov->iov_len)) {
COMM_DEBUG_LOG("(r|inner recv)\tNode[%d]:%s stream[%d] recv %zu msg:%c, "
"bufCAP[%lu] and buff_q->u_size[%lu].",
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
sid,
iov->iov_len,
((char*)iov->iov_base)[0],
cmailbox->bufCAP,
cmailbox->buff_q->u_size);
// put the message to the buffer in the c_mailbox
(void)mc_lqueue_add(cmailbox->buff_q, q_item);
if (g_instance.comm_cxt.quota_cxt.g_having_quota) {
cmailbox->bufCAP -= iov->iov_len;
}
signal_start = COMM_STAT_TIME();
// wake up the Consumer thread of executor, to notify Consumer of arriving new message
gs_poll_signal(cmailbox->semaphore);
COMM_TIMER_LOG("(r|inner recv)\tCache data from node[%d]:%s stream[%d].",
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
sid);
} else { // there is no buffer/quota to process the received data, it should not happen
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tNode[%d] stream[%d], node name[%s] has bufCAP[%lu] and got[%d].",
idx,
sid,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
cmailbox->bufCAP,
(int)iov->iov_len);
LIBCOMM_ASSERT(false, idx, sid, ROLE_CONSUMER);
}
/* update the statistic information of the mailbox */
if (cmailbox->statistic != NULL) {
time_now = COMM_STAT_TIME();
if (cmailbox->statistic->first_recv_time == 0) {
cmailbox->statistic->first_recv_time = time_now;
}
signal_end = time_now;
cmailbox->statistic->total_signal_time += ABS_SUB(signal_end, signal_start);
cmailbox->statistic->last_recv_time = time_now;
cmailbox->statistic->recv_bytes += iov->iov_len;
cmailbox->statistic->recv_loop_time += ABS_SUB(time_now, t_thrd.comm_cxt.g_receiver_loop_poll_up);
cmailbox->statistic->recv_loop_count++;
}
if (cmailbox->bufCAP < DEFULTMSGLEN) {
cmailbox->state = MAIL_HOLD;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return 0;
}
/*
* function name : gs_reload_hba
* description : send signal to postmaster thread to reload hba
* arguments : fd: recevive/flower listen socket
* ctrl_client: ctrl client's sockaddress.
* return value : retry_count: if bigger than RELOAD_HBA_RETRY_COUNT means failed
*/
static int gs_reload_hba(int fd, const sockaddr ctrl_client)
{
errno = ECOMMSCTPNOTINTERNALIP;
int retry_count = 0;
while (retry_count < RELOAD_HBA_RETRY_COUNT) {
if (is_cluster_internal_IP(ctrl_client)) {
break;
}
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tNot cluster internal IP, listen socket[%d]:%s.", fd, mc_strerror(errno));
(void)gs_signal_send(PostmasterPid, SIGHUP);
pg_usleep(WAIT_SLEEP_200MS);
retry_count++;
}
if (retry_count < RELOAD_HBA_RETRY_COUNT) {
COMM_DEBUG_LOG("(r|flow ctrl)\tRelod hba successfully, listen socket[%d]:%s.", fd, mc_strerror(errno));
} else {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tRelod hba fail, listen socket[%d]:%s.", fd, mc_strerror(errno));
}
return retry_count;
}
/*
* function name : gs_accept_data_conntion
* description : accept a logic connection and save some info to global variable
* arguments : iov: provide libcomm_connect_package
* sock: phycial sock of logic connection.
* return value : 0: succeed
* -1: net error
* -2: mem error
*/
static int gs_accept_data_conntion(struct iovec* iov, const sock_id fd_id)
{
int node_idx = -1;
struct sock_id old_fd_id;
struct libcomm_connect_package* connect_pkg = (struct libcomm_connect_package*)iov->iov_base;
if (iov->iov_len < sizeof(struct libcomm_connect_package)) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tIov len[%zu] is less than libcomm_connect_package[%zu].",
iov->iov_len, sizeof(struct libcomm_connect_package));
Assert(iov->iov_len == sizeof(struct libcomm_connect_package));
return RECV_NET_ERROR;
}
/* Network data is not trusted */
if (connect_pkg->magic_num != MSG_HEAD_MAGIC_NUM2) {
return RECV_NET_ERROR;
}
connect_pkg->node_name[NAMEDATALEN - 1] = '\0';
connect_pkg->host[HOST_ADDRSTRLEN - 1] = '\0';
node_idx = gs_get_node_idx(connect_pkg->node_name);
if (unlikely(node_idx < 0)) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tFailed to get node index for %s: %s.",
connect_pkg->node_name,
mc_strerror(errno));
return RECV_NET_ERROR;
}
if (gs_map_sock_id_to_node_idx(fd_id, node_idx) < 0) {
LIBCOMM_ELOG(WARNING, "(r|inner recv)\tFailed to save sock and sockid.");
return RECV_NET_ERROR;
}
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock);
// step6: if the old socket is ok, maybe the primary is changed, we should close the old connection
if (g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket >= 0) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tOld connection exist, maybe the primary is changed, old address of "
"node[%d] is:%s, new is:%s, the connection will be reset.",
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host,
connect_pkg->host);
old_fd_id.fd = g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket;
old_fd_id.id = g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket_id;
gs_r_close_bad_data_socket(node_idx, old_fd_id, false);
}
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket = fd_id.fd;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket_id = fd_id.id;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].msg_head.type = MSG_NULL;
g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].head_read_cursor = 0;
if (g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].iov_item) {
struct iovec* iov_data = g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].iov_item->element.data;
iov_data->iov_len = 0;
}
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock);
// step8: set the socket information
int ss_rc = strcpy_s(g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host, HOST_ADDRSTRLEN, connect_pkg->host);
securec_check(ss_rc, "\0", "\0");
// step 7: send back ack to tell the sender continue
struct libcomm_accept_package ack_msg;
ack_msg.type = SCTP_PKG_TYPE_ACCEPT;
ack_msg.result = 1;
if (g_libcomm_adapt.send_ack(fd_id.fd, (char*)&ack_msg, sizeof(ack_msg)) < 0) {
gs_r_close_bad_data_socket(node_idx, fd_id, true);
return RECV_NET_ERROR;
}
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_GSS_SCTP_FAILED)) {
errno = ECOMMSCTPGSSAUTHFAIL;
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\t[FAULT INJECTION]Data channel GSS authentication failed, listen socket[%d]:%s.",
fd_id.fd,
mc_strerror(errno));
return RECV_NET_ERROR;
}
#endif
/* server side gss kerberos authentication for data connection.
* authentication for sctp mode after connection package replay.
* if GSS authentication SUCC, no IP authentication is required.
*/
if (g_instance.comm_cxt.g_comm_tcp_mode == false) {
struct sockaddr ctrl_client;
socklen_t len = sizeof(struct sockaddr);
int ret = getpeername(fd_id.fd, (struct sockaddr*)&ctrl_client, &len);
if (ret < 0) {
LIBCOMM_ELOG(WARNING, "getpeername failed, sockfd[%d], node[%d].\n", fd_id.fd, node_idx);
return RECV_NET_ERROR;
}
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL) {
if (GssServerAuth(fd_id.fd, g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile) < 0) {
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tData channel GSS authentication failed, listen socket[%d]:%s.",
fd_id.fd,
mc_strerror(errno));
errno = ECOMMSCTPGSSAUTHFAIL;
return RECV_NET_ERROR;
} else {
LIBCOMM_ELOG(LOG, "(r|recv loop)\tData channel GSS authentication SUCC, listen socket[%d].", fd_id.fd);
}
} else {
/* send signal to postmaster thread to reload hba */
int retry_count = gs_reload_hba(fd_id.fd, ctrl_client);
if (retry_count >= RELOAD_HBA_RETRY_COUNT) {
return RECV_NET_ERROR;
}
}
}
LIBCOMM_ELOG(LOG,
"(r|recv loop)\tAccept data connection for "
"node[%d]:%s with socket[%d,%d].",
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
fd_id.fd,
fd_id.id);
return 0;
}
int gs_handle_data_delay_message(int idx, struct mc_lqueue_item* q_item, uint16 msg_type)
{
struct c_mailbox* cmailbox = NULL;
struct libcomm_delay_package* delay_msg = NULL;
struct iovec* iov = q_item->element.data;
if (idx < 0) {
return -1;
}
delay_msg = (struct libcomm_delay_package*)iov->iov_base;
if (msg_type == SCTP_PKG_TYPE_DELAY_REQUEST) {
delay_msg->recv_time = (uint32)mc_timers_us();
} else if (msg_type == SCTP_PKG_TYPE_DELAY_REPLY) {
delay_msg->finish_time = (uint32)mc_timers_us();
}
// put the message to the buffer in the c_mailbox
cmailbox = &C_MAILBOX(idx, 0);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
(void)mc_lqueue_add(cmailbox->buff_q, q_item);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return 0;
}
// the broker: receive message from give socket and put it into
// the corressponding mailbox in g_c_mailbox, then wake up Consumer of executor to fetch the data
// sock : the data socket which has epoll events
// node_idx : the node index in the global variables
// flag : flag for recv interface
// step1: initialize local variables and the message buffer
// step2: receive the data message
// setp3: get the stream id
// setp4: check the quota of the mailboxreport error or put the message into the mailbox
// step5: notify the Consumer to take away the data
// step6: check the quota and state of the c_mailbox and notify auxiliary thread to set the state
//
static int gs_internal_recv(const sock_id fd_id, int node_idx)
{
int recvsk = fd_id.fd;
int idx = node_idx;
int error = 0;
struct c_mailbox* cmailbox = NULL;
// Initialize inmessage with enough space for DATA, and control message.
COMM_TIMER_INIT();
for (;;) {
LibcommRecvInfo recv_info;
COMM_TIMER_LOG("(r|inner recv)\tInternal receive start.");
int64 curr_rcv_time;
int64 last_rcv_time = 0;
if (idx >= 0) {
last_rcv_time = g_instance.comm_cxt.g_receivers->receiver_conn[idx].last_rcv_time;
}
recv_info.socket = recvsk;
recv_info.node_idx = idx;
if (idx >= 0) {
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[idx].rwlock);
}
error = g_libcomm_adapt.recv_data(&recv_info);
if (idx >= 0) {
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[idx].rwlock);
}
COMM_TIMER_LOG("(r|inner recv)\tInternal receive something.");
// not real network error, it can be resolved by trying again
if (unlikely((error == RECV_NEED_RETRY))) {
COMM_DEBUG_LOG("(r|inner recv)\tReceiver from node[%d] socket[%d]:%s.", idx, recvsk, strerror(errno));
return RECV_NEED_RETRY;
}
// real network errors, we should report it
// errno is not EAGAIN/EWOULDBLOCK/EINTR
if (unlikely(error < 0)) {
LIBCOMM_ELOG(WARNING,
"(r|inner recv)\tFailed to receive sock[%d], "
"return %d, from node[%d]:%s, %s.",
recvsk,
error,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
strerror(errno));
return error;
}
int streamid = recv_info.streamid;
int version = recv_info.version;
struct mc_lqueue_item* iov_item = recv_info.iov_item;
struct iovec* iov = iov_item->element.data;
/*
* We recv a small message and the last message is very early,
* we want to send a ack message in order to avoid tcp ack package missing.
* The message should be less than MTU, so we use 1024 byte.
*/
if (is_tcp_mode() && streamid > 0 && g_ackchk_time > 0 && iov->iov_len < 1024 && idx >= 0) {
/* send ack message. */
curr_rcv_time = g_instance.comm_cxt.g_receivers->receiver_conn[idx].last_rcv_time;
if (last_rcv_time > 0 && curr_rcv_time - last_rcv_time > g_ackchk_time) {
send(recvsk, "ACK", sizeof("ACK"), 0);
}
}
/* Speicail message for connection request */
if (streamid == 0) {
uint16 msg_type = *(uint16*)iov->iov_base;
error = RECV_NEED_RETRY;
switch (msg_type) {
case SCTP_PKG_TYPE_CONNECT:
error = gs_accept_data_conntion(iov, fd_id);
break;
case SCTP_PKG_TYPE_DELAY_REQUEST:
case SCTP_PKG_TYPE_DELAY_REPLY:
error = gs_handle_data_delay_message(idx, iov_item, msg_type);
if (error == 0) {
/* iov save to cmailbox[idx][0], no need free */
return RECV_NEED_RETRY;
}
break;
default:
struct libcomm_delay_package* delay_msg = (struct libcomm_delay_package*)iov->iov_base;
COMM_DEBUG_LOG("[DELAY_INFO]recv invalid type[%d] sn=%d\n", msg_type, delay_msg->sn);
error = RECV_NET_ERROR;
break;
}
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return error;
} else if (streamid > 0 && idx >= 0) { // if the sid is not 0, we should receive data
cmailbox = &C_MAILBOX(idx, streamid);
if (gs_push_cmailbox_buffer(cmailbox, iov_item, version) < 0) {
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
}
COMM_TIMER_LOG("(r|inner recv)\tInternal receive finish for [%d,%d].", idx, streamid);
return RECV_NEED_RETRY;
} else {
COMM_DEBUG_LOG("(r|inner recv)\tWrong stream id %d:%d from Node %d, sock[%d], error[%d]:%s.",
streamid,
version,
idx,
recvsk,
error,
strerror(errno));
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return RECV_NEED_RETRY;
}
}
} // gs_internal_recv
// take data from the cmailbox, called by Consumer thread in executor
// step1: get mailbox by node index and stream index
// step2: check if there is data in the buffer of mailbox
// step3: take away the data in the buffer queue
// step4: notify auxiliary thread to check the state of the mailbox, then copy the data into buff, then release the
// internal buffer step5: return the length of received data
//
int gs_recv(
gsocket* gs_sock, // array of logic conn index, which labeled the logic_conn will recv data from this logic_conn
void* buff, // buffer to copy data message, allocated by caller
int buff_size) // size of buffer
{
if ((gs_sock == NULL) || (buff == NULL) || (buff_size <= 0)) {
LIBCOMM_ELOG(WARNING,
"(r|recv)\tInvalid argument: "
"%s%sbuff size:%d.",
gs_sock == NULL ? "gs_sock is NULL, " : "",
buff == NULL ? "buff is NULL, " : "",
buff_size);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
uint64 time_enter = COMM_STAT_TIME();
uint64 time_now = time_enter;
int idx = gs_sock->idx;
int streamid = gs_sock->sid;
int version = gs_sock->ver;
struct FCMSG_T fcmsgs = {0x0};
if ((idx < 0) || (idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (streamid <= 0) ||
(streamid >= g_instance.comm_cxt.counters_cxt.g_max_stream_num)) {
LIBCOMM_ELOG(WARNING, "(r|recv)\tInvalid argument: node idx:%d, stream id:%d.", idx, streamid);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
int ret = -1;
struct c_mailbox* cmailbox = NULL;
struct iovec* iov = NULL;
struct mc_lqueue_item* q_item = NULL;
bool is_notify_quota = false;
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
errno_t ss_rc = 0;
errno = 0;
COMM_TIMER_INIT();
// step1: get idx and streamid
COMM_TIMER_LOG("(r|recv)\tReceive start for node[%d,%d]:%s.",
idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx));
// step2: check mailbox
cmailbox = &(C_MAILBOX(idx, streamid));
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
// check the state of the mailbox is correct
if (false == gs_check_mailbox(cmailbox->local_version, version)) {
MAILBOX_ELOG(
cmailbox, WARNING, "(r|recv)\tStream has already closed, detail:%s.", mc_strerror(cmailbox->close_reason));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = cmailbox->close_reason;
ret = -1;
goto return_result;
}
if (cmailbox->buff_q->is_empty == 1) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = ECOMMSCTPNODATA;
ret = -1;
goto return_result;
}
// step3: get data block from mailbox
q_item = mc_lqueue_remove(cmailbox->buff_q, q_item);
if (q_item != NULL) {
iov = q_item->element.data;
} else {
iov = NULL;
}
COMM_TIMER_LOG("(r|recv)\tReceived data for node[%d,%d]:%s.",
idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx));
// there is no data
if (iov == NULL || iov->iov_len == 0) {
// release the internal buffer
libcomm_free_iov_item(&q_item, IOV_DATA_SIZE);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = ECOMMSCTPNODATA;
ret = -1;
goto return_result;
}
// step4: update quota
if (g_instance.comm_cxt.quota_cxt.g_having_quota) {
// notify auxiliary thread to check the state of the mailbox
is_notify_quota = gs_r_quota_notify(cmailbox, &fcmsgs);
COMM_TIMER_LOG("(r|recv)\tSend quota to node[%d,%d]:%s.",
idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx));
}
COMM_DEBUG_LOG("(r|recv)\tNode[%d]:%s stream[%d] recv %zu msg:%c. "
"bufCAP[%lu] and buff_q->u_size[%lu].",
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
streamid,
iov->iov_len,
((char*)iov->iov_base)[0],
cmailbox->bufCAP,
cmailbox->buff_q->u_size);
time_now = COMM_STAT_TIME();
COMM_STAT_CALL(cmailbox, cmailbox->statistic->gs_recv_time += ABS_SUB(time_now, time_enter));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
// send control message without cmailbox lock
if (is_notify_quota && (gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER) <= 0)) {
// failed to send control message, clear the information of the node
MAILBOX_ELOG(cmailbox, WARNING, "(r|quota notify)\tFailed to send quota:%s.", mc_strerror(errno));
}
// step5: copy data then return
// get the need data length
// warning: if the buffer length is smaller than real data length,
// the redundant data will be dropped
//
LIBCOMM_ASSERT((bool)(buff_size >= (int)iov->iov_len), idx, streamid, ROLE_CONSUMER);
ret = (int)iov->iov_len;
// copy the datat to the buffer of executor
ss_rc = memcpy_s(buff, buff_size, iov->iov_base, iov->iov_len);
securec_check(ss_rc, "\0", "\0");
// release the internal buffer
libcomm_free_iov_item(&q_item, IOV_DATA_SIZE);
COMM_TIMER_LOG("(r|recv)\tReceive finish for node[%d,%d]:%s.",
idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx));
return_result:
LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK);
return ret;
} // gs_recv
/*
* function name : gs_s_close_logic_connection
* description : producer close logic connetion and reset mailbox,
* if producer call this, we only set state is CTRL_TO_CLOSE,
* then really close when consumer send MAIL_CLOSED message.
* notice : we must get mailbox lock before
* arguments : _in_ cmailbox: libcomm logic conntion info.
* _in_ close_reason: close reason.
*/
static void gs_s_close_logic_connection(struct p_mailbox* pmailbox, int close_reason, FCMSG_T* msg)
{
errno_t ss_rc;
uint32 cpylen;
if (pmailbox->state == MAIL_CLOSED) {
return;
}
// when sender closes pmailbox, if close reason != remote close, and the state of pmailbox is MAIL_READY,
// we set pmailbox to MAIL_TO_CLOSE, then wait for top consumer(gs_connect()) to close it.
if ((pmailbox->state == MAIL_READY) && (close_reason != ECOMMSCTPREMOETECLOSE)) {
pmailbox->state = MAIL_TO_CLOSE;
// wake up the producer who is waiting
gs_poll_signal(pmailbox->semaphore);
pmailbox->semaphore = NULL;
return;
}
// 1, if tcp disconnect, we can not send control message on tcp channel,
// remote can receive disconnect event when flow control thread call epoll_wait.
// 2, close reason is ECOMMSCTPREMOETECLOSE means remote send MAIL_CLOSED,
// we could not reply MAIL_CLOSED message.
if (IS_NOTIFY_REMOTE(close_reason) && msg) {
msg->type = CTRL_CLOSED;
msg->node_idx = pmailbox->idx;
msg->streamid = pmailbox->streamid;
msg->streamcap = 0;
msg->version = pmailbox->remote_version;
msg->query_id = pmailbox->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
msg->nodename[cpylen] = '\0';
}
// wake up the producer who is waiting
gs_poll_signal(pmailbox->semaphore);
// At last, reset mailbox and clean hash table
gs_s_reset_pmailbox(pmailbox, close_reason);
return;
}
/*
* function name : gs_r_close_logic_connection
* description : consumer close logic connetion and reset mailbox,
* if producer call this, we only set state is CTRL_TO_CLOSE,
* then really close when consumer call this.
* notice : we must get mailbox lock before
* arguments : _in_ cmailbox: libcomm conntion info.
* _in_ close_reason: close reason.
*/
static void gs_r_close_logic_connection(struct c_mailbox* cmailbox, int close_reason, FCMSG_T* msg)
{
errno_t ss_rc;
uint32 cpylen;
if (cmailbox->state == MAIL_CLOSED) {
return;
}
// 1, if tcp disconnect, we can not send control message on tcp channel,
// remote can receive disconnect event when flow control thread call epoll_wait.
// 2, close reason is ECOMMSCTPREMOETECLOSE means remote send MAIL_CLOSED,
// we could not reply MAIL_CLOSED message.
if (IS_NOTIFY_REMOTE(close_reason) && msg) {
msg->type = CTRL_CLOSED;
msg->node_idx = cmailbox->idx;
msg->streamid = cmailbox->streamid;
msg->streamcap = 0;
msg->version = cmailbox->remote_version;
msg->query_id = cmailbox->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
msg->nodename[cpylen] = '\0';
}
// wake up the consumer who is waiting for the data at gs_wait_poll,
gs_poll_signal(cmailbox->semaphore);
// At last, reset mailbox and clean hash table
gs_r_reset_cmailbox(cmailbox, close_reason);
return;
}
// send assert fail msg via ctrl connection if debug mode enable and assert failed
//
static void gs_libcomm_handle_assert(bool condition, int nidx, int sidx, int node_role)
{
errno_t ss_rc;
uint32 cpylen;
if (mc_unlikely(!condition)) {
struct FCMSG_T fcmsgs = {0x0};
// notify peer assertion failed
fcmsgs.type = CTRL_ASSERT_FAIL;
fcmsgs.node_idx = nidx;
fcmsgs.streamid = sidx;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
if (node_role == ROLE_PRODUCER) {
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[nidx], &fcmsgs, node_role);
struct p_mailbox* pmailbox = NULL;
pmailbox = &(P_MAILBOX(nidx, sidx));
LIBCOMM_ELOG(WARNING,
"(s|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu].",
nidx,
sidx,
g_instance.comm_cxt.g_s_node_sock[nidx].remote_nodename,
pmailbox->state,
pmailbox->bufCAP);
MAILBOX_ELOG(pmailbox, WARNING, "(s|handle assert)\tMailbox Info which assert fail.");
} else {
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nidx], &fcmsgs, node_role);
struct c_mailbox* cmailbox = NULL;
cmailbox = &(C_MAILBOX(nidx, sidx));
LIBCOMM_ELOG(WARNING,
"(r|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu] and "
"buff_q->u_size[%lu].",
nidx,
sidx,
g_instance.comm_cxt.g_r_node_sock[nidx].remote_nodename,
cmailbox->state,
cmailbox->bufCAP,
cmailbox->buff_q->u_size);
MAILBOX_ELOG(cmailbox, WARNING, "(r|handle assert)\tMailbox Info which assert fail.");
}
Assert(condition);
}
}
// process requests, there are just cancel requtest and close poll request now
//
static void gs_check_requested()
{
// when executor send a cancel query request,
// we should notify all working threads(producer & consumer) to exit from wating in gs_wait_poll()
//
if (g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested) { // cancel query request
LIBCOMM_ELOG(WARNING,
"(r|chk requested)\tCancel is received for thread:%ld.",
(long)g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested);
// signal all the Consumers or they may be waiting for data,
// the threads will check if it should quit
gs_broadcast_poll();
g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested = 0;
DEBUG_QUERY_ID = 0;
}
} // gs_check_requested
/*
* function name : gs_get_libcomm_reply_socket
* description : get reply socket from g_s_node_sock.
* arguments : _in_ recv_idx: the node index.
* return value :
* -1: failed.
* >=0: reply socket.
*/
int gs_get_libcomm_reply_socket(int recv_idx)
{
int socket = g_instance.comm_cxt.g_r_node_sock[recv_idx].sctp_reply_sock;
if (socket >= 0) {
return socket;
}
for (int send_idx = 0; send_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; send_idx++) {
if (strcmp(g_instance.comm_cxt.g_r_node_sock[recv_idx].remote_nodename,
g_instance.comm_cxt.g_s_node_sock[send_idx].remote_nodename) == 0) {
socket = g_instance.comm_cxt.g_senders->sender_conn[send_idx].socket;
// save data reply socket in g_r_node_sock
g_instance.comm_cxt.g_r_node_sock[recv_idx].lock();
g_instance.comm_cxt.g_r_node_sock[recv_idx].sctp_reply_sock = socket;
g_instance.comm_cxt.g_r_node_sock[recv_idx].unlock();
break;
}
}
return socket;
}
/*
* function name : gs_delay_analysis
* description : analysis libcomm delay message from mailbox[node_idx][0].
*/
void gs_delay_analysis()
{
struct c_mailbox* cmailbox = NULL;
int node_idx = 0;
int socket = -1;
struct iovec* iov = NULL;
struct mc_lqueue_item* q_item = NULL;
struct libcomm_delay_package* msg = NULL;
while (node_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
cmailbox = &C_MAILBOX(node_idx, 0);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
// receive next consumer mailbox
if (cmailbox->buff_q->is_empty == 1) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
node_idx++;
continue;
}
// get libcomm delay message from cmailbox
q_item = mc_lqueue_remove(cmailbox->buff_q, q_item);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
// receive next message
if (q_item == NULL) {
continue;
}
iov = q_item->element.data;
// receive next message
if (iov == NULL || iov->iov_len == 0) {
libcomm_free_iov_item(&q_item, IOV_DATA_SIZE);
continue;
}
Assert(iov->iov_len == sizeof(struct libcomm_delay_package));
msg = (struct libcomm_delay_package*)iov->iov_base;
uint32 delay = 0;
int delay_array_idx = -1;
switch (msg->type) {
// set reply_time and reply sctp delay message
case SCTP_PKG_TYPE_DELAY_REQUEST:
// we get reply socket from g_s_node_sock
socket = gs_get_libcomm_reply_socket(node_idx);
if (socket < 0) {
break;
}
msg->type = SCTP_PKG_TYPE_DELAY_REPLY;
msg->reply_time = (uint32)mc_timers_us();
LibcommSendInfo send_info;
send_info.socket = socket;
send_info.node_idx = node_idx;
send_info.streamid = 0;
send_info.version = 0;
send_info.msg = (char*)msg;
send_info.msg_len = sizeof(struct libcomm_delay_package);
(void)g_libcomm_adapt.block_send(&send_info);
break;
case SCTP_PKG_TYPE_DELAY_REPLY:
delay = (msg->finish_time - msg->start_time) - (msg->reply_time - msg->recv_time);
delay_array_idx = g_instance.comm_cxt.g_delay_info[node_idx].current_array_idx;
g_instance.comm_cxt.g_delay_info[node_idx].delay[delay_array_idx] = delay;
COMM_DEBUG_LOG("[DELAY_INFO]remote_name=%s, delay[%d]=%uus",
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
delay_array_idx,
delay);
delay_array_idx++;
if (delay_array_idx >= MAX_DELAY_ARRAY_INDEX) {
delay_array_idx = 0;
}
g_instance.comm_cxt.g_delay_info[node_idx].current_array_idx = delay_array_idx;
break;
default:
break;
}
libcomm_free_iov_item(&q_item, IOV_DATA_SIZE);
}
}
// libcomm delay message number
static int g_libcomm_delay_no = 0;
/*
* function name : gs_delay_survey
* description : send libcomm delay message to all connection.
*/
void gs_delay_survey()
{
int node_idx = -1;
int socket = -1;
errno_t ss_rc = 0;
struct libcomm_delay_package msg;
ss_rc = memset_s(&msg, sizeof(msg), 0, sizeof(struct libcomm_delay_package));
securec_check(ss_rc, "\0", "\0");
msg.type = SCTP_PKG_TYPE_DELAY_REQUEST;
for (node_idx = 0; node_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; node_idx++) {
// if the connection is not ready, continue
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id == 0) {
continue;
}
socket = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
msg.sn = g_libcomm_delay_no;
msg.start_time = (uint32)mc_timers_us();
LibcommSendInfo send_info;
send_info.socket = socket;
send_info.node_idx = node_idx;
send_info.streamid = 0;
send_info.version = 0;
send_info.msg = (char*)&msg;
send_info.msg_len = sizeof(struct libcomm_delay_package);
(void)g_libcomm_adapt.block_send(&send_info);
}
g_libcomm_delay_no++;
}
#ifdef ENABLE_MULTIPLE_NODES
void* libcommProducerThread(void* arg)
{
int producer_sn = *(int*)arg;
const unsigned int plan_id = LIBCOMM_PERFORMANCE_PLAN_ID;
const unsigned int plan_node_id = LIBCOMM_PERFORMANCE_PN_ID + producer_sn;
sctpaddrinfo** consumerAddr = NULL;
int consumerNum = 0;
NodeDefinition* nodesDef = NULL;
int i;
errno_t rc = EOK;
int error = 0;
char* msg_buf = NULL;
int msg_len = g_instance.comm_cxt.tests_cxt.libcomm_test_msg_len;
int once_send = g_instance.comm_cxt.tests_cxt.libcomm_test_send_once;
int sleep_time = g_instance.comm_cxt.tests_cxt.libcomm_test_send_sleep;
int current_send = 0;
(void)mc_thread_block_signal();
gs_memprot_thread_init();
// initialize globals
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
DEBUG_QUERY_ID = plan_node_id;
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_AUX;
consumerNum = global_node_definition->num_nodes;
consumerAddr = (sctpaddrinfo**)calloc(consumerNum, sizeof(sctpaddrinfo*));
if (consumerAddr == NULL) {
goto clean_return;
}
for (i = 0; i < consumerNum; i++) {
nodesDef = &(global_node_definition->nodesDefinition[i]);
consumerAddr[i] = (sctpaddrinfo*)calloc(1, sizeof(sctpaddrinfo));
if (consumerAddr[i] == NULL) {
goto clean_return;
}
consumerAddr[i]->host = nodesDef->nodehost.data;
consumerAddr[i]->ctrl_port = nodesDef->nodectlport;
consumerAddr[i]->sctp_port = nodesDef->nodesctpport;
consumerAddr[i]->nodeIdx = nodesDef->nodeid;
rc = strncpy_s(
consumerAddr[i]->nodename, NAMEDATALEN, nodesDef->nodename.data, strlen(nodesDef->nodename.data) + 1);
securec_check(rc, "\0", "\0");
consumerAddr[i]->sctpKey.queryId = plan_id;
consumerAddr[i]->sctpKey.planNodeId = plan_node_id;
}
error = gs_connect(consumerAddr, consumerNum, -1);
if (error != 0) {
goto clean_return;
}
if (msg_len < 0 || msg_len > PG_INT32_MAX) {
goto clean_return;
}
msg_buf = (char*)malloc(msg_len);
if (msg_buf == NULL) {
goto clean_return;
}
for (;;) {
for (i = 0; i < consumerNum; i++) {
error = gs_send(&(consumerAddr[i]->gs_sock), msg_buf, msg_len, -1, true);
if (error < 0 || g_instance.comm_cxt.tests_cxt.libcomm_stop_flag == true) {
goto clean_return;
}
}
current_send += error;
if (current_send >= once_send && sleep_time > 0) {
usleep(sleep_time * 1000);
current_send = 0;
}
}
clean_return:
if (consumerAddr != NULL) {
for (i = 0; i < consumerNum; i++) {
if (consumerAddr[i] != NULL) {
gs_close_gsocket(&(consumerAddr[i]->gs_sock));
free(consumerAddr[i]);
consumerAddr[i] = NULL;
}
}
free(consumerAddr);
consumerAddr = NULL;
}
if (msg_buf != NULL) {
free(msg_buf);
msg_buf = NULL;
}
atomic_sub(&g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread, 1);
return NULL;
} // producerThread;
void* libcommConsumerThread(void* arg)
{
int consumer_sn = *(int*)arg;
const unsigned int plan_id = LIBCOMM_PERFORMANCE_PLAN_ID;
const unsigned int plan_node_id = LIBCOMM_PERFORMANCE_PN_ID + consumer_sn;
int idx, sid;
int ready_conn = 0;
char* msg_buf = NULL;
int* datamarks = NULL;
int msg_len = g_instance.comm_cxt.tests_cxt.libcomm_test_msg_len;
int once_recv = g_instance.comm_cxt.tests_cxt.libcomm_test_recv_once;
int sleep_time = g_instance.comm_cxt.tests_cxt.libcomm_test_recv_sleep;
int current_recv = 0;
gsocket* gsockAddr = NULL;
int producerNum = 0;
c_mailbox* cmailbox = NULL;
int error = 0;
int i;
(void)mc_thread_block_signal();
gs_memprot_thread_init();
// initialize globals
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
DEBUG_QUERY_ID = plan_node_id;
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_AUX;
producerNum = global_node_definition->num_nodes;
gsockAddr = (gsocket*)calloc(producerNum, sizeof(gsocket));
if (gsockAddr == NULL) {
goto clean_return;
}
for (;;) {
for (idx = 0; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { // node index
for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { // stream index
cmailbox = &C_MAILBOX(idx, sid);
if (cmailbox->state != MAIL_CLOSED && cmailbox->stream_key.queryId == plan_id &&
cmailbox->stream_key.planNodeId == plan_node_id) {
gsockAddr[ready_conn].idx = idx;
gsockAddr[ready_conn].sid = sid;
gsockAddr[ready_conn].ver = cmailbox->local_version;
gsockAddr[ready_conn].type = GSOCK_CONSUMER;
ready_conn++;
}
}
}
if (g_instance.comm_cxt.tests_cxt.libcomm_stop_flag == true) {
goto clean_return;
}
if (ready_conn == producerNum) {
break;
} else {
ready_conn = 0;
sleep(1);
}
}
msg_buf = (char*)malloc(msg_len);
datamarks = (int*)calloc(producerNum, sizeof(int));
if (msg_buf == NULL || datamarks == NULL) {
goto clean_return;
}
for (;;) {
error = gs_wait_poll(gsockAddr, producerNum, datamarks, -1, false);
if (error < 0 || g_instance.comm_cxt.tests_cxt.libcomm_stop_flag == true) {
goto clean_return;
}
for (i = 0; i < producerNum; i++) {
if (datamarks[i] > 0) {
error = gs_recv(&gsockAddr[i], msg_buf, msg_len);
if (error < 0 && errno == ECOMMSCTPNODATA) {
continue;
}
if (error < 0 || g_instance.comm_cxt.tests_cxt.libcomm_stop_flag == true) {
goto clean_return;
}
}
}
current_recv += msg_len;
if (current_recv >= once_recv && sleep_time > 0) {
usleep(sleep_time * 1000);
current_recv = 0;
}
}
clean_return:
if (gsockAddr != NULL) {
for (i = 0; i < producerNum; i++) {
gs_close_gsocket(&(gsockAddr[i]));
}
free(gsockAddr);
gsockAddr = NULL;
}
if (msg_buf != NULL) {
free(msg_buf);
msg_buf = NULL;
}
if (datamarks != NULL) {
free(datamarks);
datamarks = NULL;
}
atomic_sub(&g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread, 1);
return NULL;
} // consumerThread
void libcomm_performance_test()
{
/* connections is not build complete. */
if (global_node_definition == NULL || global_node_definition->num_nodes == 0) {
return;
}
/* all work thread is running. */
if (g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread ==
g_instance.comm_cxt.tests_cxt.libcomm_test_thread_num * 2) {
return;
}
/* set stop flag, wait all work thread exit. */
g_instance.comm_cxt.tests_cxt.libcomm_stop_flag = true;
while (g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread) {
gs_broadcast_poll();
sleep(1);
}
g_instance.comm_cxt.tests_cxt.libcomm_stop_flag = false;
if (g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg != NULL) {
free(g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg);
g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg = NULL;
}
g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg =
(int*)calloc(g_instance.comm_cxt.tests_cxt.libcomm_test_thread_num, sizeof(int));
if (g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg == NULL) {
return;
}
pthread_t t_id;
for (int i = 0; i < g_instance.comm_cxt.tests_cxt.libcomm_test_thread_num; i++) {
g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg[i] = i;
if (0 == pthread_create(
&t_id, NULL, &libcommProducerThread, &g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg[i])) {
atomic_add(&g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread, 1);
}
if (0 == pthread_create(
&t_id, NULL, &libcommConsumerThread, &g_instance.comm_cxt.tests_cxt.libcomm_test_thread_arg[i])) {
atomic_add(&g_instance.comm_cxt.tests_cxt.libcomm_test_current_thread, 1);
}
}
return;
}
#endif
/*
* function name : gs_accept_ctrl_conntion
* description : check connection, if connection is new, close old one.
* arguments : _in_ t_fd_id: the fd and fd id for this connection.
* _in_ fcmsgr: the message we received.
*/
static void gs_accept_ctrl_conntion(struct sock_id* t_fd_id, struct FCMSG_T* fcmsgr)
{
int current_mode;
int rc = -1;
char ack;
errno_t ss_rc;
uint32 cpylen;
struct sock_id old_fd_id = {-1, -1};
uint16 idx = fcmsgr->node_idx;
Assert(idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num);
/* Network data is not trusted */
fcmsgr->nodename[NAMEDATALEN - 1] = '\0';
g_instance.comm_cxt.g_r_node_sock[idx].lock();
old_fd_id.fd = g_instance.comm_cxt.g_r_node_sock[idx].ctrl_tcp_sock;
old_fd_id.id = g_instance.comm_cxt.g_r_node_sock[idx].ctrl_tcp_sock_id;
// if the two sockets are the same, we need not close the socket, it is ok
if (old_fd_id.fd == t_fd_id->fd && old_fd_id.id == t_fd_id->id) {
g_instance.comm_cxt.g_r_node_sock[idx].unlock();
} else {
g_instance.comm_cxt.g_r_node_sock[idx].unlock();
if (old_fd_id.fd >= 0) {
/* close the old tcp socket, or it will be error and leak */
gs_r_close_bad_ctrl_tcp_sock(&old_fd_id, ECOMMSCTPPEERCHANGED);
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tOld connection exist, maybe the primary is changed, old address of "
"node[%d] is:%s, the connection will be reset.",
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_host);
}
/* regist new sock and sock id to node idx */
if (gs_map_sock_id_to_node_idx(*t_fd_id, idx) < 0) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to save sock and sockid.");
gs_r_close_bad_ctrl_tcp_sock(t_fd_id, ECOMMSCTPTCPDISCONNECT);
return;
}
}
g_instance.comm_cxt.g_r_node_sock[idx].lock();
g_instance.comm_cxt.g_r_node_sock[idx].set_nl(t_fd_id->fd, CTRL_TCP_SOCK);
g_instance.comm_cxt.g_r_node_sock[idx].set_nl(t_fd_id->id, CTRL_TCP_SOCK_ID);
cpylen = comm_get_cpylen(fcmsgr->nodename, NAMEDATALEN);
ss_rc = memset_s(g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc =
strncpy_s(g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, NAMEDATALEN, fcmsgr->nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename[cpylen] = '\0';
g_instance.comm_cxt.g_r_node_sock[idx].unlock();
/* send response to remote, thus ready control msg arrived after connection has established */
if (IS_PGXC_COORDINATOR) {
ack = 'o';
rc = mc_tcp_write_block(t_fd_id->fd, &ack, sizeof(ack));
// if tcp send failed, close tcp connction
if (rc <= 0) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to send ack, error:%s.", mc_strerror(errno));
gs_r_close_bad_ctrl_tcp_sock(t_fd_id, ECOMMSCTPTCPDISCONNECT);
return;
}
} else if (fcmsgr->type == CTRL_CONN_REGIST_CN) {
if (g_instance.comm_cxt.g_ha_shm_data) {
current_mode = g_instance.comm_cxt.g_ha_shm_data->current_mode;
} else {
current_mode = UNKNOWN_MODE;
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tCannot get current mode, postmaster exit.");
}
if (current_mode != STANDBY_MODE && current_mode != PENDING_MODE && current_mode != UNKNOWN_MODE) {
ack = 'o';
} else {
ack = 'r';
}
rc = mc_tcp_write_block(t_fd_id->fd, &ack, sizeof(ack));
// if tcp send failed, close tcp connction
if (rc <= 0 || (ack == 'r')) {
if (current_mode == STANDBY_MODE) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tCannot accept connection in standby mode.");
} else if (current_mode == PENDING_MODE) {
// sleep 1 second to wait process starting
(void)sleep(1);
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tCannot accept connection in pending mode.");
} else {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tCannot accept connection in unknown mode.");
}
if (rc <= 0) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to send ack, error:%s.", mc_strerror(errno));
}
gs_r_close_bad_ctrl_tcp_sock(t_fd_id, ECOMMSCTPTCPDISCONNECT);
return;
}
}
LIBCOMM_ELOG(LOG,
"(r|flow ctrl)\tAccept control connection for "
"node[%d]:%s with socket[%d,%d].",
idx,
g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename,
t_fd_id->fd,
t_fd_id->id);
return;
}
/*
* function name : gs_receivers_flow_handle_tid_request
* description : save peer thread id when received CTRL_PEER_TID message.
* arguments : _in_ fcmsgr: the message that receivers_flow thread received.
* return value : void
*/
static void gs_receivers_flow_handle_tid_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
int version = fcmsgr->version;
struct c_mailbox* cmailbox = NULL;
cmailbox = &(C_MAILBOX(node_idx, streamid));
LIBCOMM_PTHREAD_MUTEX_LOCK(&(cmailbox->sinfo_lock));
// check stream key and mailbox state
if (true == gs_check_mailbox(cmailbox->local_version, version)) {
cmailbox->peer_thread_id = fcmsgr->extra_info;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&(cmailbox->sinfo_lock));
}
/*
* function name : gs_connect_by_unix_domain
* description : connect with postmaster thread by unix domain
* arguments : void
* return value : -1: error
* : 0: succeed
*/
static int gs_connect_by_unix_domain()
{
errno_t ss_rc;
uint32 cpylen, maxlen;
// STEP1 create new socket
if ((g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
LIBCOMM_ELOG(WARNING, "(SendUnixDomainMsg)\tCould not create socket.");
return -1;
}
// STEP2 set unix addr
struct sockaddr_un unp;
ss_rc = memset_s(&unp, sizeof(unp), 0x0, sizeof(struct sockaddr_un));
securec_check(ss_rc, "\0", "\0");
unp.sun_family = AF_UNIX;
maxlen = sizeof(unp.sun_path);
cpylen = comm_get_cpylen(g_instance.comm_cxt.g_unix_path, maxlen);
ss_rc = memset_s(unp.sun_path, maxlen, 0x0, maxlen);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(unp.sun_path, maxlen, g_instance.comm_cxt.g_unix_path, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
unp.sun_path[cpylen] = '\0';
// STEP3 connecting, server loop will be waked up
if (connect(g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop,
(struct sockaddr*)&unp,
sizeof(struct sockaddr_un)) == -1) {
LIBCOMM_ELOG(WARNING, "(SendUnixDomainMsg)\tFailed to connect by unix socket, error: %s", mc_strerror(errno));
close(g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop);
g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop = INVALID_SOCK;
return -1;
}
return 0;
}
/*
* function name : gs_send_msg_by_unix_domain
* description : send gs_sock to postmaster, as serverloop listen on the unix domain sock.
* we need to connect first then send the gs_sock
* arguments : msg: the message that we want to send, gs_sock in this case
* return value : -1: error
* : other postive value: sent bytes
*/
static int gs_send_msg_by_unix_domain(const void* msg, int msg_len)
{
int error = 0;
bool is_retry = true;
retry:
if (g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop == INVALID_SOCK) {
error = gs_connect_by_unix_domain();
}
if (error < 0) {
return error;
}
error = mc_tcp_write_block(g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop, (const void*)msg, msg_len);
if (error <= 0) {
LIBCOMM_ELOG(WARNING, "(s|unix domain)\tFailed to send through unix socket, error: %s", mc_strerror(errno));
close(g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop);
g_instance.comm_cxt.localinfo_cxt.sock_to_server_loop = INVALID_SOCK;
// send fail may due to socket is closed by postmaster
// try to make a new connection then send one more time
// if failed in second time, return error
if (is_retry) {
is_retry = false;
goto retry;
}
}
return error;
}
/*
* function name : gs_recv_msg_by_unix_domain
* description : recv gs_sock from receiver flow ctrl
* arguments : fd: unix domain socket fd
gs_sock:output pointer
* return value : -1: recv failed or value of gsocket is invalid
* : sizeof(gsocket):succeed
*/
int gs_recv_msg_by_unix_domain(int fd, gsocket* gs_sock)
{
int error;
int size = (int)sizeof(gsocket);
error = mc_tcp_read_block(fd, gs_sock, size, 0);
// recv failed
if (error != size) {
LIBCOMM_ELOG(WARNING,
"(r|unix domain)\tfailed to recv gs_sock from unix domain, result: %d, error:%s.",
error,
gs_comm_strerror());
mc_tcp_close(fd);
return -1;
}
// check the value of receiver gs_sock
if ((gs_sock->type != GSOCK_DAUL_CHANNEL) || (gs_sock->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) ||
(gs_sock->sid == 0) || (gs_sock->sid >= g_instance.comm_cxt.counters_cxt.g_max_stream_num)) {
LIBCOMM_ELOG(WARNING,
"(r|unix domain)\tinvalid gs_sock from unix domain, idx: %d, sid: %d, ver: %d, type: %d.",
gs_sock->idx,
gs_sock->sid,
gs_sock->ver,
gs_sock->type);
mc_tcp_close(fd);
return -1;
}
return error;
}
/*
* function name : gs_r_build_reply_conntion
* description : as the connection between cn & dn is duplex.
* dn need to build an inverse connection to cn.
* this func will:
* 1. build an inverse physical conn if needed.
* 2. build an inverse logic conn
* 3. initial pmailbox
* arguments : fcmsgr: provides gs_sock and remote port
* return value : -1: error
* : 0:Succeed
*/
static int gs_r_build_reply_conntion(FCMSG_T* fcmsgr, int local_version)
{
errno_t ss_rc;
uint32 cpylen;
int node_idx = fcmsgr->node_idx;
int streamid = fcmsgr->streamid;
int remote_version = fcmsgr->version;
// get remote nodename and host from global variable
char remote_host[HOST_ADDRSTRLEN] = {0x0};
char remote_nodename[NAMEDATALEN] = {0x0};
cpylen = comm_get_cpylen(g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host, HOST_ADDRSTRLEN);
ss_rc = memset_s(remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN);
securec_check(ss_rc, "\0", "\0");
ss_rc =
strncpy_s(remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
remote_host[cpylen] = '\0';
cpylen = comm_get_cpylen(g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, NAMEDATALEN);
ss_rc = memset_s(remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(
remote_nodename, NAMEDATALEN, g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
remote_nodename[cpylen] = '\0';
// streamcap contain the ctrl port and data port of remote process.
// we use streamcap to send these ports
// becuase we do not want to add too much members to FCMSG_T
// so we usually use streamcap to send some extra msgs
int remote_ctrl_port = (int)(fcmsgr->streamcap >> 32);
int remote_data_port = (int)(fcmsgr->streamcap);
libcommaddrinfo libcomm_addrinfo;
libcomm_addrinfo.host = remote_host;
libcomm_addrinfo.ctrl_port = remote_ctrl_port;
libcomm_addrinfo.sctp_port = remote_data_port;
cpylen = comm_get_cpylen(remote_nodename, NAMEDATALEN);
ss_rc = memset_s(libcomm_addrinfo.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(libcomm_addrinfo.nodename, NAMEDATALEN, remote_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
libcomm_addrinfo.nodename[cpylen] = '\0';
COMM_DEBUG_LOG("(r|build reply conn)\tBuild TCP connect for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
if (unlikely(gs_s_check_connection(&libcomm_addrinfo, node_idx, true, CTRL_CHANNEL) == false)) {
LIBCOMM_ELOG(WARNING,
"(r|build reply conn)\tFailed to connect to host:port[%s:%d], node name[%s].",
libcomm_addrinfo.host,
libcomm_addrinfo.ctrl_port,
remote_nodename);
return -1;
}
/*
* Check and build logic connection whit the remote point (if need)
*/
COMM_DEBUG_LOG("(r|build reply conn)\tBuild data connection for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
// failed to build logic connection
if (unlikely(gs_s_check_connection(&libcomm_addrinfo, node_idx, true, DATA_CHANNEL) == false)) {
LIBCOMM_ELOG(WARNING,
"(r|build reply conn)\tFailed to build data connection "
"to %s:%d for node[%d]:%s, detail:%s.",
libcomm_addrinfo.host,
libcomm_addrinfo.sctp_port,
node_idx,
libcomm_addrinfo.nodename,
mc_strerror(errno));
errno = ECOMMSCTPSCTPCONNFAIL;
return -1;
}
COMM_DEBUG_LOG("(r|build reply conn)\tBuild data logical connection for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
struct p_mailbox* pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (pmailbox->state != MAIL_CLOSED) {
MAILBOX_ELOG(pmailbox,
WARNING,
"(r|build reply conn)\tFailed to get mailbox for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
gs_s_close_logic_connection(pmailbox, ECOMMSCTPREMOETECLOSE, NULL);
}
// inital pmailbox for sending msgs to cn
pmailbox->local_version = local_version;
pmailbox->remote_version = remote_version;
pmailbox->ctrl_tcp_sock = g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock;
pmailbox->state = MAIL_RUN;
pmailbox->bufCAP = DEFULTMSGLEN;
pmailbox->stream_key = fcmsgr->stream_key;
pmailbox->query_id = fcmsgr->query_id;
pmailbox->local_thread_id = 0;
pmailbox->peer_thread_id = 0;
pmailbox->close_reason = 0;
COMM_STAT_CALL(pmailbox, pmailbox->statistic->start_time = (uint32)mc_timers_ms());
if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (pmailbox->statistic == NULL)) {
LIBCOMM_MALLOC(pmailbox->statistic, sizeof(struct pmailbox_statistic), pmailbox_statistic);
if (pmailbox->statistic == NULL) {
errno = ECOMMSCTPRELEASEMEM;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return -1;
}
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return 0;
}
/*
* function name : gs_s_build_reply_conntion
* description : as the connection between cn & dn is duplex.
* cn need to inital cmailbox to recv msgs from dn
* arguments : fcmsgr: provides gs_sock
*/
static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version)
{
int node_idx = addr_info->gs_sock.idx;
int streamid = addr_info->gs_sock.sid;
int local_version = addr_info->gs_sock.ver;
// initialize consumer cmailbox
struct c_mailbox* cmailbox = &C_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (gs_check_mailbox(cmailbox->local_version, local_version) == true) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return;
}
if (cmailbox->state != MAIL_CLOSED) {
MAILBOX_ELOG(cmailbox,
WARNING,
"(s|build reply conn)\tFailed to get mailbox for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx));
gs_r_close_logic_connection(cmailbox, ECOMMSCTPREMOETECLOSE, NULL);
}
cmailbox->local_version = local_version;
cmailbox->remote_version = remote_version;
cmailbox->ctrl_tcp_sock = g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock;
cmailbox->state = MAIL_RUN;
cmailbox->bufCAP = DEFULTMSGLEN;
cmailbox->stream_key = addr_info->sctpKey;
cmailbox->query_id = DEBUG_QUERY_ID;
cmailbox->local_thread_id = 0;
cmailbox->peer_thread_id = 0;
cmailbox->close_reason = 0;
if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (cmailbox->statistic == NULL)) {
LIBCOMM_MALLOC(cmailbox->statistic, sizeof(struct cmailbox_statistic), cmailbox_statistic);
if (cmailbox->statistic == NULL) {
errno = ECOMMSCTPRELEASEMEM;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return;
}
}
COMM_STAT_CALL(cmailbox, cmailbox->statistic->start_time = (uint32)mc_timers_ms());
COMM_DEBUG_LOG("(s|build reply conn)\tNode[%d] stream[%d], node name[%s] is in state[%s].",
node_idx,
streamid,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
stream_stat_string(cmailbox->state));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return;
}
static void gs_flow_thread_time(char *thread, uint64 *start_time, uint64 end_time,
uint64 *last_check_time, uint64 *work_all_time, int flag)
{
uint64 work_time = 0;
uint64 all_time = 0;
uint64 curr_time = 0;
if (thread == NULL || start_time == NULL || last_check_time == NULL || work_all_time == NULL) {
LIBCOMM_ELOG(WARNING, "Invalid args of gs_flow_thread_time.");
return;
}
curr_time = mc_timers_us();
all_time = ABS_SUB(curr_time, *start_time);
*start_time = curr_time;
work_time = ABS_SUB(*start_time, end_time);
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL && all_time > 0) {
g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[flag] = work_time * 100 / all_time;
}
if (work_time > THREAD_FREE_TIME_10S) {
LIBCOMM_ELOG(WARNING, "%s thread block time exceeds 10s:%lus.", thread, work_time / THREAD_FREE_TIME_10S);
}
if (ABS_SUB(*start_time, *last_check_time) >= THREAD_INTSERVAL_60S) {
if (*work_all_time * 1.0 / THREAD_INTSERVAL_60S > THREAD_WORK_PERCENT) {
LIBCOMM_ELOG(WARNING, "%s thread account for more than 80%% of working time.", thread);
}
*last_check_time = *start_time;
*work_all_time = 0;
}
*work_all_time += work_time;
return;
}
/*
* function name : gs_receivers_flow_handle_ready_request
* description : handle ready request when received MAIL_READY.
* arguments :
* _in_ fcmsgr: the message that receivers_flow thread received.
* _in_ t_fd_id: the socket that receivers_flow thread used.
* producer is building a logic connection,
* we get stream index from the message,
* and initialize consumer mailbox,
* then reply MAIL_READY message to producer.
*/
static void gs_receivers_flow_handle_ready_request(FCMSG_T* fcmsgr)
{
uint16 streamid = fcmsgr->streamid;
uint16 node_idx = fcmsgr->node_idx;
// producer send pmailbox version as fcmsgr->version,
// now save it to cmailbox->remote_version.
int remote_verion = fcmsgr->version;
int ctrl_socket = g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock;
struct FCMSG_T fcmsgs = {0x0};
struct c_mailbox* cmailbox = NULL;
int rc = -1;
uint64 time_now;
uint64 time_callback_start = 0;
uint64 time_callback_end = 0;
uint64 deal_time = 0;
errno_t ss_rc;
uint32 cpylen;
gsocket gs_sock = {0};
uint16 local_version;
// If the Consumer is ready to exist, maybe because of timeout or other errors,
// we do not need to get the stream index for the producer, or the stream index will be leaked.
//
// small quota size for slow start
long add_quota = DEFULTMSGLEN;
StreamConnInfo connInfo = {0};
COMM_TIMER_INIT();
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_CONSUMER_REJECT)) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\t[FAULT INJECTION]Consumer can not accept.");
errno = ECOMMSCTPAPPCLOSE;
goto accept_failed;
}
#endif
// the entry should be close, may be caused by cancel
// node_idx < 0 means data conntion has some errors
Assert(node_idx >= 0);
// initialize consumer cmailbox
cmailbox = &C_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (cmailbox->state != MAIL_CLOSED) {
MAILBOX_ELOG(cmailbox,
WARNING,
"(r|flow ctrl)\tFailed to get mailbox for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx));
gs_r_close_logic_connection(cmailbox, ECOMMSCTPREMOETECLOSE, NULL);
}
local_version = cmailbox->local_version + 1;
if (local_version >= MAX_MAILBOX_VERSION) {
local_version = 0;
}
cmailbox->local_version = local_version;
// producer send pmailbox version as fcmsgr->version,
// now save it to cmailbox->remote_version.
cmailbox->remote_version = remote_verion;
cmailbox->ctrl_tcp_sock = ctrl_socket;
cmailbox->state = MAIL_RUN;
cmailbox->bufCAP += add_quota;
cmailbox->stream_key = fcmsgr->stream_key;
cmailbox->query_id = fcmsgr->query_id;
cmailbox->local_thread_id = 0;
cmailbox->peer_thread_id = 0;
cmailbox->close_reason = 0;
if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (cmailbox->statistic == NULL)) {
LIBCOMM_MALLOC(cmailbox->statistic, sizeof(struct cmailbox_statistic), cmailbox_statistic);
if (cmailbox->statistic == NULL) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = ECOMMSCTPRELEASEMEM;
goto accept_failed;
}
}
time_now = COMM_STAT_TIME();
COMM_STAT_CALL(cmailbox, cmailbox->statistic->start_time = (uint32)time_now);
COMM_DEBUG_LOG("(r|flow ctrl)\tNode[%d] stream[%d], node name[%s] is in state[%s].",
node_idx,
streamid,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
stream_stat_string(cmailbox->state));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
deal_time = ABS_SUB((long long int)time(NULL), (long long int)gs_get_recv_ready_time());
if ((long long int)mc_tcp_get_connect_timeout() < (long long int)deal_time) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tIt takes %lus to process the ready message, query id:%lu.",
deal_time, u_sess->debug_query_id);
}
// for dual connection, we need to build an inverse logic conn with same gs_sock
if (fcmsgr->type == CTRL_CONN_DUAL) {
u_sess->pgxc_cxt.NumDataNodes = (int)(fcmsgr->extra_info);
// build pmailbox with the same version and remote_verion as cmailbox,
// when this connection is duplex.
if (gs_r_build_reply_conntion(fcmsgr, local_version) != 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
gs_r_close_logic_connection(cmailbox, ECOMMSCTPTCPDISCONNECT, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
goto accept_failed;
}
}
gs_sock.idx = node_idx;
gs_sock.sid = streamid;
gs_sock.ver = local_version;
if (fcmsgr->type == CTRL_CONN_DUAL) {
// CN request this logic connection, is a dual channel
gs_sock.type = GSOCK_DAUL_CHANNEL;
rc = gs_send_msg_by_unix_domain((void*)&gs_sock, sizeof(gs_sock));
if (rc <= 0) {
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\t fail to notify main thread from node[%d]:%s with socket[%d].",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
ctrl_socket);
gs_close_gsocket(&gs_sock);
goto accept_failed;
}
}
// reply MAIL_READY message to producer
fcmsgs.type = CTRL_CONN_ACCEPT;
fcmsgs.node_idx = node_idx;
fcmsgs.streamid = streamid;
fcmsgs.streamcap = add_quota;
fcmsgs.version = remote_verion;
fcmsgs.query_id = fcmsgr->query_id;
fcmsgs.extra_info = local_version;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER);
if (rc <= 0) {
errno = ECOMMSCTPTCPDISCONNECT;
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tFailed to send ready msg to node[%d]:%s with socket[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
ctrl_socket,
mc_strerror(errno));
return;
}
COMM_DEBUG_LOG("(r|flow ctrl)\tSuccess to send CTRL_CONN_ACCEPT msg to node[%d]:%s with socket[%d].",
node_idx, REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx), ctrl_socket);
deal_time = ABS_SUB((long long int)time(NULL), (long long int)gs_get_recv_ready_time());
if ((uint64)mc_tcp_get_connect_timeout() < deal_time) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tIt takes %lus to process the ready message, query id:%lu.",
deal_time, u_sess->debug_query_id);
}
if (fcmsgr->type == CTRL_CONN_DUAL) {
return;
}
#ifdef LIBCOMM_SPEED_TEST_ENABLE
else if (fcmsgr->stream_key.queryId == LIBCOMM_PERFORMANCE_PLAN_ID) {
; // do nothing
}
#endif
else {
Assert(g_instance.comm_cxt.gs_wakeup_consumer != NULL);
// DN request this logic connection, is a single channel
gs_sock.type = GSOCK_CONSUMER;
// wake up Conumser in executor to continue
ss_rc = memset_s(&connInfo, sizeof(StreamConnInfo), 0x0, sizeof(StreamConnInfo));
securec_check(ss_rc, "\0", "\0");
connInfo.port.sctpLayer.gsock = gs_sock;
cpylen = comm_get_cpylen(fcmsgr->nodename, NAMEDATALEN);
ss_rc = memset_s(connInfo.nodeName, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(connInfo.nodeName, NAMEDATALEN, fcmsgr->nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
connInfo.nodeName[cpylen] = '\0';
time_callback_start = time(NULL);
bool wakeup_if = (*g_instance.comm_cxt.gs_wakeup_consumer)(fcmsgr->stream_key, connInfo);
time_callback_end = time(NULL);
deal_time = ABS_SUB(time_callback_end, time_callback_start);
if ((uint64)mc_tcp_get_connect_timeout() < deal_time) {
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tWake up consumer timeout for node[%d]:%s, it takes %lus, "
"because the lock wait timeout, query id:%lu :%s.",
node_idx, REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
deal_time, u_sess->debug_query_id, mc_strerror(errno));
}
if (!wakeup_if) {
COMM_DEBUG_LOG("(r|flow ctrl)\tFailed to wake up consumer "
"for node[%d]:%s, query maybe already quit:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
mc_strerror(errno));
(void)gs_r_close_stream(&gs_sock);
// we needn't to send Reject.
return;
}
}
COMM_TIMER_LOG("(r|flow ctrl)\tWake up consumer, node[%d, %d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx));
return;
accept_failed:
// Failed to accept connction,
// reply CTRL_INIT message to producer
fcmsgs.type = CTRL_CONN_REJECT;
fcmsgs.node_idx = node_idx;
fcmsgs.streamid = streamid;
fcmsgs.streamcap = 0;
fcmsgs.version = remote_verion;
fcmsgs.query_id = fcmsgr->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER);
if (rc <= 0) {
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tFailed to send init msg to node[%d]:%s with socket[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx),
ctrl_socket,
mc_strerror(errno));
}
return;
} // gs_receivers_flow_handle_ready_request
/*
* function name : gs_receivers_flow_handle_close_request
* description : handle close request when received MAIL_CLOSED.
* arguments : _in_ fcmsgr: the message that receivers_flow thread received.
* Producer closed the stream, which indicate error happened when sender is sending data
* if it is a Closed message, we should close local mailbox and tell the thread of executor to quit
*/
static void gs_receivers_flow_handle_close_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct c_mailbox* cmailbox = NULL;
// set node index and get stream index from hash table (g_r_htab_nodeid_skey_to_stream)
//
cmailbox = &(C_MAILBOX(node_idx, streamid));
LIBCOMM_PTHREAD_MUTEX_LOCK(&(cmailbox->sinfo_lock));
// If the stream is closed already, may because of tcp error or upper consumer closed actively,
// we just try to delete the entry in hash table(g_r_htab_nodeid_skey_to_stream)
//
if (false == gs_check_mailbox(cmailbox->local_version, fcmsgr->version)) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&(cmailbox->sinfo_lock));
return;
}
COMM_DEBUG_LOG("(r|flow ctrl)\tStream[%d] is closed "
"by remote node[%d]:%s, query[%lu].",
streamid,
node_idx,
g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename,
fcmsgr->query_id);
gs_r_close_logic_connection(cmailbox, ECOMMSCTPREMOETECLOSE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&(cmailbox->sinfo_lock));
return;
} // gs_receivers_flow_handle_close_request
/*
* function name : gs_receivers_flow_handle_assert_fail_request
* description : handle assert fail when receivers_flow thread received CTRL_ASSERT_FAIL.
* arguments : _in_ fcmsgr: the message that receivers_flow thread received.
* return value : void
* if the sender assert fail and core, we will get a CTRL_ASSERT_FAIL message,
* we should make the consumer core as well
*/
static void gs_receivers_flow_handle_assert_fail_request(FCMSG_T* fcmsgr)
{
int sidx = fcmsgr->streamid;
int nidx = fcmsgr->node_idx;
struct c_mailbox* cmailbox = NULL;
cmailbox = &(C_MAILBOX(nidx, sidx));
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu] and "
"buff_q->u_size[%lu].",
nidx,
sidx,
g_instance.comm_cxt.g_r_node_sock[nidx].remote_nodename,
cmailbox->state,
cmailbox->bufCAP,
cmailbox->buff_q->u_size);
MAILBOX_ELOG(cmailbox, WARNING, "(r|flow ctrl)\tMailbox Info which assert fail.");
Assert(0 != 0);
}
/*
* function name : gs_senders_flow_handle_tid_request
* description : save peer thread id when received CTRL_PEER_TID.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
*/
static void gs_senders_flow_handle_tid_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
int version = fcmsgr->version;
struct p_mailbox* pmailbox = NULL;
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
// 1: check the pmailbox[idx][streamid] is correct
//
// check stream key
if (gs_check_mailbox(pmailbox->local_version, version) == false) {
MAILBOX_ELOG(pmailbox, WARNING, "(s|flow ctrl)\tStream has already closed.");
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// 2: set the new quota and peer thread id to the pmailbox
//
pmailbox->peer_thread_id = fcmsgr->extra_info;
pmailbox->bufCAP += fcmsgr->streamcap;
// 3: set the new state to the pmailbox, then tell the executor thread who may be waiting in gs_send to continue
//
if (pmailbox->state == MAIL_HOLD) {
gs_poll_signal(pmailbox->semaphore);
}
COMM_DEBUG_LOG("(s|flow ctrl)\tWake up mailbox[%d][%d], node[%s], bufCAP[%lu] add[%ld] type[%s].",
node_idx,
streamid,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->bufCAP,
fcmsgr->streamcap,
stream_stat_string(pmailbox->state));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
/*
* function name : gs_senders_flow_handle_init_request
* description : handle init request when received CTRL_INIT.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
* if the receiver failed to get an usable stream index, we will get a CTRL_INIT message,
* we should tell the executor thread to exit and report the error.
*/
static void gs_senders_flow_handle_init_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (gs_check_mailbox(pmailbox->local_version, fcmsgr->version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// close logic conneion and sigal producer
gs_s_close_logic_connection(pmailbox, ECOMMSCTPREJECTSTREAM, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
/*
* function name : gs_senders_flow_handle_ready_request
* description : handle ready request when received MAIL_READY.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
* if the receiver succeed to get an usable stream index, we will get a MAIL_READY message,
* we should change the state of pmailbox and tell the executor thread to continue
*/
static void gs_senders_flow_handle_ready_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
uint64 time_now = 0;
// 1: check the pmailbox state is CLOSED, we will retry for 10 times
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (gs_check_mailbox(pmailbox->local_version, fcmsgr->version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
if (pmailbox->state != MAIL_READY) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// 2: change the state of the mailbox
pmailbox->state = MAIL_RUN;
pmailbox->bufCAP += fcmsgr->streamcap;
// consumer send cmailbox version as fcmsgr->extra_info,
// now save it to pmailbox->remote_version.
pmailbox->remote_version = (uint16)(fcmsgr->extra_info);
COMM_DEBUG_LOG("(s|flow ctrl)\tnode[%d] stream[%d] is in state[%s], node name[%s], bufCAP[%lu].",
node_idx,
streamid,
stream_stat_string(pmailbox->state),
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->bufCAP);
// 3: signal the producer which should be waiting in gs_connect
gs_poll_signal(pmailbox->semaphore);
if (pmailbox->statistic != NULL) {
time_now = COMM_STAT_TIME();
pmailbox->statistic->connect_time = ABS_SUB(time_now, pmailbox->statistic->connect_time);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
/*
* function name : gs_senders_flow_handle_resume_request
* description : handle resume request when received MAIL_RUN.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
* if the receiver have enough buffer to continue receive, we will get a MAIL_RUN message,
* we should change the state of pmailbox and tell the executor thread to continue
*/
static void gs_senders_flow_handle_resume_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
// 1: check the pmailbox[idx][fcmsgr.streamid] is correct, if not wait a few seconds and retry
//
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (gs_check_mailbox(pmailbox->local_version, fcmsgr->version) ==
false) { // if the pmailbox is not matched, we will break here and report error
if (pmailbox->state != MAIL_CLOSED) {
MAILBOX_ELOG(pmailbox, WARNING, "(s|flow ctrl)\tStream not work.");
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// 2: set the new quota and state to the pmailbox
//
pmailbox->bufCAP += fcmsgr->streamcap;
COMM_DEBUG_LOG("(s|flow ctrl)\tWake up node[%d] stream[%d], node name[%s], bufCAP[%lu] add[%ld].",
node_idx,
streamid,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->bufCAP,
fcmsgr->streamcap);
// 3: tell the executor thread who may be waiting in gs_send to continue
//
if (pmailbox->state == MAIL_HOLD) {
gs_poll_signal(pmailbox->semaphore);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
/*
* function name : gs_senders_flow_handle_close_request
* description : handle close request when senders_flow thread received MAIL_CLOSED.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
* if the receiver quit, we will get a MAIL_CLOSED message,
* we should close the pmailbox and tell the executor thread to continue
*/
static void gs_senders_flow_handle_close_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
// 1: get the pmailbox[idx][fcmsgr.streamid] and check it is in correct state
//
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
COMM_DEBUG_LOG("(s|flow ctrl)\tStream[%d] is closed "
"by remote node[%d]:%s, query[%lu].",
streamid,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
fcmsgr->query_id);
// 2: if the pmailbox is not matched, we will break here and report error
//
if (gs_check_mailbox(pmailbox->local_version, fcmsgr->version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// 3: if gs_connect already return correctly, delete the record in the hash table,
// or, the entry will be deleted in gs_connect
//
gs_s_close_logic_connection(pmailbox, ECOMMSCTPREMOETECLOSE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
/*
* function name : gs_senders_flow_handle_assert_fail_request
* description : handle assert fail when senders_flow thread received CTRL_ASSERT_FAIL.
* arguments : _in_ fcmsgr: the message that senders_flow thread received.
* return value : void
* if the receiver assert fail and core, we will get a CTRL_ASSERT_FAIL message,
* we should make the producer core as well
*/
static void gs_senders_flow_handle_assert_fail_request(FCMSG_T* fcmsgr)
{
int sidx = fcmsgr->streamid;
int nidx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
pmailbox = &(P_MAILBOX(nidx, sidx));
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu].",
nidx,
sidx,
g_instance.comm_cxt.g_s_node_sock[nidx].remote_nodename,
pmailbox->state,
pmailbox->bufCAP);
MAILBOX_ELOG(pmailbox, WARNING, "(s|flow ctrl)\tMailbox Info which assert fail.");
Assert(0 != 0);
}
extern ThreadId getThreadIdForLibcomm(int logictid);
static void gs_senders_flow_handle_stop_query_request(FCMSG_T* fcmsgr)
{
int streamid = fcmsgr->streamid;
int node_idx = fcmsgr->node_idx;
struct p_mailbox* pmailbox = NULL;
// 1: get the pmailbox[idx][fcmsgr.streamid] and check it is in correct state
//
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
COMM_DEBUG_LOG("(s|flow ctrl)\tQuery[%lu] is stop "
"by remote node[%d]:%s with stream[%d].",
fcmsgr->query_id,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
streamid);
// 2: if the pmailbox is not matched, we will break here and report error
//
if (gs_check_mailbox(pmailbox->local_version, fcmsgr->version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return;
}
// 3: send SIGUSR1 to backend, then set the stop flag to stop query
//
int logictid = (int)ntohl(fcmsgr->extra_info);
ThreadId backendTID = getThreadIdForLibcomm(logictid);
if (0 != backendTID) {
StreamNodeGroup::stopAllThreadInNodeGroup(backendTID, fcmsgr->query_id);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
static int libcomm_build_tcp_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx)
{
struct sock_id fd_id = {-1, -1};
ip_key addr;
int msg_len = NAMEDATALEN;
int error = -1;
errno_t ss_rc = 0;
uint32 cpylen;
/*
* Historical residual problem!
* comm_control_port and comm_sctp_port is the same,
* it is well on sctp mode, because we use two protocol.
* and it is conflict when we only use tcp protocol on tcp mode.
* so we use sctp_port+1 for data connection for tcp mode.
*/
int sock = mc_tcp_connect(libcomm_addrinfo->host, libcomm_addrinfo->sctp_port);
if (sock < 0) {
LIBCOMM_ELOG(WARNING,
"(s|build tcp connection)\tFailed to build data connection "
"to %s:%d for node[%d]:%s, error[%d:%d]:%s.",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
error,
errno,
mc_strerror(errno));
return -1;
}
/* Client side gss kerberos authentication for data connection. */
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL && GssClientAuth(sock, libcomm_addrinfo->host) < 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tData channel GSS authentication failed, "
"remote:%s[%s:%d]:%s.",
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
mc_strerror(errno));
errno = ECOMMSCTPGSSAUTHFAIL;
// Failed to build sctp connection
mc_tcp_close(sock);
return -1;
}
fd_id.fd = sock;
fd_id.id = 0;
if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) {
mc_tcp_close(sock);
LIBCOMM_ELOG(WARNING, "(s|build tcp connection)\tFailed to save socket[%d,%d], close it.", fd_id.fd, fd_id.id);
return -1;
}
struct libcomm_connect_package connect_package;
connect_package.type = SCTP_PKG_TYPE_CONNECT;
connect_package.magic_num = MSG_HEAD_MAGIC_NUM2;
ss_rc = strcpy_s(connect_package.node_name, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename);
securec_check(ss_rc, "\0", "\0");
ss_rc = strcpy_s(connect_package.host, HOST_ADDRSTRLEN, g_instance.comm_cxt.localinfo_cxt.g_local_host);
securec_check(ss_rc, "\0", "\0");
msg_len = sizeof(struct libcomm_connect_package);
MsgHead msg_head;
msg_head.type = 'C';
msg_head.magic_num = MSG_HEAD_MAGIC_NUM;
msg_head.checksum = MSG_HEAD_TEMP_CHECKSUM;
msg_head.logic_id = 0;
msg_head.msg_len = msg_len;
msg_head.version = 0;
error = mc_tcp_write_block(sock, (char*)&msg_head, sizeof(MsgHead));
if (error > 0) {
error = mc_tcp_write_block(sock, (char*)&connect_package, msg_len);
}
if (error <= 0) {
LIBCOMM_ELOG(WARNING,
"(s|build tcp connection)\tFailed to send assoc id to %s:%d "
"for node[%d]:%s on socket[%d].",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
sock);
mc_tcp_close(sock);
return -1;
}
if (gs_map_sock_id_to_node_idx(fd_id, node_idx) < 0) {
LIBCOMM_ELOG(WARNING, "(s|build tcp connection)\tFailed to save sock and sockid.");
mc_tcp_close(sock);
return -1;
}
retry_read:
struct libcomm_accept_package ack_msg = {0, 0};
error = mc_tcp_read_block(sock, &ack_msg, sizeof(ack_msg), 0);
// if failed, we close the bad one and return -1
if (error < 0 || ack_msg.result != 1 || ack_msg.type != SCTP_PKG_TYPE_ACCEPT) {
LIBCOMM_ELOG(WARNING,
"(s|build tcp connection)\tFailed to recv assoc id from %s:%d "
"for node[%d]:%s on socket[%d].",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
sock);
mc_tcp_close(sock);
return -1;
} else if (error == 0) {
usleep(1000);
goto retry_read;
}
g_instance.comm_cxt.g_senders->sender_conn[node_idx].ip_changed = true;
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
/*
* check the host with ctrl channel
* make sure the connection of control
* channel and data channel is same node
*/
if (strcmp(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, libcomm_addrinfo->host) != 0) {
g_instance.comm_cxt.g_senders->sender_conn[node_idx].ip_changed = false;
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
return -1;
}
/* close old connection */
struct sock_id sctp_fd_id = {g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id};
gs_s_close_bad_data_socket(&sctp_fd_id, ECOMMSCTPPEERCHANGED, node_idx);
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_ADDRSTRLEN);
ss_rc = memset_s(
g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host,
HOST_ADDRSTRLEN,
libcomm_addrinfo->host,
cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host[cpylen] = '\0';
g_instance.comm_cxt.g_senders->sender_conn[node_idx].port = libcomm_addrinfo->sctp_port;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 1;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket = fd_id.fd;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id = fd_id.id;
g_instance.comm_cxt.g_senders->sender_conn[node_idx].ip_changed = false;
/* set reply socket for g_r_node_sock */
gs_set_reply_sock(node_idx);
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(addr.ip, HOST_LEN_OF_HTAB, libcomm_addrinfo->host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
addr.port = libcomm_addrinfo->sctp_port;
/* update connection state to succeed when connect succeed */
gs_update_connection_state(addr, CONNSTATESUCCEED, true, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
LIBCOMM_ELOG(LOG,
"(s|build tcp connection)\tSucceed to connect %s:%d with socket[%d:%d] for node[%d]:%s.",
libcomm_addrinfo->host,
libcomm_addrinfo->sctp_port,
fd_id.fd,
fd_id.id,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
return 0;
}
/*
* function name : gs_s_build_tcp_ctrl_connection
* description : build tcp connection to the remote,
* and then update g_s_node_sock.
* notice : we must get g_s_poller_list_lock lock before!
* arguments :
* _in_ sctp_addrinfo: remote infomation.
* _in_ node_idx: remote node index.
* return value :
* -1: failed.
* 0: succeed.
*/
static int gs_s_build_tcp_ctrl_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx, bool is_reply)
{
int tcp_sock = -1;
int ctrl_sock = -1;
int ctrl_sock_id = -1;
ip_key addr;
int error;
errno_t ss_rc;
uint32 cpylen;
char ack = 'r';
char* remote_host = libcomm_addrinfo->host;
int remote_tcp_port = libcomm_addrinfo->ctrl_port;
char* remote_nodename = libcomm_addrinfo->nodename;
// do connect to remote tcp listening port
tcp_sock = mc_tcp_connect(remote_host, remote_tcp_port);
// failed to connect, report error
if (tcp_sock < 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tTCP connect failed to node:%s[%s:%d]:%s.",
remote_nodename,
remote_host,
remote_tcp_port,
mc_strerror(errno));
errno = ECOMMSCTPTCPCONNFAIL;
return -1;
}
/* Client side gss kerberos authentication for tcp connection. */
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL && GssClientAuth(tcp_sock, remote_host) < 0) {
mc_tcp_close(tcp_sock);
LIBCOMM_ELOG(WARNING,
"(s|connect)\tControl channel GSS authentication failed, remote:%s[%s:%d]:%s.",
remote_nodename,
remote_host,
remote_tcp_port,
mc_strerror(errno));
errno = ECOMMSCTPGSSAUTHFAIL;
return -1;
} else {
COMM_DEBUG_LOG("(s|connect)\tControl channel GSS authentication SUCC, remote:%s[%s:%d]:%s.",
remote_nodename,
remote_host,
remote_tcp_port,
mc_strerror(errno));
}
// wait ack from remote node, reject when the state of remote node is incorrect, such as standby mode;
struct FCMSG_T fcmsgs = {0x0};
if (IS_PGXC_COORDINATOR) {
fcmsgs.type = CTRL_CONN_REGIST_CN;
} else {
fcmsgs.type = CTRL_CONN_REGIST;
}
fcmsgs.node_idx = node_idx;
fcmsgs.streamid = 1;
fcmsgs.extra_info = 0xEA;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
error = gs_send_ctrl_msg_by_socket(tcp_sock, &fcmsgs);
if (error < 0) {
mc_tcp_close(tcp_sock);
LIBCOMM_ELOG(WARNING,
"(s|connect)\tSend ctrl msg failed remote[%s] with addr[%s:%d].",
remote_nodename,
remote_host,
remote_tcp_port);
errno = ECOMMSCTPTCPCONNFAIL;
return -1;
}
// cn need to ask the remote datanode status when make connection
// 'r' is received when remote is standby or pending mode
// for conn between dns, skip this step, ip is given by executor
if (IS_PGXC_COORDINATOR) {
error = mc_tcp_read_block(tcp_sock, &ack, sizeof(char), 0);
if (error < 0 || ack != 'o') {
mc_tcp_close(tcp_sock);
LIBCOMM_ELOG(WARNING,
"(s|connect)\tControl channel connect reject by remote[%s] with addr[%s:%d], remote is not a primary "
"node.",
remote_nodename,
remote_host,
remote_tcp_port);
errno = ECOMMSCTPTCPCONNFAIL;
return -1;
}
} else if (is_reply) {
/* when DN build reply connecion to CN
* wait the reply of CN, to make sure CN has received ctrl connection request
* then send ctrl msgs to CN
*/
error = mc_tcp_read_block(tcp_sock, &ack, sizeof(char), 0);
if (error < 0 || ack != 'o') {
mc_tcp_close(tcp_sock);
LIBCOMM_ELOG(WARNING,
"(s|connect)\tControl channel connect reject by remote[%s] with addr[%s:%d].",
remote_nodename,
remote_host,
remote_tcp_port);
errno = ECOMMSCTPTCPCONNFAIL;
return -1;
}
}
struct sock_id fd_id = {tcp_sock, 0};
// if we successfully to connect, we should record the socket(fd) and the version(id)
if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) {
mc_tcp_close(tcp_sock);
LIBCOMM_ELOG(WARNING, "(s|connect)\tFailed to malloc for socket.");
errno = ECOMMSCTPMEMALLOC;
return -1;
}
if (gs_map_sock_id_to_node_idx(fd_id, node_idx) < 0) {
LIBCOMM_ELOG(WARNING, "(s|connect)\tFailed to save sock and sockid.");
mc_tcp_close(tcp_sock);
return -1;
}
/* close old data connection */
g_instance.comm_cxt.g_senders->sender_conn[node_idx].ip_changed = true;
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
struct sock_id sctp_fd_id = {g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id};
gs_s_close_bad_data_socket(&sctp_fd_id, ECOMMSCTPPEERCHANGED, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
/* close old ctrl connection */
g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed = true;
g_instance.comm_cxt.g_s_node_sock[node_idx].lock();
ctrl_sock = g_instance.comm_cxt.g_s_node_sock[node_idx].get_nl(CTRL_TCP_SOCK, &ctrl_sock_id);
struct sock_id ctrl_fd_id = {ctrl_sock, ctrl_sock_id};
gs_s_close_bad_ctrl_tcp_sock(&ctrl_fd_id, ECOMMSCTPPEERCHANGED, false, node_idx);
/* save remote datanode information */
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(fd_id.fd, CTRL_TCP_SOCK);
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(fd_id.id, CTRL_TCP_SOCK_ID);
cpylen = comm_get_cpylen(remote_host, HOST_ADDRSTRLEN);
ss_rc = memset_s(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN);
securec_check(ss_rc, "\0", "\0");
ss_rc =
strncpy_s(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, HOST_ADDRSTRLEN, remote_host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host[cpylen] = '\0';
cpylen = comm_get_cpylen(remote_nodename, NAMEDATALEN);
ss_rc = memset_s(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, NAMEDATALEN, remote_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename[cpylen] = '\0';
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(remote_tcp_port, CTRL_TCP_PORT);
g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed = false;
/* add the socket to the epoll list for monitoring network events */
if (g_instance.comm_cxt.pollers_cxt.g_s_poller_list->add_fd(&fd_id) < 0) {
g_instance.comm_cxt.g_s_node_sock[node_idx].close_socket_nl(CTRL_TCP_SOCK);
g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_PORT);
g_instance.comm_cxt.g_s_node_sock[node_idx].unlock();
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_REMOVE, NULL);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING, "(s|connect)\tFailed to malloc for poll.");
errno = ECOMMSCTPMEMALLOC;
return -1;
}
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(addr.ip, HOST_LEN_OF_HTAB, libcomm_addrinfo->host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
addr.port = libcomm_addrinfo->ctrl_port;
/* update connection state to succeed when connect succeed */
gs_update_connection_state(addr, CONNSTATESUCCEED, true, node_idx);
g_instance.comm_cxt.g_s_node_sock[node_idx].unlock();
LIBCOMM_ELOG(LOG,
"(s|connect)\tTCP connect successed to node:%s[%s:%d] on socket[%d,%d] with node id[%d].",
remote_nodename,
remote_host,
remote_tcp_port,
fd_id.fd,
fd_id.id,
node_idx);
return 0;
} // gs_s_build_tcp_ctrl_connection
/*
* function name : gs_s_check_connection
* description : sender check that is receiver changed.
* if the destination ip is changed,
* which means the primary and the standby are reverted,
* then we close tcp/sctp connection and rebuild later.
* arguments :
* _in_ sctp_addrinfo: remote infomation.
* _in_ node_idx: remote node index.
* return value :
* false: failed.
* true : succeed.
*/
static bool gs_s_check_connection(libcommaddrinfo* libcomm_addrinfo, int node_idx, bool is_reply, int type)
{
ip_key addr;
errno_t ss_rc;
uint32 cpylen;
struct sock_id fd_id = {-1, -1};
cpylen = comm_get_cpylen(libcomm_addrinfo->host, HOST_LEN_OF_HTAB);
ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(addr.ip, HOST_LEN_OF_HTAB, libcomm_addrinfo->host, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
addr.ip[cpylen] = '\0';
if (type == CTRL_CHANNEL) {
addr.port = libcomm_addrinfo->ctrl_port;
} else {
addr.port = libcomm_addrinfo->sctp_port;
}
retry:
int rc = gs_s_get_connection_state(addr, node_idx, type);
if (likely(rc == CONNSTATESUCCEED)) {
/* data channel need to check socket even if the connection state in htap is succeed */
if (type == DATA_CHANNEL) {
LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
if (g_libcomm_adapt.check_socket(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket) != 0) {
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
LIBCOMM_ELOG(WARNING,
"(s|connect)\tFailed to check libcomm socket "
"node[%d]:%s, errno[%d]:%s, close socket[%d,%d].",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
errno,
mc_strerror(errno),
fd_id.fd,
fd_id.id);
gs_s_close_bad_data_socket(&fd_id, ECOMMSCTPSCTPDISCONNECT, node_idx);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
/* retry to build new connection, next rc must be CONNSTATECONNECTING */
goto retry;
}
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
}
COMM_DEBUG_LOG("(s|connect)\tAlready has connection:port[%s:%d], node name[%s]].",
addr.ip,
addr.port,
libcomm_addrinfo->nodename);
return true;
} else if (rc == CONNSTATECONNECTING) {
if (type == CTRL_CHANNEL) {
rc = gs_s_build_tcp_ctrl_connection(libcomm_addrinfo, node_idx, is_reply);
} else {
rc = g_libcomm_adapt.connect(libcomm_addrinfo, node_idx);
}
/* update connection state to fail when connect failed */
if (rc < 0) {
gs_update_connection_state(addr, CONNSTATEFAIL, true, node_idx);
return false;
}
return true;
} else {
COMM_DEBUG_LOG("(s|connect)\tFailed checking connection state:port[%s:%d], node name[%s] error[%s].",
libcomm_addrinfo->host,
libcomm_addrinfo->ctrl_port,
libcomm_addrinfo->nodename,
mc_strerror(errno));
return false;
}
} // gs_s_check_connection
// Sender use the function to connect to the receiver.
// 1. connect to the remote datanode
// 2. get stream index for Producer
//
static int gs_internal_connect(libcommaddrinfo* libcomm_addrinfo)
{
/*
* Step 1: Initialize local variable
*/
if (libcomm_addrinfo == NULL) {
LIBCOMM_ELOG(WARNING, "(s|connect)\tInvalid argument: libcomm addr info is NULL");
errno = ECOMMSCTPARGSINVAL;
return -1;
}
if (unlikely((libcomm_addrinfo->host == NULL) || (libcomm_addrinfo->ctrl_port <= 0) ||
(libcomm_addrinfo->sctp_port <= 0))) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tInvalid argument: %s"
"tcp port:%d, sctp port:%d.",
libcomm_addrinfo->host == NULL ? "host is NULL, " : "",
libcomm_addrinfo->ctrl_port,
libcomm_addrinfo->sctp_port);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
int rc = 0;
int node_idx = -1;
int streamid = -1;
errno_t ss_rc;
uint32 cpylen;
int to_ctrl_tcp_port = libcomm_addrinfo->ctrl_port;
uint64 time_enter = COMM_STAT_TIME();
uint64 time_now = time_enter;
/*
* Step 1: Modify localhost to IP
*/
if (IS_LOCAL_HOST(libcomm_addrinfo->host)) {
libcomm_addrinfo->host = "127.0.0.1";
}
/*
* Step 2: Get or set node index by remote nodename
*/
COMM_TIMER_INIT();
node_idx = gs_get_node_idx(libcomm_addrinfo->nodename);
if (unlikely(node_idx < 0)) {
LIBCOMM_ELOG(WARNING,
"(s|send connect)\tFailed to get node index for %s: %s.",
libcomm_addrinfo->nodename,
mc_strerror(errno));
return -1;
}
COMM_TIMER_LOG("(s|send connect)\tConnect start for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
/* Step 3: Check and build ctrl connection with the remote point (if need) */
COMM_TIMER_LOG("(s|send connect)\tBuild ctrl channel connect for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
if (unlikely(gs_s_check_connection(libcomm_addrinfo, node_idx, false, CTRL_CHANNEL) == false)) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tFailed to connect to host:port[%s:%d], node name[%s].",
libcomm_addrinfo->host,
to_ctrl_tcp_port,
libcomm_addrinfo->nodename);
return -1;
}
/* Step 4: Check and build data connection with the remote point (if need) */
COMM_TIMER_LOG("(s|send connect)\tBuild data connection for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
// failed to build data connection
if (unlikely(gs_s_check_connection(libcomm_addrinfo, node_idx, false, DATA_CHANNEL) == false)) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tFailed to build data connection "
"to %s:%d for node[%d]:%s, detail:%s.",
libcomm_addrinfo->host,
to_ctrl_tcp_port,
node_idx,
libcomm_addrinfo->nodename,
mc_strerror(errno));
errno = ECOMMSCTPSCTPCONNFAIL;
return -1;
}
COMM_TIMER_LOG("(s|send connect)\tBuild data logical connection for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
/*
* Step 6: Get stream id, initialize pmailbox and save information keys -> stream index in
* hash table (g_s_htab_nodeid_skey_to_stream)
*/
streamid = gs_get_stream_id(node_idx);
if (streamid < 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tFailed to obtain logic stream idx for connect %s:%d, node:%s.",
libcomm_addrinfo->host,
to_ctrl_tcp_port,
libcomm_addrinfo->nodename);
return -1;
}
struct p_mailbox* pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
Assert(pmailbox->state == MAIL_CLOSED);
int version = pmailbox->local_version + 1;
if (version >= MAX_MAILBOX_VERSION) {
version = 0;
}
pmailbox->local_version = version;
pmailbox->ctrl_tcp_sock = g_instance.comm_cxt.g_s_node_sock[node_idx].get_nl(CTRL_TCP_SOCK, NULL);
pmailbox->state = MAIL_READY;
pmailbox->bufCAP = 0;
pmailbox->stream_key = libcomm_addrinfo->sctpKey;
pmailbox->query_id = DEBUG_QUERY_ID;
pmailbox->local_thread_id = 0;
pmailbox->peer_thread_id = 0;
pmailbox->close_reason = 0;
if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (pmailbox->statistic == NULL)) {
LIBCOMM_MALLOC(pmailbox->statistic, sizeof(struct pmailbox_statistic), pmailbox_statistic);
if (pmailbox->statistic == NULL) {
errno = ECOMMSCTPRELEASEMEM;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&(pmailbox->sinfo_lock));
return -1;
}
}
/* update the statistic information of the mailbox */
if (pmailbox->statistic != NULL) {
time_now = COMM_STAT_TIME();
pmailbox->statistic->start_time = time_now;
pmailbox->statistic->connect_time = time_enter;
}
/*
* Step 7: Send connection request to consumer with stream id
* Send ready control message
*/
COMM_TIMER_LOG("(s|send connect)\tSend ready message to node[%d, %d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
struct FCMSG_T fcmsgs = {0x0};
// for connect between cn and dn, channel is duplex
if (IS_PGXC_COORDINATOR) {
fcmsgs.type = CTRL_CONN_DUAL;
fcmsgs.extra_info = (u_sess != NULL) ? (unsigned long)u_sess->pgxc_cxt.NumDataNodes : 0;
} else {
fcmsgs.type = CTRL_CONN_REQUEST;
fcmsgs.extra_info = 0;
}
fcmsgs.node_idx = node_idx;
fcmsgs.streamid = streamid;
// send pmailbox version to cmailbox,
// and cmailbox will save as cmailbox->remote_version.
fcmsgs.version = version;
fcmsgs.stream_key = libcomm_addrinfo->sctpKey;
fcmsgs.query_id = DEBUG_QUERY_ID;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
// streamcap contain the ctrl port and data port of remote process.
// we use streamcap to send these ports
// becuase we do not want to add too much members to FCMSG_T
// so we usually use streamcap to send some extra msgs
fcmsgs.streamcap = ((unsigned long)g_instance.attr.attr_network.comm_control_port << 32) +
(long)g_instance.attr.attr_network.comm_sctp_port;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&(pmailbox->sinfo_lock));
rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER);
if (rc <= 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tFailed to send ready msg to node[%d]:%s, detail:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
mc_strerror(errno));
errno = ECOMMSCTPTCPDISCONNECT;
return -1;
}
/*
* Step 8: set the node index and stream index for caller, and return the stream index
*/
libcomm_addrinfo->gs_sock.idx = node_idx;
libcomm_addrinfo->gs_sock.sid = streamid;
libcomm_addrinfo->gs_sock.ver = version;
if (IS_PGXC_COORDINATOR) {
libcomm_addrinfo->gs_sock.type = GSOCK_DAUL_CHANNEL;
} else {
libcomm_addrinfo->gs_sock.type = GSOCK_PRODUCER;
}
COMM_DEBUG_LOG("(s|send connect)\tConnect finish for node[%d]:%s.",
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
return streamid;
} // gs_connect
// Registed a Consumer callback function to wake up the Consumer (thread at executor) when a Producer (thread at
// executor) connected successfully
//
void gs_connect_regist_callback(wakeup_hook_type wakeup_callback)
{
g_instance.comm_cxt.gs_wakeup_consumer = wakeup_callback;
} // gs_connect_regist_callback
/*
* function name : gs_connect
* description : build connects one or multiple address.
* arguments : _in_ libcomm_addrinfo: the address info list.
* _in_ addr_num: the number of address info list.
* _in_ error_index: error index of address info list wher connect failed.
* return value : 0: succeed
* : -1: all connection failed
: other value: failed connection index
* call gs_internal_connect, cause we want to receive a MAIL_READY message,
* which means the logic connection has been build successfully.
*/
int gs_connect(libcommaddrinfo** libcomm_addrinfo, int addr_num, int timeout)
{
int re = -1;
int wait_index = -1;
int i;
int node_idx = -1;
int streamid = -1;
int version = -1;
if (timeout == -1) {
timeout = CONNECT_TIMEOUT;
}
libcommaddrinfo* addr_info = NULL;
struct p_mailbox* pmailbox = NULL;
int remote_version = -1;
int error_index = -1;
bool build_reply_conn = false;
if (libcomm_addrinfo == NULL || addr_num == 0) {
LIBCOMM_ELOG(WARNING,
"(s|connect)\tInvalid argument: %saddress number is %d.",
libcomm_addrinfo == NULL ? "libcomm addr info is NULL, " : "",
addr_num);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
errno = 0;
if (gs_poll_create() != 0) {
LIBCOMM_ELOG(WARNING, "(s|parallel connect)\tFailed to malloc for create poll!");
LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK);
return -1;
}
// producer build connections with address list,
// and send MAIL_READY message to consumers.
for (i = 0; i < addr_num; i++) {
addr_info = libcomm_addrinfo[i];
addr_info->gs_sock = GS_INVALID_GSOCK;
pgstat_report_waitstatus_comm(STATE_STREAM_WAIT_CONNECT_NODES,
addr_info->nodeIdx,
addr_num - i,
-1,
global_node_definition ? global_node_definition->num_nodes : -1);
re = gs_internal_connect(addr_info);
// errno set in gs_connect
if (re < 0) {
if (IS_PGXC_COORDINATOR) {
continue;
} else {
error_index = i;
goto clean_connection;
}
}
}
// check all mailboxs state is MAIL_READY
// if the state of mailbox is MAIL_TO_CLOSE, top consumer will close the logic connection.
// if mailbox state is CTRL_CLOSE(gs_check_mailbox return false),
// means consumer do not need the data from this datanode, not report error
for (;;) {
wait_index = -1;
for (i = 0; i < addr_num; i++) {
addr_info = libcomm_addrinfo[i];
node_idx = addr_info->gs_sock.idx;
streamid = addr_info->gs_sock.sid;
version = addr_info->gs_sock.ver;
build_reply_conn = false;
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
// if gs_check_mailbox return false, means consumer close it, not need report error
if (gs_check_mailbox(pmailbox->local_version, version) == true) {
if (pmailbox->state == MAIL_READY) {
pmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore;
COMM_DEBUG_LOG(
"(s|parallel connect)\tWait node[%d] stream[%d] state[%s], node name[%s], bufCAP[%lu].",
node_idx,
streamid,
stream_stat_string(pmailbox->state),
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->bufCAP);
wait_index = i;
} else if (pmailbox->state == MAIL_TO_CLOSE) { // mail would close later, sender closes pmailbox,
LIBCOMM_ELOG(WARNING,
"(s|parallel connect)\tMAIL_TO_CLOSE node[%d] stream[%d] state[%s], node name[%s].",
node_idx,
streamid,
stream_stat_string(pmailbox->state),
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
gs_s_close_logic_connection(pmailbox, ECOMMSCTPREMOETECLOSE, NULL);
// before continue or goto clean_connection, we must release the sinfo_lock.
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
if (IS_PGXC_COORDINATOR) {
addr_info->gs_sock = GS_INVALID_GSOCK;
continue;
} else {
errno = ECOMMSCTPSCTPCONNFAIL;
error_index = i;
goto clean_connection;
}
} else {
pmailbox->semaphore = NULL;
// for cn initial cmailbox as well as the connection is duplex
if (IS_PGXC_COORDINATOR) {
remote_version = pmailbox->remote_version;
build_reply_conn = true;
}
}
} else {
// if gs_check_mailbox return false, means consumer close it, we need to reset gsockt
addr_info->gs_sock = GS_INVALID_GSOCK;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
// for cn initial cmailbox as well as the connection is duplex
if (build_reply_conn) {
// build cmailbox with the same version and remote_verion as pmailbox,
// when this connection is duplex.
gs_s_build_reply_conntion(addr_info, remote_version);
}
}
// we wait on the last mailbox that state is not MAIL_READY
if (wait_index >= 0) {
pgstat_report_waitstatus_comm(STATE_STREAM_WAIT_CONNECT_NODES,
libcomm_addrinfo[wait_index]->nodeIdx,
wait_index + 1,
-1,
global_node_definition ? global_node_definition->num_nodes : -1);
re = gs_poll(timeout);
if (re == ETIMEDOUT) {
if (IS_PGXC_COORDINATOR) {
/* close all timeout connections */
for (i = 0; i < addr_num; i++) {
pmailbox = &P_MAILBOX(libcomm_addrinfo[i]->gs_sock.idx, libcomm_addrinfo[i]->gs_sock.sid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if ((gs_check_mailbox(pmailbox->local_version, libcomm_addrinfo[i]->gs_sock.ver)) &&
(pmailbox->state == MAIL_READY)) {
LIBCOMM_ELOG(WARNING,
"(s|parallel connect)\t wait ready response timeout node[%d] stream[%d], node "
"name[%s].",
node_idx,
streamid,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
gs_s_close_logic_connection(pmailbox, ECOMMSCTPEPOLLTIMEOUT, NULL);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
} else {
errno = ETIMEDOUT;
error_index = wait_index;
goto clean_connection;
}
}
} else {
break;
}
}
LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK);
// if gs_check_mailbox return false, means consumer close it, not need report error
return 0;
// clean all connecions, and return failed.
clean_connection:
LIBCOMM_ELOG(WARNING,
"(s|parallel connect)\tFailed to connect node:%s, detail:%s.",
libcomm_addrinfo[error_index]->nodename,
mc_strerror(errno));
for (i = 0; i < addr_num; i++) {
addr_info = libcomm_addrinfo[i];
gs_close_gsocket(&(addr_info->gs_sock));
}
// For timeout, we need to check the interruption
LIBCOMM_INTERFACE_END((re == ETIMEDOUT), TempImmediateInterruptOK);
// when error_index is 0, return 1; 0 means succeed
return (error_index + 1);
}
/*
* function name : gs_s_send_start_ctrl_msg
* description : producer send local thread id to consumer,
* it is means producer start to send data.
* notice : we must get mailbox lock before.
* arguments :
* _in_ pmailbox: logic conntion info.
* return value :
* false: failed.
* true : succeed.
*/
static bool gs_s_form_start_ctrl_msg(p_mailbox* pmailbox, FCMSG_T* msg)
{
pid_t local_tid = t_thrd.comm_cxt.MyPid;
errno_t ss_rc;
uint32 cpylen;
if (pmailbox->query_id != DEBUG_QUERY_ID) {
pmailbox->query_id = DEBUG_QUERY_ID;
}
// send local thread id to remote
if (local_tid != pmailbox->local_thread_id) {
int node_idx = pmailbox->idx;
int streamid = pmailbox->streamid;
// change local stream state and quota first
pmailbox->local_thread_id = local_tid;
// change local stream state and quota first
msg->type = CTRL_PEER_TID;
msg->node_idx = node_idx;
msg->streamid = streamid;
msg->streamcap = 0;
msg->version = pmailbox->remote_version;
msg->extra_info = pmailbox->local_thread_id;
msg->query_id = pmailbox->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
msg->nodename[cpylen] = '\0';
return true;
}
return false;
} // gs_s_send_start_ctrl_msg
/*
* @Description: push the data package to local cmailbox buffer.
* @IN streamid: the producer and consumer have the same stream id.
* @IN message: data message.
* @IN m_len: message len.
* @Return: -1: push data failed.
* m_len: push data succsessed.
* @See also: local producer can use memcpy to push data package,
* no need push to data stack
*/
static int gs_push_local_buffer(int streamid, const char* message, int m_len, int cmailbox_version)
{
int cmailbox_idx = -1;
struct char_key ckey;
c_mailbox* cmailbox = NULL;
errno_t ss_rc;
bool found = false;
uint32 cpylen;
t_thrd.comm_cxt.g_receiver_loop_poll_up = COMM_STAT_TIME();
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(ckey.name, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
ckey.name[cpylen] = '\0';
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock);
nodename_entry* entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_FIND, &found);
if (found) {
cmailbox_idx = entry_name->entry.val;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock);
if (cmailbox_idx < 0) {
errno = ECOMMSCTPREMOETECLOSE;
return -1;
}
cmailbox = &C_MAILBOX(cmailbox_idx, streamid);
struct iovec* iov = NULL;
struct mc_lqueue_item* iov_item = NULL;
// use share memory malloc for buffer received data message
if (libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE) != 0) {
return -1;
}
iov = iov_item->element.data;
// copy the datat to the buffer of executor
ss_rc = memcpy_s(iov->iov_base, IOV_DATA_SIZE, message, m_len);
securec_check(ss_rc, "\0", "\0");
iov->iov_len = m_len;
if (gs_push_cmailbox_buffer(cmailbox, iov_item, cmailbox_version) < 0) {
libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE);
return -1;
}
return m_len;
}
/*
* @Description: Communication library external interface for send.
* @IN gs_sock: all information libcomm needed.
* @IN message: data.
* @IN m_len: data len.
* @Return:-1: -1: send failed.
* m_len: send succsessed.
* @See also:
* send message to the destination datanode through logic channel
* 1. check if stream info (quota and state), sending messages whenever there is enough quota
* 2. if no quota, wait on gs_poll
* 3. decrease quota after successful send, if quota is used up, set stream in HOLD state and wait on gs_poll
* 4. return error or sent message size
*/
int gs_send(gsocket* gs_sock, char* message, int m_len, int time_out, bool block_mode)
{
if (gs_sock == NULL) {
LIBCOMM_ELOG(WARNING, "(s|send)\tInvalid argument: gs_sock is NULL");
errno = ECOMMSCTPARGSINVAL;
return -1;
}
// step 1: get node index, stream index, and connection information
//
int node_idx = gs_sock->idx;
int streamid = gs_sock->sid;
int local_version = gs_sock->ver;
int remote_version = -1;
if ((message == NULL) || (m_len <= 0) || (node_idx < 0) ||
(node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (streamid <= 0) ||
(streamid >= g_instance.comm_cxt.counters_cxt.g_max_stream_num)) {
LIBCOMM_ELOG(WARNING,
"(s|send)\tInvalid argument: %s"
"len=%d, "
"node idx=%d, stream id=%d.",
message == NULL ? "message is NULL, " : "",
m_len,
node_idx,
streamid);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
errno = 0;
COMM_TIMER_INIT();
int ret = 0;
int sent_size = 0;
bool send_msg = false;
uint64 time_enter = COMM_STAT_TIME();
uint64 time_now = time_enter;
uint64 wait_quota_start = 0;
uint64 wait_quota_end = 0;
unsigned long need_send_len = 0;
struct FCMSG_T fcmsgs = {0x0};
struct sock_id fd_id = {0, 0};
bool notify_remote = false;
struct p_mailbox* pmailbox = NULL;
// set default time out to 600 seconds(10 minutes)
if (time_out == -1) {
time_out = WAITQUOTA_TIMEOUT;
}
int total_wait_time = 0;
int single_timeout = SINGLE_WAITQUOTA;
char* node_name = NULL;
uint32 send_start, send_end;
WaitStatePhase oldPhase = pgstat_report_waitstatus_phase(PHASE_NONE, true);
// regist current thread id, so we can wait in poll when need quota
if (gs_poll_create() != 0) {
LIBCOMM_ELOG(WARNING, "(s|send)\tFailed to malloc for create poll!");
return -1;
}
// step 2: find the pmailbox[node idx][stream id], to check quota and stream state, prepare to do send data
//
pmailbox = &P_MAILBOX(node_idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
// the mailbox is closed already, we should return error
if (gs_check_mailbox(pmailbox->local_version, local_version) == false) {
COMM_DEBUG_LOG("(s|send)\tStream already closed, remote:%s, detail:%s.",
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
mc_strerror(pmailbox->close_reason));
errno = pmailbox->close_reason;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
ret = -1;
goto return_result;
}
pmailbox->local_thread_id = t_thrd.comm_cxt.MyPid;
// check the stream state and quota size, if the state is not RESUME, we should wait
//
COMM_TIMER_LOG("(s|send)\tWait quota start for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
if (m_len > DEFULTMSGLEN) {
need_send_len = DEFULTMSGLEN;
} else {
need_send_len = (unsigned long)m_len;
}
fd_id.fd = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket;
fd_id.id = g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id;
for (;;) {
// stream state is right and quota size is enough
if (pmailbox->bufCAP >= need_send_len) {
pmailbox->state = MAIL_RUN;
break;
}
// if the quota is not enough for next sending, the state should be changed
pmailbox->state = MAIL_HOLD;
pmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore;
COMM_DEBUG_LOG("(s|send)\tNode[%d] stream[%d], node name[%s] in MAIL_HOLD, cap[%lu].",
node_idx,
streamid,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
pmailbox->bufCAP);
// if no need wait quota, we return -2, you can resend
if (block_mode == FALSE) {
ret = BROADCAST_WAIT_QUOTA;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
goto return_result;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
pgstat_report_waitstatus_phase(PHASE_WAIT_QUOTA);
/* wait quota start */
StreamTimeWaitQuotaStart(t_thrd.pgxc_cxt.GlobalNetInstr);
wait_quota_start = mc_timers_ms();
// wait quota 3 seconds, then wake up and check mailbox
ret = gs_poll(single_timeout);
wait_quota_end = mc_timers_ms();
StreamTimeWaitQuotaEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
/* wait quota end */
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
// the mailbox is closed or reused by other query, we should return error
if (gs_check_mailbox(pmailbox->local_version, local_version) == false) {
COMM_DEBUG_LOG("(s|send)\tStream has already closed by remote:%s, detail:%s.",
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
mc_strerror(pmailbox->close_reason));
errno = pmailbox->close_reason;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
ret = -1;
goto return_result;
}
pmailbox->semaphore = NULL;
// stream state is right and quota size is enough, can send now
if (pmailbox->bufCAP >= need_send_len) {
pmailbox->state = MAIL_RUN;
break;
}
// if it is waked up normal, and quota size is not enough,
// there must be interruption, we should return and CHECK_FOR_INTERRUPT
if (ret == 0) {
ret = 0;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
goto return_result;
}
// wait quota timedout, if total_wait_time > time_out,
// we should return and CHECK_FOR_INTERRUPT
if (ret == ETIMEDOUT) {
total_wait_time += single_timeout;
if (total_wait_time >= time_out) {
errno = ECOMMSCTPEPOLLTIMEOUT;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
ret = 0;
goto return_result;
}
}
// wait quota error (not timedout)
if (ret != 0 && ret != ETIMEDOUT) {
MAILBOX_ELOG(pmailbox, WARNING, "(s|send)\tStream waked up by error[%d]:%s.", ret, mc_strerror(errno));
gs_s_close_logic_connection(pmailbox, ECOMMSCTPWAITQUOTAFAIL, &fcmsgs);
notify_remote = true;
errno = ECOMMSCTPWAITQUOTAFAIL;
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
ret = -1;
goto return_result;
}
}
COMM_TIMER_LOG("(s|send)\tWait quota end for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
send_start = COMM_STAT_TIME();
remote_version = pmailbox->remote_version;
send_msg = gs_s_form_start_ctrl_msg(pmailbox, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
// send local thread id to remote without cmailbox lock
if (send_msg && (gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER) <= 0)) {
errno = ECOMMSCTPTCPDISCONNECT;
ret = -1;
goto return_result;
}
// step 3: if the state is RESUME and quota is enough, we do send
//
StreamTimeOSSendStart(t_thrd.pgxc_cxt.GlobalNetInstr);
COMM_TIMER_LOG("(s|send)\tSend message start for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
node_name = g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename;
if (0 == strcmp(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, node_name)) {
LIBCOMM_PTHREAD_RWLOCK_RDLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
// remote_version is the cmailbox version, consumer use it to check.
ret = gs_push_local_buffer(streamid, message, need_send_len, remote_version);
LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock);
if (ret < 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
gs_s_close_logic_connection(pmailbox, errno, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
if (IS_NOTIFY_REMOTE(errno)) {
notify_remote = true;
}
MAILBOX_ELOG(
pmailbox, WARNING, "(s|send)\tFailed to push local cmailbox, error[%d]:%s.", errno, mc_strerror(errno));
ret = -1;
goto return_result;
}
} else {
LibcommSendInfo send_info;
send_info.socket = fd_id.fd;
send_info.socket_id = fd_id.id;
send_info.node_idx = node_idx;
send_info.streamid = streamid;
send_info.version = remote_version;
send_info.msg = message;
send_info.msg_len = need_send_len;
do {
ret = g_libcomm_adapt.send_data(&send_info);
// Maybe no socket buffer in kernel or get EAGAIN, we do retry
if (ret == 0) {
(void)usleep(100);
}
} while (ret == 0);
// if we failed to send message, we will close and rebuild the socket, but the current query will fail
if (ret < 0) {
MAILBOX_ELOG(pmailbox,
WARNING,
"(s|send)\tFailed to send data message on socket[%d], error[%d]:%s.",
g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket,
errno,
mc_strerror(errno));
errno = ECOMMSCTPSCTPSND;
ret = -1;
goto return_result;
}
if (is_tcp_mode() && g_ackchk_time > 0) {
recv_ackchk_msg(send_info.socket);
}
}
// step 4: if we send successfully, change the state and quota of the stream
//
sent_size = ret;
send_end = COMM_STAT_TIME();
StreamTimeOSSendEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
COMM_TIMER_LOG("(s|send)\tSend message end for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
// the mailbox is closed or reused by other query, we should return error
if (gs_check_mailbox(pmailbox->local_version, local_version) == false) {
errno = pmailbox->close_reason;
COMM_DEBUG_LOG("(s|send)\tStream has already closed by remote:%s, detail:%s.",
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx),
mc_strerror(errno));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
ret = -1;
goto return_result;
}
// this should not happen
LIBCOMM_ASSERT((bool)(pmailbox->bufCAP >= (unsigned long)sent_size), node_idx, streamid, ROLE_PRODUCER);
// update quota
if (g_instance.comm_cxt.quota_cxt.g_having_quota) {
pmailbox->bufCAP -= sent_size;
}
if (pmailbox->state != MAIL_RUN) {
LIBCOMM_ELOG(WARNING, "(s|send)\tsend state is wrong[%d].", pmailbox->state);
}
/* update the statistic information of the mailbox */
if (pmailbox->statistic != NULL) {
if (pmailbox->statistic->first_send_time == 0) {
pmailbox->statistic->first_send_time = send_start;
/* first time, calculate time difference between connect finish time and first send time */
pmailbox->statistic->producer_elapsed_time += (uint32)ABS_SUB(time_enter, pmailbox->statistic->start_time);
} else {
/* other time, calculate time difference between last send time and this send time */
pmailbox->statistic->producer_elapsed_time +=
(uint32)ABS_SUB(time_enter, t_thrd.comm_cxt.g_producer_process_duration);
}
pmailbox->statistic->last_send_time = send_start;
pmailbox->statistic->wait_quota_overhead += (uint32)ABS_SUB(wait_quota_end, wait_quota_start);
pmailbox->statistic->os_send_overhead += ABS_SUB(send_end, send_start);
time_now = COMM_STAT_TIME();
pmailbox->statistic->total_send_time += ABS_SUB(time_now, time_enter);
pmailbox->statistic->send_bytes += (uint64)sent_size;
pmailbox->statistic->call_send_count++;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
COMM_DEBUG_LOG("(s|send)\tSend to node[%d]:%s on stream[%d] with msg:%c, m_len[%d] @ bufCAP[%lu].",
node_idx,
g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename,
streamid,
message[0],
sent_size,
pmailbox->bufCAP);
// step 5: send successfully, reture the sent size
//
COMM_TIMER_LOG("(s|send)\tSend finish for node[%d,%d]:%s.",
node_idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
return_result:
if (notify_remote) {
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER);
}
/* Do not check interruption to maintain the atomicity of data sending */
LIBCOMM_INTERFACE_END(false, false);
t_thrd.int_cxt.ImmediateInterruptOK = TempImmediateInterruptOK;
t_thrd.comm_cxt.g_producer_process_duration = time_now;
pgstat_report_waitstatus_phase(oldPhase);
return ret;
} // gs_send
/*
* @Description: send the message to all connection in address info list.
* @IN libcomm_addr_head: the head of address info list.
* @IN message: data.
* @IN m_len: data len.
* @Return: -1: broadcast send failed.
* m_len: broadcast send succsessed.
* @See also:
*/
int gs_broadcast_send(struct libcommaddrinfo* libcomm_addr_head, char* message, int m_len, int time_out)
{
if ((libcomm_addr_head == NULL) || (message == NULL) || (m_len <= 0)) {
LIBCOMM_ELOG(WARNING,
"(s|send)\tInvalid argument: %s%s, len=%d.",
libcomm_addr_head == NULL ? "addr list is NULL, " : "",
message == NULL ? "message is NULL, " : "",
m_len);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
int i = 0;
int send_re = -1;
int wait_quota = 0;
if (time_out == -1) {
time_out = WAITQUOTA_TIMEOUT;
}
struct libcommaddrinfo* addr_info = libcomm_addr_head;
int addr_num = libcomm_addr_head->addr_list_size;
int error_conn_count = 0;
struct libcommaddrinfo* wait_addr_info = NULL;
struct p_mailbox* wait_quota_pmailbox = NULL;
if (addr_num == 0) {
LIBCOMM_ELOG(WARNING, "(s|broad case)\tNo one need send in addr list.");
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
COMM_TIMER_INIT();
COMM_DEBUG_LOG("(s|broad case)\tStart to send to %d datanodes, msg_len=%d.", addr_num, m_len);
// send to all connections that needed send
COMM_TIMER_LOG("(s|broad case)\tBroad cast send start.");
// regist current thread id, so we can wait in poll when no data arrived
if (gs_poll_create() != 0) {
LIBCOMM_ELOG(WARNING, "(r|wait poll)\tFailed to malloc for create poll!");
return -1;
}
wait_quota = 0;
error_conn_count = 0;
addr_info = libcomm_addr_head;
for (i = 0; i < addr_num; i++) {
// this connection need to send
if (addr_info->status == BROADCAST_NEED_SEND) {
// really send without waiting quota
send_re = gs_send(&addr_info->gs_sock, message, m_len, time_out, FALSE);
// this connection send successed
if (send_re > 0) {
addr_info->status = BROADCAST_SEND_FINISH;
} else if (send_re == BROADCAST_WAIT_QUOTA) { // this connection need wait quota
addr_info->status = BROADCAST_WAIT_QUOTA;
wait_quota_pmailbox = &P_MAILBOX(addr_info->gs_sock.idx, addr_info->gs_sock.sid);
wait_quota = 1;
wait_addr_info = addr_info;
COMM_DEBUG_LOG("(s|broad case)\tNeed wait quota for %s.", addr_info->nodename);
} else if (send_re == 0) { // we should return and CHECK_FOR_INTERRUPT
LIBCOMM_ELOG(WARNING, "(s|broad case)\trecv EINTR.");
goto clean_return;
} else if (send_re < 0) { // this connection send failed, continue to send other connecions
// close this connection in libcomm side
(void)gs_s_close_stream(&addr_info->gs_sock);
addr_info->gs_sock = GS_INVALID_GSOCK;
// close this connection in app side
addr_info->sctp_port = 0;
addr_info->status = BROADCAST_CONNECT_CLOSED;
// set error connection flag
error_conn_count++;
COMM_DEBUG_LOG("(s|broad case)\tFail to send to %s.", addr_info->nodename);
}
} else if (addr_info->status == BROADCAST_CONNECT_CLOSED) {
error_conn_count++;
}
addr_info = addr_info->addr_list_next;
}
// all connections that needed send has already closed, broadcast send failed!
if (error_conn_count == addr_num) {
COMM_DEBUG_LOG("(s|broad case)\tFail to send to all connection.");
COMM_TIMER_LOG("(s|broad case)\tStart broad cast error.");
libcomm_addr_head->addr_list_size = 0;
return -1;
}
// some connections need wait quota
if (wait_quota == 1) {
StreamTimeWaitQuotaStart(t_thrd.pgxc_cxt.GlobalNetInstr);
COMM_DEBUG_LOG("(s|broad case)\tWait quota start.");
if (wait_addr_info != NULL) {
pgstat_report_waitstatus_comm(STATE_WAIT_FLUSH_DATA,
wait_addr_info->nodeIdx,
-1,
u_sess->stream_cxt.producer_obj->getParentPlanNodeId(),
global_node_definition ? global_node_definition->num_nodes : -1);
}
COMM_TIMER_LOG("(s|broad case)\tWait quota start.");
uint64 wait_quota_start = mc_timers_ms();
(void)gs_poll(time_out);
uint64 wait_quota_end = mc_timers_ms();
COMM_TIMER_LOG("(s|broad case)\tWait quota end.");
// add wait quota overhead time
LIBCOMM_PTHREAD_MUTEX_LOCK(&wait_quota_pmailbox->sinfo_lock);
COMM_STAT_CALL(wait_quota_pmailbox,
wait_quota_pmailbox->statistic->wait_quota_overhead += ABS_SUB(wait_quota_end, wait_quota_start));
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&wait_quota_pmailbox->sinfo_lock);
StreamTimeWaitQuotaEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
// we should return and CHECK_FOR_INTERRUPT
goto clean_return;
}
addr_info = libcomm_addr_head;
while (addr_info != NULL) {
// if this connection send finish, ready to send next data package
if (addr_info->status == BROADCAST_SEND_FINISH) {
addr_info->status = BROADCAST_NEED_SEND;
}
addr_info = addr_info->addr_list_next;
}
COMM_TIMER_LOG("(s|broad case)\tBroad cast send end.");
// all connection send successed, exception error connections
return m_len;
clean_return:
addr_info = libcomm_addr_head;
for (i = 0; i < addr_num; i++) {
if (addr_info->status == BROADCAST_WAIT_QUOTA) {
addr_info->status = BROADCAST_NEED_SEND;
wait_quota_pmailbox = &P_MAILBOX(addr_info->gs_sock.idx, addr_info->gs_sock.sid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&wait_quota_pmailbox->sinfo_lock);
if (true == gs_check_mailbox(wait_quota_pmailbox->local_version, addr_info->gs_sock.ver)) {
wait_quota_pmailbox->semaphore = NULL;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&wait_quota_pmailbox->sinfo_lock);
}
addr_info = addr_info->addr_list_next;
}
return 0;
}
/*
* function name : gs_r_send_start_ctrl_msg
* description : consumer send local thread id to producer,
* it is means consumer start to receive data,
* so we also send quota to producer.
* notice : we must get mailbox lock before.
* arguments :
* _in_ cmailbox: logic conntion info.
* return value :
* false: failed.
* true : succeed.
*/
static bool gs_r_form_start_ctrl_msg(c_mailbox* cmailbox, FCMSG_T* msg)
{
pid_t local_tid = t_thrd.comm_cxt.MyPid;
errno_t ss_rc;
uint32 cpylen;
if (cmailbox->query_id != DEBUG_QUERY_ID) {
cmailbox->query_id = DEBUG_QUERY_ID;
}
// send local thread id and quota to remote
if (local_tid != cmailbox->local_thread_id) {
int node_idx = cmailbox->idx;
int streamid = cmailbox->streamid;
long add_quota = 0;
if (g_instance.comm_cxt.quota_cxt.g_having_quota) {
add_quota = gs_add_quota_size(cmailbox);
}
// change local stream state and quota first
cmailbox->local_thread_id = local_tid;
cmailbox->bufCAP += add_quota;
cmailbox->state = MAIL_RUN;
// then change remote stream state and quota
msg->type = CTRL_PEER_TID;
msg->node_idx = node_idx;
msg->streamid = streamid;
msg->streamcap = add_quota;
msg->version = cmailbox->remote_version;
msg->extra_info = cmailbox->local_thread_id;
msg->query_id = cmailbox->query_id;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
msg->nodename[cpylen] = '\0';
return true;
}
return false;
} // gs_r_send_start_ctrl_msg
// Consumer thread (at executor) use gs_wait_poll for waiting for data,
// just like waiting data from network using system function poll()
//
int gs_wait_poll(gsocket* gs_sock_array, // array of producers node index
int nproducer, // number of producers
int* producer, // producers number triggers poll
int timeout, // time out in seconds, -1 for block mode
bool close_expected) // is logic connection closed by remote is an expected result
{
if ((gs_sock_array == NULL) || (producer == NULL) || (nproducer <= 0)) {
LIBCOMM_ELOG(WARNING,
"(r|wait poll)\tInvalid argument: %s%s"
"nproducer=%d.",
gs_sock_array == NULL ? "gs_sock_array is NULL, " : "",
producer == NULL ? "producer is NULL, " : "",
nproducer);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
if (0 != gs_poll_create()) {
LIBCOMM_ELOG(WARNING, "(r|wait poll)\tPoll create failed! Detail:%s.", mc_strerror(errno));
return -1;
}
// step 1: initialize local variables
//
int i = 0;
int n_got_data = 0;
int first_cycle = 1;
int ret = 0;
int idx = -1;
int streamid = -1;
int version = -1;
// if waked by other threads, the flag is set to 1
int poll_error_flag = 0;
struct c_mailbox* cmailbox = NULL;
uint64 wait_lock_start = 0;
uint64 wait_data_time = 0;
uint64 wait_data_start = 0;
uint64 wait_data_end = 0;
uint64 time_enter = COMM_STAT_TIME();
uint64 time_now = time_enter;
bool send_msg = false;
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
errno = 0;
struct FCMSG_T fcmsgs = {0x0};
COMM_TIMER_INIT();
COMM_TIMER_LOG("(r|wait poll)\tStart timer log.");
for (;;) {
wait_lock_start = COMM_STAT_TIME();
// step 2: check if there is data in the c_mailbox of the given node index and stream index already
//
for (i = 0; i < nproducer; i++) {
idx = gs_sock_array[i].idx;
streamid = gs_sock_array[i].sid;
version = gs_sock_array[i].ver;
Assert(idx >= 0 && idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num && streamid > 0 &&
streamid < g_instance.comm_cxt.counters_cxt.g_max_stream_num);
// get the cmailbox then lock it
cmailbox = &C_MAILBOX(idx, streamid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
// check the state of the mailbox is correct
// ret -2 means close by remote
if (false == gs_check_mailbox(cmailbox->local_version, version)) {
if (!close_expected) {
MAILBOX_ELOG(cmailbox,
WARNING,
"(r|wait poll)\tStream has already closed, detail:%s.",
mc_strerror(cmailbox->close_reason));
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
errno = cmailbox->close_reason;
// set the error flag for Consumer thread
producer[i] = WAIT_POLL_FLAG_ERROR;
ret = -2;
goto return_result;
}
// there is data in the mailbox already
if (cmailbox->buff_q->count > 0) {
n_got_data++;
// set the having label for Consumer thread
producer[i] = WAIT_POLL_FLAG_GOT;
}
/* update the statistic information of the mailbox */
if (cmailbox->statistic != NULL) {
wait_data_time = ABS_SUB(wait_data_end, wait_data_start);
cmailbox->statistic->wait_data_time += wait_data_time;
time_now = COMM_STAT_TIME();
cmailbox->statistic->wait_lock_time += ABS_SUB(time_now, wait_lock_start);
if (cmailbox->statistic != NULL && cmailbox->statistic->first_poll_time == 0) {
cmailbox->statistic->first_poll_time = time_enter;
cmailbox->statistic->consumer_elapsed_time += ABS_SUB(time_enter, cmailbox->statistic->start_time);
} else {
cmailbox->statistic->consumer_elapsed_time +=
ABS_SUB(time_enter, t_thrd.comm_cxt.g_consumer_process_duration);
}
if (first_cycle) {
cmailbox->statistic->call_poll_count++;
cmailbox->statistic->last_poll_time = time_enter;
}
if (n_got_data > 0 || poll_error_flag == 1) {
cmailbox->statistic->total_poll_time += ABS_SUB(time_now, time_enter);
}
}
// need return
if (n_got_data > 0 || poll_error_flag == 1) {
/* gs_wait_poll will return, clean semaphore */
cmailbox->semaphore = NULL;
if (producer[i] == WAIT_POLL_FLAG_WAIT) {
producer[i] = WAIT_POLL_FLAG_IDLE;
}
} else {
/* gs_wait_poll will enter gs_poll, regist semaphore */
cmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore;
producer[i] = WAIT_POLL_FLAG_WAIT;
}
// send local thread id and quota to remote
send_msg = gs_r_form_start_ctrl_msg(cmailbox, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
// send local thread id and quota to remote without cmailbox lock
if (send_msg && (gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER) <= 0)) {
errno = ECOMMSCTPTCPDISCONNECT;
ret = -1;
goto return_result;
}
}
first_cycle = 0;
// step 3: if data is found, return the number of mailboxes which have data
// or, if it is waked up here but no data found, there must be interruption, we should return and
// CHECK_FOR_INTERRUPT
//
if (n_got_data > 0) {
COMM_TIMER_LOG("(r|wait poll)\tGet data for node[%d,%d]:%s.",
idx,
streamid,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx));
ret = n_got_data;
goto return_result;
} else if (poll_error_flag == 1) {
COMM_DEBUG_LOG("(r|wait poll)\tWaked up but no data.");
errno = ECOMMSCTPWAITPOLLERROR;
ret = -1;
goto return_result;
}
// step 4: if no data found and waked up flag is not set, we should wait on CV
//
int pool_re = 0;
int time_out = -1;
if (timeout > 0) {
time_out = timeout;
}
wait_data_start = COMM_STAT_TIME();
COMM_TIMER_LOG("(r|wait poll)\tWait poll start.");
pool_re = gs_poll(time_out);
COMM_TIMER_LOG("(r|wait poll)\tWait poll end.");
wait_data_end = COMM_STAT_TIME();
// step 5: if it is waked up normal, return 0, then receive again
// it is used to deal with cancel and interruption
//
if (pool_re == 0) {
/* we set the return value to 0, the caller will check this return value
* if there is no interrupt, the caller will call this function again
*/
if (InterruptPending || t_thrd.int_cxt.ProcDiePending) {
ret = 0;
goto return_result;
} else if (t_thrd.int_cxt.ProcDiePending) {
/* t_thrd.int_cxt.ProcDiePending when process startup packet return -1 then thread will exit */
ret = -1;
} else {
continue;
}
}
// if poll has already closed, we continue to check closed mailbox
if (-1 == pool_re) {
poll_error_flag = 1;
continue;
}
// timed out, in fact, we do not set the timeout now !!!
if (pool_re == ETIMEDOUT) {
LIBCOMM_ELOG(WARNING, "(r|wait poll)\tFailed to wait for notify, timeout:%ds.", time_out);
errno = ECOMMSCTPEPOLLTIMEOUT;
} else { // waked up abnormal, unknown error
LIBCOMM_ELOG(WARNING, "(r|wait poll)\tFailed to wait poll, detail: %s.", mc_strerror(errno));
errno = ECOMMSCTPWAITPOLLERROR;
}
ret = -1;
goto return_result;
}
return_result:
for (i = 0; i < nproducer; i++) {
if (producer[i] == WAIT_POLL_FLAG_WAIT) {
producer[i] = WAIT_POLL_FLAG_IDLE;
cmailbox = &C_MAILBOX(gs_sock_array[i].idx, gs_sock_array[i].sid);
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (true == gs_check_mailbox(cmailbox->local_version, gs_sock_array[i].ver)) {
cmailbox->semaphore = NULL;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
}
}
// ret 0 means poll is waked unexpected, so we check interruption
LIBCOMM_INTERFACE_END((ret == 0), TempImmediateInterruptOK);
t_thrd.comm_cxt.g_consumer_process_duration = COMM_STAT_TIME();
return ret;
} // gs_wait_poll
/* Handle the same message and print message receiving and sending log. */
void gs_comm_ipc_print(MessageIpcLog *ipc_log, char *remotenode, gsocket *gs_sock, CommMsgOper msg_oper)
{
/* if the same number of messages is greater than 1, print this message. */
if (ipc_log->last_msg_count > 1) {
ereport(LOG,
(errmsg("(%s) comm_ipc_log, msgtype:%c, total_len:%d, msg_count:%d, node:%s, last msg time:%s.",
msg_oper_string(msg_oper), ipc_log->last_msg_type, ipc_log->last_msg_len,
ipc_log->last_msg_count, remotenode, ipc_log->last_msg_time)));
}
/* print current message */
if ((gs_sock != NULL && gs_sock->idx == 0 && gs_sock->sid == 0) || gs_sock == NULL) {
ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s.",
msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len, remotenode)));
} else {
ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s[nid:%d,sid:%d].",
msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len,
remotenode, gs_sock->idx, gs_sock->sid)));
}
}
void handle_message(MessageIpcLog *ipc_log, void *ptr, int n, char *remotenode,
gsocket *gs_sock, CommMsgOper msg_oper)
{
int offset;
int rc;
int i = n;
char *tmp = NULL;
while (i > 0) {
offset = 0;
/* step1.parse type */
if (ipc_log->type == 0) {
ipc_log->type = ((char*)ptr)[0];
offset = 1;
} else if (ipc_log->len_cursor < IPC_MSG_LEN) {
/* step2.parse length complete length */
if (i >= IPC_MSG_LEN - ipc_log->len_cursor) {
offset = IPC_MSG_LEN - ipc_log->len_cursor;
} else {
/* partial length(that is less than 4 bytes) */
offset = i;
}
rc = memcpy_s((char*)(&(ipc_log->len_cache)) + ipc_log->len_cursor, IPC_MSG_LEN, (char*)ptr, offset);
securec_check(rc, "\0", "\0");
ipc_log->len_cursor += offset;
/* parse length */
if (ipc_log->len_cursor == IPC_MSG_LEN) {
ipc_log->msg_len = (int) ntohl(ipc_log->len_cache) - IPC_MSG_LEN;
}
} else if (ipc_log->msg_cursor <= ipc_log->msg_len) {
/* step3.parse msg compose a complete message */
if (i >= ipc_log->msg_len - ipc_log->msg_cursor) {
offset = ipc_log->msg_len - ipc_log->msg_cursor;
/* save the last message information and do not print the log */
if (ipc_log->last_msg_type == ipc_log->type) {
ipc_log->last_msg_count++;
ipc_log->last_msg_len += ipc_log->msg_len;
comm_ipc_log_get_time(ipc_log->last_msg_time, MSG_TIME_LEN);
} else {
/* new message, print the current message and the previous message log */
gs_comm_ipc_print(ipc_log, remotenode, gs_sock, msg_oper);
ipc_log->last_msg_count = 1;
ipc_log->last_msg_type = ipc_log->type;
ipc_log->last_msg_len = ipc_log->msg_len;
}
/* restored data structure */
ipc_log->type = 0;
ipc_log->msg_cursor = 0;
ipc_log->msg_len = 0;
ipc_log->len_cursor = 0;
ipc_log->len_cache = 0;
} else {
offset = i;
ipc_log->msg_cursor += offset;
}
}
i -= offset;
tmp = (char *)ptr;
tmp += offset;
ptr = (void*)tmp;
}
return;
}
/* Send and receive data between nodes, for performance problem location.
* A complete message consists of three parts, 1 byte type + 4 bytes length + contents.
* 'ptr' may contain several messages, parse the 'ptr' and print the log.
*/
MessageCommLog* gs_comm_ipc_performance(MessageCommLog *msgLog, void *ptr, int n,
char *remotenode, gsocket *gs_sock, CommMsgOper msg_oper)
{
MemoryContext oldcontext = MemoryContextSwitchTo(u_sess->top_mem_cxt);
MessageIpcLog *ipc_log = NULL;
if (msgLog == NULL) {
msgLog = (MessageCommLog*) palloc0(sizeof(MessageCommLog));
if (msgLog == NULL) {
MemoryContextSwitchTo(oldcontext);
return NULL;
}
}
/* initializing variable */
if (msg_oper == SEND_SOME || msg_oper == SECURE_WRITE) {
ipc_log = &(msgLog->send_ipc_log);
} else if (msg_oper == SECURE_READ || msg_oper == READ_DATA || msg_oper == READ_DATA_FROM_LOGIC) {
ipc_log = &(msgLog->recv_ipc_log);
}
handle_message(ipc_log, ptr, n, remotenode, gs_sock, msg_oper);
MemoryContextSwitchTo(oldcontext);
return msgLog;
}
// shutdown the communication layer
//
void gs_shutdown_comm()
{
if (g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested == true) {
return;
}
LIBCOMM_ELOG(LOG, "Communication layer will be shutdown.");
g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested = true;
}
// cancel request for receiver (called by die() or StatementCancelHandler() in postgresMain )
//
void gs_r_cancel()
{
// use g_cancel_requested save DEBUG_QUERY_ID as a flag
g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested =
(t_thrd.proc_cxt.MyProcPid != 0) ? t_thrd.proc_cxt.MyProcPid : 1;
}
// receiver close logic stream, call by Consumer thread
// when no data need or all data are received, or error happed
//
static int gs_r_close_stream(gsocket* gsock)
{
int node_idx = gsock->idx;
int stream_idx = gsock->sid;
int version = gsock->ver;
int type = gsock->type;
struct FCMSG_T fcmsgs = {0x0};
if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) ||
(stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_PRODUCER)) {
COMM_DEBUG_LOG("(r|cls stream)\tInvalid argument: "
"node idx[%d], stream id[%d], type[%d].",
node_idx,
stream_idx,
type);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
// step 1: get the mailbox and check the state of the cmailbox
// if it is closed, delete the entry in the hash table (g_r_htab_nodeid_skey_to_stream)
//
struct c_mailbox* cmailbox = &(C_MAILBOX(node_idx, stream_idx));
if (cmailbox->state == MAIL_CLOSED) {
return 0;
}
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
// if it was closed or reused, we need do nothing here,
// but we will do close poll and delete the entry for sure,
// there is no side effect
if (gs_check_mailbox(cmailbox->local_version, version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
return 0;
}
// step 2: reset the cmailbox, close poll and delete the entry in hash table (g_r_htab_nodeid_skey_to_stream)
//
COMM_DEBUG_LOG("(r|cls stream)\tTo close stream[%d] for node[%d]:%s.",
stream_idx,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx));
gs_r_close_logic_connection(cmailbox, ECOMMSCTPAPPCLOSE, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
// Send close ctrl msg to remote without cmailbox lock
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER);
return 0;
} // gs_r_close_stream
// sender close logic stream, call by Producer thread when it failed to send data
//
static int gs_s_close_stream(gsocket* gsock)
{
int node_idx = gsock->idx;
int stream_idx = gsock->sid;
int version = gsock->ver;
int type = gsock->type;
struct FCMSG_T fcmsgs = {0x0};
if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) ||
(stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_CONSUMER)) {
COMM_DEBUG_LOG("(s|cls stream)\tInvalid argument: "
"node idx[%d], stream id[%d], type[%d].",
node_idx,
stream_idx,
type);
errno = ECOMMSCTPARGSINVAL;
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
// step 1: get the mailbox and check the state of the cmailbox,
// if the keys of the pmailbox is not matched, return error
//
struct p_mailbox* pmailbox = &P_MAILBOX(node_idx, stream_idx);
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (gs_check_mailbox(pmailbox->local_version, version) == false) {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
return 0;
}
// step 2: reset the state of the pmailbox
//
COMM_DEBUG_LOG("(s|cls stream)\tTo close stream[%d] for node[%d]:%s.",
stream_idx,
node_idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx));
gs_s_close_logic_connection(pmailbox, ECOMMSCTPAPPCLOSE, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
// Send close ctrl msg to remote without pmailbox lock
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER);
return 0;
}
// close logic socket, it will return when the sock type is invalid.
//
void gs_close_gsocket(gsocket* gsock)
{
int type = gsock->type;
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK;
t_thrd.int_cxt.ImmediateInterruptOK = false;
if (type == GSOCK_INVALID) {
LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK);
return;
}
if (type == GSOCK_DAUL_CHANNEL || type == GSOCK_PRODUCER) {
(void)gs_s_close_stream(gsock);
}
if (type == GSOCK_DAUL_CHANNEL || type == GSOCK_CONSUMER) {
(void)gs_r_close_stream(gsock);
}
*gsock = GS_INVALID_GSOCK;
LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK);
return;
}
bool gs_stop_query(gsocket* gsock, uint32 remote_pid)
{
struct FCMSG_T fcmsgs = {0x0};
int rc;
errno_t ss_rc;
uint32 cpylen;
fcmsgs.type = CTRL_STOP_QUERY;
fcmsgs.node_idx = gsock->idx;
fcmsgs.streamid = gsock->sid;
fcmsgs.version = gsock->ver;
fcmsgs.query_id = DEBUG_QUERY_ID;
fcmsgs.extra_info = remote_pid;
cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN);
ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN);
securec_check(ss_rc, "\0", "\0");
ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1);
securec_check(ss_rc, "\0", "\0");
fcmsgs.nodename[cpylen] = '\0';
rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[gsock->idx], &fcmsgs, ROLE_CONSUMER);
return (rc > 0);
}
/*
* check if the OS is RELIABLE for SCTP protocol
* we do not support SUSE linux who's kernel version under 2.6.32.22
*/
int gs_check_SLESSP2_version()
{
return mc_check_SLESSP2_version();
}
/* check if the OS support SCTP */
int gs_check_sctp_support()
{
return mc_check_sctp_support();
}
/* get the error information of communication layer */
const char* gs_comm_strerror()
{
return mc_strerror(errno);
}
/* get communication layer stream status at receiver end as a tuple for pg_comm_stream_status */
bool get_next_recv_stream_status(CommRecvStreamStatus* stream_status)
{
int idx, sid;
uint32 time_now = (uint32)mc_timers_ms();
uint64 run_time = 0;
struct c_mailbox* cmailbox = NULL;
/* if node index is invalid or stream index is invalid, return false */
if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num ||
stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) {
return false;
}
/* traves all mailbox and get the stream status in C_MAILBOX. */
for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) {
for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
/* do not need return the closed stream status. */
cmailbox = &C_MAILBOX(idx, sid);
if (cmailbox->state != MAIL_CLOSED) {
stream_status->idx = cmailbox->idx;
stream_status->stream_id = cmailbox->streamid;
stream_status->stream_state = stream_stat_string(cmailbox->state);
stream_status->quota_size = cmailbox->bufCAP;
stream_status->query_id = cmailbox->query_id;
stream_status->stream_key = cmailbox->stream_key;
stream_status->buff_usize = cmailbox->buff_q->u_size;
stream_status->bytes = cmailbox->statistic ? cmailbox->statistic->recv_bytes : 0;
stream_status->local_thread_id = cmailbox->local_thread_id;
stream_status->peer_thread_id = cmailbox->peer_thread_id;
stream_status->time = cmailbox->statistic ? (uint64)(time_now - cmailbox->statistic->start_time) : 0;
run_time = (stream_status->time > 0) ? stream_status->time : 1;
stream_status->speed = stream_status->bytes * 1000 / run_time;
stream_status->tcp_sock = g_instance.comm_cxt.g_r_node_sock[idx].ctrl_tcp_sock;
errno_t ss_rc;
ss_rc = strcpy_s(
stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_host);
securec_check(ss_rc, "\0", "\0");
ss_rc = strcpy_s(
stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename);
securec_check(ss_rc, "\0", "\0");
return true;
}
}
/* set stream index to -1 for next node */
stream_status->stream_id = -1;
}
return false;
}
/* get communication layer stream status at sender end as a tuple for pg_comm_send_stream */
bool get_next_send_stream_status(CommSendStreamStatus* stream_status)
{
int idx, sid;
uint32 time_now = (uint32)mc_timers_ms();
uint64 run_time = 0;
struct p_mailbox* pmailbox = NULL;
/* if node index is invalid or stream index is invalid, return false */
if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num ||
stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) {
return false;
}
/* traves all mailbox and get the stream status in P_MAILBOX. */
for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) {
for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
/* no need return the closed stream status */
pmailbox = &P_MAILBOX(idx, sid);
if (pmailbox->state != MAIL_CLOSED) {
stream_status->idx = pmailbox->idx;
stream_status->stream_id = pmailbox->streamid;
stream_status->stream_state = stream_stat_string(pmailbox->state);
stream_status->quota_size = pmailbox->bufCAP;
stream_status->query_id = pmailbox->query_id;
stream_status->stream_key = pmailbox->stream_key;
stream_status->bytes = pmailbox->statistic ? pmailbox->statistic->send_bytes : 0;
stream_status->wait_quota = pmailbox->statistic ? (uint64)pmailbox->statistic->wait_quota_overhead : 0;
stream_status->local_thread_id = pmailbox->local_thread_id;
stream_status->peer_thread_id = pmailbox->peer_thread_id;
stream_status->time = pmailbox->statistic ? (uint64)(time_now - pmailbox->statistic->start_time) : 0;
stream_status->tcp_sock = g_instance.comm_cxt.g_s_node_sock[idx].ctrl_tcp_sock;
run_time = (stream_status->time > 0) ? stream_status->time : 1;
stream_status->speed = stream_status->bytes * 1000 / run_time;
errno_t ss_rc;
ss_rc = strcpy_s(
stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_host);
securec_check(ss_rc, "\0", "\0");
ss_rc = strcpy_s(
stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_nodename);
securec_check(ss_rc, "\0", "\0");
return true;
}
}
/* set stream index to -1 for next node */
stream_status->stream_id = -1;
}
return false;
}
/*
* function name : get_next_comm_delay_info
* description : get libcomm delay info with delay_info->idx .
* arguments : _in_ delay_info->idx: the node index.
* _out_ delay_info: return delay info
* return value :
* true: return delay info.
* false: no delay info.
*/
bool get_next_comm_delay_info(CommDelayInfo* delay_info)
{
int node_idx = delay_info->idx;
int array_idx = -1;
uint32 delay = 0;
uint32 delay_min = 0;
uint32 delay_max = 0;
uint32 delay_sum = 0;
g_instance.comm_cxt.g_delay_survey_switch = true;
/* if node index is invalid, return false */
if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
return false;
}
for (;;) {
if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id == 0) {
node_idx++;
} else {
break;
}
if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
return false;
}
}
/* calculate delay info */
for (array_idx = 0; array_idx < MAX_DELAY_ARRAY_INDEX; array_idx++) {
delay = g_instance.comm_cxt.g_delay_info[node_idx].delay[array_idx];
if (delay < delay_min || delay_min == 0) {
delay_min = delay;
}
if (delay > delay_max) {
delay_max = delay;
}
delay_sum += delay;
}
/* save delay info in delay_info */
errno_t ss_rc;
ss_rc = strcpy_s(delay_info->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host);
securec_check(ss_rc, "\0", "\0");
ss_rc = strcpy_s(delay_info->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename);
securec_check(ss_rc, "\0", "\0");
delay_info->stream_num =
g_instance.comm_cxt.counters_cxt.g_max_stream_num - g_instance.comm_cxt.g_usable_streamid[node_idx].count - 1;
delay_info->min_delay = delay_min;
delay_info->dev_delay = delay_sum / MAX_DELAY_ARRAY_INDEX;
delay_info->max_delay = delay_max;
/* move to next node index */
delay_info->idx = node_idx + 1;
return true;
}
/* get communication layer status as a tuple for pg_comm_status */
bool gs_get_comm_stat(CommStat* comm_stat)
{
if (comm_stat == NULL || g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0) {
return false;
}
if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL) {
comm_stat->postmaster = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[POSTMASTER];
}
if (g_instance.attr.attr_storage.comm_cn_dn_logic_conn == false && IS_PGXC_COORDINATOR) {
return true;
}
int idx, sid, i;
int used_stream = 0;
struct c_mailbox* cmailbox = NULL;
struct p_mailbox* pmailbox = NULL;
const int G_CUR_NODE_NUM = g_instance.comm_cxt.counters_cxt.g_cur_node_num;
int *libcomm_used_rate = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate;
long recv_bytes[G_CUR_NODE_NUM] = {0};
int recv_count[G_CUR_NODE_NUM] = {0};
int recv_count_speed = 0;
long recv_speed = 0;
long send_speed = 0;
long send_bytes[G_CUR_NODE_NUM] = {0};
int send_count[G_CUR_NODE_NUM] = {0};
int send_count_speed = 0;
if (libcomm_used_rate != NULL) {
comm_stat->postmaster = libcomm_used_rate[POSTMASTER];
comm_stat->gs_sender_flow = libcomm_used_rate[GS_SEND_flow];
comm_stat->gs_receiver_flow = libcomm_used_rate[GS_RECV_FLOW];
comm_stat->gs_receiver_loop = libcomm_used_rate[GS_RECV_LOOP];
for (i = GS_RECV_LOOP + 1; i < g_instance.comm_cxt.counters_cxt.g_recv_num + GS_RECV_LOOP; i++) {
if (comm_stat->gs_receiver_loop < libcomm_used_rate[i]) {
comm_stat->gs_receiver_loop = libcomm_used_rate[i];
}
}
}
/* sum of recv_speed/send_speed in all stream */
i = 0;
while (i < 2) {
/* idx: node index */
for (idx = 0; idx < G_CUR_NODE_NUM; idx++) {
if (i == 0) {
recv_bytes[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes;
recv_count[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count;
send_bytes[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes;
send_count[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count;
/* sid: stream index */
for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
cmailbox = &C_MAILBOX(idx, sid);
if (cmailbox->state != MAIL_CLOSED) {
comm_stat->buffer += cmailbox->buff_q->u_size;
}
pmailbox = &P_MAILBOX(idx, sid);
if (pmailbox->state != MAIL_CLOSED) {
used_stream++;
}
}
} else if (i == 1) {
recv_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes - recv_bytes[idx];
recv_count_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count - recv_count[idx];
send_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes - send_bytes[idx];
send_count_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count - send_count[idx];
}
}
i++;
usleep(100000);
}
comm_stat->recv_speed = recv_speed * 10 / 1024;
comm_stat->recv_count_speed = recv_count_speed * 10;
comm_stat->send_speed = send_speed * 10 / 1024;
comm_stat->send_count_speed = send_count_speed * 10;
comm_stat->mem_libcomm = libcomm_used_memory;
comm_stat->mem_libpq = libpq_used_memory;
comm_stat->stream_conn_num = used_stream;
return true;
}
/* Output the contents of structure into log file */
void gs_log_comm_status()
{
LIBCOMM_ELOG(LOG, "[LOG STATUS]Comm Layer Status: Do nothing now, please ignore it.");
}
/* release memory of communication layer, just for LLT */
int gs_release_comm_memory()
{
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
gs_r_release_comm_memory();
return 0;
}
int gs_close_all_stream_by_debug_id(uint64 query_id)
{
int idx, sid;
struct c_mailbox* cmailbox = NULL;
struct p_mailbox* pmailbox = NULL;
int cmailbox_count = 0;
int pmailbox_count = 0;
struct FCMSG_T fcmsgs = {0x0};
if (query_id == 0) {
LIBCOMM_ELOG(WARNING, "(cls all stream)\tInvalid argument: query id is 0!");
return -1;
}
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
for (idx = 0; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) {
for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) {
cmailbox = &C_MAILBOX(idx, sid);
if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock);
if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0 &&
cmailbox->state != MAIL_CLOSED) {
cmailbox_count++;
gs_r_close_logic_connection(cmailbox, ECOMMSCTPAPPCLOSE, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
/* Send close ctrl msg to remote without pmailbox lock */
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER);
} else {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock);
}
}
pmailbox = &P_MAILBOX(idx, sid);
if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock);
if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0 &&
pmailbox->state != MAIL_CLOSED) {
pmailbox_count++;
gs_s_close_logic_connection(pmailbox, ECOMMSCTPAPPCLOSE, &fcmsgs);
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
/* Send close ctrl msg to remote without pmailbox lock */
(void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[idx], &fcmsgs, ROLE_PRODUCER);
} else {
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock);
}
}
}
}
if (cmailbox_count != 0 || pmailbox_count != 0) {
LIBCOMM_ELOG(LOG,
"(cls all stream)\tClose all stream by debug id[%lu], "
"close %d cmailbox and %d pmailbox.",
query_id,
cmailbox_count,
pmailbox_count);
}
return 0;
}
/*
* @Description: Add stream key to g_r_htab_nodeid_skey_to_memory_poll
*
* @param[IN] key_s: stream key
*/
void gs_memory_init_entry(StreamSharedContext* sharedContext, int consumerNum, int producerNum)
{
struct hash_entry* entry = NULL;
struct hash_entry** poll_entrys = NULL;
struct hash_entry*** quota_entrys = NULL;
poll_entrys = (struct hash_entry**)palloc(sizeof(struct hash_entry*) * consumerNum);
quota_entrys = (struct hash_entry***)palloc(sizeof(struct hash_entry**) * consumerNum);
for (int i = 0; i < consumerNum; i++) {
entry = (struct hash_entry*)palloc(sizeof(struct hash_entry));
(void)entry->_init();
poll_entrys[i] = entry;
quota_entrys[i] = (struct hash_entry**)palloc(sizeof(struct hash_entry*) * producerNum);
for (int j = 0; j < producerNum; j++) {
entry = (struct hash_entry*)palloc(sizeof(struct hash_entry));
(void)entry->_init();
quota_entrys[i][j] = entry;
}
}
sharedContext->poll_entrys = poll_entrys;
sharedContext->quota_entrys = quota_entrys;
}
/*
* @Description: Send Error/Notice through memory
*
* @param[IN] buf: Error/Notice string info
* @param[IN] sharedContext: context for shared memory stream
* @param[IN] nthChannel: destination consumer
*/
void gs_message_by_memory(StringInfo buf, StreamSharedContext* sharedContext, int nthChannel)
{
StringInfo buf_dst = NULL;
struct hash_entry* entry = NULL;
/* Copy Error/Notice messages to shared context. */
buf_dst = sharedContext->messages[nthChannel][u_sess->stream_cxt.smp_id];
/*
* If producer is waked up and shared buffer has been consumed while waiting,
* it can continue to append data to its messages of sharedContext.
*/
entry = sharedContext->quota_entrys[nthChannel][u_sess->stream_cxt.smp_id];
while (buf_dst->len > 0) {
(void)entry->_timewait(SINGLE_WAITQUOTA);
}
appendBinaryStringInfo(buf_dst, buf->data, buf->len);
buf_dst->cursor = buf->cursor;
/* Send signal to dest consumer. */
entry = sharedContext->poll_entrys[nthChannel];
entry->_signal();
pfree(buf->data);
buf->data = NULL;
}
void gs_memory_disconnect(StreamSharedContext* sharedContext, int nthChannel)
{
struct hash_entry* entry = NULL;
sharedContext->dataStatus[nthChannel][u_sess->stream_cxt.smp_id] = CONN_ERR;
entry = sharedContext->poll_entrys[nthChannel];
entry->_signal();
}
#ifdef __aarch64__
/*
* @Description: Judge whether the databuff is empty
*
* @param[IN] sharedContext: context for shared memory stream
* @param[IN] nthChannel: destination consumer
*/
bool gs_is_databuff_empty(StreamSharedContext* sharedContext, int nthChannel)
{
if (sharedContext->vectorized) {
VectorBatch* batch = sharedContext->sharedBatches[nthChannel][u_sess->stream_cxt.smp_id];
if (batch->m_rows == 0) {
return true;
}
} else {
TupleVector* tupleVec = sharedContext->sharedTuples[nthChannel][u_sess->stream_cxt.smp_id];
if (tupleVec->tuplePointer == 0) {
return true;
}
}
return false;
}
#endif
/*
* @Description: Send data to local consumer through shared memory
*
* @param[IN] tuple: tuple to be sent
* @param[IN] batchsrc: batch to be send
* @param[IN] sharedContext: context for shared memory stream
* @param[IN] nthChannel: destination consumer
* @param[IN] nthRow: the Nth row to be sent in batch
*/
void gs_memory_send(
TupleTableSlot* tuple, VectorBatch* batchsrc, StreamSharedContext* sharedContext, int nthChannel, int nthRow)
{
VectorBatch* batch = NULL;
TupleVector* tupleVec = NULL;
bool ready_to_send = false;
DataStatus dataStatus;
struct hash_entry* entry = NULL;
WaitState oldStatus = pgstat_report_waitstatus_comm(STATE_WAIT_FLUSH_DATA,
u_sess->pgxc_cxt.PGXCNodeId,
-1,
u_sess->stream_cxt.producer_obj->getParentPlanNodeId(),
global_node_definition ? global_node_definition->num_nodes : -1);
StreamTimeSendStart(t_thrd.pgxc_cxt.GlobalNetInstr);
entry = sharedContext->quota_entrys[nthChannel][u_sess->stream_cxt.smp_id];
for (;;) {
/* Check for interrupt at the beginning of the loop. */
CHECK_FOR_INTERRUPTS();
/* Check if we should early stop. */
/* Quit if the connection close, especially in a early close case. */
if (executorEarlyStop() || sharedContext->is_connect_end[nthChannel][u_sess->stream_cxt.smp_id]) {
(void)pgstat_report_waitstatus(oldStatus);
return;
}
dataStatus = sharedContext->dataStatus[nthChannel][u_sess->stream_cxt.smp_id];
/* Break the loop if we find quota. */
if ((dataStatus == DATA_EMPTY
#ifdef __aarch64__
&& gs_is_databuff_empty(sharedContext, nthChannel)
#endif
) ||
dataStatus == DATA_PREPARE) {
break;
}
StreamTimeWaitQuotaStart(t_thrd.pgxc_cxt.GlobalNetInstr);
(void)entry->_timewait(SINGLE_WAITQUOTA);
StreamTimeWaitQuotaEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
}
StreamTimeCopyStart(t_thrd.pgxc_cxt.GlobalNetInstr);
/* Copy data to shared context. */
if (sharedContext->vectorized) {
Assert(sharedContext->sharedBatches != NULL);
batch = sharedContext->sharedBatches[nthChannel][u_sess->stream_cxt.smp_id];
/* data copy */
if (-1 == nthRow) {
/* Do deep copy of all rows, for local roundrobin & local broadcast. */
Assert(batch->m_rows == 0);
batch->Copy<true, false>(batchsrc);
ready_to_send = true;
} else {
batch->CopyNth(batchsrc, nthRow);
if (BatchMaxSize == batch->m_rows) {
ready_to_send = true;
}
}
} else {
Assert(sharedContext->sharedTuples != NULL);
tupleVec = sharedContext->sharedTuples[nthChannel][u_sess->stream_cxt.smp_id];
int n = tupleVec->tuplePointer;
ExecCopySlot(tupleVec->tupleVector[n], tuple);
tupleVec->tuplePointer++;
if (TupleVectorMaxSize == tupleVec->tuplePointer) {
ready_to_send = true;
}
}
StreamTimeCopyEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
/* send the signal if copy finished */
if (ready_to_send) {
#ifdef __aarch64__
pg_memory_barrier();
#endif
/* set flag */
sharedContext->dataStatus[nthChannel][u_sess->stream_cxt.smp_id] = DATA_READY;
/* send signal */
entry = sharedContext->poll_entrys[nthChannel];
entry->_signal();
} else {
sharedContext->dataStatus[nthChannel][u_sess->stream_cxt.smp_id] = DATA_PREPARE;
}
StreamTimeSendEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
(void)pgstat_report_waitstatus(oldStatus);
}
/*
* @Description: catch a tuple from stream's buffer.
*
* @param[IN] node: stream state
* @return bool: true -- found data
*/
FORCE_INLINE
bool gs_return_tuple(StreamState* node)
{
TupleVector* tupleVec = node->tempTupleVec;
if (tupleVec->tuplePointer == 0) {
return false;
}
tupleVec->tuplePointer--;
int n = tupleVec->tuplePointer;
node->ss.ps.ps_ResultTupleSlot = tupleVec->tupleVector[n];
return true;
}
/*
* @Description: Consume the data in shared memory from local producers.
*
* @param[IN] node: stream state
* @param[IN] loc: data location
* @return bool: true -- found data
*/
bool gs_consume_memory_data(StreamState* node, int loc)
{
StreamSharedContext* sharedContext = node->sharedContext;
NetWorkTimeCopyStart(t_thrd.pgxc_cxt.GlobalNetInstr);
/* Take data from the shared context. */
if (sharedContext->vectorized) {
VectorBatch* batchsrc = sharedContext->sharedBatches[u_sess->stream_cxt.smp_id][loc];
VectorBatch* batchdst = ((VecStreamState*)node)->m_CurrentBatch;
if (batchsrc->m_rows == 0) {
return false;
}
batchdst->Copy<true, false>(batchsrc);
batchsrc->Reset();
} else {
TupleVector* tuplesrc = sharedContext->sharedTuples[u_sess->stream_cxt.smp_id][loc];
TupleVector* tupledst = node->tempTupleVec;
if (tuplesrc->tuplePointer == 0) {
return false;
}
for (int i = 0; i < tuplesrc->tuplePointer; i++) {
(void)ExecCopySlot(tupledst->tupleVector[i], tuplesrc->tupleVector[i]);
}
tupledst->tuplePointer = tuplesrc->tuplePointer;
tuplesrc->tuplePointer = 0;
(void)gs_return_tuple(node);
}
NetWorkTimeCopyEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
struct hash_entry* entry = NULL;
entry = sharedContext->quota_entrys[u_sess->stream_cxt.smp_id][loc];
#ifdef __aarch64__
pg_memory_barrier();
#endif
/* Reset flag */
sharedContext->dataStatus[u_sess->stream_cxt.smp_id][loc] = DATA_EMPTY;
/* send signal */
entry->_signal();
node->sharedContext->scanLoc[u_sess->stream_cxt.smp_id] = loc;
return true;
}
/*
* @Description: Scan the producer status to find the data.
*
* @param[IN] node: stream state
* @return char: STREAM_SCAN_DATA -- successfully find data from producer.
* STREAM_SCAN_WAIT -- still need to poll to wait for data.
* STREAM_SCAN_FINISH -- stream scan finished.
*/
char gs_find_memory_data(StreamState* node, int* waitnode_count)
{
DataStatus dataStatus;
StringInfo buf = NULL;
int scanLoc = node->sharedContext->scanLoc[u_sess->stream_cxt.smp_id];
int i = scanLoc;
bool finished = true;
bool is_conn_end = false;
int waitnodeCount = 0;
struct hash_entry* entry = NULL;
/* Check if there is available data, and scan from last time location. */
do {
i++;
if (i == node->conn_count) {
i = 0;
}
/* Update scan location. */
node->sharedContext->scanLoc[u_sess->stream_cxt.smp_id] = i;
dataStatus = node->sharedContext->dataStatus[u_sess->stream_cxt.smp_id][i];
is_conn_end = node->sharedContext->is_connect_end[u_sess->stream_cxt.smp_id][i];
if (!is_conn_end) {
finished = false;
waitnodeCount++;
}
/*
* Firstly, we handle error or notice messages.
* If an error occured, we should stop scan now.
* If an notice occured, we can still receive data.
*/
buf = node->sharedContext->messages[u_sess->stream_cxt.smp_id][i];
if (buf->len > 0) {
if (buf->cursor == 'E') {
HandleStreamError(node, buf->data, buf->len);
return STREAM_SCAN_FINISH;
} else if (buf->cursor == 'N') {
HandleStreamNotice(node, buf->data, buf->len);
resetStringInfo(buf);
/* After one notice message has handled, send signal and wake up the dest producer. */
entry = node->sharedContext->quota_entrys[u_sess->stream_cxt.smp_id][i];
entry->_signal();
return STREAM_SCAN_WAIT;
}
}
switch (dataStatus) {
case DATA_EMPTY:
break;
case DATA_PREPARE:
/* Take the rest data away when the connection is end. */
if (is_conn_end) {
/* Return data if any. */
if (gs_consume_memory_data(node, i)) {
return STREAM_SCAN_DATA;
}
}
break;
case DATA_READY:
if (gs_consume_memory_data(node, i)) {
return STREAM_SCAN_DATA;
} else {
break;
}
case CONN_ERR:
ereport(ERROR,
(errcode(ERRCODE_STREAM_REMOTE_CLOSE_SOCKET),
errmsg("Failed to read response from Local Stream Node,"
" Detail: Node %s, Plan Node ID %u, SMP ID %d",
g_instance.attr.attr_common.PGXCNodeName,
node->sharedContext->key_s.planNodeId,
i)));
break;
// dataStatus is enum,
default:
break;
}
} while (i != scanLoc);
*waitnode_count = waitnodeCount;
if (finished) {
return STREAM_SCAN_FINISH;
} else {
return STREAM_SCAN_WAIT;
}
}
/*
* @Description: Receive data from shared memory for local stream.
*
* @param[IN] node: stream state
* @return bool: true -- successed to find data and need more data.
* false -- all connection finished or recerive error.
*/
bool gs_memory_recv(StreamState* node)
{
char result;
struct hash_entry* entry = NULL;
entry = node->sharedContext->poll_entrys[u_sess->stream_cxt.smp_id];
bool re = true;
int waitnode_count = 0;
/* If there is already tuple in buffer, return the data at once. */
if (!node->sharedContext->vectorized && gs_return_tuple(node)) {
return true;
}
for (;;) {
/* Check for interrupt at the beginning of the loop. */
CHECK_FOR_INTERRUPTS();
/* Check if we can early stop now. */
if (executorEarlyStop()) {
re = false;
break;
}
/* Search all producers to find data. */
result = gs_find_memory_data(node, &waitnode_count);
if (result == STREAM_SCAN_DATA) {
re = true;
break;
} else if (result == STREAM_SCAN_FINISH) {
re = false;
break;
}
WaitStatePhase oldPhase = pgstat_report_waitstatus_phase(PHASE_NONE, true);
WaitState oldStatus = pgstat_report_waitstatus_comm(STATE_WAIT_NODE,
u_sess->pgxc_cxt.PGXCNodeId,
waitnode_count,
node->sharedContext->key_s.planNodeId,
global_node_definition ? global_node_definition->num_nodes : -1);
/* Poll to wait data from producers. */
NetWorkTimePollStart(t_thrd.pgxc_cxt.GlobalNetInstr);
(void)entry->_timewait(SINGLE_WAITQUOTA);
NetWorkTimePollEnd(t_thrd.pgxc_cxt.GlobalNetInstr);
pgstat_reset_waitStatePhase(oldStatus, oldPhase);
}
return re;
}
/*
* @Description: Inform all related consuemrs that there is no more data to send.
*
* @param[IN] sharedContext: context for shared memory stream
* @param[IN] connNum: producer connection number
*/
void gs_memory_send_finish(StreamSharedContext* sharedContext, int connNum)
{
struct hash_entry* entry = NULL;
for (int i = 0; i < connNum; i++) {
/* Set flags. */
sharedContext->is_connect_end[i][u_sess->stream_cxt.smp_id] = true;
/* send signal */
entry = sharedContext->poll_entrys[i];
entry->_signal();
}
}
/*
* @Description: Set all connections with this producer to close.
*
* @param[IN] sharedContext: context for shared memory stream
* @param[IN] connNum: producer connection number
* @param[IN] smpId: producer smp id
*/
void gs_memory_close_conn(StreamSharedContext* sharedContext, int connNum, int consumerId)
{
struct hash_entry* entry = NULL;
for (int i = 0; i < connNum; i++) {
/* Set flags. */
sharedContext->is_connect_end[consumerId][i] = true;
/*
* Send signal to the producers which may be still waiting quota,
* in a query like "limit XXX", when consumer don't need data anymore,
* but the producers haven't send all data yet.
*/
entry = sharedContext->quota_entrys[consumerId][i];
entry->_signal();
}
}
void SetupCommSignalHook()
{
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGUSR1, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGTERM, SIG_IGN);
(void)gspqsignal(SIGQUIT, SIG_IGN);
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGFPE, SIG_IGN);
(void)gspqsignal(SIGCHLD, SIG_IGN);
/* when support guc online change, we can accept sighup, but now we don't handle it */
(void)gspqsignal(SIGHUP, SIG_IGN);
}
/* SIGTERM: set flag to exit normally */
static void PoolCleanerShutdownHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.poolcleaner_cxt.shutdown_requested = true;
if (t_thrd.proc)
SetLatch(&t_thrd.proc->procLatch);
errno = save_errno;
}
void SetupPoolerCleanSignalHook()
{
(void)gspqsignal(SIGHUP, SIG_IGN);
(void)gspqsignal(SIGQUIT, SIG_IGN);
(void)gspqsignal(SIGTERM, PoolCleanerShutdownHandler);
(void)gspqsignal(SIGINT, PoolCleanerShutdownHandler); /* cancel current query */
(void)gspqsignal(SIGALRM, SIG_IGN); /* timeout conditions */
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, SIG_IGN);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGFPE, FloatExceptionHandler);
(void)gspqsignal(SIGCHLD, SIG_DFL);
}
int comm_sender_flow_init()
{
int error = 0;
/* initialize epoll list */
error = g_instance.comm_cxt.pollers_cxt.g_s_poller_list->init();
if (error < 0) {
ereport(FATAL, (errmsg("(s|flow ctrl init)\tFailed to init poller list:%s.", strerror(errno))));
}
LIBCOMM_PTHREAD_MUTEX_INIT(g_instance.comm_cxt.pollers_cxt.g_s_poller_list_lock, 0);
return error;
}
void comm_receivers_comm_init()
{
#ifdef ENABLE_MULTIPLE_NODES
int error = 0;
/* initialize sctp address storage */
error = mc_sctp_addr_init(g_instance.comm_cxt.localinfo_cxt.g_local_host,
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.port,
&(g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.ss),
&(g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.ss_len));
/* intialize sctp receivers socket */
if (error == 0) {
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket = g_libcomm_adapt.listen();
}
if (error != 0 || g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket < 0) {
ereport(FATAL, (errmsg("(r|receiver init)\tFailed to init receiver listen socket:%s.", mc_strerror(errno))));
}
error = g_instance.comm_cxt.quota_cxt.g_quota_changing->init(); // initiaize quota notification semaphore
if (error != 0) {
ereport(FATAL, (errmsg("(r|receiver init)\tFailed to init receiver semaphore:%s.", mc_strerror(errno))));
}
#endif
}
int comm_receiver_flow_init(int ctrl_tcp_port)
{
socklen_t addr_len;
int error = 0;
/* tcp listen */
g_instance.comm_cxt.localinfo_cxt.g_local_ctrl_tcp_sock =
mc_tcp_listen(g_instance.comm_cxt.localinfo_cxt.g_local_host, ctrl_tcp_port, &addr_len);
if (g_instance.comm_cxt.localinfo_cxt.g_local_ctrl_tcp_sock < 0) {
ereport(FATAL, (errmsg("(r|flow ctrl init)\tFailed to do listen:%s.", strerror(errno))));
}
/* poll list initialization */
error = g_instance.comm_cxt.pollers_cxt.g_r_poller_list->init();
if (error < 0) {
ereport(FATAL, (errmsg("(r|flow ctrl init)\tFailed to init poller list:%s.", strerror(errno))));
}
LIBCOMM_PTHREAD_MUTEX_INIT(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock, 0);
return error;
}
void commSenderFlowerLoop(uint64 *end_time)
{
/* step1: define and intialize local variables */
int i, rc, idx;
bool found = false;
sock_id_entry* entry_id = NULL;
struct FCMSG_T fcmsgr = {0x0};
/* step2: waiting for network events */
(void)mc_poller_wait(t_thrd.comm_cxt.g_libcomm_poller_list, EPOLL_TIMEOUT);
*end_time = mc_timers_us();
/* step3: get the epoll events */
int nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents;
if (nevents < 0) {
/*
* EBADF epfd is not a valid file descriptor.
* EFAULT The memory area pointed to by events is not accessible with write permissions.
* EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.
*/
if (errno == EBADF || errno == EFAULT || errno == EINVAL) {
ereport(PANIC,
(errmsg("(s|flow ctrl)\tFailed to do epoll wait[%d] with errno[%d]:%s.",
t_thrd.comm_cxt.g_libcomm_poller_list->ep,
errno,
mc_strerror(errno))));
}
return;
}
/* step4: process each epoll event */
for (i = 0; i < t_thrd.comm_cxt.g_libcomm_poller_list->nevents; i++) {
/* step5: get the socket related to the current event */
struct sock_id f_fd_id;
f_fd_id.fd =
(int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64) >> MC_POLLER_FD_ID_OFFSET);
f_fd_id.id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64) & MC_POLLER_FD_ID_MASK);
/* step6: get the node index by the socket */
/* errror happended to the connection */
if ((t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLERR) ||
(t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLHUP)) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &f_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
int error = 0;
socklen_t errlen = sizeof(error);
(void)getsockopt(f_fd_id.fd, SOL_SOCKET, SO_ERROR, (void*)&error, &errlen);
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tPoller receive error, "
"close tcp socket[%d] to node[%d]:%s, events[%u], error[%d]:%s.",
f_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, idx),
t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events,
error,
mc_strerror(error));
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].lock();
}
gs_s_close_bad_ctrl_tcp_sock(&f_fd_id, ECOMMSCTPTCPDISCONNECT, true, idx);
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].unlock();
}
continue;
}
/* step7: receive data from the connection */
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_S_TCP_DISCONNECT)) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &f_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\t[FAULT INJECTION]TCP disconnect with socket[%d] for node[%d]:%s, detail:%s.",
f_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, idx),
gs_comm_strerror());
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].lock();
}
gs_s_close_bad_ctrl_tcp_sock(&f_fd_id, ECOMMSCTPTCPDISCONNECT, true, idx);
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].unlock();
}
continue;
}
#endif
/* do receiving data */
if ((rc = mc_tcp_read_block(f_fd_id.fd, &fcmsgr, sizeof(fcmsgr), 0)) > 0) {
/* step8: resolve the message */
/* src is from the network, maybe do not have '\0' */
fcmsgr.nodename[NAMEDATALEN - 1] = '\0';
DEBUG_QUERY_ID = fcmsgr.query_id;
int retry_count = 10;
do {
/* node_idx is uint16, so if gs_get_node_idx() returns -1, node_idx=65535 */
if ((fcmsgr.node_idx = gs_get_node_idx(fcmsgr.nodename)) >=
g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
usleep(10000);
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tReveive fault message with socket[%d] "
"for[%s], type[%d], node index[%d], stream id[%d], get node_idx again.",
f_fd_id.fd,
fcmsgr.nodename,
fcmsgr.type,
fcmsgr.node_idx,
fcmsgr.streamid);
} else {
break;
}
} while (retry_count--);
COMM_DEBUG_CALL(printfcmsg("s|flow ctrl", &fcmsgr));
/* step9: check the type of the message, and do different processing */
if (fcmsgr.node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || fcmsgr.streamid == 0 ||
fcmsgr.streamid >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) {
fcmsgr.nodename[NAMEDATALEN - 1] = '\0';
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tReveive fault message with socket[%d] "
"for[%s], type[%d], node index[%d], stream id[%d].",
f_fd_id.fd,
fcmsgr.nodename,
fcmsgr.type,
fcmsgr.node_idx,
fcmsgr.streamid);
errno = ECOMMSCTPNODEIDXSCTPFD;
printfcmsg("s|flow ctrl", &fcmsgr);
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &f_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].lock();
}
gs_s_close_bad_ctrl_tcp_sock(&f_fd_id, ECOMMSCTPTCPDISCONNECT, true, idx);
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].unlock();
}
continue;
}
switch (fcmsgr.type) {
case CTRL_PEER_TID:
gs_senders_flow_handle_tid_request(&fcmsgr);
break;
case CTRL_CONN_REJECT:
gs_senders_flow_handle_init_request(&fcmsgr);
break;
case CTRL_CONN_ACCEPT:
gs_senders_flow_handle_ready_request(&fcmsgr);
break;
case CTRL_ADD_QUOTA:
gs_senders_flow_handle_resume_request(&fcmsgr);
break;
case CTRL_CLOSED:
gs_senders_flow_handle_close_request(&fcmsgr);
break;
case CTRL_ASSERT_FAIL:
gs_senders_flow_handle_assert_fail_request(&fcmsgr);
break;
case CTRL_STOP_QUERY:
gs_senders_flow_handle_stop_query_request(&fcmsgr);
break;
default:
/* should not happen */
LIBCOMM_ASSERT(false, fcmsgr.node_idx, fcmsgr.streamid, ROLE_PRODUCER);
break;
}
DEBUG_QUERY_ID = 0;
} else if (rc < 0) {
/* failed to read message */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &f_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tTCP disconnect with socket[%d] for node[%d]:%s, detail:%s.",
f_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, idx),
gs_comm_strerror());
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].lock();
}
gs_s_close_bad_ctrl_tcp_sock(&f_fd_id, ECOMMSCTPTCPDISCONNECT, true, idx);
if (idx >= 0) {
g_instance.comm_cxt.g_s_node_sock[idx].unlock();
}
} else if (rc == 0) {
/* error can be resolved by retry, just report warning */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &f_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(s|flow ctrl)\tTCP receive reply with socket[%d] for node[%d]:%s, detail:%s.",
f_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, idx),
gs_comm_strerror());
continue;
}
}
}
void commReceiverFlowLoop(int ltk, uint64 *end_time)
{
int rc, i;
bool found = false;
sock_id_entry* entry_id = NULL;
struct FCMSG_T fcmsgr = {0x0};
/* step3: waiting for network events */
(void)mc_poller_wait(t_thrd.comm_cxt.g_libcomm_poller_list, EPOLL_TIMEOUT);
*end_time = mc_timers_us();
/* step4: get the epoll events */
int nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents;
if (nevents < 0) {
/*
* EBADF epfd is not a valid file descriptor.
* EFAULT The memory area pointed to by events is not accessible with write permissions.
* EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.
*/
if (errno == EBADF || errno == EFAULT || errno == EINVAL) {
ereport(PANIC,
(errmsg("(r|flow ctrl)\tFailed to do epoll wait[%d] with errno[%d]:%s.",
t_thrd.comm_cxt.g_libcomm_poller_list->ep,
errno,
mc_strerror(errno))));
}
return;
;
}
/* step5: process each epoll event */
for (i = 0; i < t_thrd.comm_cxt.g_libcomm_poller_list->nevents; i++) {
/* step6: get the socket related to the current event */
struct sock_id t_fd_id;
t_fd_id.fd =
(int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64) >> MC_POLLER_FD_ID_OFFSET);
t_fd_id.id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64) & MC_POLLER_FD_ID_MASK);
if (t_fd_id.fd == ltk) {
/* step7: if the socket is listening tcp control socket, do accept and update the new socket and its
* version, then add it to epoll list for monitoring data */
if (t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLIN) {
struct sockaddr ctrl_client;
socklen_t s_len = sizeof(struct sockaddr);
/* do accept */
int ctk = mc_tcp_accept(ltk, (struct sockaddr*)&ctrl_client, (socklen_t*)&s_len);
if (ctk < 0) {
COMM_DEBUG_LOG("(r|flow ctrl)\tFailed to accept tcp connection on listen socket[%d]:%s",
ltk,
mc_strerror(errno));
continue;
}
sockaddr_in* pSin = (sockaddr_in*)&ctrl_client;
char* ipstr = inet_ntoa(pSin->sin_addr);
LIBCOMM_ELOG(LOG, "(r|flow ctrl)\tDetect incoming connection, socket[%d] from [%s].", ctk, ipstr);
/* Server side gss kerberos authentication for tcp connection. */
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_GSS_TCP_FAILED)) {
/* Server side gss kerberos authentication for tcp connection. */
mc_tcp_close(ctk);
errno = ECOMMSCTPGSSAUTHFAIL;
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\t[FAULT INJECTION]Control channel GSS authentication failed, listen "
"socket[%d]:%s.",
ltk,
mc_strerror(errno));
continue;
}
#endif
/* Server side gss kerberos authentication for tcp connection.
* if GSS authentication SUCC, no IP authentication is required.
*/
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL) {
if (GssServerAuth(ctk, g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile) < 0) {
mc_tcp_close(ctk);
errno = ECOMMSCTPGSSAUTHFAIL;
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tControl channel GSS authentication failed, listen socket[%d]:%s.",
ltk,
mc_strerror(errno));
continue;
} else {
COMM_DEBUG_LOG("(r|flow ctrl)\tControl channel GSS authentication SUCC, listen socket[%d]:%s.",
ltk,
mc_strerror(errno));
}
} else {
/* send signal to postmaster thread to reload hba */
int retry_count = gs_reload_hba(ltk, ctrl_client);
if (retry_count >= RELOAD_HBA_RETRY_COUNT) {
mc_tcp_close(ctk);
continue;
}
}
struct sock_id ctk_fd_id = {ctk, 0};
/* update the new socket and its version */
if (gs_update_fd_to_htab_socket_version(&ctk_fd_id) < 0) {
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tFailed to save socket[%d] and version[%d].",
ctk_fd_id.fd,
ctk_fd_id.id);
}
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
/* add the new socket to epoll list for monitoring data */
if (g_instance.comm_cxt.pollers_cxt.g_r_poller_list->add_fd(&ctk_fd_id) < 0) {
mc_tcp_close(ctk);
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to add socket[%d] to poller list.", ctk);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
LIBCOMM_ELOG(LOG,
"(r|flow ctrl)\tContoller tcp listener on socket[%d,%d] accepted socket[%d,%d].",
t_fd_id.fd,
t_fd_id.id,
ctk_fd_id.fd,
ctk_fd_id.id);
}
} else {
/* step8: if the socket is a normal conection socket, do receive message and other processes by the contents
* of the message */
int idx = -1; // node index
#ifdef ENABLE_LLT
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
#endif
/* errror happended to the connection */
if ((t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLERR) ||
(t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLHUP)) {
/* if error happened, we get the node index by the socket, it maybe fail */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
int error = 0;
socklen_t errlen = sizeof(error);
(void)getsockopt(t_fd_id.fd, SOL_SOCKET, SO_ERROR, (void*)&error, &errlen);
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tPoller receive error, "
"close tcp socket[%d] to node[%d]:%s, events[%u], error[%d]:%s.",
t_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events,
error,
mc_strerror(error));
/* close the socket and clean the related information */
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPTCPDISCONNECT);
continue;
}
/* step9: receive data from the connection */
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
if (is_comm_fault_injection(LIBCOMM_FI_R_TCP_DISCONNECT)) {
/* at receiver, if the tcp connection is broken, which caused the failure of receiving, we do cleanning
* and report the error */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\t[FAULT INJECTION]TCP disconnect with socket[%d] for node[%d]:%s, detail:%s.",
t_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
gs_comm_strerror());
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPTCPDISCONNECT);
continue;
}
#endif
/* when query is started, other instances will send message to asking for a connection */
if ((rc = mc_tcp_read_block(t_fd_id.fd, &fcmsgr, sizeof(fcmsgr), 0)) > 0) {
/* step10: resolve the message */
/* src is from the network, maybe do not have '\0' */
gs_update_recv_ready_time();
fcmsgr.nodename[NAMEDATALEN - 1] = '\0';
/* fault node checksum (to avoid port injection)
* "g_htab_fd_id_node_idx.get_value() < 0" means first connection, so the message type must be REGIST
* we need to check the type and extra_info.
*/
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
if ((idx < 0) && !((fcmsgr.type == CTRL_CONN_REGIST || fcmsgr.type == CTRL_CONN_REGIST_CN) &&
fcmsgr.extra_info == 0xEA)) {
fcmsgr.nodename[NAMEDATALEN - 1] = '\0';
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tReveive fault message with socket[%d] for[%s], type[%d].",
t_fd_id.fd,
fcmsgr.nodename,
fcmsgr.type);
errno = ECOMMSCTPNODEIDXSCTPFD;
printfcmsg("r|flow ctrl", &fcmsgr);
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPTCPDISCONNECT);
continue;
}
DEBUG_QUERY_ID = fcmsgr.query_id;
int retry_count = 10;
do {
/* node_idx is uint16, so if gs_get_node_idx() returns -1, node_idx=65535 */
if ((fcmsgr.node_idx = gs_get_node_idx(fcmsgr.nodename)) >=
g_instance.comm_cxt.counters_cxt.g_cur_node_num) {
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tReveive fault message with socket[%d] for[%s], type[%d], get node_idx "
"again.",
t_fd_id.fd,
fcmsgr.nodename,
fcmsgr.type);
usleep(10000);
} else {
break;
}
} while (retry_count--);
COMM_DEBUG_CALL(printfcmsg("r|flow ctrl", &fcmsgr));
/* fault node index or fault stream id */
/* node_idx is uint16, so if gs_get_node_idx() returns -1, node_idx=65535 */
if (fcmsgr.node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || fcmsgr.streamid == 0 ||
fcmsgr.streamid >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) {
fcmsgr.nodename[NAMEDATALEN - 1] = '\0';
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tReveive fault message with socket[%d] for[%s], type[%d].",
t_fd_id.fd,
fcmsgr.nodename,
fcmsgr.type);
errno = ECOMMSCTPNODEIDXSCTPFD;
printfcmsg("r|flow ctrl", &fcmsgr);
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPTCPDISCONNECT);
continue;
}
/* step13: check the type of the message, and do different processing */
switch (fcmsgr.type) {
case CTRL_PEER_TID:
gs_receivers_flow_handle_tid_request(&fcmsgr);
break;
case CTRL_CONN_DUAL:
case CTRL_CONN_REQUEST:
gs_receivers_flow_handle_ready_request(&fcmsgr);
break;
case CTRL_CLOSED:
gs_receivers_flow_handle_close_request(&fcmsgr);
break;
case CTRL_ASSERT_FAIL:
gs_receivers_flow_handle_assert_fail_request(&fcmsgr);
break;
case CTRL_CONN_REGIST:
case CTRL_CONN_REGIST_CN:
gs_accept_ctrl_conntion(&t_fd_id, &fcmsgr);
break;
case CTRL_PEER_CHANGED:
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPREMOETECLOSE);
break;
/* case CTRL_INIT, CTRL_RESUME, CTRL_HOLD */
default:
/* should not happen */
LIBCOMM_ASSERT(false, fcmsgr.node_idx, fcmsgr.streamid, ROLE_CONSUMER);
break;
}
DEBUG_QUERY_ID = 0;
} else if (rc < 0) {
/* failed to read message */
/* at receiver, if the tcp connection is broken, which caused the failure of receiving, we do cleanning
* and report the error */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tTCP disconnect with socket[%d] for node[%d]:%s, detail:%s.",
t_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
gs_comm_strerror());
gs_r_close_bad_ctrl_tcp_sock(&t_fd_id, ECOMMSCTPTCPDISCONNECT);
} else if (rc == 0) {
/* error can be resolved by retry, just report warning */
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &t_fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(r|flow ctrl)\tTCP receive reply with socket[%d] for node[%d]:%s, detail:%s.",
t_fd_id.fd,
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
gs_comm_strerror());
continue;
}
}
}
}
void commAuxiliaryLoop(uint64* last_print_time)
{
uint64 current_time;
/* step1: wait signal */
(void)g_instance.comm_cxt.quota_cxt.g_quota_changing->timed_wait(5);
/* step3: check other request */
gs_check_requested();
/* step4: send/receive delay survey messages and analysis */
if (g_instance.comm_cxt.g_delay_survey_switch) {
gs_delay_survey();
gs_delay_analysis();
}
#ifdef ENABLE_MULTIPLE_NODES
libcomm_performance_test();
#endif
/* if g_cur_node_num==0, it means postmaster thread doesn't finish initilaization. */
if ((g_instance.comm_cxt.counters_cxt.g_cur_node_num != 0) &&
(g_instance.comm_cxt.counters_cxt.g_expect_node_num != g_instance.comm_cxt.counters_cxt.g_cur_node_num)) {
gs_online_change_capacity();
}
if (g_instance.comm_cxt.commutil_cxt.g_debug_mode) {
current_time = mc_timers_ms();
if (*last_print_time + g_print_interval_time < current_time) {
print_stream_sock_info();
*last_print_time = current_time;
}
}
}
void commReceiverLoop(int selfid, uint64 *end_time, int *epoll_timeout_count)
{
int nevents, idx, ret_recv, client_socket, retry_count, max_len, error, i, j;
bool found = false;
sock_id_entry* entry_id = NULL;
struct sock_id fd_id;
struct sockaddr ctrl_client;
socklen_t s_len = 0;
int curr_len = 0;
int dst_idx = 0;
/* Wait poll */
(void)mc_poller_wait(t_thrd.comm_cxt.g_libcomm_poller_list, EPOLL_TIMEOUT);
*end_time = mc_timers_us();
t_thrd.comm_cxt.g_receiver_loop_poll_up = COMM_STAT_TIME();
nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents;
if (nevents < 0) {
/*
* EBADF epfd is not a valid file descriptor.
* EFAULT The memory area pointed to by events is not accessible with write permissions.
* EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.
*/
if (errno == EBADF || errno == EFAULT || errno == EINVAL) {
/* it should not happen, so we assert here */
ereport(PANIC,
(errmsg("(r|recv loop)\tFailed to do epoll wait[%d] with errno[%d]:%s.",
t_thrd.comm_cxt.g_libcomm_poller_list->ep,
errno,
mc_strerror(errno))));
}
return;
}
if (nevents == 0) {
(*epoll_timeout_count)++;
if (*epoll_timeout_count > MAX_EPOLL_TIMEOUT_COUNT) {
*epoll_timeout_count = 0;
COMM_DEBUG_LOG(
"(r|recv loop)\tepoll[%d] wait timeout[%dms].",
t_thrd.comm_cxt.g_libcomm_poller_list->ep, EPOLL_TIMEOUT);
}
} else {
*epoll_timeout_count = 0;
}
for (i = 0; i < t_thrd.comm_cxt.g_libcomm_poller_list->nevents; i++) {
ret_recv = 0;
s_len = sizeof(struct sockaddr);
fd_id.fd = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 >> MC_POLLER_FD_ID_OFFSET));
fd_id.id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 & MC_POLLER_FD_ID_MASK));
mc_assert(fd_id.fd > 0);
/* Special case: handle with accepting connection */
if (unlikely(fd_id.fd == g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket)) {
if (t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLIN) {
client_socket = g_libcomm_adapt.accept(g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket,
(struct sockaddr*)&ctrl_client,
(socklen_t*)&s_len);
if (client_socket < 0) {
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tFailed to accept data connection on listen socket[%d]:%s.",
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket,
mc_strerror(errno));
continue;
}
sockaddr_in* pSin = (sockaddr_in*)&ctrl_client;
char* ipstr = inet_ntoa(pSin->sin_addr);
LIBCOMM_ELOG(
LOG, "(r|recv loop)\tDetect incoming connection, socket[%d] from [%s].", client_socket, ipstr);
/*
* server side gss kerberos authentication for data connection.
* authentication for tcp mode after accept.
* if GSS authentication SUCC, no IP authentication is required.
*/
if (g_instance.comm_cxt.g_comm_tcp_mode == true) {
if (g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile != NULL) {
if (GssServerAuth(client_socket, g_instance.comm_cxt.localinfo_cxt.gs_krb_keyfile) < 0) {
mc_tcp_close(client_socket);
errno = ECOMMSCTPGSSAUTHFAIL;
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tData channel GSS authentication failed, listen socket[%d]:%s.",
fd_id.fd,
mc_strerror(errno));
continue;
} else {
LIBCOMM_ELOG(LOG,
"(r|recv loop)\tData channel GSS authentication SUCC, listen socket[%d].",
fd_id.fd);
}
} else {
/* send signal to postmaster thread to reload hba */
retry_count = gs_reload_hba(fd_id.fd, ctrl_client);
if (retry_count >= RELOAD_HBA_RETRY_COUNT) {
mc_tcp_close(client_socket);
continue;
}
}
}
struct sock_id ctk_fd_id = {client_socket, 0};
if (gs_update_fd_to_htab_socket_version(&ctk_fd_id) < 0) {
mc_tcp_close(client_socket);
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tFailed to save socket[%d] and version[%d]:%s.",
ctk_fd_id.fd,
ctk_fd_id.id,
mc_strerror(errno));
continue;
}
/* add data socket to the poll list with minimal elements for waiting data */
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
max_len = g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[0].get_socket_count();
curr_len = 0;
dst_idx = 0;
for (j = 1; j < g_instance.comm_cxt.counters_cxt.g_recv_num; j++) {
curr_len = g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[j].get_socket_count();
if (max_len > curr_len) {
dst_idx = j;
break;
}
}
if (g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[dst_idx].add_fd(&ctk_fd_id) < 0) {
mc_tcp_close(client_socket);
LIBCOMM_ELOG(
WARNING, "(r|recv loop)\tFailed to add socket[%d] to poller list[%d].", client_socket, selfid);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
LIBCOMM_ELOG(LOG,
"(r|recv loop)\tReceiver data listener on socket[%d] accepted socket[%d,%d].",
g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket,
ctk_fd_id.fd,
ctk_fd_id.id);
}
continue;
}
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
/* Recv data message */
if (t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLIN) {
ret_recv = gs_internal_recv(fd_id, idx);
DEBUG_QUERY_ID = 0;
if (ret_recv < 0) {
LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock);
entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_FIND, &found);
if (found) {
idx = entry_id->entry.val;
} else {
idx = -1;
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock);
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tFailed to recv message from node[%d]:%s on data socket[%d].",
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
fd_id.fd);
if (ret_recv == RECV_MEM_ERROR) {
/*
* If there is no usable memory, we traverse all the c_mailbox(es) to find the query who occupies
* maximun memory, and make it failure to release the memory. Otherwise, the communication layer
* maybe hang up.
*/
LIBCOMM_ELOG(WARNING, "(r|recv loop)\tFailed to malloc, begin release memory.");
gs_r_release_comm_memory();
} else if (ret_recv == RECV_NET_ERROR) {
gs_r_close_bad_data_socket(idx, fd_id, true);
}
}
} else if ((t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLERR) ||
(t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events & EPOLLHUP)) { /* ERROR handling */
error = 0;
s_len = sizeof(error);
(void)getsockopt(fd_id.fd, SOL_SOCKET, SO_ERROR, (void*)&error, &s_len);
LIBCOMM_ELOG(WARNING,
"(r|recv loop)\tBroken receive channel to node[%d]:%s "
"on socket[%d], events[%u], error[%d]:%s.",
idx,
REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx),
fd_id.fd,
t_thrd.comm_cxt.g_libcomm_poller_list->events[i].events,
error,
mc_strerror(error));
gs_r_close_bad_data_socket(idx, fd_id, true);
}
}
}
void commSenderFlowMain()
{
uint64 thread_start_time = 0;
uint64 thread_end_time = 0;
uint64 thread_work_all_time = 0;
uint64 thread_last_check_time = 0;
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
/* reset MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
knl_thread_set_name("CommSendStream");
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* Identify myself via ps */
init_ps_display("CommSendStream worker process", "", "", "");
/* setup signal process hook */
SetupCommSignalHook();
/* initialize globals */
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_SEND_CTRL;
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
t_thrd.comm_cxt.g_libcomm_poller_list =
g_instance.comm_cxt.pollers_cxt.g_s_poller_list->get_poller(); // get poller for monitoring network events
LIBCOMM_ELOG(LOG, "CommSendStream thread is initialized.");
sigjmp_buf local_sigjmp_buf;
int curTryCounter;
int *oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Forget any pending QueryCancel request */
t_thrd.int_cxt.QueryCancelPending = false;
disable_sig_alarm(true);
t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the client and/or server log */
EmitErrorReport();
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
FlushErrorState();
LIBCOMM_ELOG(ERROR, "Sender flow control encounter ERROR level ereport which is unsupported.");
}
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
t_thrd.int_cxt.ImmediateInterruptOK = false;
thread_start_time = thread_last_check_time = thread_end_time = mc_timers_us();
for (;;) {
/* if the datanode is to shutdown, close the listening socket and exit the thread */
if (g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested) {
break;
}
gs_flow_thread_time("(s|flow ctrl)\tgs_senders_flow_controller", &thread_start_time, thread_end_time,
&thread_last_check_time, &thread_work_all_time, GS_SEND_flow);
commSenderFlowerLoop(&thread_end_time);
}
/* All done, go away */
proc_exit(0);
}
void commReceiverFlowMain()
{
uint64 thread_start_time = 0;
uint64 thread_end_time = 0;
uint64 thread_work_all_time = 0;
uint64 thread_last_check_time = 0;
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
/* reset MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
knl_thread_set_name("CommRcvStream");
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* Identify myself via ps */
init_ps_display("CommRcvStream worker process", "", "", "");
/* setup signal process hook */
SetupCommSignalHook();
// initialize globals
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_RECV_CTRL;
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
/* step1: define and intialize local variables */
/* get the listening socket for tcp control message */
int ltk = g_instance.comm_cxt.localinfo_cxt.g_local_ctrl_tcp_sock;
mc_assert(ltk != INVALID_SOCK);
/* get poller for monitoring network events */
t_thrd.comm_cxt.g_libcomm_poller_list = g_instance.comm_cxt.pollers_cxt.g_r_poller_list->get_poller();
struct sock_id ltk_fd_id = {ltk, 0};
/* update the listening socket and socket version */
while (gs_update_fd_to_htab_socket_version(&ltk_fd_id) < 0) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to save socket[%d] and version[%d].", ltk_fd_id.fd, ltk_fd_id.id);
(void)sleep(1);
}
/* step2: add the listening socket to poller for monitoring connecting events from senders */
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
while (g_instance.comm_cxt.pollers_cxt.g_r_poller_list->add_fd(&ltk_fd_id) < 0) {
LIBCOMM_ELOG(WARNING, "(r|flow ctrl)\tFailed to add socket[%d] to poller list.", ltk);
(void)sleep(1);
}
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
LIBCOMM_ELOG(LOG, "CommRcvStream thread is initialized, ready to accept on socket[%d].", ltk);
sigjmp_buf local_sigjmp_buf;
int curTryCounter;
int *oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Forget any pending QueryCancel request */
t_thrd.int_cxt.QueryCancelPending = false;
disable_sig_alarm(true);
t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the client and/or server log */
EmitErrorReport();
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
FlushErrorState();
LIBCOMM_ELOG(ERROR, "Receiver flow control encounter ERROR level ereport which is unsupported.");
}
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
t_thrd.int_cxt.ImmediateInterruptOK = false;
thread_start_time = thread_last_check_time = thread_end_time = mc_timers_us();
for (;;) {
/* if the datanode is to shutdown, close the listening socket and exit the thread */
if (g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested) {
LIBCOMM_PTHREAD_MUTEX_DESTORY(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock);
break;
}
gs_flow_thread_time("(r|flow ctrl)\tgs_receivers_flow_controller", &thread_start_time, thread_end_time,
&thread_last_check_time, &thread_work_all_time, GS_RECV_FLOW);
commReceiverFlowLoop(ltk, &thread_end_time);
}
/* All done, go away */
proc_exit(0);
}
void commAuxiliaryMain()
{
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
/* reset MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
knl_thread_set_name("CommAuxStream");
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* Identify myself via ps */
init_ps_display("CommAuxStream worker process", "", "", "");
/* setup signal process hook */
SetupCommSignalHook();
/* initialize globals */
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_AUX;
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
uint64 last_print_time;
last_print_time = mc_timers_ms();
LIBCOMM_ELOG(LOG, "CommAuxStream thread is initialized.");
sigjmp_buf local_sigjmp_buf;
int curTryCounter;
int *oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Forget any pending QueryCancel request */
t_thrd.int_cxt.QueryCancelPending = false;
disable_sig_alarm(true);
t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the client and/or server log */
EmitErrorReport();
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
FlushErrorState();
LIBCOMM_ELOG(ERROR, "Auxiliary encounter ERROR level ereport which is unsupported.");
}
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
t_thrd.int_cxt.ImmediateInterruptOK = false;
for (;;) {
/* step2: need shutdown */
if (g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested) {
break;
}
commAuxiliaryLoop(&last_print_time);
}
/* All done, go away */
proc_exit(0);
}
#ifdef ENABLE_MULTIPLE_NODES
void init_clean_pooler_idle_connections()
{
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
t_thrd.role = COMM_POOLER_CLEAN;
/* reset t_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
knl_thread_set_name("CommPoolCleaner");
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* Identify myself via ps */
init_ps_display("CommPoolCleaner process", "", "", "");
/* set processing mode */
SetProcessingMode(InitProcessing);
/* setup signal process hook */
SetupPoolerCleanSignalHook();
/* We allow SIGQUIT (quickdie) at all times */
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
/* Early initialization */
BaseInit();
#ifndef EXEC_BACKEND
InitProcess();
#endif
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
return;
}
extern void clean_pooler_idle_connections(void);
void commPoolCleanerMain()
{
sigjmp_buf local_sigjmp_buf;
uint64 current_time, last_start_time, poolerMaxIdleTime, nodeNameHashVal;
MemoryContext poolCleaner_context;
char *curNodeName = g_instance.attr.attr_common.PGXCNodeName;
const Size nodeNameHashMaxVal = 60000;
ereport(LOG, (errmsg("commPoolCleanerMain started")));
init_clean_pooler_idle_connections();
/*
* Create the memory context we will use in the main loop.
*
* t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon
* completion of processing of each command message from the client.
*/
poolCleaner_context = AllocSetContextCreate(t_thrd.top_mem_cxt, "Pool Cleaner", ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
(void)MemoryContextSwitchTo(poolCleaner_context);
/* If an exception is encountered, processing resumes here. */
int curTryCounter;
int* oldTryCounter = NULL;
/* Normal exit */
if (t_thrd.poolcleaner_cxt.shutdown_requested) {
g_instance.pid_cxt.CommPoolerCleanPID = 0;
proc_exit(0); /* done */
}
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Prevents interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Report the error to the server log */
EmitErrorReport();
(void)MemoryContextSwitchTo(poolCleaner_context);
FlushErrorState();
/* Flush any leaked data in the top-level context */
MemoryContextResetAndDeleteChildren(poolCleaner_context);
/*
* process exit. Note that because we called InitProcess, a
* callback was registered to do ProcKill, which will clean up
* necessary state.
*/
proc_exit(0);
}
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
/* We can now handle ereport(ERROR) */
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
/* report this backend in the PgBackendStatus array */
pgstat_report_appname("PoolCleaner");
/*
* Create a resource owner to keep track of our resources (currently only
* buffer pins).
*/
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Pool cleaner");
SetProcessingMode(NormalProcessing);
/* To ensure the minimum pool concept, do not clean the idle connections of each CN at the same time.
* Time difference: within 60s
*/
if (curNodeName != NULL) {
nodeNameHashVal = (uint64)(string_hash((void*)curNodeName, (strlen(curNodeName) + 1))) % nodeNameHashMaxVal;
}
last_start_time = mc_timers_ms() + nodeNameHashVal;
for (;;) {
if (t_thrd.poolcleaner_cxt.shutdown_requested == true || g_instance.status > NoShutdown) {
break;
}
sleep(5);
current_time = mc_timers_ms();
poolerMaxIdleTime = (g_instance.attr.attr_network.PoolerMaxIdleTime) * SECS_PER_MINUTE * MS_PER_S;
if (IS_PGXC_COORDINATOR && (current_time - last_start_time) >= poolerMaxIdleTime) {
clean_pooler_idle_connections();
last_start_time = current_time;
}
}
/* All done, go away */
g_instance.pid_cxt.CommPoolerCleanPID = 0;
proc_exit(0);
}
#endif
void commReceiverMain(void* tid_callback)
{
int epoll_timeout_count = 0;
uint64 thread_start_time = 0;
uint64 thread_end_time = 0;
uint64 thread_work_all_time = 0;
uint64 thread_last_check_time = 0;
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
/* reset MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
knl_thread_set_name("CommRcvWorker");
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* Identify myself via ps */
init_ps_display("CommRcvWorker process", "", "", "");
/* setup signal process hook */
SetupCommSignalHook();
/* initialize globals */
log_timezone = g_instance.comm_cxt.libcomm_log_timezone;
t_thrd.comm_cxt.LibcommThreadType = LIBCOMM_RECV_LOOP;
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
/* Initialize env */
int selfid = *(int*)tid_callback;
*(int*)tid_callback = -1;
LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
t_thrd.comm_cxt.g_libcomm_poller_list =
g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[selfid].get_poller();
t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list =
&g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[selfid];
LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock);
LIBCOMM_ELOG(LOG, "CommRcvWorker thread[%d] is initialized.", selfid + 1);
sigjmp_buf local_sigjmp_buf;
int curTryCounter;
int *oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
/* Since not using PG_TRY, must reset error stack by hand */
t_thrd.log_cxt.error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Forget any pending QueryCancel request */
t_thrd.int_cxt.QueryCancelPending = false;
disable_sig_alarm(true);
t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the client and/or server log */
EmitErrorReport();
(void)MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
FlushErrorState();
LIBCOMM_ELOG(ERROR, "Receiver Loop encounter ERROR level ereport which is unsupported.");
}
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
t_thrd.int_cxt.ImmediateInterruptOK = false;
thread_start_time = thread_last_check_time = thread_end_time = mc_timers_us();
/* loop forever on waiting for epoll events */
for (;;) {
/* if the datanode will shutdown, thread should exit */
if (g_instance.comm_cxt.reqcheck_cxt.g_shutdown_requested) {
mc_poller_term(t_thrd.comm_cxt.g_libcomm_poller_list);
if (g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket >= 0) {
mc_tcp_close(g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket);
}
break;
}
gs_flow_thread_time("(r|recv loop)\tgs_receivers_loop",
&thread_start_time, thread_end_time, &thread_last_check_time, &thread_work_all_time, selfid + GS_RECV_LOOP);
commReceiverLoop(selfid, &thread_end_time, &epoll_timeout_count);
}
/* All done, go away */
proc_exit(0);
}
/*
* Description: forkexec routine for the communicator process.
* Format up the arglist, then fork and exec.
*
* Returns: ThreadId
*/
ThreadId commSenderFlowForkexec(void)
{
return initialize_util_thread(COMM_SENDERFLOWER);
}
ThreadId commReceiverFlowForkexec(void)
{
return initialize_util_thread(COMM_RECEIVERFLOWER);
}
ThreadId commAuxiliaryForkexec(void)
{
return initialize_util_thread(COMM_AUXILIARY);
}
ThreadId commReceiverForkexec(void* tid)
{
return initialize_util_thread(COMM_RECEIVER, tid);
}
/*
* Description: Main entry point for libcomm sender flow thread, to be called from the postmaster.
*
* Returns: ThreadId
*/
ThreadId startCommSenderFlow(void)
{
ThreadId comm_sender_flower_pid;
comm_sender_flow_init();
#ifdef EXEC_BACKEND
switch ((comm_sender_flower_pid = commSenderFlowForkexec())) {
#else
switch ((comm_sender_flower_pid = fork_process())) {
#endif
case -1:
ereport(LOG, (errmsg("could not fork comm sender flower process: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* the code below is executing in postmaster child ... */
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
/* Lose the postmaster's on-exit routines */
on_exit_reset();
commSenderFlowMain(0, NULL);
return 0;
#endif
default:
return comm_sender_flower_pid;
}
}
/*
* Description: Main entry point for libcomm receiver flow thread, to be called from the postmaster.
*
* Returns: ThreadId
*/
ThreadId startCommReceiverFlow()
{
ThreadId comm_receiver_flower_pid;
comm_receivers_comm_init();
comm_receiver_flow_init(g_instance.comm_cxt.g_receivers->server_ctrl_tcp_port);
#ifdef EXEC_BACKEND
switch ((comm_receiver_flower_pid = commReceiverFlowForkexec())) {
#else
switch ((comm_receiver_flower_pid = fork_process())) {
#endif
case -1:
ereport(LOG, (errmsg("could not fork comm sender flower process: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* the code below is executing in postmaster child ... */
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
/* Lose the postmaster's on-exit routines */
on_exit_reset();
commReceiverFlowMain();
return 0;
#endif
default:
return comm_receiver_flower_pid;
}
}
/*
* Description: Main entry point for libcomm auxiliary thread, to be called from the postmaster.
*
* Returns: ThreadId
*/
ThreadId startCommAuxiliary()
{
ThreadId comm_auxiliary_pid;
#ifdef EXEC_BACKEND
switch ((comm_auxiliary_pid = commAuxiliaryForkexec())) {
#else
switch ((comm_auxiliary_pid = fork_process())) {
#endif
case -1:
ereport(LOG, (errmsg("could not fork comm auxiliary flower process: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* the code below is executing in postmaster child ... */
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
/* Lose the postmaster's on-exit routines */
on_exit_reset();
commAuxiliaryMain();
return 0;
#endif
default:
return comm_auxiliary_pid;
}
}
/*
* Description: Main entry point for libcomm receiver thread, to be called from the postmaster.
*
* Returns: ThreadId
*/
ThreadId startCommReceiver(int* tid)
{
ThreadId comm_receiver_pid;
#ifdef EXEC_BACKEND
switch ((comm_receiver_pid = commReceiverForkexec(tid)))
#else
switch ((comm_receiver_pid = fork_process()))
#endif
{
case -1:
ereport(LOG, (errmsg("could not fork comm receiver process: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* the code below is executing in postmaster child ... */
/* Close the postmaster's sockets */
ClosePostmasterPorts(false);
/* Lose the postmaster's on-exit routines */
on_exit_reset();
commReceiverMain(0, NULL);
return 0;
#endif
default:
return comm_receiver_pid;
}
}
void startCommReceiverWorker(ThreadId* threadid)
{
LIBCOMM_PTHREAD_MUTEX_INIT(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock, 0);
/* intialize broker(receiver) thread to do receive */
int* thd_receiver_id = NULL;
int error = 0;
uint64 thd_receiver_id_size = (uint64)(g_instance.comm_cxt.counters_cxt.g_recv_num * sizeof(int));
LIBCOMM_MALLOC(thd_receiver_id, thd_receiver_id_size, int);
if (NULL == thd_receiver_id) {
ereport(FATAL, (errmsg("(r|receiver init)\tFailed to init thd_receiver_id:%s.", strerror(errno))));
}
for (int i = 0; i < g_instance.comm_cxt.counters_cxt.g_recv_num; i++) {
error = g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[i].init();
if (error < 0) {
ereport(FATAL, (errmsg("(r|receiver init)\tFailed to init poller list:%s.", strerror(errno))));
}
thd_receiver_id[i] = i;
/* if this is the first thread, it will listen and accept sctp connection */
if (i == 0) {
struct sock_id ltk_fd_id = {g_instance.comm_cxt.g_receivers->server_listen_sctp_conn.socket, 0};
/* save the sctp socket & socket version to hash table */
error = gs_update_fd_to_htab_socket_version(&ltk_fd_id);
if (error < 0) {
ereport(FATAL,
(errmsg("(r|receiver init)\tFailed to save sctp"
" listen socket[%d] and version[%d].",
ltk_fd_id.fd,
ltk_fd_id.id)));
}
/* add fd to poller cabinet */
error = g_instance.comm_cxt.pollers_cxt.g_libcomm_receiver_poller_list[i].add_fd(&ltk_fd_id);
if (error < 0) {
ereport(FATAL,
(errmsg("(r|receiver init)\tFailed to add sctp "
"listen socket[%d] and version[%d] to poller list.",
ltk_fd_id.fd,
ltk_fd_id.id)));
}
}
}
for (int i = 0; i < g_instance.comm_cxt.counters_cxt.g_recv_num; i++) {
/* start broker thread */
threadid[i] = startCommReceiver(thd_receiver_id + i);
if (threadid[i] == 0) {
ereport(FATAL, (errmsg("(r|receiver init)\tFailed to init broker thread.")));
} else {
while (thd_receiver_id[i] != -1) {
(void)usleep(100);
}
}
}
LIBCOMM_FREE(thd_receiver_id, thd_receiver_id_size);
return;
}