openGauss-server/src/gausskernel/process/postmaster/postmaster.cpp

11034 lines
395 KiB
C++
Executable File

/* -------------------------------------------------------------------------
*
* postmaster.cpp
* This program acts as a clearing house for requests to the
* POSTGRES system. Frontend programs send a startup message
* to the Postmaster and the postmaster uses the info in the
* message to setup a backend process.
*
* The postmaster also manages system-wide operations such as
* startup and shutdown. The postmaster itself doesn't do those
* operations, mind you --- it just forks off a subprocess to do them
* at the right times. It also takes care of resetting the system
* if a backend crashes.
*
* The postmaster process creates the shared memory and semaphore
* pools during startup, but as a rule does not touch them itself.
* In particular, it is not a member of the PGPROC array of backends
* and so it cannot participate in lock-manager operations. Keeping
* the postmaster away from shared memory operations makes it simpler
* and more reliable. The postmaster is almost always able to recover
* from crashes of individual backends by resetting shared memory;
* if it did much with shared memory then it would be prone to crashing
* along with the backends.
*
* When a request message is received, we now fork() immediately.
* The child process performs authentication of the request, and
* then becomes a backend if successful. This allows the auth code
* to be written in a simple single-threaded style (as opposed to the
* crufty "poor man's multitasking" code that used to be needed).
* More importantly, it ensures that blockages in non-multithreaded
* libraries like SSL or PAM cannot cause denial of service to other
* clients.
*
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 2010-2012 Postgres-XC Development Group
*
*
* IDENTIFICATION
* src/gausskernel/process/postmaster/postmaster.cpp
*
* NOTES
*
* Initialization:
* The Postmaster sets up shared memory data structures
* for the backends.
*
* Synchronization:
* The Postmaster shares memory with the backends but should avoid
* touching shared memory, so as not to become stuck if a crashing
* backend screws up locks or shared memory. Likewise, the Postmaster
* should never block on messages from frontend clients.
*
* Garbage Collection:
* The Postmaster cleans up after backends if they have an emergency
* exit and/or core dump.
*
* Error Reporting:
* Use write_stderr() only for reporting "interactive" errors
* (essentially, bogus arguments on the command line). Once the
* postmaster is launched, use ereport().
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include <bbox.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <openssl/rand.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <sys/resource.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include "access/cbmparsexlog.h"
#include "access/obs/obs_am.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/pg_control.h"
#include "instruments/instr_unique_sql.h"
#include "instruments/instr_user.h"
#include "instruments/percentile.h"
#include "opfusion/opfusion_util.h"
#include "lib/dllist.h"
#include "libpq/auth.h"
#include "libpq/ip.h"
#include "libpq/libpq.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgxc/pgxc.h"
/* COORD */
#include "pgxc/locator.h"
#include "nodes/nodes.h"
#include "nodes/memnodes.h"
#include "pgxc/poolmgr.h"
#include "access/gtm.h"
#include "pgstat.h"
#include "instruments/snapshot.h"
#include "pgaudit.h"
#include "job/job_scheduler.h"
#include "job/job_worker.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgworker_internals.h"
#include "postmaster/pagewriter.h"
#include "postmaster/fork_process.h"
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "postmaster/alarmchecker.h"
#include "postmaster/aiocompleter.h"
#include "postmaster/fencedudf.h"
#include "replication/heartbeat.h"
#include "replication/catchup.h"
#include "replication/dataqueue.h"
#include "replication/datasender.h"
#include "replication/datasender_private.h"
#include "replication/datareceiver.h"
#include "replication/replicainternal.h"
#include "replication/walsender.h"
#include "replication/walsender_private.h"
#include "replication/walreceiver.h"
#include "postmaster/bgwriter.h"
#include "postmaster/cbmwriter.h"
#include "postmaster/remoteservice.h"
#include "postmaster/startup.h"
#include "postmaster/twophasecleaner.h"
#include "postmaster/licensechecker.h"
#include "postmaster/walwriter.h"
#include "postmaster/lwlockmonitor.h"
#include "replication/walreceiver.h"
#include "replication/datareceiver.h"
#include "replication/slot.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pg_sema.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/remote_read.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/postinit.h"
#include "utils/ps_status.h"
#include "utils/plog.h"
#include "utils/zfiles.h"
#include "utils/inval.h"
#include "utils/resowner.h"
#include "workload/cpwlm.h"
#include "workload/workload.h"
#ifdef EXEC_BACKEND
#include "storage/spin.h"
#endif
#include "access/dfs/dfs_insert.h"
#include "access/twophase.h"
#include "alarm/alarm.h"
#include "auditfuncs.h"
#include "catalog/pg_type.h"
#include "common/config/cm_config.h"
#include "executor/execStream.h"
#include "funcapi.h"
#include "gs_thread.h"
#include "gssignal/gs_signal.h"
#include "libcomm/libcomm.h"
#include "libpq/pqformat.h"
#include "postmaster/startup.h"
#include "storage/spin.h"
#include "threadpool/threadpool.h"
#include "utils/guc.h"
#include "utils/guc_tables.h"
#include "utils/mmpool.h"
#include "libcomm/libcomm.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "utils/memprot.h"
#include "pgstat.h"
#ifdef ENABLE_UT
#define static
int bbox_handler_exit = 0;
#endif
#include "distributelayer/streamProducer.h"
#include "eSDKOBS.h"
#include "cjson/cJSON.h"
#include "tcop/stmt_retry.h"
#include "gaussdb_version.h"
#include "hotpatch/hotpatch.h"
#include "hotpatch/hotpatch_backend.h"
// Parallel recovery
#include "access/parallel_recovery/page_redo.h"
#include "access/multi_redo_api.h"
#include "postmaster/postmaster.h"
#include "access/parallel_recovery/dispatcher.h"
#include "utils/distribute_test.h"
#include "commands/user.h"
extern int S3_init();
extern void TermMOT();
static const int RECOVERY_PARALLELISM_DEFAULT = 1;
/* flag to get logic cluster name for dn alarm */
static bool isNeedGetLCName = true;
/* logic cluster name list file name */
#define LOGIC_CLUSTER_LIST "logic_cluster_name.txt"
/* judge whether a char is digital */
#define isDigital(_ch) (((_ch) >= '0') && ((_ch) <= '9'))
#define IS_FD_TO_RECV_GSSOCK(fd) \
((fd) == t_thrd.postmaster_cxt.sock_for_libcomm || (fd) == t_thrd.libpq_cxt.listen_fd_for_recv_flow_ctrl)
/* These two are only here before of the SSL multithread initialization of OpenSSL component */
#include "ssl/gs_openssl_client.h"
#define MAXLISTEN 64
#define CACHETHRESHOLD 268435456
#define PM_BUSY_ALARM_USED_US 30000000L
#define PM_BUSY_ALARM_US 1000000L
#define PM_POLL_TIMEOUT_SECOND 20
#define PM_POLL_TIMEOUT_MINUTE 58*SECS_PER_MINUTE*60*1000000L
static char gaussdb_state_file[MAXPGPATH] = {0};
char redo_stats_file[MAXPGPATH] = {0};
uint32 noProcLogicTid = 0;
volatile int Shutdown = NoShutdown;
extern void gs_set_hs_shm_data(HaShmemData* ha_shm_data);
extern void ReaperBackendMain();
#define EXTERN_SLOTS_NUM 17
volatile PMState pmState = PM_INIT;
bool dummyStandbyMode = false;
volatile uint64 sync_system_identifier = 0;
bool FencedUDFMasterMode = false;
extern char* optarg;
extern int optind, opterr;
#ifdef HAVE_INT_OPTRESET
extern int optreset; /* might not be declared by system headers */
#endif
#ifdef USE_BONJOUR
static DNSServiceRef bonjour_sdref = NULL;
#endif
/* for backtrace function */
pthread_mutex_t bt_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_rwlock_t hba_rwlock = PTHREAD_RWLOCK_INITIALIZER;
extern bool data_catchup;
extern bool wal_catchup;
char g_bbox_dump_path[1024] = {0};
#define CHECK_FOR_PROCDIEPENDING() \
do { \
if (t_thrd.int_cxt.ProcDiePending) { \
if (t_thrd.storage_cxt.cancel_from_timeout) { \
ereport(FATAL, \
(errcode(ERRCODE_QUERY_CANCELED), \
errmsg("terminate because pooler connect timeout(%ds) when process startup packet", \
u_sess->attr.attr_network.PoolerConnectTimeout))); \
} else { \
proc_exit(1); \
} \
} \
} while (0)
#define WalRcvIsOnline() \
((g_instance.pid_cxt.WalReceiverPID != 0 && t_thrd.walreceiverfuncs_cxt.WalRcv && \
t_thrd.walreceiverfuncs_cxt.WalRcv->isRuning))
#define DataRcvIsOnline() \
((IS_DN_DUMMY_STANDYS_MODE() ? (g_instance.pid_cxt.DataReceiverPID != 0 && t_thrd.datareceiver_cxt.DataRcv && \
t_thrd.datareceiver_cxt.DataRcv->isRuning) \
: true))
/*
* postmaster.c - function prototypes
*/
static void CloseServerPorts(int status, Datum arg);
static void getInstallationPaths(const char* argv0);
static void checkDataDir(void);
static void CheckGUCConflicts(void);
static Port* ConnCreateToRecvGssock(pollfd* ufds, int idx, int* nSockets);
static Port* ConnCreate(int serverFd);
static void reset_shared(int port);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void startup_alarm(SIGNAL_ARGS);
static void SetWalsndsNodeState(ClusterNodeState requester, ClusterNodeState others);
static void ProcessDemoteRequest(void);
static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
static void CleanupBackend(ThreadId pid, int exitstatus);
static bool CleanupBackgroundWorker(ThreadId pid, int exitstatus);
static const char* GetProcName(ThreadId pid);
static void LogChildExit(int lev, const char* procname, ThreadId pid, int exitstatus);
static void PostmasterStateMachine(void);
static void BackendInitialize(Port* port);
static int BackendRun(Port* port);
static void ExitPostmaster(int status);
static int ServerLoop(void);
static int BackendStartup(Port* port);
static void processCancelRequest(Port* port, void* pkt);
#ifdef ENABLE_MULTIPLE_NODES
static void processConnectStreamRequest(Port* port, void* pkt, bool reject);
static void processStopRequest(Port* port, void* pkt);
#endif
#ifndef HAVE_POLL
static int initMasks(fd_set* rmask);
#endif
static int initPollfd(struct pollfd* ufds);
static void report_fork_failure_to_client(Port* port, int errnum, const char* specialErrorInfo = NULL);
void signal_child(ThreadId pid, int signal, int be_mode = -1);
void signal_child(ThreadId pid, int signal, int be_mode, bool is_thread_pool);
static bool SignalSomeChildren(int signal, int targets);
static bool IsChannelAdapt(Port* port, ReplConnInfo* repl);
static bool IsLocalPort(Port* port);
static bool IsLocalIp(const char* address);
static bool IsInplicitIp(const char* address);
static int NeedPoolerPort(void);
static void SetHaShmemData(void);
static bool IsAlreadyListen(const char* ip, int port);
static void IntArrayRegulation(int array[], int len, int def);
static void ListenSocketRegulation(void);
static bool ParseHaListenAddr(LISTEN_ADDRS* pListenList);
static void CreateHaListenSocket(void);
static void RemoteHostInitilize(Port* port);
static int StartupPacketInitialize(Port* port);
static void PsDisplayInitialize(Port* port);
static ServerMode get_cur_mode(void);
static int get_cur_repl_num(void);
static void PMReadDBStateFile(GaussState* state);
static void PMSetDBStateFile(GaussState* state);
static void PMUpdateDBState(DbState db_state, ServerMode mode, int conn_num);
static void PMUpdateDBStateLSN(void);
static void PMUpdateDBStateHaRebuildReason(void);
#define SignalChildren(sig) SignalSomeChildren(sig, BACKEND_TYPE_ALL)
static void StartPgjobWorker(void);
static void StartPoolCleaner(void);
static void check_and_reset_ha_listen_port(void);
static void* cJSON_internal_malloc(size_t size);
static bool NeedHeartbeat();
static ServerMode GetHaShmemMode(void);
static bool assign_backendlist_entry(RegisteredBgWorker *rw);
static void maybe_start_bgworkers(void);
bool PMstateIsRun(void);
/*
* Possible types of a backend. These are OR-able request flag bits
* for SignalSomeChildren() and CountChildren().
*/
#define BACKEND_TYPE_NORMAL 0x0001 /* normal backend */
#define BACKEND_TYPE_AUTOVAC 0x0002 /* autovacuum worker process */
#define BACKEND_TYPE_WALSND 0x0004 /* walsender process */
#define BACKEND_TYPE_DATASND 0x0008 /* datasender process */
#define BACKEND_TYPE_TEMPBACKEND \
0x0010 /* temp thread processing cancel signal \
or stream connection */
#define BACKEND_TYPE_BGWORKER 0x0020
#define BACKEND_TYPE_ALL 0x003F /* OR of all the above */
static int CountChildren(int target);
static bool CreateOptsFile(int argc, const char* argv[], const char* fullprogname);
static void UpdateOptsFile(void);
static void StartAutovacuumWorker(void);
static ThreadId StartCatchupWorker(void);
static void InitPostmasterDeathWatchHandle(void);
static void NotifyShutdown(void);
static void NotifyProcessActive(void);
static int init_stream_comm(void);
int GaussDbThreadMain(knl_thread_arg* arg);
#ifdef EXEC_BACKEND
typedef int InheritableSocket;
typedef struct LWLock LWLock; /* ugly kluge */
/*
* Structure contains all variables passed to exec:ed backends
*/
typedef struct {
Port port;
pgsocket portsocket;
char DataDir[MAXPGPATH];
pgsocket ListenSocket[MAXLISTEN];
long MyCancelKey;
int MyPMChildSlot;
#ifndef WIN32
unsigned long UsedShmemSegID;
#else
HANDLE UsedShmemSegID;
#endif
void* UsedShmemSegAddr;
slock_t* ShmemLock;
VariableCache ShmemVariableCache;
LWLock* mainLWLockArray;
PMSignalData* PMSignalState;
char LocalAddrList[MAXLISTEN][IP_LEN];
int LocalIpNum;
HaShmemData* HaShmData;
TimestampTz PgStartTime;
TimestampTz PgReloadTime;
pg_time_t first_syslogger_file_time;
bool redirection_done;
bool IsBinaryUpgrade;
int max_safe_fds;
int max_files_per_process;
int max_userdatafiles;
int postmaster_alive_fds[2];
int syslogPipe[2];
char my_exec_path[MAXPGPATH];
char pkglib_path[MAXPGPATH];
Oid myTempNamespace;
Oid myTempToastNamespace;
bool comm_ipc_log;
} BackendParameters;
static BackendParameters backend_save_para;
static void read_backend_variables(char* id, Port* port);
static void restore_backend_variables(BackendParameters* param, Port* port);
#ifndef WIN32
static bool save_backend_variables(BackendParameters* param, Port* port);
#else
static bool save_backend_variables(BackendParameters* param, Port* port, HANDLE childProcess, pid_t childPid);
#endif
#endif /* EXEC_BACKEND */
static void BackendArrayAllocation(void);
static void BackendArrayRemove(Backend* bn);
PMStateInfo pmStateDescription[] = {{PM_INIT, "PM_INIT"},
{PM_STARTUP, "PM_STARTUP"},
{PM_RECOVERY, "PM_RECOVERY"},
{PM_HOT_STANDBY, "PM_HOT_STANDBY"},
{PM_RUN, "PM_RUN"},
{PM_WAIT_BACKUP, "PM_WAIT_BACKUP"},
{PM_WAIT_READONLY, "PM_WAIT_READONLY"},
{PM_WAIT_BACKENDS, "PM_WAIT_BACKENDS"},
{PM_SHUTDOWN, "PM_SHUTDOWN"},
{PM_SHUTDOWN_2, "PM_SHUTDOWN_2"},
{PM_WAIT_DEAD_END, "PM_WAIT_DEAD_END"},
{PM_NO_CHILDREN, "PM_NO_CHILDREN"}};
/* convert PM state to string */
const char* GetPMState(const PMState pmStateCode)
{
uint32_t count;
for (count = 0; count < lengthof(pmStateDescription); count++) {
if (pmStateCode == pmStateDescription[count].pmState) {
return pmStateDescription[count].pmStateMsg;
}
}
return "UNKOWN";
}
/*
* While adding a new node to the cluster we need to restore the schema of
* an existing database to the new node.
* If the new node is a datanode and we connect directly to it,
* it does not allow DDL, because it is in read only mode &
* If the new node is a coordinator it will send DDLs to all the other
* coordinators which we do not want it to do
* To provide ability to restore on the new node a new command line
* argument is provided called --restoremode
* It is to be provided in place of --coordinator OR --datanode.
* In restore mode both coordinator and datanode are internally
* treated as a datanode.
*/
bool isRestoreMode = false;
/*
*The securitymode is to satisfy the security request of database on
*cloud environment. In securitymode, some operations are carried out
*to satisfy the security request.
*/
bool isSecurityMode = false;
#define SECURITY_MODE_NAME "securitymode"
#define SECURITY_MODE_NAME_LEN 12
bool isSingleMode = false;
#ifdef ENABLE_MULTIPLE_NODES
#define StartPoolManager() PoolManagerInit()
#endif
/* Macros to check exit status of a child process */
#define EXIT_STATUS_0(st) ((st) == 0)
#define EXIT_STATUS_1(st) (1 == (st))
extern THR_LOCAL bool executorStopFlag;
extern bool StreamThreadAmI();
extern pthread_mutex_t nodeDefCopyLock;
/*
* @hdfs
* deleteHdfsUser() function is used to clean Hdfs User List and
* release alocated memory
*/
extern void deleteHdfsUser();
extern void CodeGenProcessInitialize();
extern void CodeGenProcessTearDown();
extern void CPmonitorMain(void);
extern void load_searchserver_library();
/*
* Protocol versions we supported:
* For x.y, if y < 50, it's from the postgres's front end.
* And for now, we only support versions: 2.0, 3.0, 3.50
* Attention:
* For protocol version x.y, x and y are integers eachly, and x.y is not a float.
* Which means that 3.50 != 3.5, 3.5 == 3.05
*
* Version desc:
* 2.0 and 3.0 are from postgres.
* 3.50: backend does not send signature to frontend anymore while authorization.
* 3.51: backend send iteration to frontend in the authenication.
*/
const unsigned short protoVersionList[][2] = {{2, 0}, {3, 0}, {3, 50}, {3, 51}};
/* transparent encryption database encryption key. */
extern bool getAndCheckTranEncryptDEK();
void SetServerMode(ServerMode mode)
{
t_thrd.xlog_cxt.server_mode = mode;
}
ServerMode GetServerMode()
{
return (ServerMode)t_thrd.xlog_cxt.server_mode;
}
void ReportAlarmAbnormalDataHAInstListeningSocket()
{
Alarm alarmItem[1];
AlarmAdditionalParam tempAdditionalParam;
// Initialize the alarm item
AlarmItemInitialize(alarmItem, ALM_AI_AbnormalDataHAInstListeningSocket, ALM_AS_Reported, NULL);
// fill the alarm message
WriteAlarmAdditionalInfo(&tempAdditionalParam,
g_instance.attr.attr_common.PGXCNodeName,
"",
"",
alarmItem,
ALM_AT_Fault,
g_instance.attr.attr_common.PGXCNodeName);
// report the alarm
AlarmReporter(alarmItem, ALM_AT_Fault, &tempAdditionalParam);
}
void ReportResumeAbnormalDataHAInstListeningSocket()
{
Alarm alarmItem[1];
AlarmAdditionalParam tempAdditionalParam;
// Initialize the alarm item
AlarmItemInitialize(alarmItem, ALM_AI_AbnormalDataHAInstListeningSocket, ALM_AS_Normal, NULL);
// fill the alarm message
WriteAlarmAdditionalInfo(
&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem, ALM_AT_Resume);
// report the alarm
AlarmReporter(alarmItem, ALM_AT_Resume, &tempAdditionalParam);
}
void SetFlagForGetLCName(bool falg)
{
isNeedGetLCName = falg;
}
uint32 GetNodeId(const char* nodeIdStr)
{
if (nodeIdStr == NULL)
return 0;
int index = 0;
while (nodeIdStr[index] != '\0') {
if (!isDigital(nodeIdStr[index])) {
return 0;
}
index++;
}
uint32 nodeId = (uint32)atoi(nodeIdStr);
return nodeId;
}
/*
* "Safe" wrapper around strdup()
*/
char* pg_strdup(const char* string)
{
char* tmp = NULL;
if (NULL == string) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OPERATION),
errmsg("pg_strdup: cannot duplicate null pointer (internal error)\n")));
}
tmp = strdup(string);
if (NULL == tmp) {
ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("out of memory\n")));
}
return tmp;
}
/* Init the hash table to save the debug query id and the used space */
static void InitDnHashTable(void)
{
HASHCTL hctl;
errno_t rc = 0;
rc = memset_s(&hctl, sizeof(HASHCTL), 0, sizeof(HASHCTL));
securec_check(rc, "", "");
hctl.keysize = sizeof(uint64);
hctl.entrysize = sizeof(DnUsedSpaceHashEntry);
hctl.hash = tag_hash;
hctl.hcxt = g_instance.instance_context;
g_instance.comm_cxt.usedDnSpace = hash_create("SQL used space on datanode",
g_instance.attr.attr_network.MaxConnections,
&hctl,
HASH_ELEM | HASH_FUNCTION | HASH_SHRCTX);
}
/*
*Get master node id from node name
*/
uint32 GetMasterIdByNodeName()
{
uint32 node_id = 0;
char* nodeName = pg_strdup(g_instance.attr.attr_common.PGXCNodeName);
const char delims[] = "_";
char* ptr = NULL;
char* outer_ptr = NULL;
ptr = strtok_r(nodeName, delims, &outer_ptr);
if (ptr != NULL) {
ptr = strtok_r(NULL, delims, &outer_ptr);
}
if (ptr != NULL) {
node_id = GetNodeId(ptr);
}
if (nodeName != NULL) {
free(nodeName);
nodeName = NULL;
}
return node_id;
}
/*
*Get Logic Cluster From LC StaticConfig file for given node
*
*return true if success else return false
*/
bool GetLogicClusterFromConfig(const char* filepath, char* lcname)
{
g_logicClusterName = lcname;
g_datanodeid = GetMasterIdByNodeName();
if (g_datanodeid == 0) {
ereport(LOG, (errmsg("cannot get node id of datanode %s\n", g_instance.attr.attr_common.PGXCNodeName)));
return false;
}
int status = 0;
int err_no = 0;
status = read_logic_cluster_config_files(filepath, &err_no);
switch (status) {
case OPEN_FILE_ERROR:
case READ_FILE_ERROR:
ereport(LOG, (errmsg("could not open or read logic cluster static config files: %s", gs_strerror(err_no))));
return false;
case OUT_OF_MEMORY:
ereport(LOG, (errmsg("out of memory")));
return false;
default:
break;
}
if (g_logicClusterName[0] == '\0') {
int ret = snprintf_s(g_logicClusterName, CLUSTER_NAME_LEN, CLUSTER_NAME_LEN - 1, "elastic_group");
securec_check_ss_c(ret, "\0", "\0");
}
return true;
}
/*
*Get logic cluster name for datanode alarm
*
*if exist logic_cluster_name.txt get logic cluster from LC static config
*else reset logic cluster name
*/
void GetLogicClusterForAlarm(char* lcname)
{
if (!IS_PGXC_DATANODE || !isNeedGetLCName)
return;
int ret;
char LCListfile[MAXPGPATH] = {'\0'};
char* gausshome = getGaussHome();
if (gausshome == NULL)
return;
if (*gausshome == '\0') {
pfree(gausshome);
return;
}
ret = snprintf_s(LCListfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, LOGIC_CLUSTER_LIST);
securec_check_ss_c(ret, "\0", "\0");
pfree(gausshome);
gausshome = NULL;
if (0 == access(LCListfile, F_OK)) {
/* if false we need reset the logic cluster name */
if (!GetLogicClusterFromConfig(LCListfile, lcname))
lcname[0] = '\0';
else
SetFlagForGetLCName(false);
} else
lcname[0] = '\0';
}
/*
* get_coredump_pattern_path - get the core dump path from the file "/proc/sys/kernel/core_pattern"
*/
void get_coredump_pattern_path(char* path, Size len)
{
FILE* fp = NULL;
char* p = NULL;
struct stat stat_buf;
if (NULL == (fp = fopen("/proc/sys/kernel/core_pattern", "r"))) {
ereport(LOG, (errmsg("cannot open file: /proc/sys/kernel/core_pattern.")));
return;
}
if (NULL == fgets(path, len, fp)) {
fclose(fp);
ereport(LOG, (errmsg("failed to get the core pattern path.")));
return;
}
fclose(fp);
if ((p = strrchr(path, '/')) == NULL) {
*path = '\0';
} else {
*(++p) = '\0';
}
if (0 != stat(path, &stat_buf) || !S_ISDIR(stat_buf.st_mode) || 0 != access(path, W_OK)) {
ereport(LOG, (errmsg("The core dump path in core_pattern is an invalid directory.")));
*path = '\0';
return;
}
}
/*
* Only update gaussdb.state file's state field.
*
* PARAMETERS:
* state: INPUT new state
* RETURN:
* true if success, otherwise false.
*/
bool SetDBStateFileState(DbState state)
{
/* do nothing while core dump be appeared so early. */
if (strlen(gaussdb_state_file) > 0) {
char temppath[MAXPGPATH] = {0};
GaussState s;
/* zero it in case gaussdb.state doesn't exist. */
int rc = memset_s(&s, sizeof(GaussState), 0, sizeof(GaussState));
securec_check_c(rc, "\0", "\0");
rc = snprintf_s(temppath, MAXPGPATH, MAXPGPATH - 1, "%s.temp", gaussdb_state_file);
securec_check_intval(rc, , false);
/* Write the new content into a temp file and rename it at last. */
int fd = open(gaussdb_state_file, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT) {
ereport(LOG, (errmsg("gaussdb.state does not exist, and skipt setting since it is optional.")));
return true;
} else {
ereport(LOG, (errmsg("Failed to open gaussdb.state.temp: %m.")));
return false;
}
}
/* Read old content from file. */
int len = read(fd, &s, sizeof(GaussState));
if (len != sizeof(GaussState)) {
ereport(LOG, (errmsg("Failed to read gaussdb.state: %m.")));
(void)close(fd);
return false;
}
if (close(fd) != 0) {
ereport(LOG, (errmsg("Failed to close gaussdb.state: %m.")));
return false;
}
/* replace state with the new value. */
s.state = state;
fd = open(temppath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
ereport(LOG, (errmsg("Failed to open gaussdb.state.temp: %m.")));
return false;
}
len = write(fd, &s, sizeof(GaussState));
if (len != sizeof(GaussState)) {
ereport(LOG, (errmsg("Failed to write gaussdb.state.temp: %m.")));
(void)close(fd);
return false;
}
if (close(fd) != 0) {
ereport(LOG, (errmsg("Failed to close gaussdb.state.temp: %m.")));
return false;
}
if (rename(temppath, gaussdb_state_file) != 0) {
ereport(LOG, (errmsg("Failed to rename gaussdb.state.temp: %m.")));
return false;
}
}
return true;
}
#ifdef ENABLE_MULTIPLE_NODES
static void coredump_handler(SIGNAL_ARGS)
{
if (g_instance.status != ImmediateShutdown) {
(void)SetDBStateFileState(COREDUMP_STATE);
}
if (postgres_signal_arg != SIGABRT)
abort();
}
#endif
/*
* bbox_handler - handle signal conditions for bbox
*/
static void bbox_handler(SIGNAL_ARGS)
{
sigset_t intMask;
sigset_t oldMask;
(void)SetDBStateFileState(COREDUMP_STATE);
sigfillset(&intMask);
pthread_sigmask(SIG_SETMASK, &intMask, &oldMask);
(void)BBOX_CreateCoredump(NULL);
#ifdef ENABLE_UT
if (bbox_handler_exit == 0)
#endif
_exit(0);
}
void gs_hotpatch_log_callback(int level, char* logstr)
{
ereport(level, (errmsg("%s", logstr)));
}
/*
* check_bbox_corepath - check coredump path for bbox
*/
bool check_bbox_corepath(char** newval, void** extra, GucSource source)
{
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
return true;
struct stat stat_buf;
errno_t rc = 0;
const int path_len = 1024;
/* initialize the first character */
char core_dump_path[path_len] = {0};
/*
* the guc parameter bbox_dump_path is set to NULL as default.
* bbox_dump_path has to be a valid directory, if it is altered by users.
*/
if (newval != NULL && *newval != NULL && (*newval)[0] != '\0') {
if (0 != stat(*newval, &stat_buf) || !S_ISDIR(stat_buf.st_mode) || 0 != access(*newval, W_OK)) {
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("bbox_dump_path %s is an invalid directory!", *newval)));
/* if bbox_dump_path is invalid, the path of core dump will be set as default. */
get_coredump_pattern_path(core_dump_path, sizeof(core_dump_path));
} else {
rc = strcpy_s(core_dump_path, sizeof(core_dump_path), *newval);
securec_check(rc, "\0", "\0");
}
} else {
/*
* default path of the core dump will be obtained
* by reading the file "/proc/sys/kernel/core_pattern"
*/
get_coredump_pattern_path(core_dump_path, sizeof(core_dump_path));
}
if (*core_dump_path) {
(void)BBOX_SetCoredumpPath(core_dump_path);
}
char* result = (char*)MemoryContextAlloc(u_sess->top_mem_cxt, path_len);
if (newval != NULL && *newval != NULL)
pfree(*newval);
rc = strcpy_s(result, path_len, core_dump_path);
securec_check(rc, "\0", "\0");
rc = strcpy_s(g_bbox_dump_path, sizeof(g_bbox_dump_path), result);
securec_check(rc, "\0", "\0");
if (newval != NULL) {
*newval = result;
}
return true;
}
/*
* assign_bbox_corepath - set coredump path for bbox
*/
void assign_bbox_corepath(const char* newval, void* extra)
{
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
return;
}
/*
* assign_bbox_coredump - set coredump for bbox or not
*/
void assign_bbox_coredump(const bool newval, void* extra)
{
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
return;
if (newval) {
(void)pqsignal(SIGABRT, bbox_handler);
(void)pqsignal(SIGBUS, bbox_handler);
(void)pqsignal(SIGILL, bbox_handler);
(void)pqsignal(SIGSEGV, bbox_handler);
} else {
(void)pqsignal(SIGABRT, SIG_DFL);
(void)pqsignal(SIGBUS, SIG_DFL);
(void)pqsignal(SIGILL, SIG_DFL);
(void)pqsignal(SIGSEGV, SIG_DFL);
}
}
void signal_sysloger_flush(void)
{
if (g_instance.status == ImmediateShutdown) { // fast quit
return;
}
if (t_thrd.postmaster_cxt.redirection_done == false) {
fflush(stdout);
fflush(stderr);
} else if (g_instance.pid_cxt.SysLoggerPID != 0) {
set_flag_to_flush_buffer();
signal_child(g_instance.pid_cxt.SysLoggerPID, SIGUSR1);
pg_usleep(100000);
}
}
void SetShmemCxt(void)
{
int thread_pool_worker_num = 0;
int thread_pool_group_num = 0;
if (g_threadPoolControler != NULL) {
thread_pool_worker_num = g_threadPoolControler->GetThreadNum();
thread_pool_group_num = g_threadPoolControler->GetGroupNum();
}
/* Keep enough slot for thread pool. */
g_instance.shmem_cxt.MaxConnections = Max((g_instance.attr.attr_network.MaxConnections +
g_instance.attr.attr_network.maxInnerToolConnections), thread_pool_worker_num);
g_instance.shmem_cxt.MaxBackends = g_instance.shmem_cxt.MaxConnections +
g_instance.attr.attr_sql.job_queue_processes + 1 +
g_instance.attr.attr_storage.autovacuum_max_workers +
g_instance.attr.attr_storage.max_background_workers +
AUXILIARY_BACKENDS +
AV_LAUNCHER_PROCS;
g_instance.shmem_cxt.MaxReserveBackendId = g_instance.attr.attr_sql.job_queue_processes + 1 +
g_instance.attr.attr_storage.autovacuum_max_workers +
(thread_pool_worker_num * STREAM_RESERVE_PROC_TIMES) +
AUXILIARY_BACKENDS +
AV_LAUNCHER_PROCS;
g_instance.shmem_cxt.ThreadPoolGroupNum = thread_pool_group_num;
Assert(g_instance.shmem_cxt.MaxBackends <= MAX_BACKENDS);
}
static void print_port_info()
{
FILE* fp = NULL;
StringInfoData strinfo;
initStringInfo(&strinfo);
char buf[MAXPGPATH];
appendStringInfo(&strinfo, "lsof -i:%d", g_instance.attr.attr_network.PostPortNumber);
fp = popen(strinfo.data, "r");
if (fp == NULL) {
ereport(LOG, (errmsg("Unable to use 'lsof' to read port info.")));
} else {
ereport(LOG, (errmsg("exec cmd: %s", strinfo.data)));
while (fgets(buf, sizeof(buf), fp) != NULL) {
ereport(LOG, (errmsg("<lsof>:%s", buf)));
}
pclose(fp);
}
resetStringInfo(&strinfo);
appendStringInfo(&strinfo, "netstat -anp | grep %d", g_instance.attr.attr_network.PostPortNumber);
fp = popen(strinfo.data, "r");
if (fp == NULL) {
ereport(LOG, (errmsg("Unable to use 'netstat' to read port info.")));
} else {
ereport(LOG, (errmsg("exec cmd: %s", strinfo.data)));
while (fgets(buf, sizeof(buf), fp) != NULL) {
ereport(LOG, (errmsg("<netstat>:%s", buf)));
}
pclose(fp);
}
pfree_ext(strinfo.data);
return;
}
/*
* Postmaster main entry point
*/
int PostmasterMain(int argc, char* argv[])
{
int opt;
int status = STATUS_OK;
char* output_config_variable = NULL;
char* userDoption = NULL;
bool listen_addr_saved = false;
int use_pooler_port = -1;
int i;
GaussState state;
struct stat stat_buf;
MemoryContext oldcontext;
OptParseContext optCtxt;
errno_t rc = 0;
t_thrd.proc_cxt.MyProcPid = PostmasterPid = gs_thread_self();
t_thrd.proc_cxt.MyStartTime = time(NULL);
IsPostmasterEnvironment = true;
knl_thread_set_name("GaussMaster");
/*
* for security, no dir or file created can be group or other accessible
*/
umask(S_IRWXG | S_IRWXO);
/*
* Fire up essential subsystems: memory management
*/
/*
* By default, palloc() requests in the postmaster will be allocated in
* the t_thrd.mem_cxt.postmaster_mem_cxt, which is space that can be recycled by backends.
* Allocated data that needs to be available to backends should be
* allocated in t_thrd.top_mem_cxt.
*/
t_thrd.mem_cxt.postmaster_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt,
"Postmaster",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemoryContextSwitchTo(t_thrd.mem_cxt.postmaster_mem_cxt);
/*
* We switch to HDFSUSERContext. There maybe some tasks in getHdfsUser using palloc().
* Everything we need related to HDFSUSER will be alloced on HDFSUSERContext.
*/
oldcontext = MemoryContextSwitchTo(g_instance.instance_context);
/**
* initialize version info.
*/
initialize_feature_flags();
/*
* @OBS
* Create a global OBS CA object shared among threads
*/
initOBSCacheObject();
S3_init();
/* set memory manager for minizip libs */
pm_set_unzip_memfuncs();
/* set memory manager for cJSON */
cJSON_Hooks hooks = {cJSON_internal_malloc, cJSON_internal_free};
cJSON_InitHooks(&hooks);
/*
* Prepare codegen enviroment.
*/
CodeGenProcessInitialize();
/* Switch to oldcontext */
MemoryContextSwitchTo(oldcontext);
/* Initialize paths to installation files */
getInstallationPaths(argv[0]);
/*
* Options setup
*/
InitializeGUCOptions();
/*
*Initialize Callback function type of cb_for_getlc
*/
SetcbForGetLCName(GetLogicClusterForAlarm);
optCtxt.opterr = 1;
/*
* Parse command-line options. CAUTION: keep this in sync with
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
initOptParseContext(&optCtxt);
while ((opt = getopt_r(argc, argv, "A:B:bc:C:D:d:EeFf:h:ijk:lM:N:nOo:Pp:Rr:S:sTt:u:W:-:", &optCtxt)) != -1) {
switch (opt) {
case 'A':
SetConfigOption("debug_assertions", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'B':
SetConfigOption("shared_buffers", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'b':
/* Undocumented flag used for binary upgrades */
u_sess->proc_cxt.IsBinaryUpgrade = true;
break;
case 'C':
output_config_variable = optCtxt.optarg;
break;
case 'D':
userDoption = optCtxt.optarg;
break;
case 'd':
set_debug_options(atoi(optCtxt.optarg), PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'E':
SetConfigOption("log_statement", "all", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'e':
SetConfigOption("datestyle", "euro", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'F':
SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'f':
if (!set_plan_disabling_options(optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV)) {
write_stderr("%s: invalid argument for option -f: \"%s\"\n", progname, optCtxt.optarg);
ExitPostmaster(1);
}
break;
case 'h':
SetConfigOption("listen_addresses", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'i':
SetConfigOption("listen_addresses", "*", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'j':
/* only used by interactive backend */
break;
case 'k':
SetConfigOption("unix_socket_directory", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'l':
SetConfigOption("ssl", "true", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'M':
if (0 == strncmp(optCtxt.optarg, "primary", strlen("primary")) &&
'\0' == optCtxt.optarg[strlen("primary")]) {
t_thrd.xlog_cxt.server_mode = PRIMARY_MODE;
} else if (0 == strncmp(optCtxt.optarg, "standby", strlen("standby")) &&
'\0' == optCtxt.optarg[strlen("standby")]) {
t_thrd.xlog_cxt.server_mode = STANDBY_MODE;
} else if (0 == strncmp(optCtxt.optarg, "pending", strlen("pending")) &&
'\0' == optCtxt.optarg[strlen("pending")]) {
t_thrd.xlog_cxt.server_mode = PENDING_MODE;
} else if (0 == strncmp(optCtxt.optarg, "normal", strlen("normal")) &&
'\0' == optCtxt.optarg[strlen("normal")]) {
t_thrd.xlog_cxt.server_mode = NORMAL_MODE;
} else {
ereport(FATAL, (errmsg("the options of -M is not recognized")));
}
break;
case 'N':
SetConfigOption("max_connections", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'n':
/* Don't reinit shared mem after abnormal exit */
ereport(FATAL, (errmsg("the options of -n is deprecated")));
break;
case 'O':
SetConfigOption("allow_system_table_mods", "true", PGC_INTERNAL, PGC_S_ARGV);
break;
case 'o':
/* Other options to pass to the backend on the command line */
rc = snprintf_s(g_instance.ExtraOptions + strlen(g_instance.ExtraOptions),
sizeof(g_instance.ExtraOptions) - strlen(g_instance.ExtraOptions),
sizeof(g_instance.ExtraOptions) - strlen(g_instance.ExtraOptions) - 1,
" %s",
optCtxt.optarg);
securec_check_ss_c(rc, "\0", "\0");
break;
case 'P':
SetConfigOption("ignore_system_indexes", "true", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'p':
SetConfigOption("port", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
#ifdef ENABLE_MULTIPLE_NODES
case 'R':
/* indicate run as xlogreiver.Only used with -M standby */
dummyStandbyMode = true;
break;
#endif
case 'r':
/* only used by single-user backend */
break;
case 'S':
SetConfigOption("work_mem", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 's':
SetConfigOption("log_statement_stats", "true", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'T':
/*
* In the event that some backend dumps core, send SIGSTOP,
* rather than SIGQUIT, to all its peers. This lets the wily
* post_hacker collect core dumps from everyone.
*/
ereport(FATAL, (errmsg("the options of -T is deprecated")));
break;
case 't': {
const char* tmp = get_stats_option_name(optCtxt.optarg);
if (tmp != NULL) {
SetConfigOption(tmp, "true", PGC_POSTMASTER, PGC_S_ARGV);
} else {
write_stderr("%s: invalid argument for option -t: \"%s\"\n", progname, optCtxt.optarg);
ExitPostmaster(1);
}
break;
}
case 'u':
/* Undocumented version used for inplace or online upgrades */
errno = 0;
pg_atomic_write_u32(&WorkingGrandVersionNum, (uint32)strtoul(optCtxt.optarg, NULL, 10));
if (errno != 0 || pg_atomic_read_u32(&WorkingGrandVersionNum) > GRAND_VERSION_NUM) {
write_stderr("%s: invalid argument for option -u: \"%s\", GRAND_VERSION_NUM is %u\n",
progname,
optCtxt.optarg,
(uint32)GRAND_VERSION_NUM);
ExitPostmaster(1);
} else if (pg_atomic_read_u32(&WorkingGrandVersionNum) == INPLACE_UPGRADE_PRECOMMIT_VERSION) {
pg_atomic_write_u32(&WorkingGrandVersionNum, GRAND_VERSION_NUM);
InplaceUpgradePrecommit = true;
g_instance.comm_cxt.force_cal_space_info = true;
}
break;
case 'W':
SetConfigOption("post_auth_delay", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'c':
case '-': {
char* name = NULL;
char* value = NULL;
ParseLongOption(optCtxt.optarg, &name, &value);
#ifndef ENABLE_MULTIPLE_NODES
if (opt == '-' && (strcmp(name, "coordinator") == 0 || strcmp(name, "datanode") == 0)) {
ereport(FATAL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Single node mode: must start as single node (--single_node)\n")));
}
#endif
/* A Coordinator is being activated */
if (name != NULL && strcmp(name, "coordinator") == 0 && value == NULL)
g_instance.role = VCOORDINATOR;
else if (name != NULL && strcmp(name, "datanode") == 0 && value == NULL)
g_instance.role = VDATANODE;
/* A SingleDN mode is being activated */
else if (strcmp(name, "single_node") == 0 && value == NULL) {
g_instance.role = VSINGLENODE;
useLocalXid = true;
} else if (name != NULL && strcmp(name, "restoremode") == 0 && value == NULL) {
/*
* In restore mode both coordinator and datanode
* are internally treated as datanodes
*/
isRestoreMode = true;
g_instance.role = VDATANODE;
#ifdef ENABLE_MULTIPLE_NODES
} else if (name != NULL && 0 == strncasecmp(name, "fenced", strlen(name)) && value == NULL) {
FencedUDFMasterMode = true;
#endif
} else if (name != NULL && strlen(name) == SECURITY_MODE_NAME_LEN &&
strncmp(name, SECURITY_MODE_NAME, SECURITY_MODE_NAME_LEN) == 0 && value == NULL) {
/* In securitymode, safety strategy is opened */
isSecurityMode = true;
} else {
/* default case */
if (value == NULL) {
if (opt == '-')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg)));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg)));
}
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
}
pfree(name);
if (value != NULL) {
pfree(value);
}
break;
}
default:
write_stderr("Try \"%s --help\" for more information.\n", progname);
ExitPostmaster(1);
}
}
ereport(LOG, (errmsg("Begin to start openGauss Database.")));
#ifndef ENABLE_MULTIPLE_NODES
/* single_node mode default role is VSINGLENODE and must be it */
if (g_instance.role == VUNKNOWN) {
g_instance.role = VSINGLENODE;
useLocalXid = true;
} else if (g_instance.role != VSINGLENODE) {
write_stderr("Single node mode: must start as single node.\n");
ExitPostmaster(1);
}
#else
if (!IS_PGXC_COORDINATOR && !IS_PGXC_DATANODE && !FencedUDFMasterMode) {
write_stderr(
"%s: Postgres-XC: must start as either a Coordinator (--coordinator) or Datanode (--datanode)\n", progname);
ExitPostmaster(1);
}
#endif
/*
* Postmaster accepts no non-option switch arguments.
*/
if (optCtxt.optind < argc) {
write_stderr("%s: invalid argument: \"%s\"\n", progname, argv[optCtxt.optind]);
write_stderr("Try \"%s --help\" for more information.\n", progname);
ExitPostmaster(1);
}
/*
* Locate the proper configuration files and data directory, and read
* postgresql.conf for the first time.
*/
if (!FencedUDFMasterMode && !SelectConfigFiles(userDoption, progname))
ExitPostmaster(1);
if ((g_instance.attr.attr_security.transparent_encrypted_string != NULL &&
g_instance.attr.attr_security.transparent_encrypted_string[0] != '\0') &&
(g_instance.attr.attr_common.transparent_encrypt_kms_url != NULL &&
g_instance.attr.attr_common.transparent_encrypt_kms_url[0] != '\0') &&
(g_instance.attr.attr_security.transparent_encrypt_kms_region != NULL &&
g_instance.attr.attr_security.transparent_encrypt_kms_region[0] != '\0')) {
isSecurityMode = true;
}
/*
* Initiaize Postmaster level GUC option.
*
* Note that some guc can't get right value because of some global var not init,
* for example single_node mode,
* so need this function to init postmaster level guc.
*/
InitializePostmasterGUC();
t_thrd.myLogicTid = noProcLogicTid + POSTMASTER_LID;
if (output_config_variable != NULL) {
/*
* permission is handled because the user is reading inside the data
* dir
*/
puts(GetConfigOption(output_config_variable, false, false));
ExitPostmaster(0);
}
noProcLogicTid = GLOBAL_ALL_PROCS;
/* Run as FencedUDF master */
if (FencedUDFMasterMode) {
/* t_thrd.proc_cxt.DataDir must be set */
if (userDoption != NULL && userDoption[0] == '/') {
if (chdir(userDoption) == -1)
ExitPostmaster(1);
SetDataDir(userDoption);
} else
ExitPostmaster(1);
/* init thread args pool for ever sub threads except signal moniter */
gs_thread_args_pool_init(GLOBAL_ALL_PROCS + EXTERN_SLOTS_NUM, sizeof(BackendParameters));
/* Init signal manage struct */
gs_signal_slots_init(GLOBAL_ALL_PROCS + EXTERN_SLOTS_NUM);
gs_signal_startup_siginfo("PostmasterMain");
gs_signal_monitor_startup();
} else {
/* Verify that t_thrd.proc_cxt.DataDir looks reasonable */
checkDataDir();
/* And switch working directory into it */
ChangeToDataDir();
/* Set parallel recovery config */
ConfigRecoveryParallelism();
/*
* Check for invalid combinations of GUC settings.
*/
CheckGUCConflicts();
/*
* Other one-time internal sanity checks can go here, if they are fast.
* (Put any slow processing further down, after postmaster.pid creation.)
*/
if (!CheckDateTokenTables()) {
write_stderr("%s: invalid datetoken tables, please fix\n", progname);
ExitPostmaster(1);
}
/*
* Now that we are done processing the postmaster arguments, reset
* getopt(3) library so that it will work correctly in subprocesses.
*/
optCtxt.optind = 1;
#ifdef HAVE_INT_OPTRESET
optreset = 1; /* some systems need this too */
#endif
int rc1 = snprintf_s(
gaussdb_state_file, sizeof(gaussdb_state_file), MAXPGPATH - 1, "%s/gaussdb.state", t_thrd.proc_cxt.DataDir);
securec_check_intval(rc1, , -1);
gaussdb_state_file[MAXPGPATH - 1] = '\0';
if (!SetDBStateFileState(UNKNOWN_STATE)) {
write_stderr("Failed to set gaussdb.state with UNKNOWN_STATE");
ExitPostmaster(1);
}
rc1 = snprintf_s(
redo_stats_file, sizeof(redo_stats_file), MAXPGPATH - 1, "%s/redo.state", t_thrd.proc_cxt.DataDir);
securec_check_intval(rc1, , -1);
redo_stats_file[MAXPGPATH - 1] = '\0';
/* For debugging: display postmaster environment */
{
extern char** environ;
char** p;
ereport(DEBUG3, (errmsg_internal("%s: PostmasterMain: initial environment dump:", progname)));
ereport(DEBUG3, (errmsg_internal("-----------------------------------------")));
for (p = environ; *p; ++p)
ereport(DEBUG3, (errmsg_internal("\t%s", *p)));
ereport(DEBUG3, (errmsg_internal("-----------------------------------------")));
}
rc = memcpy_s(g_alarmComponentPath, MAXPGPATH - 1, Alarm_component, strlen(Alarm_component));
securec_check_c(rc, "\0", "\0");
g_alarmReportInterval = AlarmReportInterval;
AlarmEnvInitialize();
/* check if the dek for transparent is correct */
if (IS_PGXC_COORDINATOR || IS_PGXC_DATANODE) {
if (!getAndCheckTranEncryptDEK()) {
ExitPostmaster(1);
}
}
/*
* Create lockfile for data directory.
*
* We want to do this before we try to grab the input sockets, because the
* data directory interlock is more reliable than the socket-file
* interlock (thanks to whoever decided to put socket files in /tmp :-().
* For the same reason, it's best to grab the TCP socket(s) before the
* Unix socket.
*/
CreateDataDirLockFile(true);
/*
* Initialize SSL library, if specified.
*/
#ifdef USE_SSL
if (g_instance.attr.attr_security.EnableSSL)
secure_initialize();
#endif
/* Module load callback */
pgaudit_agent_init();
/*
* process any libraries that should be preloaded at postmaster start
*/
process_shared_preload_libraries();
/*
* Establish input sockets.
*/
for (i = 0; i < MAXLISTEN; i++)
t_thrd.postmaster_cxt.ListenSocket[i] = PGINVALID_SOCKET;
if (SCTP_CN_DN_CONN) {
for (i = 0; i < MAXLISTEN; i++)
t_thrd.postmaster_cxt.SctpListenSocket[i] = PGINVALID_SOCKET;
}
use_pooler_port = NeedPoolerPort();
if (g_instance.attr.attr_network.ListenAddresses && !dummyStandbyMode) {
char* rawstring = NULL;
List* elemlist = NULL;
ListCell* l = NULL;
int success = 0;
/* Need a modifiable copy of g_instance.attr.attr_network.ListenAddresses */
rawstring = pstrdup(g_instance.attr.attr_network.ListenAddresses);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawstring, ',', &elemlist)) {
/* syntax error in list */
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"listen_addresses\"")));
}
foreach (l, elemlist) {
char* curhost = (char*)lfirst(l);
if (strcmp(curhost, "*") == 0)
status = StreamServerPort(AF_UNSPEC,
NULL,
(unsigned short)g_instance.attr.attr_network.PostPortNumber,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
true,
true,
false);
else
status = StreamServerPort(AF_UNSPEC,
curhost,
(unsigned short)g_instance.attr.attr_network.PostPortNumber,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
true,
true,
false);
if (status == STATUS_OK)
success++;
else {
print_port_info();
ereport(FATAL,
(errmsg("could not create listen socket for \"%s:%d\"",
curhost,
g_instance.attr.attr_network.PostPortNumber)));
}
/* At present, we do not listen replconn channels under NORMAL_MODE, so pooler port is needed */
if (t_thrd.xlog_cxt.server_mode == NORMAL_MODE || use_pooler_port == -1) {
/* In om and other maintenance tools, pooler port is hardwired to be gsql port plus one */
if (g_instance.attr.attr_network.PoolerPort != (g_instance.attr.attr_network.PostPortNumber + 1)) {
ereport(FATAL, (errmsg("pooler_port must equal to gsql listen port plus one!")));
}
if (strcmp(curhost, "*") == 0) {
status = StreamServerPort(AF_UNSPEC,
NULL,
(unsigned short)g_instance.attr.attr_network.PoolerPort,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
false,
false);
} else {
status = StreamServerPort(AF_UNSPEC,
curhost,
(unsigned short)g_instance.attr.attr_network.PoolerPort,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
false,
false);
}
if (status != STATUS_OK)
ereport(FATAL,
(errmsg("could not create ha listen socket for \"%s:%d\"",
curhost,
g_instance.attr.attr_network.PoolerPort)));
/*
* Record the first successful host addr which does not mean 'localhost' in lockfile.
* Inner maintanence tools, such as cm_agent and gs_ctl, will use that host for connecting cn.
*/
if (!listen_addr_saved && !IsInplicitIp(curhost)) {
AddToDataDirLockFile(LOCK_FILE_LINE_LISTEN_ADDR, curhost);
listen_addr_saved = true;
}
}
}
if (!success && list_length(elemlist))
ereport(FATAL, (errmsg("could not create any TCP/IP sockets")));
list_free(elemlist);
pfree(rawstring);
}
if (t_thrd.xlog_cxt.server_mode != NORMAL_MODE) {
int i = 0;
int success = 0;
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL) {
if (use_pooler_port == i && !IsInplicitIp(t_thrd.postmaster_cxt.ReplConnArray[i]->localhost)) {
Assert(!listen_addr_saved);
/*
* Record the first successful ha host addr which does not mean 'localhost' in lockfile.
* Inner maintanence tools, such as cm_agent and gs_ctl, will use that host for connecting.
*/
AddToDataDirLockFile(
LOCK_FILE_LINE_LISTEN_ADDR, t_thrd.postmaster_cxt.ReplConnArray[i]->localhost);
listen_addr_saved = true;
}
if (IsAlreadyListen(t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
t_thrd.postmaster_cxt.ReplConnArray[i]->localport)) {
success++;
continue;
}
status = StreamServerPort(AF_UNSPEC,
t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
(unsigned short)t_thrd.postmaster_cxt.ReplConnArray[i]->localport,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
false,
false);
if (status == STATUS_OK) {
success++;
} else {
ReportAlarmAbnormalDataHAInstListeningSocket();
ereport(FATAL,
(errmsg("could not create Ha listen socket for ReplConnInfoArr[%d]\"%s:%d\"",
i,
t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
t_thrd.postmaster_cxt.ReplConnArray[i]->localport)));
}
}
}
if (success == 0) {
ReportAlarmAbnormalDataHAInstListeningSocket();
ereport(WARNING, (errmsg("could not create any HA TCP/IP sockets")));
}
ReportResumeAbnormalDataHAInstListeningSocket();
}
#ifdef USE_BONJOUR
/* Register for Bonjour only if we opened TCP socket(s) */
if (g_instance.attr.attr_common.enable_bonjour && t_thrd.postmaster_cxt.ListenSocket[0] != PGINVALID_SOCKET) {
DNSServiceErrorType err;
/*
* We pass 0 for interface_index, which will result in registering on
* all "applicable" interfaces. It's not entirely clear from the
* DNS-SD docs whether this would be appropriate if we have bound to
* just a subset of the available network interfaces.
*/
err = DNSServiceRegister(&bonjour_sdref,
0,
0,
g_instance.attr.attr_common.bonjour_name,
"_postgresql._tcp.",
NULL,
NULL,
htons(g_instance.attr.attr_network.PostPortNumber),
0,
NULL,
NULL,
NULL);
if (err != kDNSServiceErr_NoError)
ereport(LOG, (errmsg("DNSServiceRegister() failed: error code %ld", (long)err)));
/*
* We don't bother to read the mDNS daemon's reply, and we expect that
* it will automatically terminate our registration when the socket is
* closed at postmaster termination. So there's nothing more to be
* done here. However, the bonjour_sdref is kept around so that
* forked children can close their copies of the socket.
*/
}
#endif
#ifdef HAVE_UNIX_SOCKETS
if (!dummyStandbyMode) {
/* unix socket for gsql port */
status = StreamServerPort(AF_UNIX,
NULL,
(unsigned short)g_instance.attr.attr_network.PostPortNumber,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
true,
false);
if (status != STATUS_OK)
ereport(FATAL,
(errmsg("could not create Unix-domain socket for \"%s:%d\"",
g_instance.attr.attr_network.UnixSocketDir,
g_instance.attr.attr_network.PostPortNumber)));
/* unix socket for ha port */
status = StreamServerPort(AF_UNIX,
NULL,
(unsigned short)g_instance.attr.attr_network.PoolerPort,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
false,
false);
if (status != STATUS_OK)
ereport(FATAL,
(errmsg("could not create Unix-domain socket for \"%s:%d\"",
g_instance.attr.attr_network.UnixSocketDir,
g_instance.attr.attr_network.PoolerPort)));
/*
* create listened unix domain socket to receive gs_sock
* from receiver_loop thread.
* NOTE: as we will use global variable sock_path to init libcomm later,
* so this socket must be the last created unix domain socket.
* otherwise, the sock_path will be replace by other path.
*/
if (g_instance.attr.attr_storage.comm_cn_dn_logic_conn && !isRestoreMode && !IS_SINGLE_NODE) {
status = StreamServerPort(AF_UNIX,
NULL,
(unsigned short)g_instance.attr.attr_network.comm_sctp_port,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
false,
true,
true);
if (status != STATUS_OK)
ereport(WARNING, (errmsg("could not create Unix-domain for comm socket")));
}
}
#endif
/*
* check that we have some socket to listen on
*/
if (t_thrd.postmaster_cxt.ListenSocket[0] == PGINVALID_SOCKET) {
ereport(FATAL, (errmsg("no socket created for listening")));
}
if (SCTP_CN_DN_CONN) {
if (t_thrd.postmaster_cxt.SctpListenSocket[0] == PGINVALID_SOCKET) {
ereport(FATAL, (errmsg("sctp no socket created for listening")));
}
}
/*
* Set up an on_proc_exit function that's charged with closing the sockets
* again at postmaster shutdown. You might think we should have done this
* earlier, but we want it to run before not after the proc_exit callback
* that will remove the Unix socket file.
*/
on_proc_exit(CloseServerPorts, 0);
/*
* If no valid TCP ports, write an empty line for listen address,
* indicating the Unix socket must be used. Note that this line is not
* added to the lock file until there is a socket backing it.
*/
if (!listen_addr_saved) {
AddToDataDirLockFile(LOCK_FILE_LINE_LISTEN_ADDR, "");
ereport(WARNING, (errmsg("No explicit IP is configured for listen_addresses GUC.")));
}
if (g_instance.attr.attr_common.enable_thread_pool) {
/* No need to start thread pool for dummy standby node. */
if (!g_instance.attr.attr_storage.comm_cn_dn_logic_conn && !dummyStandbyMode) {
g_threadPoolControler = (ThreadPoolControler*)New(g_instance.instance_context) ThreadPoolControler();
g_threadPoolControler->SetThreadPoolInfo();
} else {
g_instance.attr.attr_common.enable_thread_pool = false;
g_threadPoolControler = NULL;
}
}
InitGlobalBcm();
/*
* Set up shared memory and semaphores.
*/
reset_shared(g_instance.attr.attr_network.PostPortNumber);
/* Alloc array for backend record. */
BackendArrayAllocation();
/* init thread args pool for ever sub threads except signal moniter */
gs_thread_args_pool_init(GLOBAL_ALL_PROCS + EXTERN_SLOTS_NUM, sizeof(BackendParameters));
// 1.init signal manage struct
//
gs_signal_slots_init(GLOBAL_ALL_PROCS + EXTERN_SLOTS_NUM);
gs_signal_startup_siginfo("PostmasterMain");
gs_signal_monitor_startup();
/*
* Estimate number of openable files. This must happen after setting up
* semaphores, because on some platforms semaphores count as open files.
*/
SetHaShmemData();
rc = memset_s(&state, sizeof(state), 0, sizeof(state));
securec_check_c(rc, "\0", "\0");
state.conn_num = t_thrd.postmaster_cxt.HaShmData->repl_list_num;
state.mode = t_thrd.postmaster_cxt.HaShmData->current_mode;
state.state = STARTING_STATE;
state.lsn = 0;
state.term = 0;
state.sync_stat = false;
state.ha_rebuild_reason = NONE_REBUILD;
PMSetDBStateFile(&state);
ereport(LOG,
(errmsg("create gaussdb state file success: db state(STARTING_STATE), server mode(%s)",
wal_get_role_string(t_thrd.postmaster_cxt.HaShmData->current_mode))));
set_max_safe_fds();
/*
* Set reference point for stack-depth checking.
*/
set_stack_base();
/*
* Initialize the list of active backends.
*/
g_instance.backend_list = DLNewList();
/*
* Initialize pipe (or process handle on Windows) that allows children to
* wake up from sleep on postmaster death.
*/
InitPostmasterDeathWatchHandle();
#ifdef WIN32
/*
* Initialize I/O completion port used to deliver list of dead children.
*/
win32ChildQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (win32ChildQueue == NULL)
ereport(FATAL, (errmsg("could not create I/O completion port for child queue")));
#endif
/*
* Record postmaster options. We delay this till now to avoid recording
* bogus options (eg, NBuffers too high for available memory).
*/
if (!CreateOptsFile(argc, (const char**)argv, (const char*)my_exec_path))
ExitPostmaster(1);
#ifdef EXEC_BACKEND
/* Write out nondefault GUC settings for child processes to use */
write_nondefault_variables(PGC_POSTMASTER);
#endif
}
#ifdef ENABLE_MULTIPLE_NODES
/* init hotpatch */
if (hotpatch_remove_signal_file(t_thrd.proc_cxt.DataDir) == HP_OK) {
int ret;
ret = hotpatch_init(HP_NODE_GAUSSDB, t_thrd.proc_cxt.DataDir, (HOTPATCH_LOG_FUNC)gs_hotpatch_log_callback);
if (ret != HP_OK) {
write_stderr("hotpatch init failed ret is %d!\n", ret);
}
}
#endif
/*
* Write the external PID file if requested
*/
if (g_instance.attr.attr_common.external_pid_file) {
FILE* fpidfile = fopen(g_instance.attr.attr_common.external_pid_file, "w");
if (fpidfile != NULL) {
fprintf(fpidfile, "%lu\n", t_thrd.proc_cxt.MyProcPid);
fclose(fpidfile);
/* Should we remove the pid file on postmaster exit? */
/* Make PID file world readable */
if (chmod(g_instance.attr.attr_common.external_pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0)
write_stderr("%s: could not change permissions of external PID file \"%s\": %s\n",
progname,
g_instance.attr.attr_common.external_pid_file,
gs_strerror(errno));
} else
write_stderr("%s: could not write external PID file \"%s\": %s\n",
progname,
g_instance.attr.attr_common.external_pid_file,
gs_strerror(errno));
}
#ifdef ENABLE_MULTIPLE_NODES
/* If start with fenced mode, we just startup as fenced mode */
if (FencedUDFMasterMode) {
/*
* If enabled, start up syslogger collection subprocess
*/
g_instance.attr.attr_common.Logging_collector = true;
g_instance.pid_cxt.SysLoggerPID = SysLogger_Start();
FencedUDFMasterMain(0, NULL);
return 0;
}
#endif
/*
* Set up signal handlers for the postmaster process.
*
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
* bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/walwriter.c,
* postmaster/autovacuum.c, postmaster/pgarch.c, postmaster/pgstat.c,
* postmaster/syslogger.c and postmaster/checkpointer.c.
*/
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
gs_signal_block_sigusr2();
(void)gspqsignal(SIGHUP, SIGHUP_handler); /* reread config file and have
* children do same */
(void)gspqsignal(SIGINT, pmdie); /* send SIGTERM and shut down */
(void)gspqsignal(SIGQUIT, pmdie); /* send SIGQUIT and die */
(void)gspqsignal(SIGTERM, pmdie); /* wait for children and shut down */
pqsignal(SIGALRM, SIG_IGN); /* ignored */
pqsignal(SIGPIPE, SIG_IGN); /* ignored */
pqsignal(SIGFPE, FloatExceptionHandler);
(void)gspqsignal(SIGUSR1, sigusr1_handler); /* message from child process */
(void)gspqsignal(SIGUSR2, dummy_handler); /* unused, reserve for children */
(void)gspqsignal(SIGCHLD, reaper); /* handle child termination */
(void)gspqsignal(SIGTTIN, SIG_IGN); /* ignored */
(void)gspqsignal(SIGTTOU, SIG_IGN); /* ignored */
/* ignore SIGXFSZ, so that ulimit violations work like disk full */
#ifdef SIGXFSZ
(void)gspqsignal(SIGXFSZ, SIG_IGN); /* ignored */
#endif
/* core dump injection */
/* initialize the first character */
char core_dump_path[1024] = {0};
/*
* the guc parameter bbox_dump_path is set to NULL as default.
* bbox_dump_path has to be a valid directory, if it is altered by users.
*/
if (u_sess->attr.attr_common.bbox_dump_path && *u_sess->attr.attr_common.bbox_dump_path != '\0') {
if (0 != stat(u_sess->attr.attr_common.bbox_dump_path, &stat_buf) || !S_ISDIR(stat_buf.st_mode) ||
0 != access(u_sess->attr.attr_common.bbox_dump_path, W_OK)) {
if (u_sess->attr.attr_common.enable_bbox_dump)
ereport(LOG, (errmsg("bbox_dump_path %s is an invalid directory!", u_sess->attr.attr_common.bbox_dump_path)));
/* if bbox_dump_path is invalid, the path of core dump will be set as default. */
get_coredump_pattern_path(core_dump_path, sizeof(core_dump_path));
} else {
rc = strcpy_s(core_dump_path, sizeof(core_dump_path), u_sess->attr.attr_common.bbox_dump_path);
securec_check(rc, "\0", "\0");
}
} else {
/*
* default path of the core dump will be obtained
* by reading the file "/proc/sys/kernel/core_pattern"
*/
get_coredump_pattern_path(core_dump_path, sizeof(core_dump_path));
}
if (u_sess->attr.attr_common.enable_bbox_dump && *core_dump_path) {
(void)BBOX_SetCoredumpPath(core_dump_path);
}
/*
* no matter bbox_dump_count is default (8) or set by users, call function BBOX_SetCoreFileCount.
* Note: bbox_dump_count cannot be smaller than 1.
*/
if (u_sess->attr.attr_common.bbox_dump_count &&
(0 != BBOX_SetCoreFileCount(u_sess->attr.attr_common.bbox_dump_count))) {
ereport(LOG, (errmsg("failed to set coredump count.")));
}
if (u_sess->attr.attr_common.enable_bbox_dump) {
(void)pqsignal(SIGABRT, bbox_handler);
(void)pqsignal(SIGBUS, bbox_handler);
(void)pqsignal(SIGILL, bbox_handler);
(void)pqsignal(SIGSEGV, bbox_handler);
#ifdef ENABLE_MULTIPLE_NODES
} else {
(void)pqsignal(SIGABRT, coredump_handler);
(void)pqsignal(SIGBUS, coredump_handler);
(void)pqsignal(SIGILL, coredump_handler);
(void)pqsignal(SIGSEGV, coredump_handler);
#endif
}
/*
* Initialize stats collection subsystem (this does NOT start the
* collector process!)
*/
pgstat_init();
/* initialize workload manager */
InitializeWorkloadManager();
/* Init proc's subxid cache context, parent is g_instance.instance_context */
ProcSubXidCacheContext = AllocSetContextCreate(g_instance.instance_context,
"ProcSubXidCacheContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
/*
* Create StreamInfoContext for stream thread connection, parent is g_instance.instance_context.
* All stream threads will share this context.
*/
StreamInfoContext = AllocSetContextCreate(g_instance.instance_context,
"StreamInfoContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
/* init unique sql */
InitUniqueSQL();
/* init instr user */
InitInstrUser();
/* init Opfusion function id */
InitOpfusionFunctionId();
ereport(LOG, (errmsg("Success to start openGauss Database. If you specify \"&\", please press any key to exit...")));
/*
* If enabled, start up syslogger collection subprocess
*/
g_instance.pid_cxt.SysLoggerPID = SysLogger_Start();
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_DATANODE && !dummyStandbyMode && !isRestoreMode) {
StreamObj::startUp();
StreamNodeGroup::StartUp();
pthread_mutex_init(&nodeDefCopyLock, NULL);
}
#endif
/* init the usedDnSpace hash table */
InitDnHashTable();
/*
* Load configuration files for client authentication.
* Load pg_hba.conf before communication thread.
*/
int loadhbaCount = 0;
while (!load_hba()) {
loadhbaCount++;
pg_usleep(200000L); // slepp 200ms for reload
if (loadhbaCount >= 3) {
/*
* It makes no sense to continue if we fail to load the HBA file,
* since there is no way to connect to the database in this case.
*/
ereport(FATAL, (errmsg("could not load pg_hba.conf")));
}
}
if ((!IS_SINGLE_NODE) &&
((IS_PGXC_DATANODE && !dummyStandbyMode && !isRestoreMode) ||
(IS_PGXC_COORDINATOR && g_instance.attr.attr_storage.comm_cn_dn_logic_conn && !isRestoreMode))) {
status = init_stream_comm();
if (status != STATUS_OK)
ereport(FATAL, (errmsg("Init libcomm for stream failed, maybe listen port already in use")));
}
if (g_instance.attr.attr_storage.enable_adio_function)
AioResourceInitialize();
/* start alarm checker thread. */
if (!dummyStandbyMode)
g_instance.pid_cxt.AlarmCheckerPID = startAlarmChecker();
/* start reaper backend thread which is always alive. */
g_instance.pid_cxt.ReaperBackendPID = initialize_util_thread(REAPER);
/*
* Reset whereToSendOutput from DestDebug (its starting state) to
* DestNone. This stops ereport from sending log messages to stderr unless
* t_thrd.aes_cxt.Log_destination permits. We don't do this until the postmaster is
* fully launched, since startup failures may as well be reported to
* stderr.
*/
t_thrd.postgres_cxt.whereToSendOutput = DestNone;
/*
* Initialize the autovacuum subsystem (again, no process start yet)
*/
autovac_init();
load_ident();
/*
* Remove old temporary files. At this point there can be no other
* Postgres processes running in this directory, so this should be safe.
*/
RemovePgTempFiles();
RemoveErrorCacheFiles();
/*
* Remember postmaster startup time
*/
t_thrd.time_cxt.pg_start_time = GetCurrentTimestamp();
/* node stat validation timestamp */
gs_lock_test_and_set_64(&g_instance.stat_cxt.NodeStatResetTime, GetCurrentTimestamp());
/* PostmasterRandom wants its own copy */
gettimeofday(&t_thrd.postmaster_cxt.random_start_time, NULL);
/*
* We're ready to rock and roll...
*/
g_instance.pid_cxt.StartupPID = initialize_util_thread(STARTUP);
Assert(g_instance.pid_cxt.StartupPID != 0);
pmState = PM_STARTUP;
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR) {
oldcontext = MemoryContextSwitchTo(t_thrd.top_mem_cxt);
/*
* Initialize the Data Node connection pool.
* pooler thread don't exist any more, StartPoolManager() is an alias of
* PoolManagerInit().
*/
StartPoolManager();
MemoryContextSwitchTo(oldcontext);
}
load_searchserver_library();
#endif
if (status == STATUS_OK)
status = ServerLoop();
/*
* ServerLoop probably shouldn't ever return, but if it does, close down.
*/
ExitPostmaster(status != STATUS_OK);
return 0; /* not reached */
}
/*
* Compute and check the directory paths to files that are part of the
* installation (as deduced from the postgres executable's own location)
*/
static void getInstallationPaths(const char* argv0)
{
DIR* pdir = NULL;
/* Locate the postgres executable itself */
if (find_my_exec(argv0, my_exec_path) < 0) {
ereport(FATAL, (errmsg("%s: could not locate my own executable path", argv0)));
}
#ifdef EXEC_BACKEND
/* Locate executable backend before we change working directory */
if (find_other_exec(argv0, "gaussdb", PG_BACKEND_VERSIONSTR, t_thrd.proc_cxt.postgres_exec_path) < 0) {
ereport(FATAL, (errmsg("%s: could not locate matching postgres executable", argv0)));
}
#endif
/*
* Locate the pkglib directory --- this has to be set early in case we try
* to load any modules from it in response to postgresql.conf entries.
*/
get_pkglib_path(my_exec_path, t_thrd.proc_cxt.pkglib_path);
/*
* Verify that there's a readable directory there; otherwise the Postgres
* installation is incomplete or corrupt. (A typical cause of this
* failure is that the postgres executable has been moved or hardlinked to
* some directory that's not a sibling of the installation lib/
* directory.)
*/
pdir = AllocateDir(t_thrd.proc_cxt.pkglib_path);
if (pdir == NULL) {
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open directory \"%s\": %m", t_thrd.proc_cxt.pkglib_path),
errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been "
"moved away from its proper location.",
my_exec_path)));
}
FreeDir(pdir);
/*
* XXX is it worth similarly checking the share/ directory? If the lib/
* directory is there, then share/ probably is too.
*/
}
/*
* Validate the proposed data directory
*/
static void checkDataDir(void)
{
#define BUILD_TAG_START "build_completed.start"
char path[MAXPGPATH];
char identFile[MAXPGPATH] = {0};
FILE* fp = NULL;
struct stat stat_buf;
AssertEreport(t_thrd.proc_cxt.DataDir, MOD_OPT, "");
int rc = snprintf_s(identFile, sizeof(identFile), MAXPGPATH - 1, "%s/%s", t_thrd.proc_cxt.DataDir, BUILD_TAG_START);
securec_check_intval(rc, , );
if (0 == stat(identFile, &stat_buf)) {
write_stderr(
"%s: Uncompleted build is detected, please build again and then start database after its success.\n",
progname);
ExitPostmaster(2);
}
if (stat(t_thrd.proc_cxt.DataDir, &stat_buf) != 0) {
if (errno == ENOENT)
ereport(FATAL,
(errcode_for_file_access(), errmsg("data directory \"%s\" does not exist", t_thrd.proc_cxt.DataDir)));
else
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not read permissions of directory \"%s\": %m", t_thrd.proc_cxt.DataDir)));
}
/* eventual chdir would fail anyway, but let's test ... */
if (!S_ISDIR(stat_buf.st_mode))
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("specified data directory \"%s\" is not a directory", t_thrd.proc_cxt.DataDir)));
/*
* Check that the directory belongs to my userid; if not, reject.
*
* This check is an essential part of the interlock that prevents two
* postmasters from starting in the same directory (see CreateLockFile()).
* Do not remove or weaken it.
*
* XXX can we safely enable this check on Windows?
*/
#if !defined(WIN32) && !defined(__CYGWIN__)
if (stat_buf.st_uid != geteuid())
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("data directory \"%s\" has wrong ownership", t_thrd.proc_cxt.DataDir),
errhint("The server must be started by the user that owns the data directory.")));
#endif
/*
* Check if the directory has group or world access. If so, reject.
*
* It would be possible to allow weaker constraints (for example, allow
* group access) but we cannot make a general assumption that that is
* okay; for example there are platforms where nearly all users
* customarily belong to the same group. Perhaps this test should be
* configurable.
*
* XXX temporarily suppress check when on Windows, because there may not
* be proper support for Unix-y file permissions. Need to think of a
* reasonable check to apply on Windows.
*/
#if !defined(WIN32) && !defined(__CYGWIN__)
if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("data directory \"%s\" has group or world access", t_thrd.proc_cxt.DataDir),
errdetail("Permissions should be u=rwx (0700).")));
#endif
/* Look for PG_VERSION before looking for pg_control */
ValidatePgVersion(t_thrd.proc_cxt.DataDir);
int ret = snprintf_s(path, sizeof(path), MAXPGPATH - 1, "%s/global/pg_control", t_thrd.proc_cxt.DataDir);
securec_check_intval(ret, , );
fp = AllocateFile(path, PG_BINARY_R);
if (fp == NULL) {
write_stderr("%s: could not find the database system\n"
"Expected to find it in the directory \"%s\",\n"
"but could not open file \"%s\": %s\n",
progname,
t_thrd.proc_cxt.DataDir,
path,
gs_strerror(errno));
ExitPostmaster(2);
}
(void)FreeFile(fp);
}
static void CheckExtremeRtoGUCConflicts(void)
{
const int minReceiverBufSize = 32 * 1024;
if ((g_instance.attr.attr_storage.recovery_parse_workers > 1) && IS_DN_DUMMY_STANDYS_MODE()) {
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("when starting as dummy_standby mode, we couldn't support extreme rto."),
errhint("Either turn off extreme rto, or set \"replication_type\" to 1.")));
}
if ((g_instance.attr.attr_storage.recovery_parse_workers > 1) && g_instance.attr.attr_storage.EnableHotStandby) {
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("extreme rto could not support hot standby."),
errhint("Either turn off extreme rto, or turn off hot_standby.")));
}
if ((g_instance.attr.attr_storage.recovery_parse_workers > 1) &&
g_instance.attr.attr_storage.WalReceiverBufSize < minReceiverBufSize) {
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("when starting extreme rto, wal receiver buf should not smaller than %dMB",
minReceiverBufSize / 1024),
errhint("recommend config \"wal_receiver_buffer_size=64MB\"")));
}
}
static void CheckRecoveryParaConflict()
{
if (g_instance.attr.attr_storage.max_recovery_parallelism > RECOVERY_PARALLELISM_DEFAULT
&& IS_DN_DUMMY_STANDYS_MODE()) {
ereport(WARNING,
(errmsg("when starting as dummy_standby mode, we couldn't support parallel redo, down it")));
g_instance.attr.attr_storage.max_recovery_parallelism = RECOVERY_PARALLELISM_DEFAULT;
}
}
/*
* Check for invalid combinations of GUC settings during starting up.
*/
static void CheckGUCConflicts(void)
{
if (g_instance.attr.attr_network.ReservedBackends >= g_instance.attr.attr_network.MaxConnections) {
write_stderr("%s: sysadmin_reserved_connections must be less than max_connections\n", progname);
ExitPostmaster(1);
}
if (g_instance.attr.attr_storage.max_wal_senders >= g_instance.attr.attr_network.MaxConnections) {
write_stderr("%s: max_wal_senders must be less than max_connections\n", progname);
ExitPostmaster(1);
}
if (dummyStandbyMode && STANDBY_MODE != t_thrd.xlog_cxt.server_mode) {
write_stderr("%s: dummy standby should be running under standby mode\n", progname);
ExitPostmaster(1);
}
if (u_sess->attr.attr_common.XLogArchiveMode && g_instance.attr.attr_storage.wal_level == WAL_LEVEL_MINIMAL)
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg(
"WAL archival (archive_mode=on) requires wal_level \"archive\", \"hot_standby\" or \"logical\"")));
if (g_instance.attr.attr_storage.max_wal_senders > 0 && g_instance.attr.attr_storage.wal_level == WAL_LEVEL_MINIMAL)
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("WAL streaming (max_wal_senders > 0) requires wal_level \"archive\", \"hot_standby\" or "
"\"logical\"")));
if (g_instance.attr.attr_storage.EnableHotStandby == true &&
g_instance.attr.attr_storage.wal_level < WAL_LEVEL_HOT_STANDBY)
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("hot standby is not possible because wal_level was not set to \"hot_standby\""),
errhint("Either set wal_level to \"hot_standby\", or turn off hot_standby.")));
if ((g_instance.attr.attr_storage.wal_level == WAL_LEVEL_MINIMAL ||
g_instance.attr.attr_storage.max_wal_senders < 1) &&
(t_thrd.xlog_cxt.server_mode == PRIMARY_MODE || t_thrd.xlog_cxt.server_mode == PENDING_MODE ||
t_thrd.xlog_cxt.server_mode == STANDBY_MODE))
ereport(ERROR,
(errcode(ERRCODE_SYSTEM_ERROR),
errmsg("when starting as dual mode, we must ensure wal_level was not \"minimal\" and max_wal_senders "
"was set at least 1")));
if (u_sess->attr.attr_storage.enable_data_replicate && IS_DN_MULTI_STANDYS_MODE()) {
/* when init, we should force change the option */
ereport(LOG, (errmsg("when starting as multi_standby mode, we couldn't support data replicaton.")));
u_sess->attr.attr_storage.enable_data_replicate = false;
}
CheckRecoveryParaConflict();
if (g_instance.attr.attr_storage.enable_mix_replication &&
g_instance.attr.attr_storage.MaxSendSize >= g_instance.attr.attr_storage.DataQueueBufSize) {
write_stderr("%s: the data queue buffer size must be larger than the wal sender max send size for the "
"replication data synchronized by the WAL streaming.\n",
progname);
ExitPostmaster(1);
}
CheckExtremeRtoGUCConflicts();
}
static bool save_backend_variables_for_callback_thread()
{
Port port;
/* This entry point passes dummy values for the Port variables */
int ss_rc = memset_s(&port, sizeof(port), 0, sizeof(port));
securec_check(ss_rc, "\0", "\0");
/*
* Socket 0 may be closed if we do not use it, so we
* must set socket to invalid socket instead of 0.
*/
port.sock = PGINVALID_SOCKET;
return save_backend_variables(&backend_save_para, &port);
}
/*
* Determine how long should we let ServerLoop sleep.
*
* In normal conditions we wait at most one minute, to ensure that the other
* background tasks handled by ServerLoop get done even when no requests are
* arriving. However, if there are background workers waiting to be started,
* we don't actually sleep so that they are quickly serviced. Other exception
* cases are as shown in the code.
*/
static void DetermineSleepTime(struct timeval *timeout)
{
TimestampTz next_wakeup = 0;
/*
* Normal case: either there are no background workers at all, or we're in
* a shutdown sequence (during which we ignore bgworkers altogether).
*/
if (Shutdown > NoShutdown ||
(!g_instance.bgworker_cxt.start_worker_needed && !g_instance.bgworker_cxt.have_crashed_worker)) {
timeout->tv_sec = PM_POLL_TIMEOUT_SECOND;
timeout->tv_usec = 0;
return;
}
if (g_instance.bgworker_cxt.start_worker_needed) {
timeout->tv_sec = 0;
timeout->tv_usec = 0;
return;
}
if (g_instance.bgworker_cxt.have_crashed_worker) {
slist_mutable_iter siter;
/*
* When there are crashed bgworkers, we sleep just long enough that
* they are restarted when they request to be. Scan the list to
* determine the minimum of all wakeup times according to most recent
* crash time and requested restart interval.
*/
slist_foreach_modify(siter, &t_thrd.bgworker_cxt.background_worker_list)
{
RegisteredBgWorker *rw = slist_container(RegisteredBgWorker, rw_lnode, siter.cur);
if (rw->rw_crashed_at == 0) {
continue;
}
if (rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART || rw->rw_terminate) {
ForgetBackgroundWorker(&siter);
continue;
}
TimestampTz this_wakeup = TimestampTzPlusMilliseconds(rw->rw_crashed_at,
1000L * rw->rw_worker.bgw_restart_time);
if (next_wakeup == 0 || this_wakeup < next_wakeup) {
next_wakeup = this_wakeup;
}
}
}
if (next_wakeup != 0) {
long secs;
int microsecs;
TimestampDifference(GetCurrentTimestamp(), next_wakeup, &secs, &microsecs);
timeout->tv_sec = secs;
timeout->tv_usec = microsecs;
/* Ensure we don't exceed PM_POLL_TIMEOUT_SECOND */
if (timeout->tv_sec > PM_POLL_TIMEOUT_SECOND) {
timeout->tv_sec = PM_POLL_TIMEOUT_SECOND;
timeout->tv_usec = 0;
}
} else {
timeout->tv_sec = PM_POLL_TIMEOUT_SECOND;
timeout->tv_usec = 0;
}
}
/*
* Main idle loop of postmaster
*/
static int ServerLoop(void)
{
fd_set readmask;
int nSockets;
uint64 this_start_poll_time, last_touch_time, last_start_loop_time, last_start_poll_time;
/* Database Security: Support database audit */
char details[PGAUDIT_MAXLENGTH] = {0};
bool threadPoolActivated =
g_instance.attr.attr_common.enable_thread_pool && !g_instance.attr.attr_storage.comm_cn_dn_logic_conn;
#ifdef HAVE_POLL
struct pollfd ufds[MAXLISTEN * 2 + 1];
#endif
FD_ZERO(&readmask);
last_start_loop_time = last_touch_time = last_start_poll_time = mc_timers_us();
#ifdef HAVE_POLL
nSockets = initPollfd(ufds);
#else
nSockets = initMasks(&readmask);
#endif
/* for rpc function call */
if (!save_backend_variables_for_callback_thread()) {
ereport(LOG, (errmsg("save_backend_variables_for_callback_thread error")));
return STATUS_ERROR;
}
ereport(LOG, (errmsg("start create thread!")));
/* Init backend thread pool */
if (threadPoolActivated) {
bool enableNumaDistribute = (g_instance.shmem_cxt.numaNodeNum > 1);
g_threadPoolControler->Init(enableNumaDistribute);
}
ereport(LOG, (errmsg("create thread end!")));
for (;;) {
fd_set rmask;
int selres;
if (t_thrd.postmaster_cxt.HaShmData->current_mode != NORMAL_MODE) {
check_and_reset_ha_listen_port();
#ifdef HAVE_POLL
nSockets = initPollfd(ufds);
#else
nSockets = initMasks(&readmask);
#endif
}
/*
* Wait for a connection request to arrive.
*
* We wait at most one minute, to ensure that the other background
* tasks handled below get done even when no requests are arriving.
*
* If we are in PM_WAIT_DEAD_END state, then we don't want to accept
* any new connections, so we don't call select() at all; just sleep
* for a little bit with signals unblocked.
*/
int ss_rc = memcpy_s((char*)&rmask, sizeof(fd_set), (char*)&readmask, sizeof(fd_set));
securec_check(ss_rc, "\0", "\0");
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
this_start_poll_time = mc_timers_us();
if ((this_start_poll_time - last_start_loop_time) != 0) {
gs_set_libcomm_used_rate(
(this_start_poll_time - last_start_poll_time) * 100 / (this_start_poll_time - last_start_loop_time));
}
/*
*check how many seconds has took in this loop
*Detail: If work time > PM_BUSY_ALARM_USED_US, we think the ServerLoop is busy.
*/
if (this_start_poll_time - last_start_poll_time > PM_BUSY_ALARM_USED_US) {
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
ereport(WARNING, (errmsg("postmaster is busy, this cycle used %lu seconds.",
(this_start_poll_time - last_start_loop_time) / PM_BUSY_ALARM_US)));
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
}
last_start_loop_time = this_start_poll_time;
/*
* Touch the socket and lock file every 58 minutes, to ensure that
* they are not removed by overzealous /tmp-cleaning tasks. We assume
* no one runs cleaners with cutoff times of less than an hour ...
*/
if (this_start_poll_time - last_touch_time >= PM_POLL_TIMEOUT_MINUTE) {
TouchSocketFile();
TouchSocketLockFile();
last_touch_time = this_start_poll_time;
}
if (pmState == PM_WAIT_DEAD_END) {
pg_usleep(100000L); /* 100 msec seems reasonable */
selres = 0;
} else {
/* must set timeout each time; some OSes change it! */
struct timeval timeout;
DetermineSleepTime(&timeout);
#ifdef HAVE_POLL
selres = poll(ufds, nSockets, timeout.tv_sec * 1000);
last_start_poll_time = mc_timers_us();
#else
int ss_rc = memcpy_s((char*)&rmask, sizeof(fd_set), (char*)&readmask, sizeof(fd_set));
securec_check(ss_rc, "\0", "\0");
selres = select(nSockets, &rmask, NULL, NULL, &timeout);
last_start_poll_time = mc_timers_us();
#endif
}
/*
* Block all signals until we wait again. (This makes it safe for our
* signal handlers to do nontrivial work.)
*/
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
gs_signal_block_sigusr2();
/* Now check the select() result */
if (selres < 0) {
if (errno != EINTR && errno != EWOULDBLOCK) {
ereport(LOG, (errcode_for_socket_access(), errmsg("select()/poll() failed in postmaster: %m")));
return STATUS_ERROR;
}
}
/*
* New connection pending on any of our sockets? If so, fork a child
* process to deal with it.
*/
if (selres > 0) {
int i;
for (i = 0; i < (MAXLISTEN * 2); i++) {
if (ufds[i].fd == PGINVALID_SOCKET)
break;
#ifdef HAVE_POLL
if (ufds[i].revents & POLLIN) {
ufds[i].revents = 0;
#else
if (FD_ISSET(t_thrd.postmaster_cxt.ListenSocket[i], &rmask)) {
#endif
Port* port = NULL;
ufds[i].revents = 0;
if (IS_FD_TO_RECV_GSSOCK(ufds[i].fd)) {
port = ConnCreateToRecvGssock(ufds, i, &nSockets);
} else {
port = ConnCreate(ufds[i].fd);
}
if (port != NULL) {
int result = STATUS_OK;
/*
* Since at present, HA only uses TCP sockets, we can directly compare
* the corresponding enty in t_thrd.postmaster_cxt.listen_sock_type, even
* though ufds are not one-to-one mapped to tcp and sctp socket array.
* If HA adopts STCP sockets later, we will need to maintain socket type
* array for ufds in initPollfd.
*/
if (threadPoolActivated &&
!(i < MAXLISTEN && t_thrd.postmaster_cxt.listen_sock_type[i] == HA_LISTEN_SOCKET))
result = g_threadPoolControler->DispatchSession(port);
else
result = BackendStartup(port);
if (result != STATUS_OK) {
if (port->is_logic_conn)
gs_close_gsocket(&port->gs_sock);
else
closesocket(port->sock);
}
/* do not free port for unix domain conn from receiver flow control */
if (!IS_FD_TO_RECV_GSSOCK(ufds[i].fd))
/*
* We no longer need the open socket or port structure
* in this process
*/
ConnFree((void*)port);
}
}
}
}
/*
* If the AioCompleters have not been started start them.
* These should remain run indefinitely.
*/
ADIO_RUN()
{
if (!g_instance.pid_cxt.AioCompleterStarted && !dummyStandbyMode) {
int aioStartErr = 0;
if ((aioStartErr = AioCompltrStart()) == 0) {
g_instance.pid_cxt.AioCompleterStarted = 1;
} else {
ereport(LOG, (errmsg_internal("Cannot start AIO completer threads error=%d", aioStartErr)));
/*
* If we failed to fork a aio process, just shut down.
* Any required cleanup will happen at next restart. We
* set g_instance.fatal_error so that an "abnormal shutdown" message
* gets logged when we exit.
*/
g_instance.fatal_error = true;
HandleChildCrash(g_instance.pid_cxt.AioCompleterStarted, 1, "AIO process");
}
}
}
ADIO_END();
if (threadPoolActivated && (pmState == PM_RUN || pmState == PM_HOT_STANDBY))
g_threadPoolControler->AddWorkerIfNecessary();
/* If we have lost the log collector, try to start a new one */
if (g_instance.pid_cxt.SysLoggerPID == 0 && g_instance.attr.attr_common.Logging_collector)
g_instance.pid_cxt.SysLoggerPID = SysLogger_Start();
/* start auditor process */
/* If we have lost the audit collector, try to start a new one */
#ifndef ENABLE_MULTIPLE_NODES
/* After the standby read function is added, the standby node needs to be audited. */
if (g_instance.pid_cxt.PgAuditPID == 0 && u_sess->attr.attr_security.Audit_enabled &&
(pmState == PM_RUN || pmState == PM_HOT_STANDBY) && !dummyStandbyMode) {
g_instance.pid_cxt.PgAuditPID = pgaudit_start();
ereport(LOG, (errmsg("auditor process started, pid=%lu", g_instance.pid_cxt.PgAuditPID)));
}
#else
if (g_instance.pid_cxt.PgAuditPID == 0 && u_sess->attr.attr_security.Audit_enabled && pmState == PM_RUN &&
!dummyStandbyMode) {
g_instance.pid_cxt.PgAuditPID = pgaudit_start();
ereport(LOG, (errmsg("auditor process started, pid=%lu", g_instance.pid_cxt.PgAuditPID)));
}
#endif
/* If u_sess->attr.attr_security.Audit_enabled is set to false, terminate auditor process. */
if (g_instance.pid_cxt.PgAuditPID != 0 && !u_sess->attr.attr_security.Audit_enabled) {
signal_child(g_instance.pid_cxt.PgAuditPID, SIGQUIT);
ereport(LOG, (errmsg("parameter audit_enabled is set to false, terminate auditor process.")));
}
if (g_instance.pid_cxt.AlarmCheckerPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.AlarmCheckerPID = startAlarmChecker();
/* If we have lost the reaper backend thread, try to start a new one */
if (g_instance.pid_cxt.ReaperBackendPID == 0)
g_instance.pid_cxt.ReaperBackendPID = initialize_util_thread(REAPER);
/*
* If no background writer process is running, and we are not in a
* state that prevents it, start one. It doesn't matter if this
* fails, we'll just try again later. Likewise for the checkpointer.
*/
if (pmState == PM_RUN || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY) {
if (g_instance.pid_cxt.CheckpointerPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.CheckpointerPID = initialize_util_thread(CHECKPOINT_THREAD);
if (g_instance.pid_cxt.BgWriterPID == 0 && !dummyStandbyMode &&
!g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
g_instance.pid_cxt.BgWriterPID = initialize_util_thread(BGWRITER);
}
if (g_instance.pid_cxt.CBMWriterPID == 0 && !dummyStandbyMode &&
u_sess->attr.attr_storage.enable_cbm_tracking)
g_instance.pid_cxt.CBMWriterPID = initialize_util_thread(CBMWRITER);
if (!dummyStandbyMode && g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
int i;
for (i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
if (g_instance.pid_cxt.PageWriterPID[i] == 0) {
g_instance.pid_cxt.PageWriterPID[i] = initialize_util_thread(PAGEWRITER_THREAD);
}
}
}
if (g_instance.pid_cxt.RemoteServicePID == 0 && !dummyStandbyMode && IS_PGXC_DATANODE &&
t_thrd.postmaster_cxt.HaShmData->current_mode != NORMAL_MODE && !IS_DN_WITHOUT_STANDBYS_MODE() &&
IsRemoteReadModeOn())
g_instance.pid_cxt.RemoteServicePID = initialize_util_thread(RPC_SERVICE);
}
/* WalWriter in standby is to help WalRevWriter thread. So just keep up with WalRcvWriterPID */
if (g_instance.pid_cxt.WalWriterPID == 0 && (pmState == PM_RUN ||
((pmState == PM_HOT_STANDBY || pmState == PM_RECOVERY) && g_instance.pid_cxt.WalRcvWriterPID != 0 &&
t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE))) {
g_instance.pid_cxt.WalWriterPID = initialize_util_thread(WALWRITER);
ereport(LOG,
(errmsg("ServerLoop create WalWriter(%lu) for pmState:%u, ServerMode:%u.",
g_instance.pid_cxt.WalWriterPID, pmState, t_thrd.postmaster_cxt.HaShmData->current_mode)));
}
/*
* let cbm writer thread exit if enable_cbm_track gus is switched off
*/
if (!u_sess->attr.attr_storage.enable_cbm_tracking && g_instance.pid_cxt.CBMWriterPID != 0 &&
pmState == PM_RUN) {
ereport(LOG,
(errmsg("stop cbm writer thread because enable_cbm_tracking is switched off, "
"cbm writer thread pid=%lu",
g_instance.pid_cxt.CBMWriterPID)));
signal_child(g_instance.pid_cxt.CBMWriterPID, SIGTERM);
}
/*
* If we have lost the autovacuum launcher, try to start a new one. We
* don't want autovacuum to run in binary upgrade mode because
* autovacuum might update relfrozenxid64 for empty tables before the
* physical files are put in place.
*/
if (!u_sess->proc_cxt.IsBinaryUpgrade && g_instance.pid_cxt.AutoVacPID == 0 &&
(AutoVacuumingActive() || t_thrd.postmaster_cxt.start_autovac_launcher) && pmState == PM_RUN &&
!dummyStandbyMode && u_sess->attr.attr_common.upgrade_mode != 1 && !InplaceUpgradePrecommit) {
g_instance.pid_cxt.AutoVacPID = initialize_util_thread(AUTOVACUUM_LAUNCHER);
if (g_instance.pid_cxt.AutoVacPID != 0)
t_thrd.postmaster_cxt.start_autovac_launcher = false; /* signal processed */
}
/*
* If we have lost the job scheduler, try to start a new one.
*
* Before GRAND VERSION NUM 81000, we do not support scheduled job.
*/
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID == 0 && pmState == PM_RUN &&
(g_instance.attr.attr_sql.job_queue_processes || t_thrd.postmaster_cxt.start_job_scheduler) &&
u_sess->attr.attr_common.upgrade_mode != 1) {
g_instance.pid_cxt.PgJobSchdPID = initialize_util_thread(JOB_SCHEDULER);
if (g_instance.pid_cxt.PgJobSchdPID != 0) {
t_thrd.postmaster_cxt.start_job_scheduler = false; /* signal processed */
ereport(LOG, (errmsg("job scheduler started, pid=%lu", g_instance.pid_cxt.PgJobSchdPID)));
}
}
#ifdef ENABLE_MULTIPLE_NODES
if ((IS_PGXC_COORDINATOR) && g_instance.pid_cxt.CommPoolerCleanPID == 0 && pmState == PM_RUN &&
u_sess->attr.attr_common.upgrade_mode != 1) {
StartPoolCleaner();
}
#endif
/* If g_instance.attr.attr_sql.job_queue_processes set to 0, terminate jobscheduler thread. */
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID != 0 &&
!g_instance.attr.attr_sql.job_queue_processes) {
signal_child(g_instance.pid_cxt.PgJobSchdPID, SIGTERM);
}
/* If we have lost the archiver, try to start a new one */
if (XLogArchivingActive() && g_instance.pid_cxt.PgArchPID == 0 && pmState == PM_RUN && !dummyStandbyMode)
g_instance.pid_cxt.PgArchPID = pgarch_start();
/* If we have lost the stats collector, try to start a new one */
if (g_instance.pid_cxt.PgStatPID == 0 && (pmState == PM_RUN || pmState == PM_HOT_STANDBY) && !dummyStandbyMode)
g_instance.pid_cxt.PgStatPID = pgstat_start();
/* If we have lost the stats collector, try to start a new one */
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.SnapshotPID == 0 &&
pmState == PM_RUN)
g_instance.pid_cxt.SnapshotPID = snapshot_start();
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PercentilePID == 0 &&
pmState == PM_RUN)
g_instance.pid_cxt.PercentilePID = initialize_util_thread(PERCENTILE_WORKER);
/* if workload manager is off, we still use this thread to build user hash table */
if ((ENABLE_WORKLOAD_CONTROL || !WLMIsInfoInit()) && g_instance.pid_cxt.WLMCollectPID == 0 &&
pmState == PM_RUN && !dummyStandbyMode)
g_instance.pid_cxt.WLMCollectPID = initialize_util_thread(WLM_WORKER);
if (ENABLE_WORKLOAD_CONTROL && (g_instance.pid_cxt.WLMMonitorPID == 0) && (pmState == PM_RUN) &&
!dummyStandbyMode)
g_instance.pid_cxt.WLMMonitorPID = initialize_util_thread(WLM_MONITOR);
if (ENABLE_WORKLOAD_CONTROL && (g_instance.pid_cxt.WLMArbiterPID == 0) && (pmState == PM_RUN) &&
!dummyStandbyMode)
g_instance.pid_cxt.WLMArbiterPID = initialize_util_thread(WLM_ARBITER);
if (IS_PGXC_COORDINATOR && g_instance.attr.attr_sql.max_resource_package &&
(g_instance.pid_cxt.CPMonitorPID == 0) && (pmState == PM_RUN) && !dummyStandbyMode)
g_instance.pid_cxt.CPMonitorPID = initialize_util_thread(WLM_CPMONITOR);
/* If we have lost the twophase cleaner, try to start a new one */
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_common.upgrade_mode != 1 &&
g_instance.pid_cxt.TwoPhaseCleanerPID == 0 && pmState == PM_RUN)
g_instance.pid_cxt.TwoPhaseCleanerPID = initialize_util_thread(TWOPASECLEANER);
/* If we have lost the LWLock monitor, try to start a new one */
if (g_instance.pid_cxt.FaultMonitorPID == 0 && pmState == PM_RUN)
g_instance.pid_cxt.FaultMonitorPID = initialize_util_thread(FAULTMONITOR);
/* If we have lost the heartbeat service, try to start a new one */
if (NeedHeartbeat())
g_instance.pid_cxt.HeartbeatPID = initialize_util_thread(HEARTBEAT);
/* If we need to signal the autovacuum launcher, do so now */
if (t_thrd.postmaster_cxt.avlauncher_needs_signal) {
t_thrd.postmaster_cxt.avlauncher_needs_signal = false;
if (g_instance.pid_cxt.AutoVacPID != 0)
gs_signal_send(g_instance.pid_cxt.AutoVacPID, SIGUSR2);
}
/* If job worker failed to run, postmaster need send signal SIGUSR2 to job scheduler thread. */
if (IS_PGXC_COORDINATOR && t_thrd.postmaster_cxt.jobscheduler_needs_signal) {
t_thrd.postmaster_cxt.jobscheduler_needs_signal = false;
if (g_instance.pid_cxt.PgJobSchdPID != 0) {
gs_signal_send(g_instance.pid_cxt.PgJobSchdPID, SIGUSR2);
}
}
/* Database Security: Support database audit */
if (pmState == PM_RUN) {
if (t_thrd.postmaster_cxt.audit_primary_failover) {
int rcs = snprintf_s(details,
sizeof(details),
sizeof(details) - 1,
"the standby do failover success,now it is primary!");
securec_check_ss(rcs, "\0", "\0");
pgaudit_system_switchover_ok(details);
t_thrd.postmaster_cxt.audit_primary_failover = false;
}
if (t_thrd.postmaster_cxt.audit_standby_switchover) {
int rcs = snprintf_s(details,
sizeof(details),
sizeof(details) - 1,
"the standby do switchover success,now it is primary!");
securec_check_ss(rcs, "\0", "\0");
pgaudit_system_switchover_ok(details);
t_thrd.postmaster_cxt.audit_standby_switchover = false;
}
}
}
}
/*
* Initialise the ufds for poll() for the ports we are listening on.
* Return the number of sockets to listen on.
*/
#ifdef HAVE_POLL
static int initPollfd(struct pollfd* ufds)
{
int i, cnt = 0;
int fd;
int ss_rc =
memset_s(ufds, sizeof(struct pollfd) * (MAXLISTEN * 2 + 1), -1, sizeof(struct pollfd) * (MAXLISTEN * 2 + 1));
securec_check(ss_rc, "\0", "\0");
for (i = 0; i < MAXLISTEN; i++) {
fd = t_thrd.postmaster_cxt.ListenSocket[i];
if (fd == PGINVALID_SOCKET)
break;
ufds[cnt].fd = fd;
ufds[cnt].events = POLLIN | POLLPRI;
cnt++;
}
if (SCTP_CN_DN_CONN) {
for (i = 0; i < MAXLISTEN; i++) {
fd = t_thrd.postmaster_cxt.SctpListenSocket[i];
if (fd == PGINVALID_SOCKET)
break;
ufds[cnt].fd = fd;
ufds[cnt].events = POLLIN | POLLPRI;
cnt++;
}
}
if (t_thrd.postmaster_cxt.sock_for_libcomm != PGINVALID_SOCKET) {
ufds[cnt].fd = t_thrd.postmaster_cxt.sock_for_libcomm;
ufds[cnt].events = POLLIN | POLLPRI;
cnt++;
}
return cnt;
}
#else
/*
* Initialise the masks for select() for the ports we are listening on.
* Return the number of sockets to listen on.
*/
static int initMasks(fd_set* rmask)
{
int maxsock = -1;
int i;
FD_ZERO(rmask);
for (i = 0; i < MAXLISTEN; i++) {
int fd = t_thrd.postmaster_cxt.ListenSocket[i];
if (fd == PGINVALID_SOCKET) {
continue;
}
FD_SET(fd, rmask);
if (fd > maxsock) {
maxsock = fd;
}
}
return maxsock + 1;
}
#endif // end of HAVE_POLL
/*
* Read a client's startup packet and do something according to it.
*
* Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and
* not return at all.
*
* (Note that ereport(FATAL) stuff is sent to the client, so only use it
* if that's what you want. Return STATUS_ERROR if you don't want to
* send anything to the client, which would typically be appropriate
* if we detect a communications failure.)
*/
int ProcessStartupPacket(Port* port, bool SSLdone)
{
int32 len;
void* buf = NULL;
ProtocolVersion proto;
MemoryContext oldcontext;
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
bool isMaintenanceConnection = false;
bool clientIsGsql = false;
bool clientIsCmAgent = false;
bool clientIsGsClean = false;
bool clientIsGsRoach = false;
bool clientIsOM = false;
bool findProtoVer = false;
int elevel = (IS_THREAD_POOL_WORKER ? ERROR : FATAL);
CHECK_FOR_PROCDIEPENDING();
if (pq_getbytes((char*)&len, 4) == EOF) {
/*
* EOF after SSLdone probably means the client didn't like our
* response to NEGOTIATE_SSL_CODE. That's not an error condition, so
* don't clutter the log with a complaint.
*/
if (!SSLdone)
ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete startup packet")));
return STATUS_ERROR;
}
len = ntohl(len);
len -= 4;
if (len < (int32)sizeof(ProtocolVersion) || len > MAX_STARTUP_PACKET_LENGTH) {
ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid length of startup packet")));
return STATUS_ERROR;
}
/*
* Allocate at least the size of an old-style startup packet, plus one
* extra byte, and make sure all are zeroes. This ensures we will have
* null termination of all strings, in both fixed- and variable-length
* packet layouts.
*/
if (len <= (int32)sizeof(StartupPacket))
buf = palloc0(sizeof(StartupPacket) + 1);
else
buf = palloc0(len + 1);
if (pq_getbytes((char*)buf, len) == EOF) {
ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("incomplete startup packet")));
return STATUS_ERROR;
}
/*
* If we're going to reject the connection due to database state, say so
* now instead of wasting cycles on an authentication exchange. (This also
* allows a pg_ping utility to be written.)
*/
switch (port->canAcceptConnections) {
case CAC_STARTUP:
ereport(elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("the database system is starting up")));
break;
case CAC_SHUTDOWN:
ereport(elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("the database system is shutting down")));
break;
case CAC_RECOVERY:
ereport(elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("the database system is in recovery mode")));
break;
case CAC_TOOMANY:
ereport(elevel, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already")));
break;
case CAC_WAITBACKUP:
/* OK for now, will check in InitPostgres */
break;
case CAC_OK:
break;
default:
break;
}
/*
* The first field is either a protocol version number or a special
* request code.
*/
port->proto = proto = ntohl(*((ProtocolVersion*)buf));
if (proto == CANCEL_REQUEST_CODE) {
/*
* Mark it is a temp thread which will exit quickly itself.
* Then PM need not wait and retry to send SIGTERM signal to it.
* We can not mark it as temp thread in thread pool mode, because
* the thread will be reused later.
*/
if (!ENABLE_THREAD_POOL)
MarkPostmasterTempBackend();
processCancelRequest(port, buf);
/* Not really an error, but we don't want to proceed further */
return STATUS_ERROR;
#ifdef ENABLE_MULTIPLE_NODES
} else if (proto == CONNECT_STREAM_CODE) {
bool reject = (STANDBY_MODE == hashmdata->current_mode || PENDING_MODE == hashmdata->current_mode);
/*
* Mark it is a temp thread which will exit quickly itself.
* Then PM need not wait and retry to send SIGTERM signal to it.
* We can not mark it as temp thread in thread pool mode, because
* the thread will be reused later.
*/
if (!ENABLE_THREAD_POOL)
MarkPostmasterTempBackend();
processConnectStreamRequest(port, buf, reject);
return STATUS_ERROR;
} else if (proto == STOP_REQUEST_CODE) {
processStopRequest(port, buf);
return STATUS_ERROR;
#endif
}
if (proto == NEGOTIATE_SSL_CODE && !SSLdone) {
char SSLok;
#ifdef USE_SSL
/* No SSL when disabled or on Unix sockets */
if (!g_instance.attr.attr_security.EnableSSL || IS_AF_UNIX(port->laddr.addr.ss_family))
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
#else
SSLok = 'N'; /* No support for SSL */
#endif
retry1:
errno = 0;
if (send(port->sock, &SSLok, 1, 0) <= 0) {
if (errno == EINTR)
goto retry1; /* if interrupted, just retry */
ereport(COMMERROR, (errcode_for_socket_access(), errmsg("failed to send SSL negotiation response: %m")));
return STATUS_ERROR; /* close the connection */
}
#ifdef USE_SSL
if (SSLok == 'S' && secure_open_server(port) == -1)
return STATUS_ERROR;
#endif
/* regular startup packet, cancel, etc packet should follow... */
/* but not another SSL negotiation request */
return ProcessStartupPacket(port, true);
}
/* Could add additional special packet types here */
/*
* Set FrontendProtocol now so that ereport() knows what format to send if
* we fail during startup.
*/
FrontendProtocol = proto;
/* Check we can handle the protocol the frontend is using. */
for (size_t i = 0; i < sizeof(protoVersionList) / sizeof(protoVersionList[0]); i++) {
if (PG_PROTOCOL_MAJOR(proto) == protoVersionList[i][0] && PG_PROTOCOL_MINOR(proto) == protoVersionList[i][1]) {
findProtoVer = true;
break;
}
}
if (!findProtoVer) {
ereport(elevel,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported frontend protocol %u.%u.", PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto))));
}
/*
* Now fetch parameters out of startup packet and save them into the Port
* structure. All data structures attached to the Port struct must be
* allocated in t_thrd.top_mem_cxt so that they will remain available in a
* running backend (even after t_thrd.mem_cxt.postmaster_mem_cxt is destroyed). We need
* not worry about leaking this storage on failure, since we aren't in the
* postmaster process anymore.
*/
oldcontext = MemoryContextSwitchTo(u_sess->top_mem_cxt);
if (PG_PROTOCOL_MAJOR(proto) >= 3) {
int32 offset = sizeof(ProtocolVersion);
/*
* Scan packet body for name/option pairs. We can assume any string
* beginning within the packet body is null-terminated, thanks to
* zeroing extra byte above.
*/
port->guc_options = NIL;
while (offset < len) {
char* nameptr = ((char*)buf) + offset;
int32 valoffset;
char* valptr = NULL;
if (*nameptr == '\0')
break; /* found packet terminator */
valoffset = offset + strlen(nameptr) + 1;
if (valoffset >= len)
break; /* missing value, will complain below */
valptr = ((char*)buf) + valoffset;
if (strcmp(nameptr, "database") == 0)
port->database_name = pstrdup(valptr);
else if (strcmp(nameptr, "user") == 0)
port->user_name = pstrdup(valptr);
else if (strcmp(nameptr, "options") == 0) {
port->cmdline_options = pstrdup(valptr);
if (strstr(port->cmdline_options, "xc_maintenance_mode=on") != NULL) {
isMaintenanceConnection = true;
#ifndef ENABLE_MULTIPLE_NODES
} else if (strstr(port->cmdline_options, "remotetype=internaltool") != NULL) {
u_sess->proc_cxt.IsInnerMaintenanceTools = true;
#endif
}
} else if (strcmp(nameptr, "replication") == 0) {
/*
* Due to backward compatibility concerns the replication
* parameter is a hybrid beast which allows the value to be
* either boolean or the string 'database'. The latter
* connects to a specific database which is e.g. required for
* logical decoding while.
*/
/* Add data replication */
if (strcmp(valptr, "data") == 0) {
/* mark the data sender as a wal sender for some common management */
t_thrd.role = WAL_NORMAL_SENDER;
if (!g_instance.attr.attr_storage.enable_mix_replication)
t_thrd.datasender_cxt.am_datasender = true;
} else if (strcmp(valptr, "database") == 0) {
if (!IsHAPort(u_sess->proc_cxt.MyProcPort) && g_instance.attr.attr_common.enable_thread_pool) {
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("logical replication should connect HA port in thread_pool")));
}
t_thrd.role = WAL_DB_SENDER;
} else {
bool _am_normal_walsender = false;
if (!parse_bool(valptr, &_am_normal_walsender)) {
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"replication\""),
errhint("Valid values are: false, 0, true, 1, data, database.")));
} else if (_am_normal_walsender) {
t_thrd.role = WAL_NORMAL_SENDER;
}
}
} else if (strcmp(nameptr, "backend_version") == 0) {
errno = 0;
port->SessionVersionNum = (uint32)strtoul(valptr, NULL, 10);
if (errno != 0)
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"backend_version\""),
errhint("Valid values are of uint32 type.")));
if (port->SessionVersionNum > GRAND_VERSION_NUM)
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("requested backend version is larger than grand version.")));
} else if (strcmp(nameptr, "connect_timeout") == 0) {
errno = 0;
u_sess->attr.attr_network.PoolerConnectTimeout = (uint32) strtoul(valptr, NULL, 10);
if (errno != 0)
{
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value[%d] for parameter \"connect_timeout\"",
u_sess->attr.attr_network.PoolerConnectTimeout),
errhint("Valid values are of uint32 type.")));
}
} else {
if (strcmp(nameptr, "application_name") == 0) {
/* check if remote is dummystandby */
if (strcmp(valptr, "gs_ctl") == 0) {
/* mark remote as gs_ctl build */
t_thrd.postmaster_cxt.senderToBuildStandby = true;
u_sess->proc_cxt.clientIsGsCtl = true;
ereport(DEBUG5, (errmsg("gs_ctl connected")));
#ifdef ENABLE_MULTIPLE_NODES
} else if (strcmp(valptr, "dummystandby") == 0) {
/* mark remote as dummystandby */
t_thrd.postmaster_cxt.senderToDummyStandby = true;
ereport(DEBUG5, (errmsg("secondary standby connected")));
} else if (strcmp(valptr, "cm_agent") == 0) {
/* mark remote as cm_agent */
clientIsCmAgent = true;
u_sess->libpq_cxt.IsConnFromCmAgent = true;
ereport(DEBUG5, (errmsg("cm_agent connected")));
} else if (strcmp(valptr, "gs_clean") == 0) {
clientIsGsClean = true;
ereport(DEBUG5, (errmsg("gs_clean connected")));
} else if (strcmp(valptr, "gs_roach") == 0) {
clientIsGsRoach = true;
ereport(DEBUG5, (errmsg("gs_roach connected")));
} else if (strcmp(valptr, "gs_redis") == 0) {
u_sess->proc_cxt.clientIsGsredis = true;
ereport(DEBUG5, (errmsg("gs_redis connected")));
#endif
} else if (strcmp(valptr, "gs_rewind") == 0) {
/*
* mark remote as gs_rewind.
* in single-node mode, gs_ctl need gs_rewind to do inc-build.
*/
u_sess->proc_cxt.clientIsGsrewind = true;
ereport(DEBUG5, (errmsg("gs_rewind connected")));
} else if (strcmp(valptr, "gsql") == 0) {
/* mark remote as gsql */
clientIsGsql = true;
ereport(DEBUG5, (errmsg("gsql connected")));
} else if (strcmp(valptr, "OM") == 0) {
clientIsOM = true;
ereport(DEBUG5, (errmsg("OM connected")));
} else if (strcmp(valptr, "gs_dump") == 0) {
u_sess->proc_cxt.clientIsGsdump = true;
ereport(DEBUG5, (errmsg("gs_dump connected")));
} else if (strcmp(valptr, "gs_basebackup") == 0) {
u_sess->proc_cxt.clientIsGsBasebackup = true;
ereport(LOG, (errmsg("gs_basebackup connected")));
} else if (strcmp(valptr, "gs_restore") == 0) {
u_sess->proc_cxt.clientIsGsRestore = true;
ereport(DEBUG5, (errmsg("gs_restore connected")));
} else {
ereport(DEBUG5, (errmsg("application %s connected", valptr)));
}
}
/* Assume it's a generic GUC option */
port->guc_options = lappend(port->guc_options, pstrdup(nameptr));
port->guc_options = lappend(port->guc_options, pstrdup(valptr));
}
offset = valoffset + strlen(valptr) + 1;
}
/*
* If we didn't find a packet terminator exactly at the end of the
* given packet length, complain.
*/
if (offset != len - 1)
ereport(elevel,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("invalid startup packet layout: expected terminator as last byte")));
} else {
/*
* Get the parameters from the old-style, fixed-width-fields startup
* packet as C strings. The packet destination was cleared first so a
* short packet has zeros silently added. We have to be prepared to
* truncate the pstrdup result for oversize fields, though.
*/
StartupPacket* packet = (StartupPacket*)buf;
port->database_name = pstrdup(packet->database);
if (strlen(port->database_name) > sizeof(packet->database))
port->database_name[sizeof(packet->database)] = '\0';
port->user_name = pstrdup(packet->user);
if (strlen(port->user_name) > sizeof(packet->user))
port->user_name[sizeof(packet->user)] = '\0';
port->cmdline_options = pstrdup(packet->options);
if (strlen(port->cmdline_options) > sizeof(packet->options))
port->cmdline_options[sizeof(packet->options)] = '\0';
port->guc_options = NIL;
}
/* Inner tool with local sha256 will not be authenicated. */
if (clientIsCmAgent || clientIsGsClean || clientIsOM || clientIsGsRoach || u_sess->proc_cxt.clientIsGsCtl ||
u_sess->proc_cxt.clientIsGsrewind || u_sess->proc_cxt.clientIsGsredis) {
u_sess->proc_cxt.IsInnerMaintenanceTools = true;
}
/* cm_agent and gs_clean should not be controlled by workload manager */
if (clientIsCmAgent || clientIsGsClean) {
u_sess->proc_cxt.IsWLMWhiteList = true;
}
#ifdef ENABLE_MULTIPLE_NODES
if (clientIsCmAgent) {
#ifdef ENABLE_DISTRIBUTE_TEST
if (TEST_STUB(DN_CM_NEW_CONN, stub_sleep_emit)) {
ereport(get_distribute_test_param()->elevel,
(errmsg("sleep_emit happen during ProcessStartupPacket time:%ds, stub_name:%s",
get_distribute_test_param()->sleep_time,
get_distribute_test_param()->test_stub_name)));
}
#endif
}
#endif
/* Check a user name was given. */
if (port->user_name == NULL || port->user_name[0] == '\0')
ereport(elevel,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("no PostgreSQL user name specified in startup packet")));
/* The database defaults to the user name. */
if (port->database_name == NULL || port->database_name[0] == '\0')
port->database_name = pstrdup(port->user_name);
/*
* Truncate given database and user names to length of a Postgres name.
* This avoids lookup failures when overlength names are given.
*/
if (strlen(port->database_name) >= NAMEDATALEN)
port->database_name[NAMEDATALEN - 1] = '\0';
if (strlen(port->user_name) >= NAMEDATALEN)
port->user_name[NAMEDATALEN - 1] = '\0';
/*
* Normal walsender backends, e.g. for streaming replication, are not
* connected to a particular database. But walsenders used for logical
* replication need to connect to a specific database. We allow streaming
* replication commands to be issued even if connected to a database as it
* can make sense to first make a basebackup and then stream changes
* starting from that.
*/
if (AM_WAL_SENDER && !AM_WAL_DB_SENDER)
port->database_name[0] = '\0';
/* set special tcp keepalive parameters for build senders */
if (AM_WAL_SENDER && t_thrd.postmaster_cxt.senderToBuildStandby) {
if (!IS_AF_UNIX(port->laddr.addr.ss_family)) {
pq_setkeepalivesidle(7200, port);
pq_setkeepalivesinterval(75, port);
pq_setkeepalivescount(9, port);
}
}
/*
* Done putting stuff in t_thrd.top_mem_cxt.
*/
(void)MemoryContextSwitchTo(oldcontext);
if (AM_WAL_SENDER) {
int channel_adapt = 0, i = 0;
if (!IS_PGXC_COORDINATOR) {
if (NORMAL_MODE == hashmdata->current_mode) {
if (!u_sess->proc_cxt.clientIsGsBasebackup && !AM_WAL_DB_SENDER) {
ereport(elevel,
(errmsg("the current t_thrd.postmaster_cxt.server_mode is NORMAL, "
"could not accept HA connection.")));
}
}
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL &&
IsChannelAdapt(port, t_thrd.postmaster_cxt.ReplConnArray[i])) {
channel_adapt++;
}
}
if (0 == channel_adapt) {
int elevel = comm_client_bind ? FATAL : WARNING;
ereport(elevel,
(errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("the ha connection is not in the channel list")));
}
}
} else {
if (dummyStandbyMode)
ereport(
elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("Secondary Standby does not accept connection")));
/*
* clients other than inner maintenance tools and remote coordinators are only
* allowed to connect through gsql port, gsql port unix socket and ha port unix socket
*/
if (!u_sess->proc_cxt.IsInnerMaintenanceTools && !IsConnPortFromCoord(port) &&
(!IsLocalAddr(port) || !IsLocalPort(port))) {
ereport(elevel,
(errcode(ERRCODE_CANNOT_CONNECT_NOW),
errmsg("the local listen ip and port is not for the gsql client")));
}
}
/*
* We need to check whether the connection can be accepted, standby and pending
* mode can not accept connection.
* Do not accept gsql connection when promote primary node does not write empty log.
* 1: when the client is walsender we do not check, except sender to dummy standby.
* 2: when the client is gsql, and use -m(it will make xc_maintenance_mode to on)
* to connect, we do not check.
*/
if ((!AM_WAL_SENDER && !(isMaintenanceConnection &&
(clientIsGsql || clientIsCmAgent || t_thrd.postmaster_cxt.senderToBuildStandby))) ||
t_thrd.postmaster_cxt.senderToDummyStandby) {
#ifdef ENABLE_MULTIPLE_NODES
if (STANDBY_MODE == hashmdata->current_mode)
ereport(
elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("can not accept connection in standby mode.")));
else if (PENDING_MODE == hashmdata->current_mode && !IS_PGXC_COORDINATOR)
ereport(elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW),
errmsg("can not accept connection in pending mode.")));
#else
if (PENDING_MODE == hashmdata->current_mode && !IS_PGXC_COORDINATOR) {
ereport(elevel, (errcode(ERRCODE_CANNOT_CONNECT_NOW),
errmsg("can not accept connection in pending mode.")));
}
#endif
}
if ((PM_RUN != pmState) && t_thrd.postmaster_cxt.senderToDummyStandby) {
ereport(elevel,
(errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("can not accept dummy standby connection in standby mode.")));
return STATUS_ERROR;
}
#ifdef USE_SSL
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.attr.attr_security.EnableSSL &&
u_sess->attr.attr_security.RequireSSL && !IS_AF_UNIX(port->laddr.addr.ss_family) && !SSLdone) {
/*
* only deal with connections between client and server.
* for connections between coordinators, we should not use SSL.
*/
if (!u_sess->proc_cxt.IsInnerMaintenanceTools && !IsConnPortFromCoord(port)) {
ereport(elevel,
(errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("SSL connection is required by the database system")));
}
}
#endif
#ifdef ENABLE_MULTIPLE_NODES
if (port->cmdline_options != NULL && strstr(port->cmdline_options, "remotetype=coordinator") != NULL) {
u_sess->attr.attr_common.remoteConnType = REMOTE_CONN_COORD;
} else
#endif
{
u_sess->attr.attr_common.remoteConnType = REMOTE_CONN_APP;
}
return STATUS_OK;
}
#ifdef ENABLE_MULTIPLE_NODES
/*
* The client has sent a stop query request, not a normal
* start-a-new-connection packet.
*/
static void processStopRequest(Port* port, void* pkt)
{
StopRequestPacket* csp = (StopRequestPacket*)pkt;
ThreadId backendPID;
int logictid = 0;
uint64 query_id = 0;
// get thread id from logic thread id
//
logictid = (int)ntohl(csp->backendPID);
query_id = (((uint64)ntohl(csp->query_id_first)) << 32) + (uint32)ntohl(csp->query_id_end);
if (ENABLE_THREAD_POOL) {
g_threadPoolControler->GetSessionCtrl()->SendProcSignal(logictid, PROCSIG_EXECUTOR_FLAG, query_id);
return;
}
backendPID = getThreadIdFromLogicThreadId(logictid);
/* If the mian thread already exit, no need to stop. */
if (0 != backendPID)
StreamNodeGroup::stopAllThreadInNodeGroup(backendPID, query_id);
}
/*
* The client has sent a connect to stream node request packet, not a normal
* start-a-new-connection packet. Perform the necessary processing.
* Socket fd will be sent back to the client.
*/
static void processConnectStreamRequest(Port* port, void* pkt, bool reject)
{
ConnectStreamPacket* csp = (ConnectStreamPacket*)pkt;
StreamKey key;
StreamConnInfo connInfo;
char status = (reject) ? 'r' : 'o';
bool result = true;
char sebuf[256];
errno_t rc = EOK;
connInfo.nodeIdx = 0;
u_sess->debug_query_id = ntohl64(csp->query_id);
if (reject)
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("setup stream connect fail due to gaussdb is in standby or pending mode")));
// send status to remote connect thread.
retry:
errno = 0;
if (send(port->sock, &status, 1, 0) <= 0) {
if (errno == EINTR)
goto retry; /* if interrupted, just retry */
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf));
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("setup stream connect fail due to send shake hand request fail: %s", sebuf)));
// just return, ignore the error
return;
}
retry1:
errno = 0;
if (recv(port->sock, &status, 1, 0) <= 0) {
if (errno == EINTR)
goto retry1; /* if interrupted, just retry */
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf));
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("setup stream connect fail due to receive shake hand request fail: %s", sebuf)));
// just return, ignore the error
return;
}
if (reject == true) {
return;
}
/* Check whether use gss kerberos authentication */
if (u_sess->attr.attr_security.pg_krb_server_keyfile != NULL &&
strlen(u_sess->attr.attr_security.pg_krb_server_keyfile) > 0) {
/* Server side gss kerberos authentication for tcp connection. */
if (GssServerAuth(port->sock, u_sess->attr.attr_security.pg_krb_server_keyfile) < 0) {
ereport(
COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("stream connection authentication failed!")));
return;
}
}
key.queryId = ntohl64(csp->query_id);
key.planNodeId = (uint32)ntohl(csp->plan_node_id);
key.smpIdentifier = (uint32)ntohl(csp->smpIdentifier);
connInfo.port.tcpLayer.fd = port->sock;
rc = strncpy_s(connInfo.nodeName, NAMEDATALEN, csp->node_name, strlen(csp->node_name) + 1);
securec_check(rc, "\0", "\0");
connInfo.nodeoid = (Oid)ntohl(csp->node_oid);
result = StreamConsumer::wakeUpConsumer(key, connInfo);
// if set up ok ,keep the socket.
if (result)
t_thrd.postmaster_cxt.KeepSocketOpenForStream = true;
else
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("setup stream connect fail due to register socket fail")));
}
#endif
/*
* The client has sent a cancel request packet, not a normal
* start-a-new-connection packet. Perform the necessary processing.
* Nothing is sent back to the client.
*/
static void processCancelRequest(Port* port, void* pkt)
{
CancelRequestPacket* canc = (CancelRequestPacket*)pkt;
long cancelAuthCode = (long)ntohl(canc->cancelAuthCode);
if ((cancelAuthCode & 0x1) == 0) {
if (ENABLE_THREAD_POOL) {
int sess_ctrl_id = (int)ntohl(canc->backendPID);
ThreadPoolSessControl *sess_ctrl = g_threadPoolControler->GetSessionCtrl();
knl_session_context *sess = sess_ctrl->GetSessionByIdx(sess_ctrl_id);
if (sess == NULL || sess->cancel_key != cancelAuthCode) {
ereport(LOG,(errmsg("Don't found the match sess, the session slot(%d)", sess_ctrl_id)));
}
int err = sess_ctrl->SendSignal((int)sess_ctrl_id, SIGINT);
if (err != 0) {
ereport(WARNING,
(errmsg("kill(session %ld, signal %d) failed: \"%s\", pmState %d, Demotion %d, Shutdown %d",
(long)sess_ctrl_id, SIGINT, gs_strerror(err), pmState, g_instance.demotion, Shutdown)));
}
} else {
ereport(WARNING, (errmsg("Receive invalid cancel key, which suppose to be thread pool mode.")));
}
} else {
int backendSlot = 0;
// get thread id from logic thread id
backendSlot = (int)ntohl(canc->backendPID);
Backend* bn = GetBackend(backendSlot);
if (bn == NULL || bn->pid == 0 || bn->pid == InvalidTid) {
ereport(LOG,
(errmsg(
"Don't found the match process, the backend slot(%d), pid(%lu)", backendSlot, bn ? bn->pid : 0)));
return;
}
if (bn->cancel_key == cancelAuthCode) {
/* Found a match; signal that backend to cancel current op */
ereport(DEBUG2, (errmsg_internal("processing cancel request: sending SIGINT to process %lu", bn->pid)));
signal_child(bn->pid, SIGINT);
} else
/* Right PID, wrong key: no way, Jose */
ereport(LOG, (errmsg("wrong key in cancel request for process %lu", bn->pid)));
}
}
/*
* canAcceptConnections --- check to see if database state allows connections.
*/
CAC_state canAcceptConnections(bool isSession)
{
CAC_state result = CAC_OK;
/*
* Can't start backends when in startup/shutdown/inconsistent recovery
* state. bgworkers are excluded from this test; we expect
* bgworker_should_start_now() decided whether the DB state allows them.
*
* In state PM_WAIT_BACKUP only superusers can connect (this must be
* allowed so that a superuser can end online backup mode); we return
* CAC_WAITBACKUP code to indicate that this must be checked later. Note
* that neither CAC_OK nor CAC_WAITBACKUP can safely be returned until we
* have checked for too many children.
*/
if (pmState != PM_RUN) {
if (pmState == PM_WAIT_BACKUP)
result = CAC_WAITBACKUP; /* allow superusers only */
else if (g_instance.status > NoShutdown || g_instance.demotion > NoDemote)
return CAC_SHUTDOWN; /* shutdown is pending */
else if (!g_instance.fatal_error && (pmState == PM_STARTUP || pmState == PM_RECOVERY))
return CAC_STARTUP; /* normal startup */
else if (!g_instance.fatal_error && pmState == PM_HOT_STANDBY)
result = CAC_OK; /* connection OK during hot standby */
else
return CAC_RECOVERY; /* else must be crash recovery */
}
if (isSession)
return result;
/*
* Don't start too many children.
*
* We allow more connections than we can have backends here because some
* might still be authenticating; they might fail auth, or some existing
* backend might exit before the auth cycle is completed. The exact
* g_instance.shmem_cxt.MaxBackends limit is enforced when a new backend tries to join the
* shared-inval backend array.
*
* The limit here must match the sizes of the per-child-process arrays;
* see comments for MaxLivePostmasterChildren().
*/
if (CountChildren(BACKEND_TYPE_ALL) >= MaxLivePostmasterChildren())
result = CAC_TOOMANY;
return result;
}
/*
* ConnCreateToRecvGssock -- For logic connection from CN, we generate gs_socket
* for logic connection in receiver flow control thread, we use this function to generate
* a port and receive gs_socket from receiver flow control thread by unix domain socket.
*
* Returns NULL on failure, Returns Port with gs_sock on succeed.
*/
static Port* ConnCreateToRecvGssock(pollfd* ufds, int idx, int* nSockets)
{
Port* port = NULL;
int error;
/*
* receiver flow ctrl receive logic connection request
* and no unix domain socket between receiver flow ctrl and server loop
* so receiver flow ctrl make connection and listening fd is polled up
*/
if (ufds[idx].fd == t_thrd.libpq_cxt.listen_fd_for_recv_flow_ctrl) {
port = ConnCreate(ufds[idx].fd);
if (port == NULL)
return port;
/* clean old socket if exist */
if (t_thrd.postmaster_cxt.sock_for_libcomm != PGINVALID_SOCKET) {
ConnFree((void*)t_thrd.postmaster_cxt.port_for_libcomm);
close(t_thrd.postmaster_cxt.sock_for_libcomm);
t_thrd.postmaster_cxt.sock_for_libcomm = PGINVALID_SOCKET;
t_thrd.postmaster_cxt.port_for_libcomm = NULL;
ufds[--(*nSockets)].fd = PGINVALID_SOCKET;
}
/* save the port and socket for connection between receiver flow ctrl and server loop */
t_thrd.postmaster_cxt.port_for_libcomm = port;
t_thrd.postmaster_cxt.sock_for_libcomm = port->sock;
/* add in ufds and it will polled up when receive a new gs_sock */
ufds[*nSockets].events = POLLIN | POLLPRI;
ufds[*nSockets].revents = 0;
ufds[(*nSockets)++].fd = t_thrd.postmaster_cxt.sock_for_libcomm;
}
/* sock_for_libcomm is polled up when recv flow ctrl send gs_sock by unix_domain sock */
else {
port = t_thrd.postmaster_cxt.port_for_libcomm;
if (port == NULL) {
return NULL;
}
port->sock = t_thrd.postmaster_cxt.sock_for_libcomm;
}
/* receive gs_sock by unix domain sock */
port->is_logic_conn = true;
error = gs_recv_msg_by_unix_domain(port->sock, &port->gs_sock);
/*
* for logic connection gs_sock is used for communication
* and port->sock is used to receive gs_sock, as we have already get gs_sock
* port->sock is unneeded for current connection, so set to -1.
* note: port->sock is an long_term socket, only one for each process
* it will be closed when receive error.
*/
if ((error > 0)) {
port->sock = PGINVALID_SOCKET;
return port;
}
ConnFree((void*)t_thrd.postmaster_cxt.port_for_libcomm);
t_thrd.postmaster_cxt.sock_for_libcomm = PGINVALID_SOCKET;
t_thrd.postmaster_cxt.port_for_libcomm = NULL;
port = NULL;
ufds[--(*nSockets)].fd = PGINVALID_SOCKET;
return port;
}
/*
* ConnCreate -- create a local connection data structure
*
* Returns NULL on failure, other than out-of-memory which is fatal.
*/
static Port* ConnCreate(int serverFd)
{
Port* port = NULL;
port = (Port*)MemoryContextAllocZero(u_sess->top_mem_cxt, 1 * sizeof(Port));
if (port == NULL) {
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
ExitPostmaster(1);
}
port->sock = PGINVALID_SOCKET;
port->gs_sock = GS_INVALID_GSOCK;
if (StreamConnection(serverFd, port) != STATUS_OK) {
if (port->sock >= 0)
StreamClose(port->sock);
ConnFree((void*)port);
return NULL;
}
/*
* Precompute password salt values to use for this connection. It's
* slightly annoying to do this long in advance of knowing whether we'll
* need 'em or not, but we must do the random() calls before we fork, not
* after. Else the postmaster's random sequence won't get advanced, and
* all backends would end up using the same salt...
* Use openssl RAND_bytes interface to generate random salt, cast char to
* unsigned char here.
*/
RAND_bytes((unsigned char*)port->md5Salt, sizeof(port->md5Salt));
/*
* Allocate GSSAPI specific state struct
*/
#ifndef EXEC_BACKEND
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
port->gss = (pg_gssinfo*)MemoryContextAllocZero(u_sess->top_mem_cxt, 1 * sizeof(pg_gssinfo));
if (!port->gss) {
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
ExitPostmaster(1);
}
#endif
#endif
port->is_logic_conn = false;
port->gs_sock.type = GSOCK_INVALID;
return port;
}
/*
* ConnFree -- free a local connection data structure
*/
void ConnFree(void* conn)
{
int at_main_thread = (!IsPostmasterEnvironment || t_thrd.proc_cxt.MyProcPid == PostmasterPid) ? 1 : 0;
Port* tmp_conn = (Port*)conn;
#ifdef USE_SSL
secure_close(tmp_conn);
#endif
if (tmp_conn != NULL && tmp_conn->gss != NULL) {
if (0 != at_main_thread) {
pfree(tmp_conn->gss);
} else {
free(tmp_conn->gss);
}
}
if (0 != at_main_thread) {
pfree(tmp_conn);
} else {
free(tmp_conn);
tmp_conn = NULL;
}
}
/*
* ClosePostmasterPorts -- close all the postmaster's open sockets
*
* This is called during child process startup to release file descriptors
* that are not needed by that child process. The postmaster still has
* them open, of course.
*
* Note: we pass am_syslogger as a boolean because we don't want to set
* the global variable yet when this is called.
*/
void ClosePostmasterPorts(bool am_syslogger)
{
int i;
#ifndef WIN32
/*
* Close the write end of postmaster death watch pipe. It's important to
* do this as early as possible, so that if postmaster dies, others won't
* think that it's still running because we're holding the pipe open.
*/
if (close(t_thrd.postmaster_cxt.postmaster_alive_fds[POSTMASTER_FD_OWN]))
ereport(FATAL,
(errcode_for_file_access(),
errmsg_internal("could not close postmaster death monitoring pipe in child process: %m")));
t_thrd.postmaster_cxt.postmaster_alive_fds[POSTMASTER_FD_OWN] = -1;
#endif
/* Close the listen sockets */
for (i = 0; i < MAXLISTEN; i++) {
if (t_thrd.postmaster_cxt.ListenSocket[i] != PGINVALID_SOCKET) {
StreamClose(t_thrd.postmaster_cxt.ListenSocket[i]);
t_thrd.postmaster_cxt.ListenSocket[i] = PGINVALID_SOCKET;
}
}
#ifndef EXEC_BACKEND
if (SCTP_CN_DN_CONN) {
for (i = 0; i < MAXLISTEN; i++) {
if (t_thrd.postmaster_cxt.SctpListenSocket[i] != PGINVALID_SOCKET) {
StreamClose(t_thrd.postmaster_cxt.SctpListenSocket[i]);
t_thrd.postmaster_cxt.SctpListenSocket[i] = PGINVALID_SOCKET;
}
}
}
#endif
/* If using syslogger, close the read side of the pipe */
if (!am_syslogger) {
#ifndef WIN32
if (t_thrd.postmaster_cxt.syslogPipe[0] >= 0)
close(t_thrd.postmaster_cxt.syslogPipe[0]);
t_thrd.postmaster_cxt.syslogPipe[0] = -1;
#else
if (t_thrd.postmaster_cxt.syslogPipe[0])
CloseHandle(t_thrd.postmaster_cxt.syslogPipe[0]);
t_thrd.postmaster_cxt.syslogPipe[0] = 0;
#endif
}
#ifdef USE_BONJOUR
/* If using Bonjour, close the connection to the mDNS daemon */
if (bonjour_sdref)
close(DNSServiceRefSockFD(bonjour_sdref));
#endif
}
/*
* on_proc_exit callback to close server's listen sockets
*/
static void CloseServerPorts(int status, Datum arg)
{
int i;
/*
* First, explicitly close all the socket FDs. We used to just let this
* happen implicitly at postmaster exit, but it's better to close them
* before we remove the postmaster.pid lockfile; otherwise there's a race
* condition if a new postmaster wants to re-use the TCP port number.
*/
for (i = 0; i < MAXLISTEN; i++) {
if (t_thrd.postmaster_cxt.ListenSocket[i] != PGINVALID_SOCKET) {
StreamClose(t_thrd.postmaster_cxt.ListenSocket[i]);
t_thrd.postmaster_cxt.ListenSocket[i] = PGINVALID_SOCKET;
}
}
/* close sctp socket if need too. */
if (SCTP_CN_DN_CONN) {
for (i = 0; i < MAXLISTEN; i++) {
if (t_thrd.postmaster_cxt.SctpListenSocket[i] != PGINVALID_SOCKET) {
StreamClose(t_thrd.postmaster_cxt.SctpListenSocket[i]);
t_thrd.postmaster_cxt.SctpListenSocket[i] = PGINVALID_SOCKET;
}
}
}
/*
* Removal of the Unix socket file and socket lockfile will happen in
* later on_proc_exit callbacks.
*/
}
void socket_close_on_exec(void)
{
/* Close the listen sockets */
for (int i = 0; i < MAXLISTEN; i++) {
if (t_thrd.postmaster_cxt.ListenSocket[i] != PGINVALID_SOCKET) {
int flags = fcntl(t_thrd.postmaster_cxt.ListenSocket[i], F_GETFD);
if (flags < 0)
ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("fcntl F_GETFD failed!")));
flags |= FD_CLOEXEC;
if (fcntl(t_thrd.postmaster_cxt.ListenSocket[i], F_SETFD, flags) < 0)
ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("fcntl F_SETFD failed!")));
}
}
}
/*
* reset_shared -- reset shared memory and semaphores
*/
static void reset_shared(int port)
{
/*
* Create or re-create shared memory and semaphores.
*
* Note: in each "cycle of life" we will normally assign the same IPC keys
* (if using SysV shmem and/or semas), since the port number is used to
* determine IPC keys. This helps ensure that we will clean up dead IPC
* objects if the postmaster crashes and is restarted.
*/
CreateSharedMemoryAndSemaphores(false, port);
}
/*
* SIGHUP -- reread config files, and tell children to do same
*/
static void SIGHUP_handler(SIGNAL_ARGS)
{
int save_errno = errno;
ConfFileLock filelock = {NULL, 0};
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
if (g_instance.status <= SmartShutdown && g_instance.demotion <= SmartDemote) {
char gucconf_lock_file[MAXPGPATH] = {0};
ereport(LOG, (errmsg("received SIGHUP, reloading configuration files")));
int rc = snprintf_s(gucconf_lock_file,
sizeof(gucconf_lock_file),
MAXPGPATH - 1,
"%s/postgresql.conf.lock",
t_thrd.proc_cxt.DataDir);
securec_check_intval(rc, , );
if (get_file_lock(gucconf_lock_file, &filelock) != CODE_OK) {
ereport(WARNING, (errmsg("the last sigup signal is processing,get file lock failed.")));
(void)PG_SETMASK(&t_thrd.libpq_cxt.UnBlockSig);
errno = save_errno;
return;
}
ProcessConfigFile(PGC_SIGHUP);
release_file_lock(&filelock);
(void)SignalChildren(SIGHUP);
if (ENABLE_THREAD_POOL) {
g_threadPoolControler->GetSessionCtrl()->SigHupHandler();
}
if (g_instance.pid_cxt.StartupPID != 0)
signal_child(g_instance.pid_cxt.StartupPID, SIGHUP);
if (IS_PGXC_COORDINATOR && g_instance.pid_cxt.TwoPhaseCleanerPID != 0)
signal_child(g_instance.pid_cxt.TwoPhaseCleanerPID, SIGHUP);
if (g_instance.pid_cxt.FaultMonitorPID != 0)
signal_child(g_instance.pid_cxt.FaultMonitorPID, SIGHUP);
if (g_instance.pid_cxt.BgWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.BgWriterPID, SIGHUP);
}
if (g_instance.pid_cxt.CheckpointerPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CheckpointerPID, SIGHUP);
}
if (g_instance.pid_cxt.PageWriterPID != NULL) {
int i;
for (i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
if (g_instance.pid_cxt.PageWriterPID[i] != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PageWriterPID[i], SIGHUP);
}
}
}
if (g_instance.pid_cxt.WalWriterPID != 0)
signal_child(g_instance.pid_cxt.WalWriterPID, SIGHUP);
if (g_instance.pid_cxt.WalRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.WalRcvWriterPID, SIGHUP);
if (g_instance.pid_cxt.WalReceiverPID != 0)
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGHUP);
if (g_instance.pid_cxt.DataRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.DataRcvWriterPID, SIGHUP);
if (g_instance.pid_cxt.DataReceiverPID != 0)
signal_child(g_instance.pid_cxt.DataReceiverPID, SIGHUP);
if (g_instance.pid_cxt.AutoVacPID != 0)
signal_child(g_instance.pid_cxt.AutoVacPID, SIGHUP);
if (IS_PGXC_COORDINATOR && g_instance.pid_cxt.PgJobSchdPID != 0)
signal_child(g_instance.pid_cxt.PgJobSchdPID, SIGHUP);
if (g_instance.pid_cxt.PgArchPID != 0)
signal_child(g_instance.pid_cxt.PgArchPID, SIGHUP);
if (g_instance.pid_cxt.SysLoggerPID != 0)
signal_child(g_instance.pid_cxt.SysLoggerPID, SIGHUP);
/* signal the auditor process */
if (g_instance.pid_cxt.PgAuditPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PgAuditPID, SIGHUP);
}
if (g_instance.pid_cxt.PgStatPID != 0)
signal_child(g_instance.pid_cxt.PgStatPID, SIGHUP);
if (g_instance.pid_cxt.SnapshotPID != 0)
signal_child(g_instance.pid_cxt.SnapshotPID, SIGHUP);
if (g_instance.pid_cxt.AlarmCheckerPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.AlarmCheckerPID, SIGHUP);
}
if (g_instance.pid_cxt.ReaperBackendPID != 0) {
signal_child(g_instance.pid_cxt.ReaperBackendPID, SIGHUP);
}
if (g_instance.pid_cxt.WLMCollectPID != 0)
signal_child(g_instance.pid_cxt.WLMCollectPID, SIGHUP);
if (g_instance.pid_cxt.WLMMonitorPID != 0)
signal_child(g_instance.pid_cxt.WLMMonitorPID, SIGHUP);
if (g_instance.pid_cxt.WLMArbiterPID != 0)
signal_child(g_instance.pid_cxt.WLMArbiterPID, SIGHUP);
if (g_instance.pid_cxt.CBMWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CBMWriterPID, SIGHUP);
}
if (g_instance.pid_cxt.RemoteServicePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGHUP);
}
if (g_instance.pid_cxt.PercentilePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PercentilePID, SIGHUP);
}
if (g_instance.pid_cxt.HeartbeatPID != 0) {
signal_child(g_instance.pid_cxt.HeartbeatPID, SIGHUP);
}
if (g_instance.pid_cxt.CommSenderFlowPID != 0) {
signal_child(g_instance.pid_cxt.CommSenderFlowPID, SIGHUP);
}
if (g_instance.pid_cxt.CommReceiverFlowPID != 0) {
signal_child(g_instance.pid_cxt.CommReceiverFlowPID, SIGHUP);
}
if (g_instance.pid_cxt.CommAuxiliaryPID != 0) {
signal_child(g_instance.pid_cxt.CommAuxiliaryPID, SIGHUP);
}
if (g_instance.pid_cxt.CommPoolerCleanPID != 0) {
signal_child(g_instance.pid_cxt.CommPoolerCleanPID, SIGHUP);
}
if (g_instance.pid_cxt.CommReceiverPIDS != NULL) {
int recv_loop = 0;
for (recv_loop = 0; recv_loop < g_instance.attr.attr_network.comm_max_receiver; recv_loop++) {
if (g_instance.pid_cxt.CommReceiverPIDS[recv_loop] != 0) {
signal_child(g_instance.pid_cxt.CommReceiverPIDS[recv_loop], SIGHUP);
}
}
}
/* Reload authentication config files too */
int loadhbaCount = 0;
(void)pthread_rwlock_wrlock(&hba_rwlock);
while (!load_hba()) {
loadhbaCount++;
pg_usleep(200000L); // slepp 200ms for reload
if (loadhbaCount >= 3) {
/*
* It makes no sense to continue if we fail to load the HBA file,
* since there is no way to connect to the database in this case.
*/
ereport(WARNING, (errmsg("pg_hba.conf not reloaded")));
break;
}
}
(void)pthread_rwlock_unlock(&hba_rwlock);
load_ident();
/* Reload license file. */
signalReloadLicenseHandler(SIGHUP);
#ifdef EXEC_BACKEND
/* Update the starting-point file for future children */
write_nondefault_variables(PGC_SIGHUP);
#endif
}
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
errno = save_errno;
}
void KillGraceThreads(void)
{
if (g_instance.pid_cxt.PgStatPID != 0) {
signal_child(g_instance.pid_cxt.PgStatPID, SIGQUIT);
}
if (g_instance.pid_cxt.AlarmCheckerPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.AlarmCheckerPID, SIGQUIT);
}
}
static void NotifyShutdown(void)
{
will_shutdown = true;
return;
}
static void NotifyProcessActive(void)
{
will_shutdown = false;
return;
}
/*
* pmdie -- signal handler for processing various postmaster signals.
*/
static void pmdie(SIGNAL_ARGS)
{
int save_errno = errno;
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
ereport(DEBUG2, (errmsg_internal("postmaster received signal %d", postgres_signal_arg)));
NotifyShutdown();
switch (postgres_signal_arg) {
case SIGTERM:
case SIGINT:
if (STANDBY_MODE == t_thrd.postmaster_cxt.HaShmData->current_mode && !dummyStandbyMode &&
SIGTERM == postgres_signal_arg) {
/*
* Smart g_instance.status:
*
* Wait for children to end their work, then shut down.
*/
if (g_instance.status >= SmartShutdown)
break;
g_instance.status = SmartShutdown;
ereport(LOG, (errmsg("received smart shutdown request")));
/* Audit system stop */
pgaudit_system_stop_ok(SmartShutdown);
} else {
/*
* Fast g_instance.status:
*
* Abort all children with SIGTERM (rollback active transactions
* and exit) and shut down when they are gone.
*/
if (g_instance.status >= FastShutdown)
break;
g_instance.status = FastShutdown;
ereport(LOG, (errmsg("received fast shutdown request")));
/* Audit system stop */
pgaudit_system_stop_ok(FastShutdown);
}
if (g_instance.pid_cxt.StartupPID != 0)
signal_child(g_instance.pid_cxt.StartupPID, SIGTERM);
if (g_instance.pid_cxt.BgWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.BgWriterPID, SIGTERM);
}
if (g_instance.pid_cxt.WalRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.WalRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.WalReceiverPID != 0)
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGTERM);
if (g_instance.pid_cxt.DataRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.DataRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.DataReceiverPID != 0)
signal_child(g_instance.pid_cxt.DataReceiverPID, SIGTERM);
if (g_instance.pid_cxt.HeartbeatPID != 0)
signal_child(g_instance.pid_cxt.HeartbeatPID, SIGTERM);
if (g_instance.pid_cxt.FaultMonitorPID != 0)
signal_child(g_instance.pid_cxt.FaultMonitorPID, SIGTERM);
if (g_instance.pid_cxt.TwoPhaseCleanerPID != 0)
signal_child(g_instance.pid_cxt.TwoPhaseCleanerPID, SIGTERM);
if (g_instance.pid_cxt.WLMCollectPID != 0) {
WLMProcessThreadShutDown();
signal_child(g_instance.pid_cxt.WLMCollectPID, SIGTERM);
}
if (g_instance.pid_cxt.SnapshotPID != 0) {
WLMProcessThreadShutDown();
signal_child(g_instance.pid_cxt.SnapshotPID, SIGTERM);
}
if (g_instance.pid_cxt.PercentilePID != 0) {
WLMProcessThreadShutDown();
signal_child(g_instance.pid_cxt.PercentilePID, SIGTERM);
}
if (g_instance.pid_cxt.WLMMonitorPID != 0)
signal_child(g_instance.pid_cxt.WLMMonitorPID, SIGTERM);
if (g_instance.pid_cxt.WLMArbiterPID != 0)
signal_child(g_instance.pid_cxt.WLMArbiterPID, SIGTERM);
if (g_instance.pid_cxt.CPMonitorPID != 0)
signal_child(g_instance.pid_cxt.CPMonitorPID, SIGTERM);
if (g_instance.pid_cxt.CBMWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CBMWriterPID, SIGTERM);
}
if (g_instance.pid_cxt.RemoteServicePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGTERM);
}
if (g_instance.pid_cxt.CommSenderFlowPID != 0) {
signal_child(g_instance.pid_cxt.CommSenderFlowPID, SIGTERM);
}
if (g_instance.pid_cxt.CommReceiverFlowPID != 0) {
signal_child(g_instance.pid_cxt.CommReceiverFlowPID, SIGTERM);
}
if (g_instance.pid_cxt.CommAuxiliaryPID != 0) {
signal_child(g_instance.pid_cxt.CommAuxiliaryPID, SIGTERM);
}
if (g_instance.pid_cxt.CommPoolerCleanPID != 0) {
signal_child(g_instance.pid_cxt.CommPoolerCleanPID, SIGTERM);
}
if (g_instance.pid_cxt.CommReceiverPIDS != NULL) {
for (int recv_loop = 0; recv_loop < g_instance.attr.attr_network.comm_max_receiver; recv_loop++) {
if (g_instance.pid_cxt.CommReceiverPIDS[recv_loop] != 0) {
signal_child(g_instance.pid_cxt.CommReceiverPIDS[recv_loop], SIGTERM);
}
}
}
/* and the walwriter too */
if (g_instance.pid_cxt.WalWriterPID != 0)
signal_child(g_instance.pid_cxt.WalWriterPID, SIGTERM);
if (ENABLE_THREAD_POOL && (pmState == PM_RECOVERY || pmState == PM_STARTUP)) {
/*
* Although there is not connections from client at PM_RECOVERY and PM_STARTUP
* state, we still have to close thread pool thread.
*/
g_threadPoolControler->ShutDownWorker();
}
if (pmState == PM_RECOVERY) {
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_BGWORKER);
/*
* Only startup, bgwriter, walreceiver, possibly bgworkers,
* and/or checkpointer should be active in this state; we just
* signaled the first four, and we don't want to kill checkpointer yet.
*/
pmState = PM_WAIT_BACKENDS;
} else if (pmState == PM_RUN || pmState == PM_WAIT_BACKUP || pmState == PM_WAIT_READONLY ||
pmState == PM_WAIT_BACKENDS || pmState == PM_HOT_STANDBY) {
ereport(LOG, (errmsg("aborting any active transactions")));
if (ENABLE_THREAD_POOL) {
g_threadPoolControler->CloseAllSessions();
g_threadPoolControler->ShutDownWorker();
}
/* shut down all backends and bgworkers and autovac workers */
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER);
/* and the autovac launcher too */
if (g_instance.pid_cxt.AutoVacPID != 0)
signal_child(g_instance.pid_cxt.AutoVacPID, SIGTERM);
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID != 0)
signal_child(g_instance.pid_cxt.PgJobSchdPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
/*
* Now wait for backends to exit. If there are none,
* PostmasterStateMachine will take the next step.
*/
PostmasterStateMachine();
break;
case SIGQUIT:
/*
* Immediate g_instance.status:
*
* exit whole process, do not send SIGQUIT to child.
*/
ereport(LOG, (errmsg("received immediate shutdown request")));
/* Audit system stop */
pgaudit_system_stop_ok(ImmediateShutdown);
g_instance.status = ImmediateShutdown;
KillGraceThreads();
WaitGraceThreadsExit();
// threading: do not clean sema, maybe other thread is using it.
//
cancelSemphoreRelease();
cancelIpcMemoryDetach();
ExitPostmaster(0);
break;
default:
break;
}
NotifyProcessActive();
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
errno = save_errno;
}
/* set walsenders node state */
static void SetWalsndsNodeState(ClusterNodeState requester, ClusterNodeState others)
{
int i;
/* update the demote state */
for (i = 0; i < g_instance.attr.attr_storage.max_wal_senders; i++) {
/* use volatile pointer to prevent code rearrangement */
volatile WalSnd* walsnd = &t_thrd.walsender_cxt.WalSndCtl->walsnds[i];
if (0 == walsnd->pid)
continue;
if (walsnd->node_state >= NODESTATE_SMART_DEMOTE_REQUEST && walsnd->node_state <= NODESTATE_FAST_DEMOTE_REQUEST)
walsnd->node_state = requester;
else
walsnd->node_state = others;
}
}
/* prepare to response to standby for switchover */
static void PrepareDemoteResponse(void)
{
if (NoDemote == g_instance.demotion)
return;
SetWalsndsNodeState(NODESTATE_PROMOTE_APPROVE, NODESTATE_STANDBY_REDIRECT);
allow_immediate_pgstat_restart();
}
/* * process demote request from standby */
static void ProcessDemoteRequest(void)
{
DemoteMode mode;
/* get demote request type */
mode = t_thrd.walsender_cxt.WalSndCtl->demotion;
if (NoDemote == mode)
return;
/* check the postmaster state */
if (pmState != PM_RUN && NoDemote == g_instance.demotion) {
SetWalsndsNodeState(NODESTATE_NORMAL, NODESTATE_NORMAL);
t_thrd.walsender_cxt.WalSndCtl->demotion = NoDemote;
ereport(NOTICE, (errmsg("postmaster state not in PM_RUN, primary would not demote.")));
return;
}
PMUpdateDBState(DEMOTING_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(DEMOTING_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
switch (mode) {
case SmartDemote:
/*
* Smart Demote:
*
* Wait for children to end their work, then start up as standby.
*/
if (g_instance.demotion >= SmartDemote)
break;
g_instance.demotion = SmartDemote;
ereport(LOG, (errmsg("received smart demote request")));
if (pmState == PM_RUN || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_STARTUP) {
/* autovacuum workers are told to shut down immediately */
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_AUTOVAC);
/* and the autovac launcher too */
if (g_instance.pid_cxt.AutoVacPID != 0)
signal_child(g_instance.pid_cxt.AutoVacPID, SIGTERM);
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID != 0)
signal_child(g_instance.pid_cxt.PgJobSchdPID, SIGTERM);
if ((IS_PGXC_COORDINATOR) && g_instance.pid_cxt.CommPoolerCleanPID != 0)
signal_child(g_instance.pid_cxt.CommPoolerCleanPID, SIGTERM);
/* and the bgwriter too */
if (g_instance.pid_cxt.BgWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.BgWriterPID, SIGTERM);
}
/* and the walwriter too */
if (g_instance.pid_cxt.WalWriterPID != 0)
signal_child(g_instance.pid_cxt.WalWriterPID, SIGTERM);
if (g_instance.pid_cxt.CBMWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CBMWriterPID, SIGTERM);
}
// should do this ?
if (g_instance.pid_cxt.RemoteServicePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGTERM);
}
/*
* If we're in recovery, we can't kill the startup process
* right away, because at present doing so does not release
* its locks. We might want to change this in a future
* release. For the time being, the PM_WAIT_READONLY state
* indicates that we're waiting for the regular (read only)
* backends to die off; once they do, we'll kill the startup
* and walreceiver processes.
*/
pmState = (pmState == PM_RUN) ? PM_WAIT_BACKUP : PM_WAIT_READONLY;
}
break;
case FastDemote:
/*
* Fast Demote:
*
* Abort all children with SIGTERM (rollback active transactions
* and exit) and start up when they are gone.
*/
if (g_instance.demotion >= FastDemote)
break;
g_instance.demotion = FastDemote;
ereport(LOG, (errmsg("received fast demote request")));
if (g_instance.pid_cxt.StartupPID != 0)
signal_child(g_instance.pid_cxt.StartupPID, SIGTERM);
if (g_instance.pid_cxt.BgWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.BgWriterPID, SIGTERM);
}
if (g_instance.pid_cxt.WalRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.WalRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.WalReceiverPID != 0)
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGTERM);
if (g_instance.pid_cxt.DataRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.DataRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.DataReceiverPID != 0)
signal_child(g_instance.pid_cxt.DataReceiverPID, SIGTERM);
if (g_instance.pid_cxt.HeartbeatPID != 0)
signal_child(g_instance.pid_cxt.HeartbeatPID, SIGTERM);
if (g_instance.pid_cxt.WLMCollectPID != 0) {
WLMProcessThreadShutDown();
signal_child(g_instance.pid_cxt.WLMCollectPID, SIGTERM);
}
if (g_instance.pid_cxt.WLMMonitorPID != 0)
signal_child(g_instance.pid_cxt.WLMMonitorPID, SIGTERM);
if (g_instance.pid_cxt.WLMArbiterPID != 0)
signal_child(g_instance.pid_cxt.WLMArbiterPID, SIGTERM);
if (g_instance.pid_cxt.CPMonitorPID != 0)
signal_child(g_instance.pid_cxt.CPMonitorPID, SIGTERM);
if (g_instance.pid_cxt.FaultMonitorPID != 0)
signal_child(g_instance.pid_cxt.FaultMonitorPID, SIGTERM);
if (g_instance.pid_cxt.CBMWriterPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CBMWriterPID, SIGTERM);
}
if (g_instance.pid_cxt.RemoteServicePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGTERM);
}
/* as single_node model will start WLM & Snaphost, but dont't terminate it whiele switchover, now kill it */
if (g_instance.pid_cxt.WLMCollectPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.WLMCollectPID, SIGTERM);
}
if (g_instance.pid_cxt.SnapshotPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.SnapshotPID, SIGTERM);
}
if (g_instance.pid_cxt.PercentilePID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PercentilePID, SIGTERM);
}
if (pmState == PM_RECOVERY) {
/*
* Only startup, bgwriter, and checkpointer should be active
* in this state; we just signaled the first two, and we don't
* want to kill checkpointer yet.
*/
pmState = PM_WAIT_BACKENDS;
} else if (pmState == PM_RUN || pmState == PM_WAIT_BACKUP || pmState == PM_WAIT_READONLY ||
pmState == PM_WAIT_BACKENDS || pmState == PM_HOT_STANDBY) {
ereport(LOG, (errmsg("aborting any active transactions")));
if (ENABLE_THREAD_POOL) {
g_threadPoolControler->CloseAllSessions();
g_threadPoolControler->ShutDownWorker();
}
/* shut down all backends and autovac workers */
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC);
/* and the autovac launcher too */
if (g_instance.pid_cxt.AutoVacPID != 0)
signal_child(g_instance.pid_cxt.AutoVacPID, SIGTERM);
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID != 0)
signal_child(g_instance.pid_cxt.PgJobSchdPID, SIGTERM);
if ((IS_PGXC_COORDINATOR) && g_instance.pid_cxt.CommPoolerCleanPID != 0)
signal_child(g_instance.pid_cxt.CommPoolerCleanPID, SIGTERM);
/* and the walwriter too */
if (g_instance.pid_cxt.WalWriterPID != 0)
signal_child(g_instance.pid_cxt.WalWriterPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
break;
default:
break;
}
/*
* Now wait for backends to exit. If there are none,
* PostmasterStateMachine will take the next step.
*/
PostmasterStateMachine();
}
/*
* Reaper -- signal handler to cleanup after a child process dies.
*/
static void reaper(SIGNAL_ARGS)
{
int save_errno = errno;
ThreadId pid; /* process id of dead child process */
long exitstatus; /* its exit status */
int* status = NULL;
ThreadId oldpid = 0;
#define LOOPTEST() (pid = gs_thread_id(t_thrd.postmaster_cxt.CurExitThread))
#define LOOPHEADER() (exitstatus = (long)(intptr_t)status)
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
ereport(DEBUG4, (errmsg_internal("reaping dead processes")));
for (;;) {
LOOPTEST();
if (pid == oldpid) {
break;
}
oldpid = pid;
if (gs_thread_join(t_thrd.postmaster_cxt.CurExitThread, (void**)&status) != 0) {
/*
* If the thread does not exist, treat it as normal exit and we continue to
* do our clean-up work. Otherwise, we treate it as crashed 'cause we do
* not know the current status of the thread and it's better to quit directly
* which sames more safely.
*/
if (ESRCH == pthread_kill(pid, 0)) {
exitstatus = 0;
ereport(LOG, (errmsg("failed to join thread %lu, no such process", pid)));
} else {
exitstatus = 1;
HandleChildCrash(pid, exitstatus, _(GetProcName(pid)));
}
} else {
LOOPHEADER();
ereport(DEBUG1, (errmsg("have joined thread %lu, exitstatus=%ld.", pid, exitstatus)));
}
/*
* Check if this child was a startup process.
*/
if (pid == g_instance.pid_cxt.StartupPID) {
g_instance.pid_cxt.StartupPID = 0;
/*
* Startup process exited in response to a shutdown request (or it
* completed normally regardless of the shutdown request).
*/
if (g_instance.status > NoShutdown && (EXIT_STATUS_0(exitstatus) || EXIT_STATUS_1(exitstatus))) {
pmState = PM_WAIT_BACKENDS;
/* PostmasterStateMachine logic does the rest */
continue;
}
/*
* Unexpected exit of startup process (including FATAL exit)
* during PM_STARTUP is treated as catastrophic. There are no
* other processes running yet, so we can just exit.
*/
if (pmState == PM_STARTUP && !EXIT_STATUS_0(exitstatus)) {
LogChildExit(LOG, _("startup process"), pid, exitstatus);
ereport(LOG, (errmsg("aborting startup due to startup process failure")));
if (get_real_recovery_parallelism() > 1) {
HandleChildCrash(pid, exitstatus, _("startup process"));
} else {
/* Shut down threadpool worker before exit. */
if (ENABLE_THREAD_POOL) {
g_threadPoolControler->ShutDownWorker(true);
}
ExitPostmaster(1);
}
}
/*
* After PM_STARTUP, any unexpected exit (including FATAL exit) of
* the startup process is catastrophic, so kill other children,
* and set RecoveryError so we don't try to reinitialize after
* they're gone. Exception: if g_instance.fatal_error is already set, that
* implies we previously sent the startup process a SIGQUIT, so
* that's probably the reason it died, and we do want to try to
* restart in that case.
*/
if (!EXIT_STATUS_0(exitstatus)) {
if (!g_instance.fatal_error)
g_instance.recover_error = true;
HandleChildCrash(pid, exitstatus, _("startup process"));
continue;
}
/*
* Startup succeeded, commence normal operations
*/
g_instance.fatal_error = false;
g_instance.demotion = NoDemote;
t_thrd.postmaster_cxt.ReachedNormalRunning = true;
pmState = PM_RUN;
if (t_thrd.postmaster_cxt.HaShmData && (t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE ||
t_thrd.postmaster_cxt.HaShmData->current_mode == PENDING_MODE)) {
t_thrd.postmaster_cxt.HaShmData->current_mode = PRIMARY_MODE;
if (g_instance.pid_cxt.HeartbeatPID != 0)
signal_child(g_instance.pid_cxt.HeartbeatPID, SIGTERM);
UpdateOptsFile();
if (t_thrd.walreceiverfuncs_cxt.WalRcv)
t_thrd.walreceiverfuncs_cxt.WalRcv->node_state = NODESTATE_NORMAL;
}
/*
* Kill any walsenders to force the downstream standby(s) to
* reread the timeline history file, adjust their timelines and
* establish replication connections again. This is required
* because the timeline of cascading standby is not consistent
* with that of cascaded one just after failover. We LOG this
* message since we need to leave a record to explain this
* disconnection.
*
* XXX should avoid the need for disconnection. When we do,
* am_cascading_walsender should be replaced with
* RecoveryInProgress()
*/
if (g_instance.attr.attr_storage.max_wal_senders > 0 && CountChildren(BACKEND_TYPE_WALSND) > 0) {
ereport(LOG,
(errmsg("terminating all walsender processes to force cascaded "
"standby(s) to update timeline and reconnect")));
(void)SignalSomeChildren(SIGUSR2, BACKEND_TYPE_WALSND);
}
/*
* Crank up the background tasks, if we didn't do that already
* when we entered consistent recovery state. It doesn't matter
* if this fails, we'll just try again later.
*/
if (g_instance.pid_cxt.CheckpointerPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.CheckpointerPID = initialize_util_thread(CHECKPOINT_THREAD);
if (g_instance.pid_cxt.BgWriterPID == 0 && !dummyStandbyMode &&
!g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
g_instance.pid_cxt.BgWriterPID = initialize_util_thread(BGWRITER);
}
if (!dummyStandbyMode && g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
int i;
for (i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
if (g_instance.pid_cxt.PageWriterPID[i] == 0) {
g_instance.pid_cxt.PageWriterPID[i] = initialize_util_thread(PAGEWRITER_THREAD);
}
}
}
if (g_instance.pid_cxt.WalWriterPID == 0)
g_instance.pid_cxt.WalWriterPID = initialize_util_thread(WALWRITER);
if (g_instance.pid_cxt.CBMWriterPID == 0 && !dummyStandbyMode &&
u_sess->attr.attr_storage.enable_cbm_tracking)
g_instance.pid_cxt.CBMWriterPID = initialize_util_thread(CBMWRITER);
/*
* Likewise, start other special children as needed. In a restart
* situation, some of them may be alive already.
*/
if (!u_sess->proc_cxt.IsBinaryUpgrade && AutoVacuumingActive() && g_instance.pid_cxt.AutoVacPID == 0 &&
!dummyStandbyMode && u_sess->attr.attr_common.upgrade_mode != 1 && !InplaceUpgradePrecommit)
g_instance.pid_cxt.AutoVacPID = initialize_util_thread(AUTOVACUUM_LAUNCHER);
/* Before GRAND VERSION NUM 81000, we do not support scheduled job. */
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PgJobSchdPID == 0 &&
g_instance.attr.attr_sql.job_queue_processes && u_sess->attr.attr_common.upgrade_mode != 1)
g_instance.pid_cxt.PgJobSchdPID = initialize_util_thread(JOB_SCHEDULER);
if ((IS_PGXC_COORDINATOR) && g_instance.pid_cxt.CommPoolerCleanPID == 0 &&
u_sess->attr.attr_common.upgrade_mode != 1) {
StartPoolCleaner();
}
if (XLogArchivingActive() && g_instance.pid_cxt.PgArchPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.PgArchPID = pgarch_start();
if (g_instance.pid_cxt.PgStatPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.PgStatPID = pgstat_start();
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.SnapshotPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.SnapshotPID = snapshot_start();
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && g_instance.pid_cxt.PercentilePID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.PercentilePID = initialize_util_thread(PERCENTILE_WORKER);
/* Database Security: Support database audit */
/* start auditor process */
/* start the audit collector as needed. */
if (g_instance.pid_cxt.PgAuditPID == 0 && u_sess->attr.attr_security.Audit_enabled && !dummyStandbyMode)
g_instance.pid_cxt.PgAuditPID = pgaudit_start();
if (t_thrd.postmaster_cxt.audit_primary_start && !t_thrd.postmaster_cxt.audit_primary_failover &&
!t_thrd.postmaster_cxt.audit_standby_switchover) {
pgaudit_system_start_ok(g_instance.attr.attr_network.PostPortNumber);
t_thrd.postmaster_cxt.audit_primary_start = false;
}
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_common.upgrade_mode != 1 &&
g_instance.pid_cxt.TwoPhaseCleanerPID == 0)
g_instance.pid_cxt.TwoPhaseCleanerPID = initialize_util_thread(TWOPASECLEANER);
if (g_instance.pid_cxt.FaultMonitorPID == 0)
g_instance.pid_cxt.FaultMonitorPID = initialize_util_thread(FAULTMONITOR);
/* if workload manager is off, we still use this thread to build user hash table */
if ((ENABLE_WORKLOAD_CONTROL || !WLMIsInfoInit()) && g_instance.pid_cxt.WLMCollectPID == 0 &&
!dummyStandbyMode) {
/* DN need rebuild hash when upgrade to primary */
if (IS_PGXC_DATANODE)
g_instance.wlm_cxt->stat_manager.infoinit = 0;
g_instance.pid_cxt.WLMCollectPID = initialize_util_thread(WLM_WORKER);
}
if (ENABLE_WORKLOAD_CONTROL && (g_instance.pid_cxt.WLMMonitorPID == 0) && !dummyStandbyMode)
g_instance.pid_cxt.WLMMonitorPID = initialize_util_thread(WLM_MONITOR);
if (ENABLE_WORKLOAD_CONTROL && (g_instance.pid_cxt.WLMArbiterPID == 0) && !dummyStandbyMode)
g_instance.pid_cxt.WLMArbiterPID = initialize_util_thread(WLM_ARBITER);
if (IS_PGXC_COORDINATOR && g_instance.attr.attr_sql.max_resource_package &&
(g_instance.pid_cxt.CPMonitorPID == 0) && !dummyStandbyMode)
g_instance.pid_cxt.CPMonitorPID = initialize_util_thread(WLM_CPMONITOR);
if (g_instance.pid_cxt.RemoteServicePID == 0 && !dummyStandbyMode && IS_PGXC_DATANODE &&
t_thrd.postmaster_cxt.HaShmData && t_thrd.postmaster_cxt.HaShmData->current_mode != NORMAL_MODE &&
!IS_DN_WITHOUT_STANDBYS_MODE() && IsRemoteReadModeOn())
g_instance.pid_cxt.RemoteServicePID = initialize_util_thread(RPC_SERVICE);
if (NeedHeartbeat())
g_instance.pid_cxt.HeartbeatPID = initialize_util_thread(HEARTBEAT);
PMUpdateDBState(NORMAL_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(NORMAL_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
/* at this point we are really open for business */
ereport(LOG, (errmsg("database system is ready to accept connections")));
continue;
}
/*
* Was it the bgwriter? Normal exit can be ignored; we'll start a new
* one at the next iteration of the postmaster's main loop, if
* necessary. Any other exit condition is treated as a crash.
*/
if (pid == g_instance.pid_cxt.BgWriterPID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.BgWriterPID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("background writer process"));
continue;
}
/*
* Was it the pagewriter?
*/
if (g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
int i;
for (i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
if (pid == g_instance.pid_cxt.PageWriterPID[i]) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.PageWriterPID[i] = 0;
if (!EXIT_STATUS_0(exitstatus)) {
HandleChildCrash(pid, exitstatus, _("page writer process"));
}
continue;
}
}
}
/*
* Was it the checkpointer?
*/
if (pid == g_instance.pid_cxt.CheckpointerPID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.CheckpointerPID = 0;
if (EXIT_STATUS_0(exitstatus) && pmState == PM_SHUTDOWN) {
/*
* OK, we saw normal exit of the checkpointer after it's been
* told to shut down. We expect that it wrote a shutdown
* checkpoint. (If for some reason it didn't, recovery will
* occur on next postmaster start.)
*
* At this point we should have no normal backend children
* left (else we'd not be in PM_SHUTDOWN state) but we might
* have dead_end children to wait for.
*
* If we have an archiver subprocess, tell it to do a last
* archive cycle and quit. Likewise, if we have walsender
* processes, tell them to send any remaining WAL and quit.
*/
Assert(g_instance.status > NoShutdown || g_instance.demotion > NoDemote);
/* Waken archiver for the last time */
if (g_instance.pid_cxt.PgArchPID != 0)
signal_child(g_instance.pid_cxt.PgArchPID, SIGUSR2);
PrepareDemoteResponse();
/*
* Waken all senders for the last time. No regular backends
* should be around anymore except catchup process.
*/
SignalChildren(SIGUSR2);
pmState = PM_SHUTDOWN_2;
/*
* We can also shut down the stats collector now; there's
* nothing left for it to do.
*/
if (g_instance.pid_cxt.PgStatPID != 0)
signal_child(g_instance.pid_cxt.PgStatPID, SIGQUIT);
if (g_instance.pid_cxt.SnapshotPID != 0)
signal_child(g_instance.pid_cxt.SnapshotPID, SIGQUIT);
if (g_instance.pid_cxt.PercentilePID != 0)
signal_child(g_instance.pid_cxt.PercentilePID, SIGQUIT);
/*
* We can also shut down the audit collector now; there's
* nothing left for it to do.
*/
if (g_instance.pid_cxt.PgAuditPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PgAuditPID, SIGQUIT);
}
} else {
/*
* Any unexpected exit of the checkpointer (including FATAL
* exit) is treated as a crash.
*/
HandleChildCrash(pid, exitstatus, _("checkpointer process"));
}
continue;
}
/*
* Was it the wal writer? Normal exit can be ignored; we'll start a
* new one at the next iteration of the postmaster's main loop, if
* necessary. Any other exit condition is treated as a crash.
*/
if (pid == g_instance.pid_cxt.WalWriterPID) {
g_instance.pid_cxt.WalWriterPID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("WAL writer process"));
continue;
}
/*
* Was it the wal receiver? If exit status is zero (normal) or one
* (FATAL exit), we assume everything is all right just like normal
* backends.
*/
if (pid == g_instance.pid_cxt.WalReceiverPID) {
g_instance.pid_cxt.WalReceiverPID = 0;
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
HandleChildCrash(pid, exitstatus, _("WAL receiver process"));
continue;
}
/*
* Was it the wal receive writer? If exit status is zero (normal) or one
* (FATAL exit), we assume everything is all right just like normal
* backends.
*/
if (pid == g_instance.pid_cxt.WalRcvWriterPID) {
g_instance.pid_cxt.WalRcvWriterPID = 0;
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
HandleChildCrash(pid, exitstatus, _("WAL receive writer process"));
continue;
}
/*
* Was it the data receiver? If exit status is zero (normal) or one
* (FATAL exit), we assume everything is all right just like normal
* backends.
*/
if (pid == g_instance.pid_cxt.DataReceiverPID) {
g_instance.pid_cxt.DataReceiverPID = 0;
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
HandleChildCrash(pid, exitstatus, _("DATA receiver process"));
continue;
}
/*
* Was it the data receive writer? If exit status is zero (normal) or one
* (FATAL exit), we assume everything is all right just like normal
* backends.
*/
if (pid == g_instance.pid_cxt.DataRcvWriterPID) {
g_instance.pid_cxt.DataRcvWriterPID = 0;
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
HandleChildCrash(pid, exitstatus, _("DATA receive writer process"));
continue;
}
/*
* Was it the data catchup? If exit status is zero (normal) or one
* (FATAL exit), we assume everything is all right just like normal
* backends.
*/
if (pid == g_instance.pid_cxt.CatchupPID) {
g_instance.pid_cxt.CatchupPID = 0;
catchup_online = false;
/*
* Catchup likes a normal backend, do standard backend child cleanup by
* 'CleanupBackend', it will handle child crash, at here, we do not need
* to handle it like other threads(for example: g_instance.pid_cxt.DataRcvWriterPID);
*/
}
/*
* Was it the autovacuum launcher? Normal exit can be ignored; we'll
* start a new one at the next iteration of the postmaster's main
* loop, if necessary. Any other exit condition is treated as a
* crash.
*/
if (pid == g_instance.pid_cxt.AutoVacPID) {
g_instance.pid_cxt.AutoVacPID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("autovacuum launcher process"));
continue;
}
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && pid == g_instance.pid_cxt.PgJobSchdPID) {
g_instance.pid_cxt.PgJobSchdPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("job scheduler process"), pid, exitstatus);
continue;
}
/*
* Was it the archiver? If so, just try to start a new one; no need
* to force reset of the rest of the system. (If fail, we'll try
* again in future cycles of the main loop.). Unless we were waiting
* for it to shut down; don't restart it in that case, and
* PostmasterStateMachine() will advance to the next shutdown step.
*/
if (pid == g_instance.pid_cxt.PgArchPID) {
g_instance.pid_cxt.PgArchPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("archiver process"), pid, exitstatus);
if (XLogArchivingActive() && pmState == PM_RUN)
g_instance.pid_cxt.PgArchPID = pgarch_start();
continue;
}
/*
* Was it the statistics collector? If so, just try to start a new
* one; no need to force reset of the rest of the system. (If fail,
* we'll try again in future cycles of the main loop.)
*/
if (pid == g_instance.pid_cxt.PgStatPID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.PgStatPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("statistics collector process"), pid, exitstatus);
if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
g_instance.pid_cxt.PgStatPID = pgstat_start();
continue;
}
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && pid == g_instance.pid_cxt.SnapshotPID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.SnapshotPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("snapshot collector process"), pid, exitstatus);
if (pmState == PM_RUN)
g_instance.pid_cxt.SnapshotPID = snapshot_start();
continue;
}
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && pid == g_instance.pid_cxt.PercentilePID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.PercentilePID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("percentile collector process"), pid, exitstatus);
if (pmState == PM_RUN)
g_instance.pid_cxt.PercentilePID = initialize_util_thread(PERCENTILE_WORKER);
continue;
}
/* Database Security: Support database audit */
/*
* Was it the system auditor? If so, try to start a new one.
*/
if (pid == g_instance.pid_cxt.PgAuditPID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.PgAuditPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("system auditor process"), pid, exitstatus);
if (pmState == PM_RUN)
g_instance.pid_cxt.PgAuditPID = pgaudit_start();
continue;
}
/* Was it the system logger? If so, try to start a new one */
if (pid == g_instance.pid_cxt.SysLoggerPID) {
g_instance.pid_cxt.SysLoggerPID = 0;
/* for safety's sake, launch new logger *first* */
g_instance.pid_cxt.SysLoggerPID = SysLogger_Start();
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("system logger process"), pid, exitstatus);
continue;
}
/* Was it the reaper backend thead ? If so, try to start a new one */
if (pid == g_instance.pid_cxt.ReaperBackendPID) {
g_instance.pid_cxt.ReaperBackendPID = 0;
/* for safety's sake, launch new logger *first* */
g_instance.pid_cxt.ReaperBackendPID = initialize_util_thread(REAPER);
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("reaper backend process"), pid, exitstatus);
continue;
}
/* Was it the wlm collector? If so, try to start a new one */
if (pid == g_instance.pid_cxt.WLMCollectPID) {
g_instance.pid_cxt.WLMCollectPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("wlm collector process"), pid, exitstatus);
continue;
}
if (pid == g_instance.pid_cxt.WLMMonitorPID) {
g_instance.pid_cxt.WLMMonitorPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("wlm monitor process"), pid, exitstatus);
continue;
}
if (pid == g_instance.pid_cxt.WLMArbiterPID) {
g_instance.pid_cxt.WLMArbiterPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("wlm arbiter process"), pid, exitstatus);
continue;
}
if (pid == g_instance.pid_cxt.CPMonitorPID) {
g_instance.pid_cxt.CPMonitorPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("compute pool monitor process"), pid, exitstatus);
continue;
}
/* Was it the twophasecleaner? If so, try to handle */
if (IS_PGXC_COORDINATOR && pid == g_instance.pid_cxt.TwoPhaseCleanerPID) {
g_instance.pid_cxt.TwoPhaseCleanerPID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("twophase cleaner process"));
continue;
}
if (pid == g_instance.pid_cxt.FaultMonitorPID) {
g_instance.pid_cxt.FaultMonitorPID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("fault monitor process"));
continue;
}
if (pid == g_instance.pid_cxt.CBMWriterPID) {
g_instance.pid_cxt.CBMWriterPID = 0;
if (!EXIT_STATUS_0(exitstatus)) {
LogChildExit(LOG, _("CBM writer process"), pid, exitstatus);
}
t_thrd.cbm_cxt.XlogCbmSys->needReset = true;
continue;
}
if (pid == g_instance.pid_cxt.RemoteServicePID) {
Assert(!dummyStandbyMode);
g_instance.pid_cxt.RemoteServicePID = 0;
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus, _("remote service process"));
continue;
}
if (get_real_recovery_parallelism() > 1) {
PageRedoExitStatus pageredoStatus = CheckExitPageWorkers(pid);
if (pageredoStatus == PAGE_REDO_THREAD_EXIT_NORMAL) {
continue;
} else if (pageredoStatus == PAGE_REDO_THREAD_EXIT_ABNORMAL) {
ereport(LOG, (errmsg("aborting due to page redo process failure")));
HandleChildCrash(pid, exitstatus, _("page redo process"));
continue;
}
}
if (pid == g_instance.pid_cxt.HeartbeatPID) {
g_instance.pid_cxt.HeartbeatPID = 0;
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
HandleChildCrash(pid, exitstatus, _("Heartbeat process"));
continue;
}
if (pid == g_instance.pid_cxt.CommPoolerCleanPID) {
g_instance.pid_cxt.CommPoolerCleanPID = 0;
if (!EXIT_STATUS_0(exitstatus))
LogChildExit(LOG, _("pooler cleaner process"), pid, exitstatus);
continue;
}
/* Was it one of our background workers? */
if (CleanupBackgroundWorker(pid, (int)exitstatus))
{
/* have it be restarted */
g_instance.bgworker_cxt.have_crashed_worker = true;
continue;
}
/*
* Else do standard backend child cleanup.
*/
CleanupBackend(pid, exitstatus);
} /* loop over pending child-death reports */
/*
* After cleaning out the SIGCHLD queue, see if we have any state changes
* or actions to make.
*/
PostmasterStateMachine();
/* Done with signal handler */
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
errno = save_errno;
}
/*
* Get proc name due to thread id.
*/
static const char* GetProcName(ThreadId pid)
{
if (pid == 0)
return "invalid process";
else if (pid == g_instance.pid_cxt.StartupPID)
return "startup process";
else if (pid == g_instance.pid_cxt.BgWriterPID)
return "background writer process";
else if (pid == g_instance.pid_cxt.CheckpointerPID)
return "checkpointer process";
else if (pid == g_instance.pid_cxt.WalWriterPID)
return "WAL writer process";
else if (pid == g_instance.pid_cxt.WalReceiverPID)
return "WAL receiver process";
else if (pid == g_instance.pid_cxt.WalRcvWriterPID)
return "WAL receive writer process";
else if (pid == g_instance.pid_cxt.DataReceiverPID)
return "DATA receiver process";
else if (pid == g_instance.pid_cxt.DataRcvWriterPID)
return "DATA receive writer process";
else if (pid == g_instance.pid_cxt.CatchupPID)
return "catchup process";
else if (pid == g_instance.pid_cxt.AutoVacPID)
return "autovacuum launcher process";
else if (pid == g_instance.pid_cxt.PgJobSchdPID)
return "job scheduler process";
else if (pid == g_instance.pid_cxt.PgArchPID)
return "archiver process";
else if (pid == g_instance.pid_cxt.PgStatPID)
return "statistics collector process";
else if (pid == g_instance.pid_cxt.SnapshotPID)
return "snapshot collector process";
else if (pid == g_instance.pid_cxt.PercentilePID)
return "percentile collector process";
else if (pid == g_instance.pid_cxt.PgAuditPID)
return "system auditor process";
else if (pid == g_instance.pid_cxt.SysLoggerPID)
return "system logger process";
else if (pid == g_instance.pid_cxt.WLMCollectPID)
return "wlm collector process";
else if (pid == g_instance.pid_cxt.WLMMonitorPID)
return "wlm monitor process";
else if (pid == g_instance.pid_cxt.WLMArbiterPID)
return "wlm arbiter process";
else if (pid == g_instance.pid_cxt.CPMonitorPID)
return "compute pool monitor process";
else if (pid == g_instance.pid_cxt.TwoPhaseCleanerPID)
return "twophase cleaner process";
else if (pid == g_instance.pid_cxt.FaultMonitorPID)
return "fault monitor process";
else if (g_instance.pid_cxt.AlarmCheckerPID == pid)
return "alarm checker process";
else if (g_instance.pid_cxt.AioCompleterStarted == pid)
return "aio completer process";
else if (pid == g_instance.pid_cxt.CBMWriterPID)
return "CBM writer process";
else if (g_instance.pid_cxt.RemoteServicePID == pid)
return "remote service process";
else if (g_instance.pid_cxt.HeartbeatPID == pid)
return "Heartbeat process";
else if (g_instance.pid_cxt.CommSenderFlowPID == pid)
return "libcomm sender flow process";
else if (g_instance.pid_cxt.CommReceiverFlowPID == pid)
return "libcomm receiver flow process";
else if (g_instance.pid_cxt.CommAuxiliaryPID == pid)
return "libcomm auxiliary process";
else if (g_instance.pid_cxt.CommPoolerCleanPID == pid)
return "pool cleaner process";
else if (g_instance.pid_cxt.CommReceiverPIDS != NULL) {
int recv_loop = 0;
for (recv_loop = 0; recv_loop < g_instance.attr.attr_network.comm_max_receiver; recv_loop++) {
if (g_instance.pid_cxt.CommReceiverPIDS[recv_loop] == pid) {
return "libcomm receiver loop process";
}
}
return "server process";
} else {
return "server process";
}
}
/*
* Scan the bgworkers list and see if the given PID (which has just stopped
* or crashed) is in it. Handle its shutdown if so, and return true. If not a
* bgworker, return false.
*
* This is heavily based on CleanupBackend. One important difference is that
* we don't know yet that the dying process is a bgworker, so we must be silent
* until we're sure it is.
*/
static bool CleanupBackgroundWorker(ThreadId pid,
int exitstatus) /* child's exit status */
{
char namebuf[MAXPGPATH];
slist_mutable_iter iter;
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.background_worker_list) {
RegisteredBgWorker *rw;
rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);
if (rw->rw_pid != pid) {
continue;
}
#ifdef WIN32
/* see CleanupBackend */
if (exitstatus == ERROR_WAIT_NO_CHILDREN) {
exitstatus = 0;
}
#endif
int rc = snprintf_s(namebuf, MAXPGPATH, MAXPGPATH - 1, _("background worker \"%s\""), rw->rw_worker.bgw_type);
securec_check_ss_c(rc, "\0", "\0");
if (!EXIT_STATUS_0(exitstatus)) {
/* Record timestamp, so we know when to restart the worker. */
rw->rw_crashed_at = GetCurrentTimestamp();
} else {
/* Zero exit status means terminate */
rw->rw_crashed_at = 0;
rw->rw_terminate = true;
}
/*
* Additionally, for shared-memory-connected workers, just like a
* backend, any exit status other than 0 or 1 is considered a crash
* and causes a system-wide restart.
*/
if ((rw->rw_worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0) {
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) {
HandleChildCrash(pid, exitstatus, namebuf);
return true;
}
}
/*
* We must release the postmaster child slot whether this worker is
* connected to shared memory or not, but we only treat it as a crash
* if it is in fact connected.
*/
if (!ReleasePostmasterChildSlot(rw->rw_child_slot) &&
(rw->rw_worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0) {
HandleChildCrash(pid, exitstatus, namebuf);
return true;
}
/* Get it out of the BackendList and clear out remaining data */
DLRemove(&rw->rw_backend->elem);
/*
* It's possible that this background worker started some OTHER
* background worker and asked to be notified when that worker started
* or stopped. If so, cancel any notifications destined for the
* now-dead backend.
*/
if (rw->rw_backend->bgworker_notify) {
BackgroundWorkerStopNotifications(rw->rw_pid);
}
BackendArrayRemove(rw->rw_backend);
rw->rw_backend = NULL;
rw->rw_pid = 0;
rw->rw_child_slot = 0;
ReportBackgroundWorkerExit(&iter); /* report child death */
LogChildExit(EXIT_STATUS_0(exitstatus) ? DEBUG1 : LOG,
namebuf, pid, exitstatus);
return true;
}
return false;
}
/*
* CleanupBackend -- cleanup after terminated backend.
*
* Remove all local state associated with backend.
*
* If you change this, see also CleanupBackgroundWorker.
*/
static void CleanupBackend(ThreadId pid, int exitstatus) /* child's exit status. */
{
Dlelem* curr = NULL;
LogChildExit(DEBUG2, _("server process"), pid, exitstatus);
/*
* If a backend dies in an ugly way then we must signal all other backends
* to quickdie. If exit status is zero (normal) or one (FATAL exit), we
* assume everything is all right and proceed to remove the backend from
* the active backend list.
*/
#ifdef WIN32
/*
* On win32, also treat ERROR_WAIT_NO_CHILDREN (128) as nonfatal case,
* since that sometimes happens under load when the process fails to start
* properly (long before it starts using shared memory). Microsoft reports
* it is related to mutex failure:
* http://archives.postgresql.org/pgsql-hackers/2010-09/msg00790.php
*/
if (exitstatus == ERROR_WAIT_NO_CHILDREN) {
LogChildExit(LOG, _("server process"), pid, exitstatus);
exitstatus = 0;
}
#endif
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) {
HandleChildCrash(pid, exitstatus, _("server process"));
return;
}
/*
* We always add the backend_list from head. Think about a scenario, an exit
* thread released its pid to system, which then reused by a new thread.
* After that, PM received signal to clean up the exit thread, we traversed the
* list from head and use pid to match thread Backend, we probably matched
* the new thread rather than the exit thread. To avoid this problem, we can
* traverse the backend_list from tail.
*/
for (curr = DLGetTail(g_instance.backend_list); curr; curr = DLGetPred(curr)) {
Backend* bp = (Backend*)DLE_VAL(curr);
if (bp->pid == pid) {
if (bp->dead_end) {
if (!ReleasePostmasterChildSlot(bp->child_slot)) {
/*
* Uh-oh, the child failed to clean itself up. Treat as a
* crash after all.
*/
HandleChildCrash(pid, exitstatus, _("server process"));
return;
}
BackendArrayRemove(bp);
}
if (bp->bgworker_notify) {
/*
* This backend may have been slated to receive SIGUSR1 when
* some background worker started or stopped. Cancel those
* notifications, as we don't want to signal PIDs that are not
* PostgreSQL backends. This gets skipped in the (probably
* very common) case where the backend has never requested any
* such notifications.
*/
BackgroundWorkerStopNotifications(bp->pid);
}
DLRemove(curr);
break;
}
}
}
/*
* HandleChildCrash -- cleanup after failed backend, bgwriter, checkpointer,
* walwriter or autovacuum.
*
* The objectives here are to clean up our local state about the child
* process, and to signal all other remaining children to quickdie.
*/
void HandleChildCrash(ThreadId pid, int exitstatus, const char* procname)
{
/*
* Make log entry unless there was a previous crash (if so, nonzero exit
* status is to be expected in SIGQUIT response; don't clutter log)
*/
if (!g_instance.fatal_error) {
LogChildExit(LOG, procname, pid, exitstatus);
ereport(LOG, (errmsg("terminating any other active server processes")));
}
ereport(LOG, (errmsg("%s (ThreadId %lu) exited with exit code %d", procname, pid, WEXITSTATUS(exitstatus))));
// Threading: do not handle child crash,
// &g_instance.proc_aux_base or autovacuum elog(FATAL) could reach here,
// if we handle it, we will send SIGQUIT to backend process,
// then backend may handle the signal when doing malloc, cause memory exception.
// So exit directly.
//
ereport(LOG, (errmsg("the server process exits")));
cancelIpcMemoryDetach();
fflush(stdout);
fflush(stderr);
_exit(1);
}
/*
* Log the death of a child process.
*/
static void LogChildExit(int lev, const char* procname, ThreadId pid, int exitstatus)
{
/*
* size of activity_buffer is arbitrary, but set equal to default
* track_activity_query_size
*/
char activity_buffer[1024];
const char* activity = NULL;
if (!EXIT_STATUS_0(exitstatus)) {
activity = pgstat_get_crashed_backend_activity(pid, activity_buffer, sizeof(activity_buffer));
}
if (WIFEXITED(exitstatus)) {
ereport(lev,
/* ------
translator: %s is a noun phrase describing a child process, such as
"server process" */
(errmsg("%s (ThreadId %lu) exited with exit code %d", procname, pid, WEXITSTATUS(exitstatus)),
activity ? errdetail("Failed process was running: %s", activity) : 0));
} else if (WIFSIGNALED(exitstatus)) {
#if defined(WIN32)
ereport(lev,
/* ------
translator: %s is a noun phrase describing a child process, such as
"server process" */
(errmsg("%s (ThreadId %lu) was terminated by exception 0x%X", procname, pid, WTERMSIG(exitstatus)),
errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
activity ? errdetail("Failed process was running: %s", activity) : 0));
#elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
ereport(lev,
/* ------
translator: %s is a noun phrase describing a child process, such as
"server process" */
(errmsg("%s (ThreadId %lu) was terminated by signal %d: %s",
procname,
pid,
WTERMSIG(exitstatus),
WTERMSIG(exitstatus) < NSIG ? sys_siglist[WTERMSIG(exitstatus)] : "(unknown)"),
activity ? errdetail("Failed process was running: %s", activity) : 0));
#else
ereport(lev,
/* ------
translator: %s is a noun phrase describing a child process, such as
"server process" */
(errmsg("%s (ThreadId %lu) was terminated by signal %d", procname, pid, WTERMSIG(exitstatus)),
activity ? errdetail("Failed process was running: %s", activity) : 0));
#endif
} else {
ereport(lev,
/* ------
translator: %s is a noun phrase describing a child process, such as
"server process" */
(errmsg("%s (ThreadId %lu) exited with unrecognized status %d", procname, pid, exitstatus),
activity ? errdetail("Failed process was running: %s", activity) : 0));
}
}
/*
* Advance the postmaster's state machine and take actions as appropriate
*
* This is common code for pmdie(), reaper() and sigusr1_handler(), which
* receive the signals that might mean we need to change state.
*/
static void PostmasterStateMachine(void)
{
if (pmState == PM_WAIT_BACKUP) {
/*
* PM_WAIT_BACKUP state ends when online backup mode is not active.
*/
if (!BackupInProgress())
pmState = PM_WAIT_BACKENDS;
}
if (pmState == PM_WAIT_READONLY) {
/*
* PM_WAIT_READONLY state ends when we have no regular backends that
* have been started during recovery. We kill the startup and
* walreceiver processes and transition to PM_WAIT_BACKENDS. Ideally,
* we might like to kill these processes first and then wait for
* backends to die off, but that doesn't work at present because
* killing the startup process doesn't release its locks.
*/
if (CountChildren(BACKEND_TYPE_NORMAL) == 0) {
if (g_instance.pid_cxt.StartupPID != 0)
signal_child(g_instance.pid_cxt.StartupPID, SIGTERM);
if (g_instance.pid_cxt.WalReceiverPID != 0)
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGTERM);
if (g_instance.pid_cxt.WalRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.WalRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.DataReceiverPID != 0)
signal_child(g_instance.pid_cxt.DataReceiverPID, SIGTERM);
if (g_instance.pid_cxt.DataRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.DataRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.WLMCollectPID != 0) {
WLMProcessThreadShutDown();
signal_child(g_instance.pid_cxt.WLMCollectPID, SIGTERM);
}
if (g_instance.pid_cxt.WLMMonitorPID != 0)
signal_child(g_instance.pid_cxt.WLMMonitorPID, SIGTERM);
if (g_instance.pid_cxt.WLMArbiterPID != 0)
signal_child(g_instance.pid_cxt.WLMArbiterPID, SIGTERM);
if (g_instance.pid_cxt.CPMonitorPID != 0)
signal_child(g_instance.pid_cxt.CPMonitorPID, SIGTERM);
if (g_instance.pid_cxt.RemoteServicePID != 0)
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGTERM);
if (g_instance.pid_cxt.HeartbeatPID != 0)
signal_child(g_instance.pid_cxt.HeartbeatPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
}
/*
* If we are in a state-machine state that implies waiting for backends to
* exit, see if they're all gone, and change state if so.
*/
if (pmState == PM_WAIT_BACKENDS) {
/*
* PM_WAIT_BACKENDS state ends when we have no regular backends
* (including autovac workers), no bgworkers (including
* unconnected ones), and no walwriter, autovac launcher or
* bgwriter. If we are doing crash recovery then we expect the
* checkpointer to exit as well, otherwise not. The archiver, stats,
* and syslogger processes are disregarded since they are not
* connected to shared memory; we also disregard dead_end children
* here. Walsenders are also disregarded, they will be terminated
* later after writing the checkpoint record, like the archiver
* process.
*/
if (CountChildren(BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER) == 0 &&
g_instance.pid_cxt.StartupPID == 0 &&
g_instance.pid_cxt.TwoPhaseCleanerPID == 0 && g_instance.pid_cxt.FaultMonitorPID == 0 &&
g_instance.pid_cxt.WalReceiverPID == 0 && g_instance.pid_cxt.WalRcvWriterPID == 0 &&
g_instance.pid_cxt.DataReceiverPID == 0 && g_instance.pid_cxt.DataRcvWriterPID == 0 &&
g_instance.pid_cxt.BgWriterPID == 0 &&
(g_instance.pid_cxt.CheckpointerPID == 0 || !g_instance.fatal_error) &&
g_instance.pid_cxt.WalWriterPID == 0 && g_instance.pid_cxt.AutoVacPID == 0 &&
g_instance.pid_cxt.WLMCollectPID == 0 && g_instance.pid_cxt.WLMMonitorPID == 0 &&
g_instance.pid_cxt.WLMArbiterPID == 0 && g_instance.pid_cxt.CPMonitorPID == 0 &&
g_instance.pid_cxt.PgJobSchdPID == 0 && g_instance.pid_cxt.CBMWriterPID == 0 &&
g_instance.pid_cxt.SnapshotPID == 0 && g_instance.pid_cxt.PercentilePID == 0 &&
g_instance.pid_cxt.RemoteServicePID == 0 && g_instance.pid_cxt.HeartbeatPID == 0 &&
g_instance.pid_cxt.CommPoolerCleanPID == 0 && IsAllPageWorkerExit()) {
if (g_instance.fatal_error) {
/*
* Start waiting for dead_end children to die. This state
* change causes ServerLoop to stop creating new ones.
*/
pmState = PM_WAIT_DEAD_END;
/*
* We already SIGQUIT'd the archiver and stats processes, if
* any, when we entered g_instance.fatal_error state.
*/
} else {
/*
* If we get here, we are proceeding with normal shutdown. All
* the regular children are gone, and it's time to tell the
* checkpointer to do a shutdown checkpoint.
*/
Assert(g_instance.status > NoShutdown || g_instance.demotion > NoDemote);
/* Start the checkpointer if not running */
if (g_instance.pid_cxt.CheckpointerPID == 0 && !dummyStandbyMode)
g_instance.pid_cxt.CheckpointerPID = initialize_util_thread(CHECKPOINT_THREAD);
if (g_instance.pid_cxt.PageWriterPID != NULL) {
int i;
g_instance.ckpt_cxt_ctl->page_writer_can_exit = false;
for (i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
if (g_instance.pid_cxt.PageWriterPID[0] != 0) {
signal_child(g_instance.pid_cxt.PageWriterPID[i], SIGTERM);
}
}
}
/* And tell it to shut down */
if (g_instance.pid_cxt.CheckpointerPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.CheckpointerPID, SIGUSR2);
pmState = PM_SHUTDOWN;
} else {
/*
* If we failed to fork a checkpointer, just shut down.
* Any required cleanup will happen at next restart. We
* set g_instance.fatal_error so that an "abnormal shutdown" message
* gets logged when we exit.
*/
g_instance.ckpt_cxt_ctl->page_writer_can_exit = true;
g_instance.fatal_error = true;
pmState = PM_WAIT_DEAD_END;
/* Kill the senders, archiver and stats collector too */
SignalChildren(SIGQUIT);
if (g_instance.pid_cxt.PgArchPID != 0)
signal_child(g_instance.pid_cxt.PgArchPID, SIGQUIT);
if (g_instance.pid_cxt.PgStatPID != 0)
signal_child(g_instance.pid_cxt.PgStatPID, SIGQUIT);
/* signal the auditor process */
if (g_instance.pid_cxt.PgAuditPID != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PgAuditPID, SIGQUIT);
}
}
}
}
}
if (pmState == PM_SHUTDOWN_2) {
/*
* PM_SHUTDOWN_2 state ends when there's no other children than
* dead_end children left. There shouldn't be any regular backends
* left by now anyway; what we're really waiting for is walsenders and
* archiver.
*
* Walreceiver should normally be dead by now, but not when a fast
* shutdown is performed during recovery.
*/
if (g_instance.pid_cxt.PgArchPID == 0 && CountChildren(BACKEND_TYPE_ALL) == 0 &&
g_instance.pid_cxt.WalReceiverPID == 0 && g_instance.pid_cxt.WalRcvWriterPID == 0 &&
g_instance.pid_cxt.DataReceiverPID == 0 && g_instance.pid_cxt.DataRcvWriterPID == 0) {
pmState = PM_WAIT_DEAD_END;
}
}
if (pmState == PM_WAIT_DEAD_END) {
/*
* PM_WAIT_DEAD_END state ends when the g_instance.backend_list is entirely empty
* (ie, no dead_end children remain), and the archiver and stats
* collector are gone too.
*
* The reason we wait for those two is to protect them against a new
* postmaster starting conflicting subprocesses; this isn't an
* ironclad protection, but it at least helps in the
* shutdown-and-immediately-restart scenario. Note that they have
* already been sent appropriate shutdown signals, either during a
* normal state transition leading up to PM_WAIT_DEAD_END, or during
* g_instance.fatal_error processing.
*/
if (DLGetHead(g_instance.backend_list) == NULL && g_instance.pid_cxt.PgArchPID == 0 &&
g_instance.pid_cxt.PgStatPID == 0 && g_instance.pid_cxt.PgAuditPID == 0 &&
g_instance.pid_cxt.PageWriterPID[0] == 0) {
/* These other guys should be dead already */
Assert(g_instance.pid_cxt.TwoPhaseCleanerPID == 0);
Assert(g_instance.pid_cxt.FaultMonitorPID == 0);
Assert(g_instance.pid_cxt.StartupPID == 0);
Assert(g_instance.pid_cxt.WalReceiverPID == 0);
Assert(g_instance.pid_cxt.WalRcvWriterPID == 0);
Assert(g_instance.pid_cxt.DataReceiverPID == 0);
Assert(g_instance.pid_cxt.DataRcvWriterPID == 0);
Assert(g_instance.pid_cxt.BgWriterPID == 0);
Assert(g_instance.pid_cxt.CheckpointerPID == 0);
Assert(g_instance.pid_cxt.WalWriterPID == 0);
Assert(g_instance.pid_cxt.AutoVacPID == 0);
Assert(g_instance.pid_cxt.PgJobSchdPID == 0);
Assert(g_instance.pid_cxt.CBMWriterPID == 0);
Assert(g_instance.pid_cxt.RemoteServicePID == 0);
Assert(g_instance.pid_cxt.SnapshotPID == 0);
Assert(g_instance.pid_cxt.PercentilePID == 0);
Assert(g_instance.pid_cxt.HeartbeatPID == 0);
Assert(g_instance.pid_cxt.CommPoolerCleanPID == 0);
Assert(IsAllPageWorkerExit() == true);
/* syslogger is not considered here */
pmState = PM_NO_CHILDREN;
}
}
/*
* If we've been told to shut down, we exit as soon as there are no
* remaining children. If there was a crash, cleanup will occur at the
* next startup. (Before PostgreSQL 8.3, we tried to recover from the
* crash before exiting, but that seems unwise if we are quitting because
* we got SIGTERM from init --- there may well not be time for recovery
* before init decides to SIGKILL us.)
*
* Note that the syslogger continues to run. It will exit when it sees
* EOF on its input pipe, which happens when there are no more upstream
* processes.
*/
if (g_instance.status > NoShutdown && pmState == PM_NO_CHILDREN) {
if (g_instance.fatal_error) {
ereport(LOG, (errmsg("abnormal database system shutdown")));
ExitPostmaster(1);
} else {
/*
* Terminate exclusive backup mode to avoid recovery after a clean
* fast shutdown. Since an exclusive backup can only be taken
* during normal running (and not, for example, while running
* under Hot Standby) it only makes sense to do this if we reached
* normal running. If we're still in recovery, the backup file is
* one we're recovering *from*, and we must keep it around so that
* recovery restarts from the right place.
*/
if (t_thrd.postmaster_cxt.ReachedNormalRunning)
CancelBackup();
// close the sctp communictaion layer here
//
if (IS_PGXC_DATANODE && (g_instance.attr.attr_network.comm_tcp_mode == false) &&
(gs_check_SLESSP2_version() == 1) && (gs_check_sctp_support() == 1))
gs_shutdown_comm();
/* Normal exit from the postmaster is here */
ExitPostmaster(0);
}
}
/*
* If recovery failed, or the user does not want an automatic restart
* after backend crashes, wait for all non-syslogger children to exit, and
* then exit postmaster. We don't try to reinitialize when recovery fails,
* because more than likely it will just fail again and we will keep
* trying forever.
*/
if (pmState == PM_NO_CHILDREN && (g_instance.recover_error || !u_sess->attr.attr_sql.restart_after_crash))
ExitPostmaster(1);
/*
* If we need to recover from a crash, wait for all non-syslogger children
* to exit, then reset shmem and StartupDataBase.
*/
if (g_instance.fatal_error && pmState == PM_NO_CHILDREN) {
volatile HaShmemData* hashmdata = NULL;
ServerMode cur_mode = NORMAL_MODE;
ereport(LOG, (errmsg("all server processes terminated; reinitializing")));
hashmdata = t_thrd.postmaster_cxt.HaShmData;
cur_mode = hashmdata->current_mode;
shmem_exit(1);
reset_shared(g_instance.attr.attr_network.PostPortNumber);
/* after reseting shared memory, we shall reset col-space cache.
* all the data of this cache will be out of date after switchover.
* clean up in order to corrupted data writing.
*/
CStoreAllocator::ResetColSpaceCache();
DfsInsert::ResetDfsSpaceCache();
hashmdata = t_thrd.postmaster_cxt.HaShmData;
hashmdata->current_mode = cur_mode;
g_instance.pid_cxt.StartupPID = initialize_util_thread(STARTUP);
Assert(g_instance.pid_cxt.StartupPID != 0);
pmState = PM_STARTUP;
}
/*
* If we need to recover from primary demote, wait for all non-syslogger children
* to exit, then reset shmem and StartupDataBase.
*/
if (g_instance.demotion > NoDemote && pmState == PM_NO_CHILDREN) {
ereport(LOG, (errmsg("all server processes terminated; reinitializing")));
/* allow background workers to immediately restart */
ResetBackgroundWorkerCrashTimes();
shmem_exit(1);
reset_shared(g_instance.attr.attr_network.PostPortNumber);
/* after reseting shared memory, we shall reset col-space cache.
* all the data of this cache will be out of date after switchover.
* clean up in order to corrupted data writing.
*/
CStoreAllocator::ResetColSpaceCache();
DfsInsert::ResetDfsSpaceCache();
/* if failed to enter archive-recovery state, then reboot as primary. */
{
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
hashmdata->current_mode = STANDBY_MODE;
UpdateOptsFile();
ereport(LOG, (errmsg("archive recovery started")));
}
g_instance.pid_cxt.StartupPID = initialize_util_thread(STARTUP);
Assert(g_instance.pid_cxt.StartupPID != 0);
pmState = PM_STARTUP;
}
}
void signalBackend(Backend* bn, int signal, int be_mode)
{
if ((uint32)bn->flag & THRD_EXIT)
ereport(LOG, (errmsg("Thread pid(%lu), flag(%d) may be exited repeatedly", bn->pid, bn->flag)));
int nowait = 0;
if (signal == SIGTERM) {
nowait = 1;
bn->flag = ((uint32)(bn->flag)) | THRD_SIGTERM;
}
if (gs_signal_send(bn->pid, signal, nowait) != 0) {
ereport(WARNING,
(errmsg("kill(pid %ld, signal %d) failed,"
" thread name \"%s\", pmState %d, Demotion %d, Shutdown %d, backend type %d",
(long)bn->pid,
signal,
GetProcName(bn->pid),
pmState,
g_instance.demotion,
Shutdown,
be_mode)));
}
}
/*
* Send a signal to a postmaster child process
*
* On systems that have setsid(), each child process sets itself up as a
* process group leader. For signals that are generally interpreted in the
* appropriate fashion, we signal the entire process group not just the
* direct child process. This allows us to, for example, SIGQUIT a blocked
* archive_recovery script, or SIGINT a script being run by a backend via
* system().
*
* There is a race condition for recently-forked children: they might not
* have executed setsid() yet. So we signal the child directly as well as
* the group. We assume such a child will handle the signal before trying
* to spawn any grandchild processes. We also assume that signaling the
* child twice will not cause any problems.
*
* *be_mode* is meaning fully when this is a backend pid.
*/
void signal_child(ThreadId pid, int signal, int be_mode)
{
int err = 0;
if (0 != (err = gs_signal_send(pid, signal))) {
ereport(WARNING,
(errmsg("kill(pid %ld, signal %d) failed: \"%s\","
" thread name \"%s\", pmState %d, Demotion %d, Shutdown %d, backend type %d",
(long)pid,
signal,
gs_strerror(err),
GetProcName(pid),
pmState,
g_instance.demotion,
Shutdown,
be_mode)));
}
}
/*
* Send a signal to the targeted children (but NOT special children;
* dead_end children are never signaled, either).
*/
static bool SignalSomeChildren(int signal, int target)
{
Dlelem* curr = NULL;
bool signaled = false;
for (curr = DLGetHead(g_instance.backend_list); curr; curr = DLGetSucc(curr)) {
Backend* bp = (Backend*)DLE_VAL(curr);
int child = BACKEND_TYPE_ALL;
/*
* we want to know the type of this backend thread, so
* in debug mode IF judgement is skipped. but in release
* mode, turn it on.
*/
#ifndef USE_ASSERT_CHECKING
/*
* Since target == BACKEND_TYPE_ALL is the most common case, we test
* it first and avoid touching shared memory for every child.
*/
if (target != BACKEND_TYPE_ALL)
#endif /* USE_ASSERT_CHECKING */
{
if (bp->is_autovacuum)
child = BACKEND_TYPE_AUTOVAC;
else if (IsPostmasterChildWalSender(bp->child_slot))
child = BACKEND_TYPE_WALSND;
else if (IsPostmasterChildDataSender(bp->child_slot))
child = BACKEND_TYPE_DATASND;
else if (IsPostmasterChildTempBackend(bp->child_slot))
child = BACKEND_TYPE_TEMPBACKEND;
else
child = BACKEND_TYPE_NORMAL;
if (!(target & child))
continue;
}
ereport(DEBUG4, (errmsg_internal("sending signal %d to process %lu", signal, bp->pid)));
signalBackend(bp, signal, child);
signaled = true;
}
return signaled;
}
bool SignalCancelAllBackEnd()
{
return SignalSomeChildren(SIGINT, BACKEND_TYPE_NORMAL);
}
/*
* BackendStartup -- start backend process
*
* returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
*
* Note: if you change this code, also consider StartAutovacuumWorker.
*/
static int BackendStartup(Port* port)
{
Backend* bn = NULL; /* for backend cleanup */
ThreadId pid;
int childSlot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(childSlot);
GenerateCancelKey(false);
/* Pass down canAcceptConnections state */
port->canAcceptConnections = canAcceptConnections(false);
if (port->canAcceptConnections != CAC_OK && port->canAcceptConnections != CAC_WAITBACKUP) {
(void)ReleasePostmasterChildSlot(childSlot);
if (port->canAcceptConnections == CAC_TOOMANY)
ereport(WARNING, (errmsg("could not fork new process for connection due to too many connections")));
else
ereport(
WARNING, (errmsg("could not fork new process for connection due to PMstate %s", GetPMState(pmState))));
return STATUS_ERROR;
}
// We can't fork thread when PM is shutting down to avoid PM deal with SIGCHLD in busy
// One scenario is switchover DN and many client connect to server
//
if (CAC_SHUTDOWN == port->canAcceptConnections) {
(void)ReleasePostmasterChildSlot(childSlot);
ereport(WARNING, (errmsg("could not fork new process for connection:postmaster is shutting down")));
report_fork_failure_to_client(port, 0, "could not fork new process for connection:postmaster is shutting down");
return STATUS_ERROR;
}
/*
* Unless it's a dead_end child, assign it a child slot number
*/
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = childSlot;
/* Hasn't asked to be notified about any bgworkers yet */
bn->bgworker_notify = false;
pid = initialize_worker_thread(WORKER, port);
t_thrd.proc_cxt.MyPMChildSlot = 0;
if (pid == (ThreadId)-1) {
/* in parent, fork failed */
int save_errno = errno;
(void)ReleasePostmasterChildSlot(childSlot);
errno = save_errno;
ereport(WARNING, (errmsg("could not fork new process for connection socket %d : %m", (int)port->sock)));
report_fork_failure_to_client(port, save_errno);
return STATUS_ERROR;
}
/* in parent, successful fork */
ereport(DEBUG2, (errmsg_internal("forked new backend, pid=%lu socket=%d", pid, (int)port->sock)));
/*
* Everything's been successful, it's safe to add this backend to our list
* of backends.
*/
bn->pid = pid;
bn->is_autovacuum = false;
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
return STATUS_OK;
}
/*
* Try to report backend fork() failure to client before we close the
* connection. Since we do not care to risk blocking the postmaster on
* this connection, we set the connection to non-blocking and try only once.
*
* This is grungy special-purpose code; we cannot use backend libpq since
* it's not up and running.
*/
static void report_fork_failure_to_client(Port* port, int errnum, const char* specialErrorInfo)
{
char buffer[1000] = {0};
int rc;
errno_t ret;
if (specialErrorInfo == NULL) {
/* Format the error message packet (always V2 protocol) */
ret = sprintf_s(
buffer, sizeof(buffer), "E%s%s\n", _("could not fork new process for connection: "), gs_strerror(errnum));
securec_check_ss(ret, "\0", "\0");
} else {
size_t len = strlen(specialErrorInfo);
if (len >= sizeof(buffer)) {
rc = snprintf_truncated_s(buffer, sizeof(buffer), "%s", specialErrorInfo);
securec_check_ss(rc, "\0", "\0");
} else {
rc = snprintf_truncated_s(buffer, sizeof(buffer), "%s", specialErrorInfo);
securec_check_ss(rc, "\0", "\0");
buffer[len] = 0;
}
}
if (port->is_logic_conn) {
rc = gs_send(&port->gs_sock, buffer, strlen(buffer) + 1, -1, TRUE);
} else {
/* Set port to non-blocking. Don't do send() if this fails */
if (!pg_set_noblock(port->sock))
return;
/* We'll retry after EINTR, but ignore all other failures */
do {
rc = send(port->sock, buffer, strlen(buffer) + 1, 0);
} while (rc < 0 && errno == EINTR);
}
}
/*
* BackendInitialize -- initialize an interactive (postmaster-child)
* backend process, and collect the client's startup packet.
*
* returns: nothing. Will not return at all if there's any failure.
*
* Note: this code does not depend on having any access to shared memory.
* In the EXEC_BACKEND case, we are physically attached to shared memory
* but have not yet set up most of our local pointers to shmem structures.
*/
static void BackendInitialize(Port* port)
{
PreClientAuthorize();
/* save thread start time */
t_thrd.proc_cxt.MyStartTime = timestamptz_to_time_t(GetCurrentTimestamp());
/*
* Initialize libpq to talk to client and enable reporting of ereport errors
* to the client. Must do this now because authentication uses libpq to
* send messages.
*/
pq_init();
/* now safe to ereport to client */
t_thrd.postgres_cxt.whereToSendOutput = DestRemote;
/*
* We arrange for a simple exit(1) if we receive SIGTERM or SIGQUIT or
* timeout while trying to collect the startup packet. Otherwise the
* postmaster cannot shutdown the database FAST or IMMED cleanly if a
* buggy client fails to send the packet promptly.
*/
(void)gspqsignal(SIGTERM, startup_die);
(void)gspqsignal(SIGQUIT, startup_die);
(void)gspqsignal(SIGALRM, startup_alarm);
/* Do the next initialization when we get a real connetion. */
if (IS_THREAD_POOL_WORKER)
return;
int status = ClientConnInitilize(port);
if (status == STATUS_EOF)
return;
else if (status == STATUS_ERROR)
proc_exit(0);
}
void PreClientAuthorize()
{
/*
* PreAuthDelay is a debugging aid for investigating problems in the
* authentication cycle: it can be set in postgresql.conf to allow time to
* attach to the newly-forked backend with a debugger. (See also
* PostAuthDelay, which we allow clients to pass through PGOPTIONS, but it
* is not honored until after authentication.)
*/
if (u_sess->attr.attr_security.PreAuthDelay > 0)
pg_usleep(u_sess->attr.attr_security.PreAuthDelay * 1000000L);
/* This flag will remain set until InitPostgres finishes authentication */
u_sess->ClientAuthInProgress = true; /* limit visibility of log messages */
}
int ClientConnInitilize(Port* port)
{
gs_signal_setmask(&t_thrd.libpq_cxt.StartupBlockSig, NULL);
/* Save session start time. */
port->SessionStartTime = GetCurrentTimestamp();
RemoteHostInitilize(port);
/*
* Ready to begin client interaction. We will give up and exit(1) after a
* time delay, so that a broken client can't hog a connection
* indefinitely. PreAuthDelay and any DNS interactions above don't count
* against the time limit.
* Disable pooler timeout for single-node version, cause it will interupt
* recv, which will re-calculate the TCP recv timeout.
*/
#ifdef ENABLE_MULTIPLE_NODES
if (!enable_sig_alarm((u_sess->attr.attr_network.PoolerConnectTimeout - 1) * 1000, false)) {
ereport(FATAL, (errmsg("could not set timer for startup packet timeout")));
}
#endif
int status = StartupPacketInitialize(port);
/*
* Stop here if it was bad or a cancel packet. ProcessStartupPacket
* already did any appropriate error reporting.
*/
if (u_sess->stream_cxt.stop_mythread) {
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
return STATUS_EOF;
}
if (status != STATUS_OK)
return status;
PsDisplayInitialize(port);
/*
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/
if (!disable_sig_alarm(false))
ereport(FATAL, (errmsg("could not disable timer for startup packet timeout")));
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
return STATUS_OK;
}
static void RemoteHostInitilize(Port* port)
{
char remote_host[NI_MAXHOST];
char remote_port[NI_MAXSERV];
/* set these to empty in case they are needed before we set them up */
port->remote_host = "";
port->remote_port = "";
/*
* Get the remote host name and port for logging and status display.
*/
remote_host[0] = '\0';
remote_port[0] = '\0';
if (pg_getnameinfo_all(&port->raddr.addr,
port->raddr.salen,
remote_host,
sizeof(remote_host),
remote_port,
sizeof(remote_port),
(u_sess->attr.attr_common.log_hostname ? 0 : NI_NUMERICHOST) | NI_NUMERICSERV) != 0) {
int ret = pg_getnameinfo_all(&port->raddr.addr,
port->raddr.salen,
remote_host,
sizeof(remote_host),
remote_port,
sizeof(remote_port),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret != 0)
ereport(WARNING, (errmsg_internal("pg_getnameinfo_all() failed: %s", gai_strerror(ret))));
}
if (u_sess->attr.attr_storage.Log_connections) {
if (remote_port[0])
ereport(LOG, (errmsg("connection received: host=%s port=%s", remote_host, remote_port)));
else
ereport(LOG, (errmsg("connection received: host=%s", remote_host)));
}
/*
* save remote_host and remote_port in port structure
*/
port->remote_host = MemoryContextStrdup(u_sess->top_mem_cxt, remote_host);
port->remote_port = MemoryContextStrdup(u_sess->top_mem_cxt, remote_port);
if (u_sess->attr.attr_common.log_hostname)
port->remote_hostname = port->remote_host;
}
static int StartupPacketInitialize(Port* port)
{
int status;
sigset_t old_sigset;
/*
* Unblock SIGUSR2 so that SIGALRM can be triggered if ProcessStartupPacket encounter timeout.
*/
old_sigset = gs_signal_unblock_sigusr2();
/*
* Receive the startup packet (which might turn out to be a cancel request
* packet). for logic connection interruption is not allowed.
*/
if (IS_PGXC_DATANODE && g_instance.attr.attr_storage.comm_cn_dn_logic_conn)
t_thrd.postmaster_cxt.ProcessStartupPacketForLogicConn = true;
status = ProcessStartupPacket(port, false);
if (IS_PGXC_DATANODE && g_instance.attr.attr_storage.comm_cn_dn_logic_conn)
t_thrd.postmaster_cxt.ProcessStartupPacketForLogicConn = false;
CHECK_FOR_PROCDIEPENDING();
/* recover the signal mask */
gs_signal_recover_mask(old_sigset);
return status;
}
static void PsDisplayInitialize(Port* port)
{
char remote_ps_data[NI_MAXHOST + NI_MAXSERV + 2];
errno_t rc;
if (port->remote_port[0] == '\0')
rc = snprintf_s(remote_ps_data, sizeof(remote_ps_data), NI_MAXHOST, "%s", port->remote_host);
else
rc = snprintf_s(remote_ps_data,
sizeof(remote_ps_data),
sizeof(remote_ps_data) - 1,
"%s(%s)",
port->remote_host,
port->remote_port);
securec_check_ss_c(rc, "\0", "\0");
/*
* Now that we have the user and database name, we can set the process
* title for ps. It's good to do this as early as possible in startup.
*
* For a walsender, the ps display is set in the following form:
*
* postgres: wal sender process <user> <host> <activity>
*
* To achieve that, we pass "wal sender process" as username and username
* as dbname to init_ps_display(). XXX: should add a new variant of
* init_ps_display() to avoid abusing the parameters like this.
*/
if (AM_WAL_SENDER)
init_ps_display("wal sender process",
port->user_name,
remote_ps_data,
u_sess->attr.attr_common.update_process_title ? "authentication" : "");
else
init_ps_display(port->user_name,
port->database_name,
remote_ps_data,
u_sess->attr.attr_common.update_process_title ? "authentication" : "");
}
void PortInitialize(Port* port, knl_thread_arg* arg)
{
/* When we do port init at GaussDBThreadMain, use knl_thread_arg to init */
if (arg != NULL) {
/* Read in the variables file */
int ss_rc = memset_s(port, sizeof(Port), 0, sizeof(Port));
securec_check(ss_rc, "\0", "\0");
/*
* Socket 0 may be closed if we do not use it, so we
* must set socket to invalid socket instead of 0.
*/
port->sock = PGINVALID_SOCKET;
port->gs_sock = GS_INVALID_GSOCK;
/* Save port etc. for ps status */
u_sess->proc_cxt.MyProcPort = port;
/* read variables from arg */
if (arg->role == RPC_WORKER)
read_backend_variables((char*)&backend_save_para, port);
else
read_backend_variables(arg->save_para, port);
/* fix thread pool workers and some background threads creation_time
* in pg_os_threads view not correct issue.
*/
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
}
/*
* Set up memory area for GSS information. Mirrors the code in ConnCreate
* for the non-exec case.
*/
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
port->gss = (pg_gssinfo*)MemoryContextAllocZero(u_sess->top_mem_cxt, 1 * sizeof(pg_gssinfo));
if (!port->gss)
ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
#endif
}
void CheckClientIp(Port* port)
{
/* Check whether the client ip is configured in pg_hba.conf */
char ip[IP_LEN] = {'\0'};
if (!check_ip_whitelist(port, ip, IP_LEN)) {
pq_init(); /* initialize libpq to talk to client */
t_thrd.postgres_cxt.whereToSendOutput = DestRemote; /* now safe to ereport to client */
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("no pg_hba.conf entry for host \"%s\".", ip)));
}
}
void initRandomState(TimestampTz start_time, TimestampTz stop_time)
{
long secs;
int usecs;
/*
* Don't want backend to be able to see the postmaster random number
* generator state. We have to clobber the static random_seed *and* start
* a new random sequence in the random() library function.
*/
t_thrd.postmaster_cxt.random_seed = 0;
t_thrd.postmaster_cxt.random_start_time.tv_usec = 0;
/* slightly hacky way to get integer microseconds part of timestamptz */
TimestampDifference(start_time, stop_time, &secs, &usecs);
gs_srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ usecs));
}
/*
* BackendRun -- set up the backend's argument list and invoke PostgresMain()
*
* returns:
* Shouldn't return at all.
* If PostgresMain() fails, return status.
*/
static int BackendRun(Port* port)
{
char** av;
int maxac;
int ac;
long secs;
int usecs;
int i;
/* add process definer mode */
Reset_Pseudo_CurrentUserId();
/*
* Don't want backend to be able to see the postmaster random number
* generator state. We have to clobber the static random_seed *and* start
* a new random sequence in the random() library function.
*/
t_thrd.postmaster_cxt.random_seed = 0;
t_thrd.postmaster_cxt.random_start_time.tv_usec = 0;
/* slightly hacky way to get integer microseconds part of timestamptz */
TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
gs_srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ usecs));
/*
* Now, build the argv vector that will be given to PostgresMain.
*
* The maximum possible number of commandline arguments that could come
* from ExtraOptions is (strlen(ExtraOptions) + 1) / 2; see
* pg_split_opts().
*/
maxac = 2; /* for fixed args supplied below */
maxac += (strlen(g_instance.ExtraOptions) + 1) / 2;
av = (char**)MemoryContextAlloc(t_thrd.top_mem_cxt, maxac * sizeof(char*));
ac = 0;
av[ac++] = "gaussdb";
/*
* Pass any backend switches specified with -o on the postmaster's own
* command line. We assume these are secure. (It's OK to mangle
* ExtraOptions now, since we're safely inside a subprocess.)
*/
pg_split_opts(av, &ac, g_instance.ExtraOptions);
av[ac] = NULL;
Assert(ac < maxac);
/*
* Debug: print arguments being passed to backend
*/
ereport(DEBUG3, (errmsg_internal("%s child[%d]: starting with (", progname, (int)gs_thread_self())));
for (i = 0; i < ac; ++i)
ereport(DEBUG3, (errmsg_internal("\t%s", av[i])));
ereport(DEBUG3, (errmsg_internal(")")));
/*
* Make sure we aren't in t_thrd.mem_cxt.postmaster_mem_cxt anymore. (We can't delete it
* just yet, though, because InitPostgres will need the HBA data.)
*/
MemoryContextSwitchTo(t_thrd.top_mem_cxt);
return PostgresMain(ac, av, port->database_name, port->user_name);
}
#ifdef ENABLE_LLT
extern "C" {
extern void HLLT_Coverage_SaveCoverageData();
}
#endif
/*
* ExitPostmaster -- cleanup
*
* Do NOT call exit() directly --- always go through here!
*/
static void ExitPostmaster(int status)
{
/* should cleanup shared memory and kill all backends */
/*
* Not sure of the semantics here. When the Postmaster dies, should the
* backends all be killed? probably not.
*
* MUST -- vadim 05-10-1999
*/
CloseGaussPidDir();
obs_deinitialize();
/* when exiting the postmaster process, destroy the hash table */
if (g_instance.comm_cxt.usedDnSpace != NULL) {
hash_destroy(g_instance.comm_cxt.usedDnSpace);
}
if (g_instance.comm_cxt.account_table != NULL) {
hash_destroy(g_instance.comm_cxt.account_table);
}
TermMOT(); /* shutdown memory engine before codegen is destroyed */
CodeGenProcessTearDown();
/* Save llt data to disk before postmaster exit */
#ifdef ENABLE_LLT
HLLT_Coverage_SaveCoverageData();
#endif
// flush stdout buffer before _exit
//
fflush(stdout);
LogCtlLastFlushBeforePMExit();
proc_exit(status);
}
static void handle_recovery_started()
{
if (pmState == PM_STARTUP && g_instance.status == NoShutdown && !dummyStandbyMode) {
/* WAL redo has started. We're out of reinitialization. */
g_instance.fatal_error = false;
g_instance.demotion = NoDemote;
/*
* Crank up the background tasks. It doesn't matter if this fails,
* we'll just try again later.
*/
Assert(g_instance.pid_cxt.CheckpointerPID == 0);
g_instance.pid_cxt.CheckpointerPID = initialize_util_thread(CHECKPOINT_THREAD);
Assert(g_instance.pid_cxt.BgWriterPID == 0);
if (!g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
g_instance.pid_cxt.BgWriterPID = initialize_util_thread(BGWRITER);
}
if (g_instance.attr.attr_storage.enableIncrementalCheckpoint) {
for (int i = 0; i < g_instance.attr.attr_storage.pagewriter_thread_num; i++) {
Assert(g_instance.pid_cxt.PageWriterPID[i] == 0);
g_instance.pid_cxt.PageWriterPID[i] = initialize_util_thread(PAGEWRITER_THREAD);
}
}
Assert(g_instance.pid_cxt.CBMWriterPID == 0);
if (u_sess->attr.attr_storage.enable_cbm_tracking) {
g_instance.pid_cxt.CBMWriterPID = initialize_util_thread(CBMWRITER);
}
Assert(g_instance.pid_cxt.RemoteServicePID == 0);
if (IS_PGXC_DATANODE && t_thrd.postmaster_cxt.HaShmData->current_mode != NORMAL_MODE &&
!IS_DN_WITHOUT_STANDBYS_MODE() && IsRemoteReadModeOn())
g_instance.pid_cxt.RemoteServicePID = initialize_util_thread(RPC_SERVICE);
PMUpdateDBState(NORMAL_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(NORMAL_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
pmState = PM_RECOVERY;
}
}
static void handle_begin_hot_standby()
{
if ((dummyStandbyMode || pmState == PM_RECOVERY) && g_instance.status == NoShutdown) {
/*
* Likewise, start other special children as needed.
*/
Assert(g_instance.pid_cxt.PgStatPID == 0);
if (!dummyStandbyMode)
g_instance.pid_cxt.PgStatPID = pgstat_start();
ereport(LOG, (errmsg("database system is ready to accept read only connections")));
pmState = PM_HOT_STANDBY;
}
}
static void handle_promote_signal()
{
if (g_instance.pid_cxt.StartupPID != 0 &&
(pmState == PM_STARTUP ||
pmState == PM_RECOVERY ||
pmState == PM_HOT_STANDBY ||
pmState == PM_WAIT_READONLY)) {
gs_lock_test_and_set_64(&g_instance.stat_cxt.NodeStatResetTime, GetCurrentTimestamp());
if (GetHaShmemMode() != STANDBY_MODE) {
ereport(LOG, (errmsg("Instance can't be promoted in none standby mode.")));
} else {
/* Database Security: Support database audit */
if (t_thrd.walreceiverfuncs_cxt.WalRcv &&
NODESTATE_STANDBY_PROMOTING == t_thrd.walreceiverfuncs_cxt.WalRcv->node_state) {
t_thrd.postmaster_cxt.audit_standby_switchover = true;
/* Tell startup process to finish recovery */
SendNotifySignal(NOTIFY_SWITCHOVER, g_instance.pid_cxt.StartupPID);
} else {
if (t_thrd.walreceiverfuncs_cxt.WalRcv) {
t_thrd.walreceiverfuncs_cxt.WalRcv->node_state = NODESTATE_STANDBY_FAILOVER_PROMOTING;
}
t_thrd.postmaster_cxt.audit_primary_failover = true;
/* Tell startup process to finish recovery */
ereport(LOG, (errmsg("Instance to do failover.")));
SendNotifySignal(NOTIFY_FAILOVER, g_instance.pid_cxt.StartupPID);
}
}
}
}
static void handle_primary_signal(volatile HaShmemData* hashmdata)
{
if (g_instance.pid_cxt.StartupPID != 0 &&
hashmdata->current_mode == PENDING_MODE &&
(pmState == PM_STARTUP ||
pmState == PM_RECOVERY ||
pmState == PM_HOT_STANDBY ||
pmState == PM_WAIT_READONLY)) {
/* just notify the startup process, does not set HAshmemory here. */
SendNotifySignal(NOTIFY_PRIMARY, g_instance.pid_cxt.StartupPID);
}
if (hashmdata->current_mode == NORMAL_MODE) {
if (g_instance.attr.attr_storage.max_wal_senders < 2 ||
g_instance.attr.attr_storage.wal_level != WAL_LEVEL_HOT_STANDBY ||
g_instance.attr.attr_storage.EnableHotStandby == false)
ereport(WARNING, (errmsg("when notifying normal mode to primary mode, \
wal_level requires \"hot_standby\", and hot_standby requires \"on\", \
and max_wal_senders requires at least 2.")));
else {
hashmdata->current_mode = PRIMARY_MODE;
UpdateOptsFile();
}
}
PMUpdateDBState(NORMAL_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(NORMAL_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
}
static void handle_standby_signal(volatile HaShmemData* hashmdata)
{
if (g_instance.pid_cxt.StartupPID != 0 &&
(pmState == PM_STARTUP ||
pmState == PM_RECOVERY ||
pmState == PM_HOT_STANDBY ||
pmState == PM_WAIT_READONLY)) {
hashmdata->current_mode = STANDBY_MODE;
PMUpdateDBState(NEEDREPAIR_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(NEEDREPAIR_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
/*
* wakeup startup process from sleep by signal, cause we are
* in standby mode, the signal has no specific affect.
*/
SendNotifySignal(NOTIFY_STANDBY, g_instance.pid_cxt.StartupPID);
UpdateOptsFile();
}
}
/*
* sigusr1_handler - handle signal conditions from child processes
*/
static void sigusr1_handler(SIGNAL_ARGS)
{
int mode = 0;
int save_errno = errno;
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
/* Process background worker state change. */
if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
{
BackgroundWorkerStateChange();
g_instance.bgworker_cxt.start_worker_needed = true;
}
if (g_instance.bgworker_cxt.start_worker_needed || g_instance.bgworker_cxt.have_crashed_worker) {
maybe_start_bgworkers();
}
/*
* RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
* unexpected states. If the startup process quickly starts up, completes
* recovery, exits, we might process the death of the startup process
* first. We don't want to go back to recovery in that case.
*/
if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED)) {
handle_recovery_started();
}
if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY)) {
handle_begin_hot_standby();
}
if (CheckPostmasterSignal(PMSIGNAL_LOCAL_RECOVERY_DONE) &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
g_instance.status == NoShutdown) {
PMUpdateDBStateLSN();
ereport(LOG, (errmsg("set lsn after recovery done in gaussdb state file")));
if (g_instance.pid_cxt.WalWriterPID == 0 && t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE &&
(pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY)) {
g_instance.pid_cxt.WalWriterPID = initialize_util_thread(WALWRITER);
ereport(LOG,
(errmsg("sigusr1_handler create WalWriter(%lu) after local recovery is done. pmState:%u, ServerMode:%u",
g_instance.pid_cxt.WalWriterPID, pmState, t_thrd.postmaster_cxt.HaShmData->current_mode)));
}
}
if (CheckPostmasterSignal(PMSIGNAL_UPDATE_WAITING)) {
PMUpdateDBState(WAITING_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("set gaussdb state file: db state(WAITING_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
}
if (CheckPostmasterSignal(PMSIGNAL_UPDATE_PROMOTING)) {
PMUpdateDBState(PROMOTING_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("set gaussdb state file: db state(PROMOTING_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
}
if (CheckPostmasterSignal(PMSIGNAL_UPDATE_HAREBUILD_REASON) &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
g_instance.status == NoShutdown) {
PMUpdateDBStateHaRebuildReason();
}
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) && g_instance.pid_cxt.PgArchPID != 0) {
/*
* Send SIGUSR1 to archiver process, to wake it up and begin archiving
* next transaction log file.
*/
signal_child(g_instance.pid_cxt.PgArchPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && g_instance.pid_cxt.SysLoggerPID != 0) {
/* Tell syslogger to rotate logfile */
signal_child(g_instance.pid_cxt.SysLoggerPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) && g_instance.status == NoShutdown) {
/*
* Start one iteration of the autovacuum daemon, even if autovacuuming
* is nominally not enabled. This is so we can have an active defense
* excessive clog. We set a flag for the main loop to do it rather than
* trying to do it here --- this is because the autovac process itself
* may send the signal, and we want to handle that by launching
* another iteration as soon as the current one completes.
*/
t_thrd.postmaster_cxt.start_autovac_launcher = true;
}
/* should not start a worker in shutdown or demotion procedure */
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) && g_instance.status == NoShutdown &&
g_instance.demotion == NoDemote) {
/* The autovacuum launcher wants us to start a worker process. */
StartAutovacuumWorker();
}
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && CheckPostmasterSignal(PMSIGNAL_START_JOB_SCHEDULER)) {
t_thrd.postmaster_cxt.start_job_scheduler = true;
}
if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && CheckPostmasterSignal(PMSIGNAL_START_JOB_WORKER) &&
g_instance.status == NoShutdown && g_instance.demotion == NoDemote) {
/* the parent process, return 0 if the fork failed, return the PID if fork succeed. */
StartPgjobWorker();
}
if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER) && g_instance.pid_cxt.WalReceiverPID == 0 &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
g_instance.status == NoShutdown) {
if (g_instance.pid_cxt.WalRcvWriterPID == 0) {
g_instance.pid_cxt.WalRcvWriterPID = initialize_util_thread(WALRECWRITE);
SetWalRcvWriterPID(g_instance.pid_cxt.WalRcvWriterPID);
}
/* Startup Process wants us to start the walreceiver process. */
g_instance.pid_cxt.WalReceiverPID = initialize_util_thread(WALRECEIVER);
}
if (CheckPostmasterSignal(PMSIGNAL_START_DATARECEIVER) && g_instance.pid_cxt.DataReceiverPID == 0 &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
g_instance.status == NoShutdown) {
if (g_instance.pid_cxt.DataRcvWriterPID == 0) {
g_instance.pid_cxt.DataRcvWriterPID = initialize_util_thread(DATARECWRITER);
SetDataRcvWriterPID(g_instance.pid_cxt.DataRcvWriterPID);
}
/* Startup Process wants us to start the datareceiver process. */
g_instance.pid_cxt.DataReceiverPID = initialize_util_thread(DATARECIVER);
}
if (CheckPostmasterSignal(PMSIGNAL_START_CATCHUP) && g_instance.pid_cxt.CatchupPID == 0 &&
g_instance.status == NoShutdown) {
/* The datasender wants us to start the catch-up process. */
g_instance.pid_cxt.CatchupPID = StartCatchupWorker();
}
if (CheckPostmasterSignal(PMSIGNAL_ADVANCE_STATE_MACHINE) &&
(pmState == PM_WAIT_BACKUP || pmState == PM_WAIT_BACKENDS)) {
/* Advance postmaster's state machine */
PostmasterStateMachine();
}
if ((mode = CheckSwitchoverSignal()) != 0 && WalRcvIsOnline() && DataRcvIsOnline() &&
(pmState == PM_STARTUP || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY)) {
ereport(LOG, (errmsg("to do switchover")));
/* Tell walreceiver process to start switchover */
t_thrd.walreceiverfuncs_cxt.WalRcv->node_state = (ClusterNodeState)mode;
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_DEMOTE_PRIMARY)) {
gs_lock_test_and_set_64(&g_instance.stat_cxt.NodeStatResetTime, GetCurrentTimestamp());
ProcessDemoteRequest();
}
if (CheckPromoteSignal()) {
handle_promote_signal();
}
/* If it is primary signal, then set HaShmData and send sigusr2 to startup process */
if (CheckPrimarySignal()) {
handle_primary_signal(hashmdata);
}
if (CheckStandbySignal()) {
handle_standby_signal(hashmdata);
}
if (CheckPostmasterSignal(PMSIGNAL_UPDATE_NORMAL)) {
PMUpdateDBState(NORMAL_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file: db state(NORMAL_STATE), server mode(%s)",
wal_get_role_string(get_cur_mode()))));
}
if (CheckPostmasterSignal(PMSIGNAL_ROLLBACK_STANDBY_PROMOTE)) {
PMUpdateDBState(NORMAL_STATE, get_cur_mode(), get_cur_repl_num());
ereport(LOG,
(errmsg("update gaussdb state file:"
"db state(NORMAL_STATE), server mode(%s), reason(switchover failure)",
wal_get_role_string(get_cur_mode()))));
}
if (CheckPostmasterSignal(PMSIGNAL_START_THREADPOOL_WORKER) &&
g_instance.status == NoShutdown) {
if (g_threadPoolControler != NULL) {
g_threadPoolControler->AddWorkerIfNecessary();
}
}
#ifdef ENABLE_MULTIPLE_NODES
check_and_process_hotpatch();
#endif
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
errno = save_errno;
}
/*
* Timeout or shutdown signal from postmaster while processing startup packet.
* Cleanup and exit(1).
*
* XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while
* doing so, nor mess up SSL initialization. In practice, if the client
* has wedged here, it probably couldn't do anything with the message anyway.
*/
void startup_die(SIGNAL_ARGS)
{
/*
* when process startup packet for logic conn
* gs_wait_poll will hold lock, so proc_exit here
* will occur dead lock. gs_r_cancel will signal gs_wait_poll
* without lock and then proc_exit when
* ProcessStartupPacketForLogicConn is false
*/
if (t_thrd.postmaster_cxt.ProcessStartupPacketForLogicConn) {
t_thrd.int_cxt.ProcDiePending = true;
gs_r_cancel();
} else {
t_thrd.int_cxt.ProcDiePending = true;
}
}
/* copy from startup_die, and set cancel_from_timeout flag */
static void
startup_alarm(SIGNAL_ARGS)
{
/*
* when process startup packet for logic conn
* gs_wait_poll will hold lock, so proc_exit here
* will occur dead lock. gs_r_cancel will signal gs_wait_poll
* without lock and then proc_exit when
* ProcessStartupPacketForLogicConn is false
*/
t_thrd.storage_cxt.cancel_from_timeout = true;
force_backtrace_messages = true;
if (t_thrd.postmaster_cxt.ProcessStartupPacketForLogicConn) {
t_thrd.int_cxt.ProcDiePending = true;
gs_r_cancel();
}
}
/*
* Dummy signal handler
*
* We use this for signals that we don't actually use in the postmaster,
* but we do use in backends. If we were to SIG_IGN such signals in the
* postmaster, then a newly started backend might drop a signal that arrives
* before it's able to reconfigure its signal processing. (See notes in
* tcop/postgres.c.)
*/
static void dummy_handler(SIGNAL_ARGS)
{}
/*
* PostmasterRandom
*/
long PostmasterRandom(void)
{
/*
* Select a random seed at the time of first receiving a request.
*/
if (t_thrd.postmaster_cxt.random_seed == 0) {
do {
struct timeval random_stop_time;
gettimeofday(&random_stop_time, NULL);
/*
* We are not sure how much precision is in tv_usec, so we swap
* the high and low 16 bits of 'random_stop_time' and XOR them
* with 'random_start_time'. On the off chance that the result is
* 0, we loop until it isn't.
*/
t_thrd.postmaster_cxt.random_seed =
t_thrd.postmaster_cxt.random_start_time.tv_usec ^
((random_stop_time.tv_usec << 16) | ((random_stop_time.tv_usec >> 16) & 0xffff));
} while (t_thrd.postmaster_cxt.random_seed == 0);
gs_srandom(t_thrd.postmaster_cxt.random_seed);
/* Init the thread free list seed for get buffer free list key */
free_list_srandom(t_thrd.postmaster_cxt.random_seed);
}
return gs_random();
}
/*
* Count up number of child processes of specified types (dead_end chidren
* are always excluded).
*/
static int CountChildren(int target)
{
Dlelem* curr = NULL;
int cnt = 0;
for (curr = DLGetHead(g_instance.backend_list); curr; curr = DLGetSucc(curr)) {
Backend* bp = (Backend*)DLE_VAL(curr);
/*
* Since target == BACKEND_TYPE_ALL is the most common case, we test
* it first and avoid touching shared memory for every child.
*/
if (target != BACKEND_TYPE_ALL) {
int child;
if (bp->is_autovacuum)
child = BACKEND_TYPE_AUTOVAC;
else if (IsPostmasterChildWalSender(bp->child_slot))
child = BACKEND_TYPE_WALSND;
else if (IsPostmasterChildDataSender(bp->child_slot))
child = BACKEND_TYPE_DATASND;
else
child = BACKEND_TYPE_NORMAL;
if (!(target & child))
continue;
}
cnt++;
}
return cnt;
}
/*
* StartAutovacuumWorker
* Start an autovac worker process.
*
* This function is here because it enters the resulting PID into the
* postmaster's private backends list.
*
* NB -- this code very roughly matches BackendStartup.
*/
static void StartAutovacuumWorker(void)
{
Backend* bn = NULL;
/*
* If not in condition to run a process, don't try, but handle it like a
* fork failure. This does not normally happen, since the signal is only
* supposed to be sent by autovacuum launcher when it's OK to do it, but
* we have to check to avoid race-condition problems during DB state
* changes.
*/
if (canAcceptConnections(false) == CAC_OK) {
int slot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(slot);
if (bn != NULL) {
/*
* Compute the cancel key that will be assigned to this session.
* We probably don't need cancel keys for autovac workers, but
* we'd better have something random in the field to prevent
* unfriendly people from sending cancels to them.
*/
GenerateCancelKey(false);
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
/* Autovac workers are not dead_end and need a child slot */
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
bn->bgworker_notify = false;
bn->pid = initialize_util_thread(AUTOVACUUM_WORKER, bn);
t_thrd.proc_cxt.MyPMChildSlot = 0;
if (bn->pid > 0) {
bn->is_autovacuum = true;
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
/* all OK */
return;
}
/*
* fork failed, fall through to report -- actual error message was
* logged by initialize_util_thread
*/
(void)ReleasePostmasterChildSlot(bn->child_slot);
bn->pid = 0;
} else
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
}
/*
* Report the failure to the launcher, if it's running. (If it's not, we
* might not even be connected to shared memory, so don't try to call
* AutoVacWorkerFailed.) Note that we also need to signal it so that it
* responds to the condition, but we don't do that here, instead waiting
* for ServerLoop to do it. This way we avoid a ping-pong signalling in
* quick succession between the autovac launcher and postmaster in case
* things get ugly.
*/
if (g_instance.pid_cxt.AutoVacPID != 0) {
AutoVacWorkerFailed();
t_thrd.postmaster_cxt.avlauncher_needs_signal = true;
}
}
/*
* Reaper -- signal handler to cleanup after a child process dies.
*/
static void reaper_backend(SIGNAL_ARGS)
{
int save_errno = errno;
ThreadId pid; /* process id of dead child process */
long exitstatus; /* its exit status */
int* status = NULL;
ThreadId oldpid = 0;
#define LOOPTEST() (pid = gs_thread_id(t_thrd.postmaster_cxt.CurExitThread))
#define LOOPHEADER() (exitstatus = (long)(intptr_t)status)
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
ereport(DEBUG4, (errmsg_internal("reaping dead processes")));
for (;;) {
LOOPTEST();
if (pid == oldpid) {
break;
}
oldpid = pid;
if (gs_thread_join(t_thrd.postmaster_cxt.CurExitThread, (void**)&status) != 0) {
/*
* If the thread does not exist, treat it as normal exit and we continue to
* do our clean-up work. Otherwise, we treate it as crashed 'cause we do
* not know the current status of the thread and it's better to quit directly
* which sames more safely.
*/
if (ESRCH == pthread_kill(pid, 0)) {
exitstatus = 0;
ereport(LOG, (errmsg("failed to join thread %lu, no such process", pid)));
} else {
exitstatus = 1;
HandleChildCrash(pid, exitstatus, "Backend process");
}
} else {
LOOPHEADER();
ereport(DEBUG1, (errmsg("have joined thread %lu, exitstatus=%ld.", pid, exitstatus)));
}
// copy from CleanupBackend(),but remove traversing g_instance.backend_list scope.
LogChildExit(DEBUG2, _("server process"), pid, exitstatus);
#ifdef WIN32
if (exitstatus == ERROR_WAIT_NO_CHILDREN) {
LogChildExit(LOG, _("server process"), pid, exitstatus);
exitstatus = 0;
}
#endif
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) {
HandleChildCrash(pid, exitstatus, _("server process"));
}
}
/* Done with signal handler */
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
errno = save_errno;
}
/*
* @@GaussDB@@
* Brief : the implement of the reaper backend thread
* Description :
* Notes :
*/
void ReaperBackendMain()
{
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
/* reset t_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
/* record Start Time for logging */
t_thrd.proc_cxt.MyStartTime = time(NULL);
/* reord my name */
knl_thread_set_name("Reaper");
/* Identify myself via ps */
init_ps_display("Reaper", "", "", "");
ereport(LOG, (errmsg("Reaper backend started.")));
InitializeLatchSupport(); /* needed for latch waits */
/* Initialize private latch for use by signal handlers */
InitLatch(&t_thrd.postmaster_cxt.ReaperBackendLatch);
/*
* Properly accept or ignore signals the postmaster might send us
*
* Note: we deliberately ignore SIGTERM, because during a standard Unix
* system shutdown cycle, init will SIGTERM all processes at once. We
* want to wait for the backends to exit, whereupon the postmaster will
* tell us it's okay to shut down (via SIGUSR2).
*/
(void)gspqsignal(SIGHUP, SIG_IGN);
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, SIG_IGN);
(void)gspqsignal(SIGQUIT, SIG_IGN);
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, SIG_IGN);
(void)gspqsignal(SIGUSR2, SIG_IGN);
(void)gspqsignal(SIGCHLD, reaper_backend);
/*
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
/* all is done info top memory context. */
(void)MemoryContextSwitchTo(t_thrd.top_mem_cxt);
for (;;) {
/* Clear any already-pending wakeups */
ResetLatch(&t_thrd.postmaster_cxt.ReaperBackendLatch);
/*
* Sleep until there's something to do
*/
(void)WaitLatch(&t_thrd.postmaster_cxt.ReaperBackendLatch, WL_LATCH_SET | WL_TIMEOUT, 10 * 1000);
}
ereport(LOG, (errmsg("Reaper backend thread shutting down...")));
proc_exit(0);
}
/*
* StartPgjobWorker
* Start an job worker process.
*
* This function is here because it enters the resulting PID into the
* postmaster's private backends list.
*
*/
static void StartPgjobWorker(void)
{
Backend* bn = NULL;
/*
* If not in condition to run a process, don't try, but handle it like a
* fork failure. This does not normally happen, since the signal is only
* supposed to be sent by autovacuum launcher when it's OK to do it, but
* we have to check to avoid race-condition problems during DB state
* changes.
*/
if (canAcceptConnections(false) == CAC_OK) {
int slot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(slot);
if (bn != NULL) {
/*
* Compute the cancel key that will be assigned to this session.
* We probably don't need cancel keys for autovac workers, but
* we'd better have something random in the field to prevent
* unfriendly people from sending cancels to them.
*/
GenerateCancelKey(false);
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
bn->pid = initialize_util_thread(JOB_WORKER);
t_thrd.proc_cxt.MyPMChildSlot = 0;
if (bn->pid > 0) {
bn->is_autovacuum = false;
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
/* all OK */
return;
}
/*
* fork failed, fall through to report -- actual error message was
* logged by initialize_util_thread
*/
(void)ReleasePostmasterChildSlot(bn->child_slot);
bn->pid = 0;
bn = NULL;
} else
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
}
/*
* Report the failure to the launcher, if it's running. (If it's not, we
* might not even be connected to shared memory, so don't try to call
* RecordForkJobWorkerFailed.) Note that we also need to signal it so that it
* responds to the condition, but we don't do that here, instead waiting
* for ServerLoop to do it. This way we avoid a ping-pong signalling in
* quick succession between the job scheduler and postmaster in case
* things get ugly.
*/
if (g_instance.pid_cxt.PgJobSchdPID != 0) {
RecordForkJobWorkerFailed();
t_thrd.postmaster_cxt.jobscheduler_needs_signal = true;
}
}
static void StartPoolCleaner(void)
{
Backend* bn = NULL;
/*
* If not in condition to run a process, don't try, but handle it like a
* fork failure. This does not normally happen, since the signal is only
* supposed to be sent by autovacuum launcher when it's OK to do it, but
* we have to check to avoid race-condition problems during DB state
* changes.
*/
if (canAcceptConnections(false) == CAC_OK) {
int slot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(slot);
if (bn != NULL) {
/*
* Compute the cancel key that will be assigned to this session.
* We probably don't need cancel keys for autovac workers, but
* we'd better have something random in the field to prevent
* unfriendly people from sending cancels to them.
*/
GenerateCancelKey(false);
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
bn->pid = initialize_util_thread(COMM_POOLER_CLEAN);
g_instance.pid_cxt.CommPoolerCleanPID = bn->pid;
t_thrd.proc_cxt.MyPMChildSlot = 0;
if (bn->pid > 0) {
bn->is_autovacuum = false;
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
/* all OK */
return;
}
/*
* fork failed, fall through to report -- actual error message was
* logged by initialize_util_thread
*/
(void)ReleasePostmasterChildSlot(bn->child_slot);
bn->pid = 0;
bn = NULL;
} else
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
}
}
/*
* Create the opts file
*/
static bool CreateOptsFile(int argc, const char* argv[], const char* fullprogname)
{
FILE* fp = NULL;
int i;
#define OPTS_FILE "postmaster.opts"
if ((fp = fopen(OPTS_FILE, "w")) == NULL) {
ereport(LOG, (errmsg("could not create file \"%s\": %m", OPTS_FILE)));
return false;
}
fprintf(fp, "%s", fullprogname);
for (i = 1; i < argc; i++)
fprintf(fp, " \"%s\"", argv[i]);
fputs("\n", fp);
if (fclose(fp)) {
ereport(LOG, (errmsg("could not close file \"%s\": %m", OPTS_FILE)));
return false;
}
return true;
}
/* Update the opts file */
static void UpdateOptsFile(void)
{
FILE* fp = NULL;
char* dest = NULL;
char buffer[MAXPGPATH] = {0};
const char* modeopt = " \"-M\" \"";
const char* modestr = NULL;
#define OPTS_FILE "postmaster.opts"
#define OPTS_TEMP_FILE "postmaster.opts.tmp"
if (NULL == t_thrd.postmaster_cxt.HaShmData)
return;
switch (t_thrd.postmaster_cxt.HaShmData->current_mode) {
case PRIMARY_MODE:
modestr = "primary";
break;
case STANDBY_MODE:
modestr = "standby";
break;
default:
return;
}
if ((fp = fopen(OPTS_FILE, "r")) == NULL) {
ereport(LOG, (errmsg("could not open file \"%s\": %m", OPTS_FILE)));
return;
}
(void)fgets(buffer, MAXPGPATH, fp);
if (fclose(fp)) {
ereport(LOG, (errmsg("could not close file \"%s\": %m", OPTS_FILE)));
return;
}
if ((fp = fopen(OPTS_TEMP_FILE, "w")) == NULL) {
ereport(LOG, (errmsg("could not create file \"%s\": %m", OPTS_TEMP_FILE)));
return;
}
if ((dest = strchr(buffer, '\n')) != NULL)
*dest = 0;
dest = strstr(buffer, modeopt);
if (dest != NULL) {
dest += strlen(modeopt);
*dest = 0;
(void)fprintf(fp, "%s%s\"", buffer, modestr);
dest++;
dest = strstr(dest, "\" \"");
if (dest != NULL)
(void)fprintf(fp, "%s\n", ++dest);
else
(void)fputs("\n", fp);
} else
(void)fprintf(fp, "%s%s%s\"\n", buffer, modeopt, modestr);
if (fclose(fp)) {
ereport(LOG, (errmsg("could not close file \"%s\": %m", OPTS_TEMP_FILE)));
return;
}
(void)unlink(OPTS_FILE);
if (rename(OPTS_TEMP_FILE, OPTS_FILE) != 0)
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m", OPTS_TEMP_FILE, OPTS_FILE)));
}
/*
* StartCatchupWorker
* Start catch-up process.
*
* This function is here because it enters the resulting PID into the
* postmaster's private backends list.
*
* NB -- this code very roughly matches BackendStartup.
*/
static ThreadId StartCatchupWorker(void)
{
Backend* bn = NULL;
/*
* If not in condition to run a process, don't try, but handle it like a
* fork failure. This does not normally happen, since the signal is only
* supposed to be sent by autovacuum launcher when it's OK to do it, but
* we have to check to avoid race-condition problems during DB state
* changes.
*/
if (canAcceptConnections(false) == CAC_OK) {
int slot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(slot);
if (bn != NULL) {
/*
* Compute the cancel key that will be assigned to this session.
* We probably don't need cancel keys for autovac workers, but
* we'd better have something random in the field to prevent
* unfriendly people from sending cancels to them.
*/
GenerateCancelKey(false);
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
/* Data catch-up are not dead_end and need a child slot */
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
bn->pid = initialize_util_thread(CATCHUP);
t_thrd.proc_cxt.MyPMChildSlot = 0;
if (bn->pid > 0) {
bn->is_autovacuum = false;
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
/* all OK */
return bn->pid;
}
/*
* fork failed, fall through to report -- actual error message was
* logged by StartDataCatchup
*/
(void)ReleasePostmasterChildSlot(bn->child_slot);
bn->pid = 0;
bn = NULL;
} else
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
}
return 0;
}
/*
* MaxLivePostmasterChildren
*
* This reports the number of entries needed in per-child-process arrays
* (the PMChildFlags array, and if EXEC_BACKEND the ShmemBackendArray).
* These arrays include regular backends, autovac workers and walsenders,
* but not special children nor dead_end children. This allows the arrays
* to have a fixed maximum size, to wit the same too-many-children limit
* enforced by canAcceptConnections(). The exact value isn't too critical
* as long as it's more than g_instance.shmem_cxt.MaxBackends.
*/
int MaxLivePostmasterChildren(void)
{
return 6 * g_instance.shmem_cxt.MaxBackends;
}
/*
* Start a new bgworker.
* Starting time conditions must have been checked already.
*
* Returns true on success, false on failure.
* In either case, update the RegisteredBgWorker's state appropriately.
*
* This code is heavily based on autovacuum.c, q.v.
*/
static bool do_start_bgworker(RegisteredBgWorker *rw)
{
ThreadId worker_pid = InvalidPid;
Assert(rw->rw_pid == 0);
/*
* Allocate and assign the Backend element. Note we must do this before
* forking, so that we can handle failures (out of memory or child-process
* slots) cleanly.
*
* Treat failure as though the worker had crashed. That way, the
* postmaster will wait a bit before attempting to start it again; if we
* tried again right away, most likely we'd find ourselves hitting the
* same resource-exhaustion condition.
*/
if (!assign_backendlist_entry(rw)) {
rw->rw_crashed_at = GetCurrentTimestamp();
return false;
}
ereport(DEBUG1,
(errmsg("starting background worker process \"%s\"",
rw->rw_worker.bgw_name)));
Backend* bn = rw->rw_backend;
void* bgWorkerShmAddr = GetBackgroundWorkerShmAddr(rw->rw_shmem_slot);
switch ((worker_pid = initialize_util_thread(BACKGROUND_WORKER, bgWorkerShmAddr))) {
case (ThreadId)-1:
/* in postmaster, fork failed ... */
ereport(LOG,
(errmsg("could not fork worker process: %m")));
/* undo what assign_backendlist_entry did */
(void)ReleasePostmasterChildSlot(rw->rw_child_slot);
bn->pid = 0;
rw->rw_child_slot = 0;
rw->rw_backend = NULL;
/* mark entry as crashed, so we'll try again later */
rw->rw_crashed_at = GetCurrentTimestamp();
break;
default:
/* in postmaster, fork successful ... */
rw->rw_pid = worker_pid;
bn->pid = rw->rw_pid;
ReportBackgroundWorkerPID(rw);
/* add new worker to lists of backends */
DLInitElem(&bn->elem, bn);
DLAddHead(g_instance.backend_list, &bn->elem);
return true;
}
return false;
}
/*
* Does the current postmaster state require starting a worker with the
* specified start_time?
*/
static bool
bgworker_should_start_now(BgWorkerStartTime start_time)
{
switch (pmState) {
case PM_NO_CHILDREN:
case PM_WAIT_DEAD_END:
case PM_SHUTDOWN_2:
case PM_SHUTDOWN:
case PM_WAIT_BACKENDS:
case PM_WAIT_READONLY:
case PM_WAIT_BACKUP:
break;
case PM_RUN:
if (start_time == BgWorkerStart_RecoveryFinished) {
return true;
}
/* fall through */
case PM_HOT_STANDBY:
if (start_time == BgWorkerStart_ConsistentState) {
return true;
}
/* fall through */
case PM_RECOVERY:
case PM_STARTUP:
case PM_INIT:
if (start_time == BgWorkerStart_PostmasterStart) {
return true;
}
/* fall through */
}
return false;
}
/*
* Allocate the Backend struct for a connected background worker, but don't
* add it to the list of backends just yet.
*
* On failure, return false without changing any worker state.
*
* Some info from the Backend is copied into the passed rw.
*/
static bool
assign_backendlist_entry(RegisteredBgWorker *rw)
{
Backend* bn = NULL;
/*
* Check that database state allows another connection. Currently the
* only possible failure is CAC_TOOMANY, so we just log an error message
* based on that rather than checking the error code precisely.
*/
if (canAcceptConnections(false) != CAC_OK)
{
ereport(LOG,
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
errmsg("no slot available for new worker process")));
return false;
}
int slot = AssignPostmasterChildSlot();
bn = AssignFreeBackEnd(slot);
if (bn == NULL) {
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
return false;
}
/*
* Compute the cancel key that will be assigned to this session. We
* probably don't need cancel keys for background workers, but we'd better
* have something random in the field to prevent unfriendly people from
* sending cancels to them.
*/
GenerateCancelKey(false);
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
bn->is_autovacuum = false;
bn->dead_end = false;
bn->bgworker_notify = false;
rw->rw_backend = bn;
rw->rw_child_slot = bn->child_slot;
return true;
}
/*
* If the time is right, start background worker(s).
*
* As a side effect, the bgworker control variables are set or reset
* depending on whether more workers may need to be started.
*
* We limit the number of workers started per call, to avoid consuming the
* postmaster's attention for too long when many such requests are pending.
* As long as start_worker_needed is true, ServerLoop will not block and will
* call this function again after dealing with any other issues.
*/
static void maybe_start_bgworkers(void)
{
#define MAX_BGWORKERS_TO_LAUNCH 100
int num_launched = 0;
TimestampTz now = 0;
slist_mutable_iter iter;
/*
* During crash recovery, we have no need to be called until the state
* transition out of recovery.
*/
if (g_instance.fatal_error) {
g_instance.bgworker_cxt.start_worker_needed = false;
g_instance.bgworker_cxt.have_crashed_worker = false;
return;
}
/* Don't need to be called again unless we find a reason for it below */
g_instance.bgworker_cxt.start_worker_needed = false;
g_instance.bgworker_cxt.have_crashed_worker = false;
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.background_worker_list) {
RegisteredBgWorker *rw;
rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);
/* ignore if already running */
if (rw->rw_pid != 0) {
continue;
}
/* if marked for death, clean up and remove from list */
if (rw->rw_terminate) {
ForgetBackgroundWorker(&iter);
continue;
}
/*
* If this worker has crashed previously, maybe it needs to be
* restarted (unless on registration it specified it doesn't want to
* be restarted at all). Check how long ago did a crash last happen.
* If the last crash is too recent, don't start it right away; let it
* be restarted once enough time has passed.
*/
if (rw->rw_crashed_at != 0) {
if (rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART) {
ThreadId notify_pid = rw->rw_worker.bgw_notify_pid;
ForgetBackgroundWorker(&iter);
/* Report worker is gone now. */
if (notify_pid != 0) {
(void)gs_signal_send(notify_pid, SIGUSR1);
}
continue;
}
/* read system time only when needed */
if (now == 0) {
now = GetCurrentTimestamp();
}
if (!TimestampDifferenceExceeds(rw->rw_crashed_at, now,
rw->rw_worker.bgw_restart_time * 1000)) {
/* Set flag to remember that we have workers to start later */
g_instance.bgworker_cxt.have_crashed_worker = true;
continue;
}
}
if (bgworker_should_start_now(rw->rw_worker.bgw_start_time)) {
/* reset crash time before trying to start worker */
rw->rw_crashed_at = 0;
/*
* Try to start the worker.
*
* On failure, give up processing workers for now, but set
* start_worker_needed so we'll come back here on the next iteration
* of ServerLoop to try again. (We don't want to wait, because
* there might be additional ready-to-run workers.) We could set
* have_crashed_worker as well, since this worker is now marked
* crashed, but there's no need because the next run of this
* function will do that.
*/
if (!do_start_bgworker(rw)) {
g_instance.bgworker_cxt.start_worker_needed = true;
return;
}
/*
* If we've launched as many workers as allowed, quit, but have
* ServerLoop call us again to look for additional ready-to-run
* workers. There might not be any, but we'll find out the next
* time we run.
*/
if (++num_launched >= MAX_BGWORKERS_TO_LAUNCH) {
g_instance.bgworker_cxt.start_worker_needed = true;
return;
}
}
}
}
/*
* When a backend asks to be notified about worker state changes, we
* set a flag in its backend entry. The background worker machinery needs
* to know when such backends exit.
*/
bool
PostmasterMarkPIDForWorkerNotify(ThreadId pid)
{
int count = MaxLivePostmasterChildren();
for (int i = 0; i < count; ++i) {
Backend* bp = &g_instance.backend_array[i];
if (bp->pid == pid)
{
bp->bgworker_notify = true;
return true;
}
}
return false;
}
#ifdef EXEC_BACKEND
#ifndef WIN32
#define write_inheritable_socket(dest, src) ((*(dest) = (src)))
#define read_inheritable_socket(dest, src) (*(dest) = *(src))
#else
static bool write_duplicated_handle(HANDLE* dest, HANDLE src, HANDLE child);
static bool write_inheritable_socket(InheritableSocket* dest, SOCKET src, pid_t childPid);
static void read_inheritable_socket(SOCKET* dest, InheritableSocket* src);
#endif
/* Save critical backend variables into the BackendParameters struct */
#ifndef WIN32
static bool save_backend_variables(BackendParameters* param, Port* port)
#else
static bool save_backend_variables(BackendParameters* param, Port* port, HANDLE childProcess, pid_t childPid)
#endif
{
int ss_rc = memcpy_s(&param->port, sizeof(param->port), port, sizeof(Port));
securec_check(ss_rc, "\0", "\0");
write_inheritable_socket(&param->portsocket, port->sock);
strlcpy(param->DataDir, t_thrd.proc_cxt.DataDir, MAXPGPATH);
ss_rc = memcpy_s(&param->ListenSocket,
sizeof(param->ListenSocket),
&t_thrd.postmaster_cxt.ListenSocket,
sizeof(t_thrd.postmaster_cxt.ListenSocket));
securec_check(ss_rc, "\0", "\0");
param->MyCancelKey = t_thrd.proc_cxt.MyCancelKey;
param->MyPMChildSlot = t_thrd.proc_cxt.MyPMChildSlot;
param->UsedShmemSegID = UsedShmemSegID;
param->UsedShmemSegAddr = UsedShmemSegAddr;
ereport(DEBUG1, (errmsg("segID: %lu", UsedShmemSegID)));
param->ShmemLock = t_thrd.shemem_ptr_cxt.ShmemLock;
param->ShmemVariableCache = t_thrd.xact_cxt.ShmemVariableCache;
param->mainLWLockArray = (LWLock*)t_thrd.shemem_ptr_cxt.mainLWLockArray;
param->PMSignalState = t_thrd.shemem_ptr_cxt.PMSignalState;
param->LocalIpNum = t_thrd.postmaster_cxt.LocalIpNum;
int rc = memcpy_s(param->LocalAddrList, (MAXLISTEN * IP_LEN), t_thrd.postmaster_cxt.LocalAddrList, (MAXLISTEN * IP_LEN));
securec_check(rc, "", "");
param->HaShmData = t_thrd.postmaster_cxt.HaShmData;
param->PgStartTime = t_thrd.time_cxt.pg_start_time;
param->PgReloadTime = t_thrd.time_cxt.pg_reload_time;
param->first_syslogger_file_time = t_thrd.logger.first_syslogger_file_time;
param->redirection_done = t_thrd.postmaster_cxt.redirection_done;
param->IsBinaryUpgrade = u_sess->proc_cxt.IsBinaryUpgrade;
param->max_safe_fds = t_thrd.storage_cxt.max_safe_fds;
param->max_files_per_process = g_instance.attr.attr_common.max_files_per_process;
param->max_userdatafiles = t_thrd.storage_cxt.max_userdatafiles;
#ifdef WIN32
param->PostmasterHandle = PostmasterHandle;
if (!write_duplicated_handle(&param->initial_signal_pipe, pgwin32_create_signal_listener(childPid), childProcess))
return false;
#else
ss_rc = memcpy_s(&param->postmaster_alive_fds,
sizeof(t_thrd.postmaster_cxt.postmaster_alive_fds),
&t_thrd.postmaster_cxt.postmaster_alive_fds,
sizeof(t_thrd.postmaster_cxt.postmaster_alive_fds));
securec_check(ss_rc, "\0", "\0");
#endif
ss_rc = memcpy_s(&param->syslogPipe,
sizeof(param->syslogPipe),
&t_thrd.postmaster_cxt.syslogPipe,
sizeof(t_thrd.postmaster_cxt.syslogPipe));
securec_check(ss_rc, "\0", "\0");
strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
strlcpy(param->pkglib_path, t_thrd.proc_cxt.pkglib_path, MAXPGPATH);
param->myTempNamespace = u_sess->catalog_cxt.myTempNamespace;
param->myTempToastNamespace = u_sess->catalog_cxt.myTempToastNamespace;
if (module_logging_is_on(MOD_COMM_IPC) && (t_thrd.proc && t_thrd.proc->workingVersionNum >= 92060)) {
param->comm_ipc_log = true;
} else {
param->comm_ipc_log = false;
}
return true;
}
#ifdef WIN32
/*
* Duplicate a handle for usage in a child process, and write the child
* process instance of the handle to the parameter file.
*/
static bool write_duplicated_handle(HANDLE* dest, HANDLE src, HANDLE childProcess)
{
HANDLE hChild = INVALID_HANDLE_VALUE;
if (!DuplicateHandle(
GetCurrentProcess(), src, childProcess, &hChild, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
ereport(LOG,
(errmsg_internal(
"could not duplicate handle to be written to backend parameter file: error code %lu", GetLastError())));
return false;
}
*dest = hChild;
return true;
}
/*
* Duplicate a socket for usage in a child process, and write the resulting
* structure to the parameter file.
* This is required because a number of LSPs (Layered Service Providers) very
* common on Windows (antivirus, firewalls, download managers etc) break
* straight socket inheritance.
*/
static bool write_inheritable_socket(InheritableSocket* dest, SOCKET src, pid_t childpid)
{
dest->origsocket = src;
if (src != 0 && src != PGINVALID_SOCKET) {
/* Actual socket */
if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0) {
ereport(LOG,
(errmsg(
"could not duplicate socket %d for use in backend: error code %d", (int)src, WSAGetLastError())));
return false;
}
}
return true;
}
/*
* Read a duplicate socket structure back, and get the socket descriptor.
*/
static void read_inheritable_socket(SOCKET* dest, InheritableSocket* src)
{
SOCKET s;
if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0) {
/* Not a real socket! */
*dest = src->origsocket;
} else {
/* Actual socket, so create from structure */
s = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &src->wsainfo, 0, 0);
if (s == INVALID_SOCKET) {
write_stderr("could not create inherited socket: error code %d\n", WSAGetLastError());
exit(1);
}
*dest = s;
/*
* To make sure we don't get two references to the same socket, close
* the original one. (This would happen when inheritance actually
* works..
*/
closesocket(src->origsocket);
}
}
#endif
static void read_backend_variables(char* arg, Port* port)
{
BackendParameters* pParam = (BackendParameters*)arg;
restore_backend_variables(pParam, port);
}
/* Restore critical backend variables from the BackendParameters struct */
static void restore_backend_variables(BackendParameters* param, Port* port)
{
int rc = memcpy_s(port, sizeof(Port), &param->port, sizeof(Port));
securec_check(rc, "", "");
read_inheritable_socket(&port->sock, &param->portsocket);
SetDataDir(param->DataDir);
int ss_rc = memcpy_s(&t_thrd.postmaster_cxt.ListenSocket,
sizeof(t_thrd.postmaster_cxt.ListenSocket),
&param->ListenSocket,
sizeof(t_thrd.postmaster_cxt.ListenSocket));
securec_check(ss_rc, "\0", "\0");
t_thrd.proc_cxt.MyCancelKey = param->MyCancelKey;
t_thrd.proc_cxt.MyPMChildSlot = param->MyPMChildSlot;
UsedShmemSegID = param->UsedShmemSegID;
UsedShmemSegAddr = param->UsedShmemSegAddr;
t_thrd.shemem_ptr_cxt.ShmemLock = param->ShmemLock;
t_thrd.xact_cxt.ShmemVariableCache = param->ShmemVariableCache;
t_thrd.shemem_ptr_cxt.mainLWLockArray = (LWLockPadded*)param->mainLWLockArray;
t_thrd.shemem_ptr_cxt.PMSignalState = param->PMSignalState;
t_thrd.postmaster_cxt.LocalIpNum = param->LocalIpNum;
rc = memcpy_s(t_thrd.postmaster_cxt.LocalAddrList, (MAXLISTEN * IP_LEN), param->LocalAddrList, (MAXLISTEN * IP_LEN));
securec_check(rc, "", "");
t_thrd.postmaster_cxt.HaShmData = param->HaShmData;
t_thrd.time_cxt.pg_start_time = param->PgStartTime;
t_thrd.time_cxt.pg_reload_time = param->PgReloadTime;
t_thrd.logger.first_syslogger_file_time = param->first_syslogger_file_time;
t_thrd.postmaster_cxt.redirection_done = param->redirection_done;
u_sess->proc_cxt.IsBinaryUpgrade = param->IsBinaryUpgrade;
t_thrd.storage_cxt.max_safe_fds = param->max_safe_fds;
g_instance.attr.attr_common.max_files_per_process = param->max_files_per_process;
t_thrd.storage_cxt.max_userdatafiles = param->max_userdatafiles;
#ifdef WIN32
PostmasterHandle = param->PostmasterHandle;
pgwin32_initial_signal_pipe = param->initial_signal_pipe;
#endif
ss_rc = memcpy_s(&t_thrd.postmaster_cxt.syslogPipe,
sizeof(t_thrd.postmaster_cxt.syslogPipe),
&param->syslogPipe,
sizeof(t_thrd.postmaster_cxt.syslogPipe));
securec_check(ss_rc, "\0", "\0");
strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
strlcpy(t_thrd.proc_cxt.pkglib_path, param->pkglib_path, MAXPGPATH);
if (StreamThreadAmI()) {
u_sess->catalog_cxt.myTempNamespace = param->myTempNamespace;
u_sess->catalog_cxt.myTempToastNamespace = param->myTempToastNamespace;
}
if (param->comm_ipc_log == true) {
module_logging_enable_comm(MOD_COMM_IPC);
}
}
#endif /* EXEC_BACKEND */
static void BackendArrayAllocation(void)
{
// should be the same size as PostmasterChildSlot.
Size size = mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
g_instance.backend_array = (Backend*)palloc0(size);
}
Backend* GetBackend(int slot)
{
if (slot > 0 && slot <= MaxLivePostmasterChildren())
return &g_instance.backend_array[slot - 1];
else
return NULL;
}
Backend* AssignFreeBackEnd(int slot)
{
Assert(g_instance.backend_array[slot - 1].pid == 0);
Backend* bn = &g_instance.backend_array[slot - 1];
bn->flag = 0;
bn->pid = 0;
bn->cancel_key = 0;
bn->dead_end = false;
bn->bgworker_notify = false;
return bn;
}
static void BackendArrayRemove(Backend* bn)
{
int i = bn->child_slot - 1;
Assert(g_instance.backend_array[i].pid == bn->pid);
/* Mark the slot as empty */
g_instance.backend_array[i].pid = 0;
g_instance.backend_array[i].flag = 0;
g_instance.backend_array[i].cancel_key = 0;
g_instance.backend_array[i].dead_end = false;
g_instance.backend_array[i].bgworker_notify = false;
}
#ifdef WIN32
static ThreadId win32_waitpid(int* exitstatus)
{
DWORD dwd;
ULONG_PTR key;
OVERLAPPED* ovl = NULL;
/*
* Check if there are any dead children. If there are, return the pid of
* the first one that died.
*/
if (GetQueuedCompletionStatus(win32ChildQueue, &dwd, &key, &ovl, 0)) {
*exitstatus = (int)key;
return dwd;
}
return -1;
}
#endif /* WIN32 */
/*
* Initialize one and only handle for monitoring postmaster death.
*
* Called once in the postmaster, so that child processes can subsequently
* monitor if their parent is dead.
*/
static void InitPostmasterDeathWatchHandle(void)
{
#ifndef WIN32
/*
* Create a pipe. Postmaster holds the write end of the pipe open
* (POSTMASTER_FD_OWN), and children hold the read end. Children can pass
* the read file descriptor to select() to wake up in case postmaster
* dies, or check for postmaster death with a (read() == 0). Children must
* close the write end as soon as possible after forking, because EOF
* won't be signaled in the read end until all processes have closed the
* write fd. That is taken care of in ClosePostmasterPorts().
*/
Assert(t_thrd.proc_cxt.MyProcPid == PostmasterPid);
if (pipe(t_thrd.postmaster_cxt.postmaster_alive_fds))
ereport(FATAL,
(errcode_for_file_access(), errmsg_internal("could not create pipe to monitor postmaster death: %m")));
/*
* Set O_NONBLOCK to allow testing for the fd's presence with a read()
* call.
*/
if (fcntl(t_thrd.postmaster_cxt.postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFL, O_NONBLOCK))
ereport(FATAL,
(errcode_for_socket_access(),
errmsg_internal("could not set postmaster death monitoring pipe to non-blocking mode: %m")));
#else
/*
* On Windows, we use a process handle for the same purpose.
*/
if (DuplicateHandle(GetCurrentProcess(),
GetCurrentProcess(),
GetCurrentProcess(),
&PostmasterHandle,
0,
TRUE,
DUPLICATE_SAME_ACCESS) == 0)
ereport(FATAL, (errmsg_internal("could not duplicate postmaster handle: error code %lu", GetLastError())));
#endif /* WIN32 */
}
Size CBMShmemSize(void)
{
Size size = 0;
size = add_size(size, sizeof(XlogBitmap));
return size;
}
void CBMShmemInit(void)
{
bool found = false;
t_thrd.cbm_cxt.XlogCbmSys = (XlogBitmap*)ShmemInitStruct("CBM Shmem Data", CBMShmemSize(), &found);
if (!found)
InitXlogCbmSys();
}
Size HaShmemSize(void)
{
Size size = 0;
size = add_size(size, sizeof(HaShmemData));
return size;
}
void HaShmemInit(void)
{
bool found = false;
t_thrd.postmaster_cxt.HaShmData = (HaShmemData*)ShmemInitStruct("HA Shmem Data ", HaShmemSize(), &found);
if (!found) {
int i = 0;
t_thrd.postmaster_cxt.HaShmData->current_mode = NORMAL_MODE;
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
t_thrd.postmaster_cxt.HaShmData->disconnect_count[i] = 0;
t_thrd.postmaster_cxt.HaShmData->repl_reason[i] = NONE_REBUILD;
}
SpinLockInit(&t_thrd.postmaster_cxt.HaShmData->mutex);
}
if (!IsUnderPostmaster) {
gs_set_hs_shm_data(t_thrd.postmaster_cxt.HaShmData);
}
}
/* Check whether the connect info of the port is equal to the replconninfo */
static bool IsChannelAdapt(Port* port, ReplConnInfo* repl)
{
struct sockaddr* laddr = (struct sockaddr*)&(port->laddr.addr);
struct sockaddr* raddr = (struct sockaddr*)&(port->raddr.addr);
char local_ip[IP_LEN] = {0};
char remote_ip[IP_LEN] = {0};
char* result = NULL;
Assert(repl != NULL);
if (AF_INET6 == laddr->sa_family) {
result = inet_net_ntop(AF_INET6, &((struct sockaddr_in*)laddr)->sin_addr, 128, local_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
} else if (AF_INET == laddr->sa_family) {
result = inet_net_ntop(AF_INET, &((struct sockaddr_in*)laddr)->sin_addr, 32, local_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
}
if (AF_INET6 == raddr->sa_family) {
result = inet_net_ntop(AF_INET6, &((struct sockaddr_in*)raddr)->sin_addr, 128, remote_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
} else if (AF_INET == raddr->sa_family) {
result = inet_net_ntop(AF_INET, &((struct sockaddr_in*)raddr)->sin_addr, 32, remote_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
}
if (0 == strcmp(local_ip, repl->localhost) && 0 == strcmp(remote_ip, repl->remotehost)) {
return true;
} else {
ereport(DEBUG1, (errmsg("connect local ip %s, connect remote ip %s", local_ip, remote_ip)));
return false;
}
}
/*
* check whether the login connection is from local machine.
*/
bool IsFromLocalAddr(Port* port)
{
struct sockaddr* local_addr = (struct sockaddr*)&(port->laddr.addr);
struct sockaddr* remote_addr = (struct sockaddr*)&(port->raddr.addr);
char local_ip[IP_LEN] = {0};
char remote_ip[IP_LEN] = {0};
char* result = NULL;
/* parse the local ip address */
if (AF_INET6 == local_addr->sa_family) {
result = inet_net_ntop(AF_INET6, &((struct sockaddr_in*)local_addr)->sin_addr, 128, local_ip, IP_LEN);
} else if (AF_INET == local_addr->sa_family) {
result = inet_net_ntop(AF_INET, &((struct sockaddr_in*)local_addr)->sin_addr, 32, local_ip, IP_LEN);
}
if (result == NULL) {
ereport(WARNING, (errmsg("inet_net_ntop local failed, error: %d", EAFNOSUPPORT)));
}
/* parse the remote ip address */
if (AF_INET6 == remote_addr->sa_family) {
result = inet_net_ntop(AF_INET6, &((struct sockaddr_in*)remote_addr)->sin_addr, 128, remote_ip, IP_LEN);
} else if (AF_INET == remote_addr->sa_family) {
result = inet_net_ntop(AF_INET, &((struct sockaddr_in*)remote_addr)->sin_addr, 32, remote_ip, IP_LEN);
}
if (result == NULL) {
ereport(WARNING, (errmsg("inet_net_ntop remote failed, error: %d", EAFNOSUPPORT)));
}
/* only the remote ip equal to local ip, return true */
if (strlen(local_ip) != 0 && strlen(remote_ip) != 0 && strcmp(local_ip, remote_ip) == 0)
return true;
else
return false;
}
/*
* check whether the localaddr of the ip is in the LocalAddrList
*/
bool IsLocalAddr(Port* port)
{
int i;
#ifndef WIN32
struct sockaddr* laddr = (struct sockaddr*)&(port->laddr.addr);
char local_ip[IP_LEN] = {0};
char* result = NULL;
if (AF_INET6 == laddr->sa_family) {
result = inet_net_ntop(AF_INET6, &((struct sockaddr_in*)laddr)->sin_addr, 128, local_ip, IP_LEN);
} else if (AF_INET == laddr->sa_family) {
result = inet_net_ntop(AF_INET, &((struct sockaddr_in*)laddr)->sin_addr, 32, local_ip, IP_LEN);
}
if (AF_UNIX == laddr->sa_family) {
return true;
}
for (i = 0; i != t_thrd.postmaster_cxt.LocalIpNum; ++i) {
if (0 == strcmp(local_ip, t_thrd.postmaster_cxt.LocalAddrList[i]) ||
(AF_INET == laddr->sa_family && 0 == strcmp("0.0.0.0", t_thrd.postmaster_cxt.LocalAddrList[i])) ||
(AF_INET6 == laddr->sa_family && 0 == strcmp("::", t_thrd.postmaster_cxt.LocalAddrList[i]))) {
return true;
}
}
if (NULL == result && laddr->sa_family != AF_UNSPEC) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
return false;
#else
for (i = 0; i != t_thrd.postmaster_cxt.LocalIpNum; ++i) {
ereport(DEBUG1, (errmsg("LocalAddrIP %s\n", t_thrd.postmaster_cxt.LocalAddrList[i])));
}
return true;
#endif
}
/*
* check whether the input address string is equivalent to local host
*/
static bool IsLocalIp(const char* address)
{
return (
strcmp(address, LOCAL_HOST) == 0 || strcmp(address, LOOP_IP_STRING) == 0 || strcmp(address, LOOP_IPV6_IP) == 0);
}
/*
* check whether the input address string is inplicit, including local host and *
*/
static bool IsInplicitIp(const char* address)
{
return (IsLocalIp(address) || strcmp(address, "*") == 0);
}
/*
* get the first explicit host addr in listen_addresses GUC.
* the returned addr string is allocated and caller should free it.
*/
const char* get_explicit_host_addr(void)
{
char* rawstring = NULL;
List* elemlist = NULL;
ListCell* cell = NULL;
const char* hostaddr = NULL;
/* get the first explicit host name in listen address for cn connection */
rawstring = pstrdup(g_instance.attr.attr_network.ListenAddresses);
if (!SplitIdentifierString(rawstring, ',', &elemlist)) {
/* syntax error in list */
ereport(
FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"listen_addresses\"")));
}
foreach (cell, elemlist) {
if (lfirst(cell) != NULL && !IsInplicitIp((const char*)lfirst(cell))) {
hostaddr = pstrdup((const char*)lfirst(cell));
break;
}
}
list_free(elemlist);
pfree(rawstring);
return hostaddr;
}
/*
* if certain replconn channel is equivalent to pooler port,
* we use that channel for internal tool connections for dn.
* return value: equivalent replconn array index, i.e. no need
* for pooler port, or -1 if none equivalent, i.e. pooler port needed.
* At present, this is determined only at postmaster startup.
* If we are to support dynamic modification of replconninfo,
* we would also need to do the judge dynamically.
*/
static int NeedPoolerPort(void)
{
char* addrstring = NULL;
List* list = NULL;
ListCell* cell = NULL;
int index = -1;
addrstring = pstrdup(g_instance.attr.attr_network.ListenAddresses);
if (!SplitIdentifierString(addrstring, ',', &list)) {
ereport(
FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"listen_addresses\"")));
}
for (int i = 1; i < MAX_REPLNODE_NUM; i++) {
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL &&
t_thrd.postmaster_cxt.ReplConnArray[i]->localport == g_instance.attr.attr_network.PoolerPort) {
if (strcmp(t_thrd.postmaster_cxt.ReplConnArray[i]->localhost, "*") == 0) {
index = i;
break;
} else {
bool found = false;
foreach (cell, list) {
if (lfirst(cell) != NULL) {
if (strcmp((const char*)lfirst(cell), "*") == 0) {
ereport(FATAL,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("when replconn port equals gsql port plus one, "
"listen_addresses can not contain \"*\"")));
} else if (strcmp(t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
(const char*)lfirst(cell)) == 0) {
found = true;
break;
}
}
}
if (found) {
index = i;
break;
}
}
}
}
list_free(list);
pfree(addrstring);
return index;
}
/*
* check whether the port of the socket address is the PoolerPort
*/
bool IsHASocketAddr(struct sockaddr* sock_addr)
{
int portNumber = 0;
struct sockaddr_in* ipv4addr = NULL;
struct sockaddr_in6* ipv6addr = NULL;
#ifdef HAVE_UNIX_SOCKETS
struct sockaddr_un* unixaddr = NULL;
char unixStr[MAX_UNIX_PATH_LEN] = {'\0'};
#endif
bool result = false;
if (sock_addr == NULL) {
return false;
}
switch (sock_addr->sa_family) {
case AF_INET:
ipv4addr = (struct sockaddr_in*)sock_addr;
portNumber = ntohs(ipv4addr->sin_port);
result = (portNumber == g_instance.attr.attr_network.PoolerPort);
break;
case AF_INET6:
ipv6addr = (struct sockaddr_in6*)sock_addr;
portNumber = ntohs(ipv6addr->sin6_port);
result = (portNumber == g_instance.attr.attr_network.PoolerPort);
break;
#ifdef HAVE_UNIX_SOCKETS
case AF_UNIX:
unixaddr = (struct sockaddr_un*)sock_addr;
UNIXSOCK_PATH(unixStr, g_instance.attr.attr_network.PoolerPort, g_instance.attr.attr_network.UnixSocketDir);
if (0 == (strncmp(unixaddr->sun_path, unixStr, MAX_UNIX_PATH_LEN))) {
result = true;
}
break;
#endif
default:
break;
}
return result;
}
/*
* check whether the port of the session Port structure is the PoolerPort
*/
bool IsHAPort(Port* port)
{
if (port == NULL) {
return false;
}
struct sockaddr* laddr = (struct sockaddr*)&(port->laddr.addr);
return IsHASocketAddr(laddr);
}
/*
* check whether the localaddr of the port is the PostPortNumber
*/
static bool IsLocalPort(Port* port)
{
struct sockaddr* laddr = (struct sockaddr*)&(port->laddr.addr);
int sockport = 0;
if (AF_UNIX == laddr->sa_family) {
return true;
} else {
sockport = ntohs(((struct sockaddr_in*)laddr)->sin_port);
}
if (sockport == g_instance.attr.attr_network.PostPortNumber) {
return true;
}
return false;
}
static void SetHaShmemData()
{
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
int i = 0, repl_list_num = 0;
switch (t_thrd.xlog_cxt.server_mode) {
case STANDBY_MODE: {
hashmdata->current_mode = STANDBY_MODE;
break;
}
case PRIMARY_MODE: {
hashmdata->current_mode = PRIMARY_MODE;
break;
}
case PENDING_MODE: {
hashmdata->current_mode = PENDING_MODE;
break;
}
default:
break;
}
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL)
repl_list_num++;
}
hashmdata->repl_list_num = repl_list_num;
}
/*
* check whether the ip and port is already listened
*/
static bool IsAlreadyListen(const char* ip, int port)
{
int listen_index = 0;
char sock_ip[IP_LEN] = {0};
errno_t rc = 0;
if (ip == NULL || port <= 0) {
return false;
}
for (listen_index = 0; listen_index != MAXLISTEN; ++listen_index) {
if (t_thrd.postmaster_cxt.ListenSocket[listen_index] != PGINVALID_SOCKET) {
struct sockaddr_in saddr;
socklen_t slen;
char* result = NULL;
rc = memset_s(&saddr, sizeof(saddr), 0, sizeof(saddr));
securec_check(rc, "\0", "\0");
slen = sizeof(saddr);
if (getsockname(t_thrd.postmaster_cxt.ListenSocket[listen_index],
(struct sockaddr*)&saddr,
(socklen_t*)&slen) < 0) {
ereport(LOG, (errmsg("Error in getsockname int IsAlreadyListen()")));
continue;
}
if (AF_INET6 == saddr.sin_family) {
result = inet_net_ntop(AF_INET6, &saddr.sin_addr, 128, sock_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
} else if (AF_INET == saddr.sin_family) {
result = inet_net_ntop(AF_INET, &saddr.sin_addr, 32, sock_ip, IP_LEN);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
} else if (AF_UNIX == saddr.sin_family) {
continue;
}
if ((0 == strcmp(ip, sock_ip)) && (port == ntohs(saddr.sin_port))) {
return true;
}
}
}
return false;
}
/*
* whether the sockect is corresponding with IP-Port pair
*/
bool CheckSockAddr(struct sockaddr* sock_addr, const char* szIP, int port)
{
struct sockaddr_in* ipv4addr = NULL;
struct sockaddr_in6* ipv6addr = NULL;
#ifdef HAVE_UNIX_SOCKETS
struct sockaddr_un* unixaddr = NULL;
char unixStr[MAX_UNIX_PATH_LEN] = {'\0'};
#endif
char IPstr[MAX_IP_STR_LEN] = {'\0'};
int portNumber = 0;
bool cmpResult = false;
char* result = NULL;
if ((NULL == sock_addr) || (NULL == szIP) || (0 == port)) {
ereport(LOG, (errmsg("invalid socket information or IP string or port")));
return false;
}
switch (sock_addr->sa_family) {
/* if AF_UNIX, just compare the the name of the socket file */
#ifdef HAVE_UNIX_SOCKETS
case AF_UNIX:
unixaddr = (struct sockaddr_un*)sock_addr;
UNIXSOCK_PATH(unixStr, port, g_instance.attr.attr_network.UnixSocketDir);
if (0 == (strncmp(unixaddr->sun_path, unixStr, MAX_UNIX_PATH_LEN))) {
cmpResult = true;
}
break;
#endif
case AF_INET:
ipv4addr = (struct sockaddr_in*)sock_addr;
result = inet_net_ntop(AF_INET, &(ipv4addr->sin_addr), 32, IPstr, MAX_IP_STR_LEN - 1);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
portNumber = ntohs(ipv4addr->sin_port);
if ((((0 == strncmp(szIP, LOCAL_HOST, MAX_IP_STR_LEN)) &&
(0 == strncmp(IPstr, LOOP_IP_STRING, MAX_IP_STR_LEN))) ||
(0 == strncmp(IPstr, szIP, MAX_IP_STR_LEN))) &&
(port == portNumber)) {
cmpResult = true;
}
break;
case AF_INET6:
ipv6addr = (struct sockaddr_in6*)sock_addr;
result = inet_net_ntop(AF_INET6, &(ipv6addr->sin6_addr), 128, IPstr, MAX_IP_STR_LEN - 1);
if (NULL == result) {
ereport(WARNING, (errmsg("inet_net_ntop failed, error: %d", EAFNOSUPPORT)));
}
portNumber = ntohs(ipv6addr->sin6_port);
if ((((0 == strncmp(szIP, LOCAL_HOST, MAX_IP_STR_LEN)) &&
(0 == strncmp(IPstr, LOOP_IPV6_IP, MAX_IP_STR_LEN))) ||
(0 == strncmp(IPstr, szIP, MAX_IP_STR_LEN))) &&
(port == portNumber)) {
cmpResult = true;
}
break;
default:
ereport(LOG, (errmsg("unkown socket address family")));
break;
}
return cmpResult;
}
/*
* According to createmode, create the listen socket
*/
void CreateServerSocket(
char* ipaddr, int portNumber, int enCreatemode, int* success, bool add_localaddr_flag, bool is_create_psql_sock)
{
int status = 0;
int successCount = 0;
Assert(ipaddr != NULL);
Assert(success != NULL);
successCount = *success;
/* if the createmode is NEED_CREATE_TCPIP or NEED_CREATE_UN_TCPIP and
the socket has not been created, then create it */
if (((unsigned int)enCreatemode & NEED_CREATE_TCPIP) && (!IsAlreadyListen(ipaddr, portNumber))) {
ereport(LOG, (errmsg("Create TCP/IP socket [%s:%d]", ipaddr, portNumber)));
if (strcmp(ipaddr, "*") == 0) {
status = StreamServerPort(AF_UNSPEC,
NULL,
(unsigned short)portNumber,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
add_localaddr_flag,
is_create_psql_sock,
false);
} else {
status = StreamServerPort(AF_UNSPEC,
ipaddr,
(unsigned short)portNumber,
g_instance.attr.attr_network.UnixSocketDir,
t_thrd.postmaster_cxt.ListenSocket,
t_thrd.postmaster_cxt.SctpListenSocket,
MAXLISTEN,
add_localaddr_flag,
is_create_psql_sock,
false);
}
if (status == STATUS_OK) {
successCount++;
} else {
ereport(WARNING,
(errmsg("could not create listen socket for \"ipaddr:%s,portNumber:%d\"", ipaddr, portNumber)));
}
} else {
successCount++;
ereport(
LOG, (errmsg("TCP/IP socket [%s:%d] has been created already, no need recreate it", ipaddr, portNumber)));
}
*success = successCount;
}
/*
* parse ReplConnArray1 and ReplConnArray2 into ip-port pair and save it in pListenList
*/
bool ParseHaListenAddr(LISTEN_ADDRS* pListenList)
{
int count = 0, i = 0;
errno_t rc = 0;
if (NULL == pListenList) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter error in ParseHaListenAddr()")));
return false;
}
count = pListenList->usedNum;
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL) {
pListenList->lsnArray[count].portnum = t_thrd.postmaster_cxt.ReplConnArray[i]->localport;
pListenList->lsnArray[count].createmodel = NEED_CREATE_TCPIP;
rc = strncpy_s(pListenList->lsnArray[count].ipaddr,
sizeof(pListenList->lsnArray[count].ipaddr),
t_thrd.postmaster_cxt.ReplConnArray[i]->localhost,
MAX_IPADDR_LEN - 1);
securec_check(rc, "\0", "\0");
pListenList->lsnArray[count].ipaddr[MAX_IPADDR_LEN - 1] = '\0';
count++;
}
}
pListenList->usedNum = count;
return ((count == 0) ? false : true);
}
/*
* according to ReplConnArray, close the socket not in ReplConnArray and create new socket
* if the socket in ReplConnArray is already created, just ignore and step over
*/
static void CreateHaListenSocket(void)
{
LISTEN_ADDRS newListenAddrs;
struct sockaddr_storage sock_addr;
int i = 0, j = 0, success = 0;
socklen_t s_len;
int use_pooler_port = -1;
/* we should not reset pooler port if it is used */
use_pooler_port = NeedPoolerPort();
if (MAX_REPLNODE_NUM == 8)
Assert(t_thrd.postmaster_cxt.ReplConnArray[1] || t_thrd.postmaster_cxt.ReplConnArray[2] ||
t_thrd.postmaster_cxt.ReplConnArray[3] || t_thrd.postmaster_cxt.ReplConnArray[4] ||
t_thrd.postmaster_cxt.ReplConnArray[5] || t_thrd.postmaster_cxt.ReplConnArray[6] ||
t_thrd.postmaster_cxt.ReplConnArray[7]);
else if (MAX_REPLNODE_NUM == 5)
Assert(t_thrd.postmaster_cxt.ReplConnArray[1] || t_thrd.postmaster_cxt.ReplConnArray[2] ||
t_thrd.postmaster_cxt.ReplConnArray[3] || t_thrd.postmaster_cxt.ReplConnArray[4]);
int ss_rc = memset_s(&newListenAddrs, sizeof(newListenAddrs), 0, sizeof(newListenAddrs));
securec_check(ss_rc, "\0", "\0");
/* parse the ReplConnArray into IP-Port pair */
if (!ParseHaListenAddr(&newListenAddrs)) {
ereport(WARNING, (errmsg("\"replconninfo\" has been set null or invalid ")));
}
/*
* if the IP-Port in newListenAddrs has not been created, then create the socket
* if already has been created, just set the createmodel and continue.
*/
for (i = 0; i < MAXLISTEN; i++) {
if (PGINVALID_SOCKET == t_thrd.postmaster_cxt.ListenSocket[i] ||
t_thrd.postmaster_cxt.listen_sock_type[i] != HA_LISTEN_SOCKET) {
continue;
}
s_len = sizeof(sock_addr);
if (getsockname(t_thrd.postmaster_cxt.ListenSocket[i], (struct sockaddr*)&sock_addr, (socklen_t*)&s_len) < 0) {
ereport(LOG, (errmsg("Error in getsockname int IsAlreadyListen()")));
}
success = 0;
/* if the socket in newListenAddrs is already created, just set the createmodel */
for (j = 0; j < newListenAddrs.usedNum; j++) {
if (CheckSockAddr((struct sockaddr*)&sock_addr,
newListenAddrs.lsnArray[j].ipaddr,
newListenAddrs.lsnArray[j].portnum)) {
if (AF_UNIX == ((struct sockaddr*)&sock_addr)->sa_family) {
if (newListenAddrs.lsnArray[j].portnum != g_instance.attr.attr_network.PoolerPort)
ereport(WARNING,
(errmsg("replication socket could not be Unix Domain Socket, "
"something must be wrong of the Ha listen socket")));
} else {
newListenAddrs.lsnArray[j].createmodel &= ~NEED_CREATE_TCPIP;
}
success++;
}
}
/* ha pooler port and ha pooler socket also belongs to HA_LISTEN_SOCKET, no need to close them */
if (!success && IsHASocketAddr((struct sockaddr*)&sock_addr)) {
success++;
}
/* if the socket is not match all the IP-Port pair in newListenAddrs, close it and set listen_sock_type */
if (!success) {
(void)closesocket(t_thrd.postmaster_cxt.ListenSocket[i]);
t_thrd.postmaster_cxt.ListenSocket[i] = PGINVALID_SOCKET;
t_thrd.postmaster_cxt.listen_sock_type[i] = UNUSED_LISTEN_SOCKET;
}
}
/* according to createmodel, create the socket of IP-Port in newListenAddrs */
success = 0;
for (i = 0; i < newListenAddrs.usedNum; i++) {
CreateServerSocket(newListenAddrs.lsnArray[i].ipaddr,
newListenAddrs.lsnArray[i].portnum,
(int)newListenAddrs.lsnArray[i].createmodel,
&success,
false,
false);
}
if (0 == success) {
ereport(LOG, (errmsg("could not create any TCP/IP sockets for HA listen addresses")));
} else {
ereport(LOG, (errmsg("number of valid HA TCP/IP sockets is \"%d\"", success)));
}
return;
}
/*
* move all the non-default value at the head of the array
*/
static void IntArrayRegulation(int array[], int len, int def)
{
int i = 0, j = 0;
int* tmp = NULL;
if (NULL == array || len < 0) {
ereport(WARNING, (errmsg("The parameter error in array regulation")));
return;
}
tmp = (int*)palloc(sizeof(int) * len);
if (NULL == tmp) {
ereport(WARNING, (errmsg("Error palloc in array regulation")));
return;
}
for (i = 0; i != len; ++i) {
tmp[i] = array[i];
array[i] = def;
}
for (i = 0; i != len; ++i) {
if (tmp[i] != def) {
array[j++] = tmp[i];
}
}
pfree(tmp);
}
/*
* adjust the array ListenSocket and listen_sock_type, so that the readmask
* in ServerLoop can be available
*/
static void ListenSocketRegulation(void)
{
IntArrayRegulation((int*)t_thrd.postmaster_cxt.ListenSocket, MAXLISTEN, (int)PGINVALID_SOCKET);
if (SCTP_CN_DN_CONN)
IntArrayRegulation((int*)t_thrd.postmaster_cxt.SctpListenSocket, MAXLISTEN, (int)PGINVALID_SOCKET);
IntArrayRegulation((int*)t_thrd.postmaster_cxt.listen_sock_type, MAXLISTEN, UNUSED_LISTEN_SOCKET);
}
DbState get_local_dbstate(void)
{
volatile WalRcvData* walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
ServerMode mode = t_thrd.postmaster_cxt.HaShmData->current_mode;
DbState db_state = UNKNOWN_STATE;
bool has_build_reason = true;
if (t_thrd.walsender_cxt.WalSndCtl && t_thrd.walsender_cxt.WalSndCtl->demotion > NoDemote)
db_state = DEMOTING_STATE;
else if (walrcv && NODESTATE_STANDBY_WAITING == walrcv->node_state)
db_state = WAITING_STATE;
else if (walrcv && (NODESTATE_STANDBY_PROMOTING == walrcv->node_state ||
NODESTATE_STANDBY_FAILOVER_PROMOTING == walrcv->node_state))
db_state = PROMOTING_STATE;
else {
if ((t_thrd.postmaster_cxt.HaShmData->repl_reason[t_thrd.postmaster_cxt.HaShmData->current_repl] ==
NONE_REBUILD &&
walrcv != NULL && walrcv->isRuning && walrcv->conn_target == REPCONNTARGET_PRIMARY) ||
dummyStandbyMode)
has_build_reason = false;
switch (mode) {
case NORMAL_MODE:
case PRIMARY_MODE:
db_state = NORMAL_STATE;
break;
case PENDING_MODE:
case STANDBY_MODE:
if (has_build_reason)
db_state = NEEDREPAIR_STATE;
else {
/* use keeplive msg from walsender to check if db state is catchup or not.
Also use local info from walreceiver as a supplement. */
if (wal_catchup || data_catchup) {
db_state = CATCHUP_STATE;
} else if (!WalRcvIsOnline()) {
db_state = STARTING_STATE;
} else {
db_state = NORMAL_STATE;
}
}
break;
default:
break;
}
}
return db_state;
}
const char* wal_get_db_state_string(DbState db_state)
{
switch (db_state) {
case NORMAL_STATE:
return "Normal";
case UNKNOWN_STATE:
return "Unknown";
case NEEDREPAIR_STATE:
return "Need repair";
case STARTING_STATE:
return "Starting";
case WAITING_STATE:
return "Wait promoting";
case DEMOTING_STATE:
return "Demoting";
case PROMOTING_STATE:
return "Promoting";
case BUILDING_STATE:
return "Building";
case CATCHUP_STATE:
return "Catchup";
case COREDUMP_STATE:
return "Coredump";
default:
return "Unknown";
}
}
static ServerMode get_cur_mode(void)
{
return t_thrd.postmaster_cxt.HaShmData->current_mode;
}
static int get_cur_repl_num(void)
{
return t_thrd.postmaster_cxt.HaShmData->repl_list_num;
}
static void PMReadDBStateFile(GaussState* state)
{
FILE* statef = NULL;
if (NULL == state) {
ereport(LOG, (errmsg("%s: parameter state is null in PMReadDBStateFile()", progname)));
return;
}
statef = fopen(gaussdb_state_file, "r");
if (NULL == statef) {
ereport(LOG,
(errmsg("%s: open gaussdb state file \"%s\" failed, could "
"not read the build infomation: %m",
progname,
gaussdb_state_file)));
return;
}
if (0 == (fread(state, 1, sizeof(GaussState), statef))) {
ereport(LOG,
(errmsg(
"%s: read gaussdb state infomation from the file \"%s\" failed: %m", progname, gaussdb_state_file)));
}
fclose(statef);
}
/*
* update the hashmemdata infomation in gaussdb state file
*/
static void PMSetDBStateFile(GaussState* state)
{
FILE* statef = NULL;
char temppath[MAXPGPATH] = {0};
/*
* skip update gaussdb state file in bootstrap and dummy mode.
*/
if (IsInitdb || dummyStandbyMode)
return;
Assert(t_thrd.proc_cxt.MyProcPid == PostmasterPid);
if (NULL == state) {
ereport(LOG, (errmsg("%s: parameter state is null in PMSetDBStateFile()", progname)));
return;
}
int rc = snprintf_s(temppath, MAXPGPATH, MAXPGPATH - 1, "%s.temp", gaussdb_state_file);
securec_check_intval(rc, , );
statef = fopen(temppath, "w");
if (NULL == statef) {
ereport(LOG,
(errmsg("%s: open gaussdb state file \"%s\" failed, could not "
"update the build infomation: %m",
progname,
temppath)));
return;
}
if (0 == (fwrite(state, 1, sizeof(GaussState), statef))) {
ereport(
LOG, (errmsg("%s: update gaussdb state infomation from the file \"%s\" failed: %m", progname, temppath)));
}
if (fsync(fileno(statef)) == 0) {
ereport(LOG, (errmsg("%s: fsync file \"%s\" success", progname, temppath)));
} else {
ereport(LOG, (errmsg("%s: fsync file \"%s\" fail", progname, temppath)));
}
fclose(statef);
if (rename(temppath, gaussdb_state_file) != 0)
ereport(LOG, (errmsg("can't rename \"%s\" to \"%s\": %m", temppath, gaussdb_state_file)));
}
/*
* according to input parameters, update the gaussdb state file
*/
static void PMUpdateDBState(DbState db_state, ServerMode mode, int conn_num)
{
GaussState state;
PMReadDBStateFile(&state);
state.state = db_state;
state.mode = mode;
state.conn_num = conn_num;
PMSetDBStateFile(&state);
}
static void PMUpdateDBStateLSN(void)
{
GaussState state;
XLogRecPtr recptr = GetXLogReplayRecPtrInPending();
PMReadDBStateFile(&state);
state.lsn = recptr;
state.term = g_instance.comm_cxt.localinfo_cxt.term;
PMSetDBStateFile(&state);
}
/*
* Update ha build reason to gaussdb.state file, if gs_ctl can not query
* Ha status(e.g: recovery thread hold a system table lock), we can read
* the gaussdb.state file to get it.
*/
static void PMUpdateDBStateHaRebuildReason(void)
{
GaussState state;
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
HaRebuildReason reason = NONE_REBUILD;
SpinLockAcquire(&hashmdata->mutex);
reason = hashmdata->repl_reason[hashmdata->current_repl];
SpinLockRelease(&hashmdata->mutex);
PMReadDBStateFile(&state);
state.ha_rebuild_reason = reason;
if (reason != NONE_REBUILD) {
state.state = NEEDREPAIR_STATE;
}
PMSetDBStateFile(&state);
ereport(LOG,
(errmsg("update gaussdb state file: build reason(%s), "
"db state(%s), server mode(%s)",
wal_get_rebuild_reason_string(reason),
wal_get_db_state_string(state.state),
wal_get_role_string(get_cur_mode()))));
}
static int init_stream_comm()
{
int error, rc;
char local_ip[IP_LEN] = {0};
char* rawstring = NULL;
List* elemlist = NULL;
ListCell* l = NULL;
MemoryContext oldctx = NULL;
g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context,
"CommunnicatorGlobalMemoryContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
oldctx = MemoryContextSwitchTo(g_instance.comm_cxt.comm_global_mem_cxt);
/* Need a modifiable copy of local_bind_address */
rawstring = pstrdup(sctp_link_addr);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawstring, ',', &elemlist)) {
/* syntax error in list */
ereport(
FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"listen_addresses\"")));
}
foreach (l, elemlist) {
char* curhost = (char*)lfirst(l);
if (strcmp(curhost, "*") != 0 && strcmp(curhost, "localhost") != 0 && strcmp(curhost, "0.0.0.0") != 0) {
/* Just use the first ip addr,no support multi-ip now! */
rc = strncpy_s(local_ip, IP_LEN, curhost, strlen(curhost));
securec_check(rc, "\0", "\0");
break;
}
}
if (*local_ip == '\0') {
errno_t ss_rc = strncpy_s(local_ip, IP_LEN, "127.0.0.1", IP_LEN - 1);
securec_check(ss_rc, "\0", "\0");
}
list_free(elemlist);
pfree(rawstring);
gs_init_hash_table();
error = gs_set_basic_info(local_ip,
g_instance.attr.attr_common.PGXCNodeName,
u_sess->attr.attr_network.comm_max_datanode + g_instance.attr.attr_network.MaxCoords,
t_thrd.libpq_cxt.sock_path);
if (error != 0) {
ereport(FATAL, (errmsg("set basic info of sctp failed!")));
}
gs_set_hs_shm_data(t_thrd.postmaster_cxt.HaShmData);
gs_connect_regist_callback(&StreamConsumer::wakeUpConsumerCallBack);
MemoryContextSwitchTo(oldctx);
return error;
}
// pg_logging_comm_status
// Logging status of internal structures of SCTP communication layer.
//
// This function is here so we don't have to export the
// GlobalTransactionData struct definition.
//
Datum pg_log_comm_status(PG_FUNCTION_ARGS)
{
// If this is a datanode and in sctp communication mode, do log status
//
if (IS_PGXC_DATANODE)
gs_log_comm_status();
PG_RETURN_DATUM(0);
}
/*
* Finish inplace or online upgrade and enable new features of the new gaussdb
* binary by switching the grand version num to the newer version num.
*
* Before calling this function, all system catalog changes and other upgrade
* procedures should have been completed. After calling this function, the upgrade
* process are nominally succeeded and usually can not be rollbacked.
*
* In effect, this function only changes the grand version of postmaster thread.
* Any newly started backend will inherit this version. However, for pre-existing
* backends, including those newly started backends caused by pre-existing backends,
* e.g. newly started DN backends to process queries or planes passed down by
* pre-exisitng CN backends, their backend versions are not changed and they still
* adopt pre-upgrade behavior of the old-version gaussdb binary.
*/
Datum set_working_grand_version_num_manually(PG_FUNCTION_ARGS)
{
uint32 tmp_version = 0;
if (u_sess->upg_cxt.InplaceUpgradeSwitch)
tmp_version = PG_GETARG_UINT32(0);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_OPERATION),
errmsg("could not set WorkingGrandVersionNum manually while not performing upgrade")));
if (!tmp_version)
tmp_version = GRAND_VERSION_NUM;
pg_atomic_write_u32(&WorkingGrandVersionNum, tmp_version);
PG_RETURN_VOID();
}
/*
* @Description: check the value from environment variablethe to prevent command injection.
* @in input_env_value : the input value need be checked.
*/
void check_backend_env(const char* input_env_value)
{
#define MAXENVLEN 1024
const char* danger_character_list[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL};
int i = 0;
if (input_env_value == NULL || strlen(input_env_value) >= MAXENVLEN) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("wrong environment variable \"%s\"", input_env_value)));
}
for (i = 0; danger_character_list[i] != NULL; i++) {
if (strstr((const char*)input_env_value, danger_character_list[i])) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Error: environment variable \"%s\" contain invaild symbol \"%s\".\n",
input_env_value,
danger_character_list[i])));
}
}
}
void CleanSystemCaches(bool is_in_read_command)
{
int64 totalsize = 0;
totalsize = ((AllocSet)u_sess->cache_mem_cxt)->totalSpace;
/* Over threshold, need to clean cache. */
if (totalsize > CACHETHRESHOLD) {
ereport(DEBUG1,
(errmsg("CleanSystemCaches due to "
"SystemCache(%ld) greater than (%d),in_read_command(%d).",
totalsize,
CACHETHRESHOLD,
(is_in_read_command ? 1 : 0))));
if (IsTransactionOrTransactionBlock() || !is_in_read_command) {
InvalidateSystemCaches();
}
}
}
static void check_and_reset_ha_listen_port(void)
{
int j;
/*
* when Ha replconninfo have changed and current_mode is not NORMAL,
* dynamically modify the ha socket.
*/
for (j = 1; j < MAX_REPLNODE_NUM; j++) {
if (t_thrd.postmaster_cxt.ReplConnChanged[j])
break;
}
if (j < MAX_REPLNODE_NUM) {
int i, repl_list_num = 0;
CreateHaListenSocket();
repl_list_num = 0;
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
t_thrd.postmaster_cxt.ReplConnChanged[i] = false;
if (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL)
repl_list_num++;
}
/* send SIGTERM to end process senders and receiver */
t_thrd.postmaster_cxt.HaShmData->repl_list_num = repl_list_num;
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_WALSND);
(void)SignalSomeChildren(SIGTERM, BACKEND_TYPE_DATASND);
if (g_instance.pid_cxt.WalRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.WalRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.WalReceiverPID != 0)
signal_child(g_instance.pid_cxt.WalReceiverPID, SIGTERM);
if (g_instance.pid_cxt.DataRcvWriterPID != 0)
signal_child(g_instance.pid_cxt.DataRcvWriterPID, SIGTERM);
if (g_instance.pid_cxt.DataReceiverPID != 0)
signal_child(g_instance.pid_cxt.DataReceiverPID, SIGTERM);
if (g_instance.pid_cxt.RemoteServicePID != 0)
signal_child(g_instance.pid_cxt.RemoteServicePID, SIGTERM);
ListenSocketRegulation();
}
return;
}
bool IsCBMWriterRunning(void)
{
if (g_instance.pid_cxt.CBMWriterPID != 0)
return true;
else
return false;
}
bool PMstateIsRun(void)
{
return PM_RUN == pmState;
}
/* malloc api of cJSON at backend side */
static void* cJSON_internal_malloc(size_t size)
{
return palloc(size);
}
/* free api of cJSON at backend side */
void cJSON_internal_free(void* pointer)
{
pfree(pointer);
}
/*
* Begin shutdown of an auxiliary process. This is approximately the equivalent
* of ShutdownPostgres() in postinit.c. We can't run transactions in an
* auxiliary process, so most of the work of AbortTransaction() is not needed,
* but we do need to make sure we've released any LWLocks we are holding.
* (This is only critical during an error exit.)
*/
static void ShutdownAuxiliaryProcess(int code, Datum arg)
{
LWLockReleaseAll();
/* Clear wait information */
pgstat_report_waitevent(WAIT_EVENT_END);
}
template <knl_thread_role thread_role>
static void SetAuxType()
{
switch (thread_role) {
case PAGEREDO:
t_thrd.bootstrap_cxt.MyAuxProcType = PageRedoProcess;
break;
case TWOPASECLEANER:
t_thrd.bootstrap_cxt.MyAuxProcType = TwoPhaseCleanerProcess;
break;
case FAULTMONITOR:
t_thrd.bootstrap_cxt.MyAuxProcType = FaultMonitorProcess;
break;
case BGWRITER:
t_thrd.bootstrap_cxt.MyAuxProcType = BgWriterProcess;
break;
case CHECKPOINT_THREAD:
t_thrd.bootstrap_cxt.MyAuxProcType = CheckpointerProcess;
break;
case WALWRITER:
t_thrd.bootstrap_cxt.MyAuxProcType = WalWriterProcess;
break;
case WALRECEIVER:
t_thrd.bootstrap_cxt.MyAuxProcType = WalReceiverProcess;
break;
case WALRECWRITE:
t_thrd.bootstrap_cxt.MyAuxProcType = WalRcvWriterProcess;
break;
case DATARECIVER:
t_thrd.bootstrap_cxt.MyAuxProcType = DataReceiverProcess;
break;
case DATARECWRITER:
t_thrd.bootstrap_cxt.MyAuxProcType = DataRcvWriterProcess;
break;
case CBMWRITER:
t_thrd.bootstrap_cxt.MyAuxProcType = CBMWriterProcess;
break;
case RPC_SERVICE:
t_thrd.bootstrap_cxt.MyAuxProcType = RemoteServiceProcess;
break;
case STARTUP:
t_thrd.bootstrap_cxt.MyAuxProcType = StartupProcess;
break;
case PAGEWRITER_THREAD:
t_thrd.bootstrap_cxt.MyAuxProcType = PageWriterProcess;
break;
case THREADPOOL_LISTENER:
t_thrd.bootstrap_cxt.MyAuxProcType = TpoolListenerProcess;
break;
case THREADPOOL_SCHEDULER:
t_thrd.bootstrap_cxt.MyAuxProcType = TpoolSchdulerProcess;
break;
case HEARTBEAT:
t_thrd.bootstrap_cxt.MyAuxProcType = HeartbeatProcess;
break;
default:
ereport(ERROR, (errmsg("unrecorgnized proc type %d", thread_role)));
}
}
void SetStreamWorkerInfo(StreamProducer* proObj)
{
u_sess->stream_cxt.producer_obj = proObj;
u_sess->stream_cxt.smp_id = proObj->getKey().smpIdentifier;
u_sess->stream_cxt.producer_dop = proObj->getParallelDesc().producerDop;
u_sess->debug_query_id = proObj->getKey().queryId;
u_sess->utils_cxt.sync_guc_variables = u_sess->stream_cxt.producer_obj->get_sync_guc_variables();
// set the stopFlag;
u_sess->stream_cxt.global_obj = u_sess->stream_cxt.producer_obj->getNodeGroup();
u_sess->stream_cxt.global_obj->setStopFlagPoint(
u_sess->stream_cxt.producer_obj->getNodeGroupIdx(), &u_sess->exec_cxt.executor_stop_flag);
}
template <knl_thread_role role>
void SetExtraThreadInfo(knl_thread_arg* arg)
{
if (arg->payload == NULL)
return;
switch (role) {
case THREADPOOL_WORKER: {
t_thrd.threadpool_cxt.worker = (ThreadPoolWorker*)arg->payload;
break;
}
case STREAM_WORKER: {
StreamProducer* proObj = (StreamProducer*)arg->payload;
SetStreamWorkerInfo(proObj);
break;
}
case THREADPOOL_LISTENER: {
t_thrd.threadpool_cxt.listener = (ThreadPoolListener*)arg->payload;
break;
}
case THREADPOOL_SCHEDULER:
{
t_thrd.threadpool_cxt.scheduler = (ThreadPoolScheduler*)arg->payload;
break;
}
default:
break;
}
}
void InitProcessAndShareMemory()
{
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitProcess();
CHECK_FOR_PROCDIEPENDING();
/*
* Attach process to shared data structures. If testing EXEC_BACKEND
* on Linux, you must run this as root before starting the postmaster:
*
* echo 0 >/proc/sys/kernel/randomize_va_space
*
* This prevents a randomized stack base address that causes child
* shared memory to be at a different address than the parent, making
* it impossible to attached to shared memory. Return the value to
* '1' when finished.
*/
CreateSharedMemoryAndSemaphores(false, 0);
}
template <knl_thread_role thread_role>
int GaussDbAuxiliaryThreadMain(knl_thread_arg* arg)
{
t_thrd.role = arg->role;
Assert(thread_role == t_thrd.role);
Assert(thread_role == arg->role);
/*
* initialize globals
*/
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
t_thrd.proc_cxt.MyStartTime = time(NULL);
/*
* Initialize random() for the first time, like PostmasterMain() would.
* In a regular IsUnderPostmaster backend, BackendRun() computes a
* high-entropy seed before any user query. Fewer distinct initial seeds
* can occur here.
*/
srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime));
knl_thread_set_name("AuxMain");
/* register thread information to PGPROC structure */
init_ps_display("AuxMain", "", "", "");
/* Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir */
Assert(t_thrd.proc_cxt.DataDir);
ValidatePgVersion(t_thrd.proc_cxt.DataDir);
SetProcessingMode(BootstrapProcessing);
u_sess->attr.attr_common.IgnoreSystemIndexes = true;
BaseInit();
/*
* When we are an auxiliary process, we aren't going to do the full
* InitPostgres pushups, but there are a couple of things that need to get
* lit up even in an auxiliary process.
*/
if (IsUnderPostmaster) {
/*
* Create a PGPROC so we can use LWLocks. In the EXEC_BACKEND case,
* this was already done by SubPostmasterMain().
*/
#ifndef EXEC_BACKEND
InitAuxiliaryProcess();
#endif
/*
* Assign the ProcSignalSlot for an auxiliary process. Since it
* doesn't have a BackendId, the slot is statically allocated based on
* the auxiliary process type (MyAuxProcType). Backends use slots
* indexed in the range from 1 to g_instance.shmem_cxt.MaxBackends (inclusive), so we use
* g_instance.shmem_cxt.MaxBackends + AuxProcType + 1 as the index of the slot for an
* auxiliary process.
*
* This will need rethinking if we ever want more than one of a
* particular auxiliary process type.
*/
int index = g_instance.shmem_cxt.MaxBackends + t_thrd.bootstrap_cxt.MyAuxProcType + 1;
if (thread_role == PAGEWRITER_THREAD) {
index += get_pagewriter_thread_id();
} else if (thread_role == PAGEREDO) {
SetMyPageRedoWorker(arg);
index += MultiRedoGetWorkerId() + g_instance.attr.attr_storage.pagewriter_thread_num - 1;
} else if (thread_role == THREADPOOL_LISTENER) {
index += t_thrd.threadpool_cxt.listener->GetGroup()->GetGroupId() +
(g_instance.attr.attr_storage.pagewriter_thread_num - 1) + (MAX_RECOVERY_THREAD_NUM - 1);
}
ProcSignalInit(index);
/* finish setting up bufmgr.c */
InitBufferPoolBackend();
/* register a shutdown callback for LWLock cleanup */
on_shmem_exit(ShutdownAuxiliaryProcess, 0);
}
pgstat_initialize();
pgstat_bestart();
/*
* XLOG operations
*/
SetProcessingMode(NormalProcessing);
switch (thread_role) {
case TWOPASECLEANER:
TwoPhaseCleanerMain();
proc_exit(1); /* should never return */
break;
case FAULTMONITOR:
FaultMonitorMain();
proc_exit(1);
break;
case STARTUP:
/* don't set signals, startup process has its own agenda */
StartupProcessMain();
proc_exit(1); /* should never return */
break;
case PAGEREDO:
/* don't set signals, pageredo process has its own agenda */
MultiRedoMain();
proc_exit(1); /* should never return */
break;
case BGWRITER:
/* don't set signals, bgwriter has its own agenda */
BackgroundWriterMain();
proc_exit(1); /* should never return */
break;
case CHECKPOINT_THREAD:
/* don't set signals, checkpointer has its own agenda */
CheckpointerMain();
proc_exit(1); /* should never return */
break;
case WALWRITER:
/* don't set signals, walwriter has its own agenda */
//InitXLOGAccess();
WalWriterMain();
proc_exit(1); /* should never return */
break;
case WALRECEIVER:
/* don't set signals, walreceiver has its own agenda */
WalReceiverMain();
proc_exit(1); /* should never return */
break;
case WALRECWRITE:
/* don't set signals, walrcvwriter has its own agenda */
walrcvWriterMain(); /* should never return */
proc_exit(1);
break;
case DATARECIVER:
/* don't set signals, datareceiver has its own agenda */
DataReceiverMain(); /* should never return */
proc_exit(1);
break;
case DATARECWRITER:
/* don't set signals, datarcvwriter has its own agenda */
DataRcvWriterMain(); /* should never return */
proc_exit(1);
break;
case CBMWRITER:
/* don't set signals, cbmwriter has its own agenda */
CBMWriterMain(); /* should never return */
proc_exit(1);
break;
case RPC_SERVICE:
RemoteServiceMain();
proc_exit(1); /* should never return */
break;
case PAGEWRITER_THREAD:
ckpt_pagewriter_main();
proc_exit(1);
break;
case THREADPOOL_LISTENER:
TpoolListenerMain(t_thrd.threadpool_cxt.listener);
proc_exit(1);
break;
case THREADPOOL_SCHEDULER:
TpoolSchedulerMain(t_thrd.threadpool_cxt.scheduler);
proc_exit(1);
break;
case HEARTBEAT:
heartbeat_main();
proc_exit(1);
break;
default:
ereport(PANIC, (errmsg("unrecognized process type: %d", (int)t_thrd.bootstrap_cxt.MyAuxProcType)));
proc_exit(1);
}
return 0;
}
template <knl_thread_role thread_role>
int GaussDbThreadMain(knl_thread_arg* arg)
{
Port port;
char* p_name_thread = NULL;
/* Do this sooner rather than later... */
IsUnderPostmaster = true; /* we are a postmaster subprocess now */
Assert(thread_role == arg->role);
/* Initialize the Memory Protection at the thread level */
gs_memprot_thread_init();
MemoryContextInit();
knl_thread_init(thread_role);
t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0);
t_thrd.fake_session->status = KNL_SESS_FAKE;
u_sess = t_thrd.fake_session;
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); /* reset t_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyStartTime = time(NULL);
t_thrd.role = arg->role;
SetExtraThreadInfo<thread_role>(arg);
/* Lose the postmaster's on-exit routines (really a no-op) */
on_exit_reset();
/* In EXEC_BACKEND case we will not have inherited these settings */
IsPostmasterEnvironment = true;
t_thrd.postgres_cxt.whereToSendOutput = DestNone;
init_plog_global_mem();
SelfMemoryContext = t_thrd.top_mem_cxt;
/* create timer with thread safe */
if (gs_signal_createtimer() < 0) {
ereport(FATAL, (errmsg("create timer fail at thread : %lu", t_thrd.proc_cxt.MyProcPid)));
}
InitializeGUCOptions();
/*
* Set reference point for stack-depth checking
*/
set_stack_base();
gs_signal_block_sigusr2();
gs_signal_startup_siginfo(p_name_thread);
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
PortInitialize(&port, arg);
t_thrd.bn = GetBackend(t_thrd.proc_cxt.MyPMChildSlot);
/* We don't need read GUC variables */
if (!FencedUDFMasterMode) {
/* Read in remaining GUC variables */
read_nondefault_variables();
}
if ((thread_role != WORKER && thread_role != THREADPOOL_WORKER && thread_role != STREAM_WORKER) &&
u_sess->attr.attr_resource.use_workload_manager && g_instance.attr.attr_resource.enable_backend_control &&
g_instance.wlm_cxt->gscgroup_init_done) {
if (thread_role == AUTOVACUUM_WORKER)
(void)gscgroup_attach_backend_task(GSCGROUP_VACUUM, false);
else
(void)gscgroup_attach_backend_task(GSCGROUP_DEFAULT_BACKEND, false);
}
/*
* Reload any libraries that were preloaded by the postmaster. Since we
* exec'd this process, those libraries didn't come along with us; but we
* should load them into all child processes to be consistent with the
* non-EXEC_BACKEND behavior.
*/
process_shared_preload_libraries();
switch (thread_role) {
case STREAM_WORKER: {
// restore child slot;
t_thrd.proc_cxt.MyPMChildSlot = u_sess->stream_cxt.producer_obj->getChildSlot();
#ifdef USE_SSL
if (g_instance.attr.attr_security.EnableSSL)
secure_initialize();
#endif
InitProcessAndShareMemory();
/* And run the backend */
proc_exit(StreamMain(u_sess->stream_cxt.producer_obj));
} break;
case WORKER:
CheckClientIp(&port); /* For THREADPOOL_WORKER check in InitPort */
/*
* Need to reinitialize the SSL library in the backend, since the
* context structures contain function pointers and cannot be passed
* through the parameter file.
*/
#ifdef USE_SSL
if (g_instance.attr.attr_security.EnableSSL) {
secure_initialize();
}
#endif
/* fall through */
case THREADPOOL_WORKER: {
/* Module load callback */
pgaudit_agent_init();
/* unique sql hooks */
instr_unique_sql_register_hook();
/*
* Perform additional initialization and collect startup packet.
*
* We want to do this before InitProcess() for a couple of reasons: 1.
* so that we aren't eating up a PGPROC slot while waiting on the
* client. 2. so that if InitProcess() fails due to being out of
* PGPROC slots, we have already initialized libpq and are able to
* report the error to the client.
*/
BackendInitialize(&port);
InitProcessAndShareMemory();
/* CN has sent a stop query request. For non-stream node,
* we can send sigusr1 to tell the backend thread. sigusr1 can not be used until ProcSignalSlots
* be initialized in CreateSharedMemoryAndSemaphores.
*/
if (u_sess->stream_cxt.stop_mythread) {
t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_EXECUTOR_STOP;
SendProcSignal(u_sess->stream_cxt.stop_pid, PROCSIG_EXECUTOR_FLAG, InvalidBackendId);
u_sess->stream_cxt.stop_mythread = false;
u_sess->stream_cxt.stop_pid = 0;
u_sess->stream_cxt.stop_query_id = 0;
proc_exit(0);
}
CHECK_FOR_PROCDIEPENDING();
/* And run the backend */
proc_exit(BackendRun(&port));
} break;
case PAGEREDO:
case TWOPASECLEANER:
case FAULTMONITOR:
case BGWRITER:
case CHECKPOINT_THREAD:
case WALWRITER:
case WALRECEIVER:
case WALRECWRITE:
case DATARECIVER:
case DATARECWRITER:
case CBMWRITER:
case RPC_SERVICE:
case STARTUP:
case PAGEWRITER_THREAD:
case HEARTBEAT:
case THREADPOOL_LISTENER:
case THREADPOOL_SCHEDULER: {
SetAuxType<thread_role>();
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitAuxiliaryProcess();
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
GaussDbAuxiliaryThreadMain<thread_role>(arg);
proc_exit(0);
} break;
case AUTOVACUUM_LAUNCHER: {
InitProcessAndShareMemory();
AutoVacLauncherMain();
proc_exit(0);
} break;
case AUTOVACUUM_WORKER: {
InitProcessAndShareMemory();
AutoVacWorkerMain();
proc_exit(0);
} break;
case JOB_SCHEDULER: {
InitProcessAndShareMemory();
JobScheduleMain();
proc_exit(0);
} break;
case JOB_WORKER: {
InitProcessAndShareMemory();
JobExecuteWorkerMain();
proc_exit(0);
} break;
case CATCHUP: {
InitProcessAndShareMemory();
CatchupMain();
proc_exit(0);
} break;
case WLM_WORKER: {
t_thrd.role = WLM_WORKER;
// restore child slot;
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
InitProcess();
WLMWorkerInitialize(&port);
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
WLMProcessThreadMain();
proc_exit(0);
} break;
case WLM_MONITOR: {
t_thrd.role = WLM_MONITOR;
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitProcess();
WLMWorkerInitialize(&port);
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
WLMmonitorMain();
proc_exit(0);
} break;
case WLM_ARBITER: {
t_thrd.role = WLM_ARBITER;
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitProcess();
WLMWorkerInitialize(&port);
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
WLMarbiterMain();
proc_exit(0);
} break;
case WLM_CPMONITOR: {
t_thrd.role = WLM_CPMONITOR;
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitProcess();
WLMWorkerInitialize(&port);
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
CPmonitorMain();
proc_exit(0);
} break;
case ARCH: {
/* Do not want to attach to shared memory */
PgArchiverMain();
proc_exit(0);
} break;
case PGSTAT: {
/* Restore basic shared memory pointers */
InitShmemAccess(UsedShmemSegAddr);
/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
InitAuxiliaryProcess();
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
/* Do not want to attach to shared memory */
PgstatCollectorMain();
proc_exit(0);
} break;
case SYSLOGGER: {
/* Do not want to attach to shared memory */
SysLoggerMain(arg->extra_payload.log_thread.syslog_handle);
proc_exit(0);
} break;
case ALARMCHECK: {
AlarmCheckerMain();
proc_exit(0);
} break;
case REAPER: {
/* Do not want to attach to shared memory */
ReaperBackendMain();
proc_exit(0);
} break;
case AUDIT: {
t_thrd.role = AUDIT;
/* Do not want to attach to shared memory */
PgAuditorMain();
proc_exit(0);
} break;
case RPC_WORKER: {
/* Do not init this global variable to prevent illegal access. */
if (u_sess->proc_cxt.MyProcPort != NULL)
u_sess->proc_cxt.MyProcPort = NULL;
t_thrd.postmaster_cxt.IsRPCWorkerThread = true;
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
InitProcessAndShareMemory();
/* Early initialization */
BaseInit();
return 0;
} break;
case SNAPSHOT_WORKER: {
InitShmemAccess(UsedShmemSegAddr);
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
InitProcess();
CreateSharedMemoryAndSemaphores(false, 0);
SnapshotMain();
} break;
case PERCENTILE_WORKER: {
InitShmemAccess(UsedShmemSegAddr);
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
InitProcess();
CreateSharedMemoryAndSemaphores(false, 0);
PercentileMain();
} break;
case COMM_RECEIVER: {
commReceiverMain(arg->payload);
proc_exit(0);
} break;
case COMM_SENDERFLOWER: {
commSenderFlowMain();
proc_exit(0);
} break;
case COMM_RECEIVERFLOWER: {
commReceiverFlowMain();
proc_exit(0);
} break;
case COMM_AUXILIARY: {
commAuxiliaryMain();
proc_exit(0);
} break;
#ifdef ENABLE_MULTIPLE_NODES
case COMM_POOLER_CLEAN: {
InitProcessAndShareMemory();
commPoolCleanerMain();
proc_exit(0);
} break;
#endif
case BACKGROUND_WORKER: {
t_thrd.bgworker_cxt.is_background_worker = true;
t_thrd.bgworker_cxt.worker_shutdown_requested = false;
InitProcessAndShareMemory();
StartBackgroundWorker(arg->payload);
proc_exit(0);
} break;
default:
ereport(PANIC, (errmsg("unsupport thread role type %d", arg->role)));
break;
}
return 1;
}
static GaussdbThreadEntry GaussdbThreadEntryGate[] = {GaussDbThreadMain<MASTER>,
GaussDbThreadMain<WORKER>,
GaussDbThreadMain<THREADPOOL_WORKER>,
GaussDbThreadMain<THREADPOOL_LISTENER>,
GaussDbThreadMain<THREADPOOL_SCHEDULER>,
GaussDbThreadMain<STREAM_WORKER>,
GaussDbThreadMain<AUTOVACUUM_LAUNCHER>,
GaussDbThreadMain<AUTOVACUUM_WORKER>,
GaussDbThreadMain<JOB_SCHEDULER>,
GaussDbThreadMain<JOB_WORKER>,
GaussDbThreadMain<WLM_WORKER>,
GaussDbThreadMain<WLM_MONITOR>,
GaussDbThreadMain<WLM_ARBITER>,
GaussDbThreadMain<WLM_CPMONITOR>,
GaussDbThreadMain<AUDIT>,
GaussDbThreadMain<PGSTAT>,
GaussDbThreadMain<SYSLOGGER>,
GaussDbThreadMain<RPC_WORKER>,
GaussDbThreadMain<RPC_SERVICE>,
GaussDbThreadMain<CATCHUP>,
GaussDbThreadMain<ARCH>,
GaussDbThreadMain<ALARMCHECK>,
GaussDbThreadMain<REAPER>,
GaussDbThreadMain<PAGEREDO>,
GaussDbThreadMain<TWOPASECLEANER>,
GaussDbThreadMain<STARTUP>,
GaussDbThreadMain<FAULTMONITOR>,
GaussDbThreadMain<BGWRITER>,
GaussDbThreadMain<PERCENTILE_WORKER>,
GaussDbThreadMain<SNAPSHOT_WORKER>,
GaussDbThreadMain<CHECKPOINT_THREAD>,
GaussDbThreadMain<WALWRITER>,
GaussDbThreadMain<WALRECEIVER>,
GaussDbThreadMain<WALRECWRITE>,
GaussDbThreadMain<DATARECIVER>,
GaussDbThreadMain<DATARECWRITER>,
GaussDbThreadMain<CBMWRITER>,
GaussDbThreadMain<PAGEWRITER_THREAD>,
GaussDbThreadMain<HEARTBEAT>,
GaussDbThreadMain<COMM_SENDERFLOWER>,
GaussDbThreadMain<COMM_RECEIVERFLOWER>,
GaussDbThreadMain<COMM_RECEIVER>,
GaussDbThreadMain<COMM_AUXILIARY>,
GaussDbThreadMain<COMM_POOLER_CLEAN>,
GaussDbThreadMain<BACKGROUND_WORKER>};
const char* GaussdbThreadName[] = {"main",
"worker",
"thread pool worker",
"thread pool listner",
"stream worker",
"autovacuum launcher",
"autovacuum worker",
"job scheduler",
"job execute worker",
"wlm statistics collector",
"wlm monitor launcher",
"wlm arbiter launcher",
"CPmonitor launcher",
"system auditor",
"statistics collector",
"system logger",
"remote service",
"remote service",
"catchup",
"archiver",
"alarm",
"reaper backend",
"page redo",
"twophase cleaner",
"startup",
"fault monitor",
"background writer",
"statistics collector",
"snapshot",
"checkpointer",
"WAL writer",
"WAL receiver",
"WAL receive writer",
"data receiver",
"data receive writer",
"CBM writer",
"page writer",
"heart beat",
"communicator sender flower",
"communicator receiver flower",
"communicator receiver loop",
"communicator auxiliary",
"communicator pooler auto cleaner",
"background worker"};
GaussdbThreadEntry GetThreadEntry(knl_thread_role role)
{
Assert(role > MASTER && role < THREAD_ENTRY_BOUND);
return GaussdbThreadEntryGate[role];
}
static void* InternalThreadFunc(void* args)
{
knl_thread_arg* thr_argv = (knl_thread_arg*)args;
gs_thread_exit((GetThreadEntry(thr_argv->role))(thr_argv));
return (void*)NULL;
}
ThreadId initialize_thread(ThreadArg* thr_argv)
{
gs_thread_t thread;
if (0 != gs_thread_create(&thread, InternalThreadFunc, 1, (void*)thr_argv)) {
gs_thread_release_args_slot(thr_argv);
return InvalidTid;
}
return gs_thread_id(thread);
}
ThreadId initialize_util_thread(knl_thread_role role, void* payload)
{
ThreadArg* thr_argv = gs_thread_get_args_slot();
if (thr_argv == NULL) {
return 0;
}
thr_argv->m_thd_arg.role = role;
thr_argv->m_thd_arg.payload = payload;
Port port;
ThreadId pid;
errno_t rc;
/* This entry point passes dummy values for the Port variables */
rc = memset_s(&port, sizeof(port), 0, sizeof(port));
securec_check(rc, "\0", "\0");
port.sock = PGINVALID_SOCKET;
port.SessionVersionNum = pg_atomic_read_u32(&WorkingGrandVersionNum);
if (!save_backend_variables((BackendParameters*)(thr_argv->m_thd_arg.save_para), &port)) {
gs_thread_release_args_slot(thr_argv);
return 0; /* log made by save_backend_variables */
}
switch (role) {
case SYSLOGGER: {
if (t_thrd.logger.syslogFile != NULL)
thr_argv->m_thd_arg.extra_payload.log_thread.syslog_handle = fileno(t_thrd.logger.syslogFile);
else
thr_argv->m_thd_arg.extra_payload.log_thread.syslog_handle = -1;
} break;
case STREAM_WORKER: {
((StreamProducer*)payload)->setChildSlot(AssignPostmasterChildSlot());
((StreamProducer*)payload)->setParentSessionid(t_thrd.proc->sessMemorySessionid);
} break;
default:
break;
}
pid = initialize_thread(thr_argv);
if (pid == InvalidTid) {
ereport(LOG, (errmsg("can not fork %s thread: %m", GaussdbThreadName[role])));
/*
* fork failure is fatal during startup, but there's no need to choke
* immediately if starting other child types fails.
*/
if (role == STARTUP)
ExitPostmaster(1);
return 0;
}
return pid;
}
ThreadId initialize_worker_thread(knl_thread_role role, Port* port, void* payload)
{
ThreadArg* thr_argv = gs_thread_get_args_slot();
if (thr_argv == NULL) {
return InvalidTid;
}
thr_argv->m_thd_arg.role = role;
thr_argv->m_thd_arg.payload = payload;
/*
* We initialize the backend version to be the same as
* postmaster, which should be the case at most of the time.
*/
port->SessionVersionNum = pg_atomic_read_u32(&WorkingGrandVersionNum);
if (!save_backend_variables((BackendParameters*)(thr_argv->m_thd_arg.save_para), port)) {
gs_thread_release_args_slot(thr_argv);
return InvalidTid; /* log made by save_backend_variables */
}
return initialize_thread(thr_argv);
}
bool isVaildIp(const char* ip)
{
int dots = 0;
int setions = 0;
if (NULL == ip || *ip == '.') {
return false;
}
while (*ip) {
if (*ip == '.') {
dots++;
if (setions >= 0 && setions <= 255) {
setions = 0;
ip++;
continue;
}
return false;
} else if (*ip >= '0' && *ip <= '9') {
setions = setions * 10 + (*ip - '0');
} else {
return false;
}
ip++;
}
if (setions >= 0 && setions <= 255) {
if (dots == 3) {
return true;
}
}
return false;
}
/*
* set disable_conn_primary, deny connection to this node.
*/
Datum disable_conn(PG_FUNCTION_ARGS)
{
knl_g_disconn_node_context_data disconn_node;
text* arg0 = (text*)PG_GETARG_DATUM(0);
text* arg1 = (text*)PG_GETARG_DATUM(1);
if (arg0 == NULL) {
ereport(
ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmsg("Invalid null pointer attribute for disable_conn()")));
}
if (!superuser()) {
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser account to perform disable_conn()")));
}
char* host;
const char* disconn_mode = TextDatumGetCString(arg0);
ValidateName(disconn_mode);
if (0 == strcmp(disconn_mode, POLLING_CONNECTION_STR)) {
disconn_node.conn_mode = POLLING_CONNECTION;
} else if (0 == strcmp(disconn_mode, SPECIFY_CONNECTION_STR)) {
disconn_node.conn_mode = SPECIFY_CONNECTION;
} else if (0 == strcmp(disconn_mode, PROHIBIT_CONNECTION_STR)) {
disconn_node.conn_mode = PROHIBIT_CONNECTION;
} else {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OPERATION),
errmsg("Connection mode should be polling_connection or specify_connection or prohibit_connection")));
}
/*
* Make sure that all xlog has been redo before locking.
* Sleep 1s is not a good way to check that all xlog has been redo.
*/
if (disconn_node.conn_mode == PROHIBIT_CONNECTION) {
if(!knl_g_get_is_local_redo()) {
g_instance.comm_cxt.localinfo_cxt.need_disable_connection_node = true;
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not add lock when DN is not redo all xlog, redo done flag is false")));
}
XLogRecPtr replay1 = GetXLogReplayRecPtrInPending();
sleep(0.5);
XLogRecPtr replay2 = GetXLogReplayRecPtrInPending();
if (replay1 != replay2) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not add lock when DN is not redo all xlog.")));
}
}
if (disconn_node.conn_mode != SPECIFY_CONNECTION) {
disconn_node.disable_conn_node_host[0] = '\0';
} else {
if (arg1 == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_ATTRIBUTE), errmsg("Invalid null pointer attribute for disable_conn()")));
}
host = TextDatumGetCString(arg1);
ValidateName(host);
if (!isVaildIp(host)) {
ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("host is invalid")));
}
errno_t rc = memcpy_s(disconn_node.disable_conn_node_host, NAMEDATALEN, host, strlen(host) + 1);
securec_check(rc, "\0", "\0");
}
if (disconn_node.conn_mode != SPECIFY_CONNECTION) {
disconn_node.disable_conn_node_port = 0;
} else {
disconn_node.disable_conn_node_port = PG_GETARG_INT32(2);
}
int fd;
fd = BasicOpenFile(disable_conn_file, O_CREAT | O_WRONLY | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not create file \"%s\": %m", disable_conn_file)));
}
/*
* save primary node info on disk.
*/
pgstat_report_waitevent(WAIT_EVENT_DISABLE_CONNECT_FILE_WRITE);
if (write(fd, &disconn_node, sizeof(disconn_node)) != sizeof(disconn_node)) {
pgstat_report_waitevent(WAIT_EVENT_END);
close(fd);
ereport(ERROR, (errcode_for_file_access(), errmsg("could not write to file \"%s\": %m", disable_conn_file)));
}
pgstat_report_waitevent(WAIT_EVENT_END);
pgstat_report_waitevent(WAIT_EVENT_DISABLE_CONNECT_FILE_SYNC);
if (pg_fsync(fd) != 0) {
pgstat_report_waitevent(WAIT_EVENT_END);
close(fd);
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", disable_conn_file)));
}
pgstat_report_waitevent(WAIT_EVENT_END);
close(fd);
SpinLockAcquire(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data = disconn_node;
SpinLockRelease(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
PG_RETURN_VOID();
}
/*
* return disable_conn_primary info.
*/
Datum read_disable_conn_file(PG_FUNCTION_ARGS)
{
TupleDesc tupdesc;
Datum values[6];
bool nulls[6];
HeapTuple tuple;
errno_t rc;
Datum result;
char disconn_node_port_str[NAMEDATALEN];
char* next_key = NULL;
char* key_position = NULL;
char local_host[NAMEDATALEN];
char local_port[NAMEDATALEN];
char local_info[NAMEDATALEN];
const int MAX_LOCAL_ADDRESS_LENGTH = 50;
rc = memset_s(local_info, NAMEDATALEN, 0, NAMEDATALEN);
securec_check(rc, "\0", "\0");
if (t_thrd.postmaster_cxt.ReplConnChanged[1] == false || u_sess->attr.attr_storage.ReplConnInfoArr[1] == NULL) {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("Can't get local connection address.")));
}
rc = memcpy_s(local_info, NAMEDATALEN - 1, u_sess->attr.attr_storage.ReplConnInfoArr[1], MAX_LOCAL_ADDRESS_LENGTH);
securec_check(rc, "\0", "\0");
key_position = strtok_s(local_info, " ", &next_key);
if (key_position == NULL || sscanf_s(key_position, "localhost=%s", local_host, NAMEDATALEN - 1) != 1) {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("get local host failed!")));
}
key_position = strtok_s(NULL, " ", &next_key);
if (key_position == NULL || sscanf_s(key_position, "localport=%s", local_port, NAMEDATALEN - 1) != 1) {
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("get local port failed!")));
}
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type must be a row type")));
SpinLockAcquire(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
switch (g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.conn_mode) {
case POLLING_CONNECTION:
values[0] = CStringGetTextDatum(POLLING_CONNECTION_STR);
break;
case SPECIFY_CONNECTION:
values[0] = CStringGetTextDatum(SPECIFY_CONNECTION_STR);
break;
case PROHIBIT_CONNECTION:
values[0] = CStringGetTextDatum(PROHIBIT_CONNECTION_STR);
break;
}
rc = snprintf_s(disconn_node_port_str,
sizeof(disconn_node_port_str),
sizeof(disconn_node_port_str) - 1,
"%d",
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_port);
securec_check_ss_c(rc, "\0", "\0");
values[1] = CStringGetTextDatum(
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_host);
values[2] = CStringGetTextDatum(disconn_node_port_str);
values[3] = CStringGetTextDatum(local_host);
values[4] = CStringGetTextDatum(local_port);
if (g_instance.comm_cxt.localinfo_cxt.set_term) {
values[5] = CStringGetTextDatum("true");
} else {
values[5] = CStringGetTextDatum("false");
}
SpinLockRelease(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls));
securec_check(rc, "\0", "\0");
tuple = heap_form_tuple(tupdesc, values, nulls);
result = HeapTupleGetDatum(tuple);
PG_RETURN_DATUM(result);
}
void set_disable_conn_mode()
{
int fd = 0;
size_t cnt = 0;
knl_g_disconn_node_context_data* disconn_node =
(knl_g_disconn_node_context_data*)palloc(sizeof(knl_g_disconn_node_context_data));
fd = BasicOpenFile(disable_conn_file, O_RDONLY | PG_BINARY, 0);
/*
* We do not need to handle this as we are rename()ing the directory into
* place only after we fsync()ed the state file.
*/
if (fd >= 0) {
pgstat_report_waitevent(WAIT_EVENT_DISABLE_CONNECT_FILE_SYNC);
if (pg_fsync(fd) != 0) {
pgstat_report_waitevent(WAIT_EVENT_END);
close(fd);
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", disable_conn_file)));
}
pgstat_report_waitevent(WAIT_EVENT_END);
pgstat_report_waitevent(WAIT_EVENT_DISABLE_CONNECT_FILE_READ);
cnt = read(fd, (void*)disconn_node, sizeof(knl_g_disconn_node_context_data));
close(fd);
fd = 0;
if (cnt != sizeof(knl_g_disconn_node_context_data)) {
ereport(ERROR, (0, errmsg("cannot read disable connection file: \"%s\" \n", disable_conn_file)));
}
pgstat_report_waitevent(WAIT_EVENT_END);
SpinLockAcquire(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.conn_mode = disconn_node->conn_mode;
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_port =
disconn_node->disable_conn_node_port;
errno_t rc = memcpy_s(
(void*)g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_host,
NAMEDATALEN,
(void*)disconn_node->disable_conn_node_host,
NAMEDATALEN);
securec_check(rc, "\0", "\0");
SpinLockRelease(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
} else {
SpinLockAcquire(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.conn_mode = POLLING_CONNECTION;
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_host[0] = 0;
g_instance.comm_cxt.localinfo_cxt.disable_conn_node.disable_conn_node_data.disable_conn_node_port = 0;
SpinLockRelease(&g_instance.comm_cxt.localinfo_cxt.disable_conn_node.info_lck);
}
pfree_ext(disconn_node);
return;
}
static bool NeedHeartbeat()
{
if (!(IS_PGXC_DATANODE && g_instance.pid_cxt.HeartbeatPID == 0 &&
(pmState == PM_RUN || pmState == PM_HOT_STANDBY) &&
(t_thrd.postmaster_cxt.HaShmData->current_mode == PRIMARY_MODE ||
t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE)) ||
dummyStandbyMode) {
return false;
}
struct replconninfo* replconn = NULL;
/* at least one replconninfo configures heartbeat port */
for (int i = 1; i < MAX_REPLNODE_NUM; i++) {
replconn = t_thrd.postmaster_cxt.ReplConnArray[i];
if (replconn != NULL && replconn->localheartbeatport != 0 && replconn->remoteheartbeatport != 0) {
return true;
}
}
return false;
}
/* Current mode must be get in this function */
ServerMode GetHaShmemMode(void)
{
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
ServerMode tmpMode;
Assert(t_thrd.postmaster_cxt.HaShmData != NULL);
SpinLockAcquire(&hashmdata->mutex);
tmpMode = hashmdata->current_mode;
SpinLockRelease(&hashmdata->mutex);
return tmpMode;
}
void GenerateCancelKey(bool isThreadPoolSession)
{
if (isThreadPoolSession) {
u_sess->cancel_key = gs_random();
u_sess->cancel_key &= ~0x1;
} else {
/*
* Compute the cancel key that will be assigned to this backend. The
* backend will have its own copy in the forked-off process' value of
* t_thrd.proc_cxt.MyCancelKey, so that it can transmit the key to the frontend.
*/
t_thrd.proc_cxt.MyCancelKey = PostmasterRandom();
t_thrd.proc_cxt.MyCancelKey |= 0x1;
}
}