forked from huawei/openGauss-server
Compare commits
1 Commits
master
...
undorecycl
| Author | SHA1 | Date |
|---|---|---|
|
|
0c4cf14576 |
|
|
@ -72,7 +72,7 @@ select_package_command
|
|||
export PLAT_FORM_STR=$(sh "${ROOT_DIR}/src/get_PlatForm_str.sh")
|
||||
if [ "${PLAT_FORM_STR}"x == "Failed"x -o "${PLAT_FORM_STR}"x == ""x ]
|
||||
then
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
|
|
@ -96,21 +96,16 @@ elif [[ "$PLAT_FORM_STR" =~ "kylin" ]]; then
|
|||
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
|
||||
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
|
||||
fi
|
||||
elif [[ "$PLAT_FORM_STR" =~ "asianux" ]]; then
|
||||
dist_version="Asianux"
|
||||
if [ "$PLATFORM_ARCH"X == "aarch64"X ];then
|
||||
GAUSSDB_EXTRA_FLAGS=" -D__USE_NUMA"
|
||||
fi
|
||||
else
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64), Asianux platform."
|
||||
echo "We only support openEuler(aarch64), EulerOS(aarch64), CentOS, Kylin(aarch64) platform."
|
||||
echo "Kernel is $kernel"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
##add platform architecture information
|
||||
if [ "$PLATFORM_ARCH"X == "aarch64"X ] ; then
|
||||
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] && [ "$dist_version" != "Asianux" ]; then
|
||||
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64), Asianux platform."
|
||||
if [ "$dist_version" != "openEuler" ] && [ "$dist_version" != "EulerOS" ] && [ "$dist_version" != "Kylin" ] ; then
|
||||
echo "We only support NUMA on openEuler(aarch64), EulerOS(aarch64), Kylin(aarch64) platform."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ Complete list of usable sgml source files in this directory.
|
|||
<!ENTITY alterOperator SYSTEM "alter_operator.sgml">
|
||||
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
|
||||
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
|
||||
<!ENTITY alterProcedure SYSTEM "alter_procedure.sgml">
|
||||
<!ENTITY alterRole SYSTEM "alter_role.sgml">
|
||||
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
|
||||
<!ENTITY alterServer SYSTEM "alter_server.sgml">
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
<refentry id="sql-alterprocedure">
|
||||
<indexterm zone="sql-alterprocedure">
|
||||
<primary>ALTER PROCEDURE</primary>
|
||||
</indexterm>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ALTER PROCEDURE</refentrytitle>
|
||||
<manvolnum>7</manvolnum>
|
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>ALTER PROCEDURE</refname>
|
||||
<refpurpose>change the definition of a procedure</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
<replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
RENAME TO <replaceable>new_name</replaceable>
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
|
||||
ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
|
||||
SET SCHEMA <replaceable>new_schema</replaceable>
|
||||
|
||||
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
|
||||
|
||||
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||
SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
|
||||
SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
|
||||
RESET <replaceable class="parameter">configuration_parameter</replaceable>
|
||||
RESET ALL
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
</refentry>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
DROP TABLE [ IF EXISTS ]
|
||||
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ];
|
||||
{[schema.]table_name} [, ...] [ CASCADE | RESTRICT ] [ PURGE ]};
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
</refentry>
|
||||
|
|
@ -27,14 +27,7 @@
|
|||
#include "securec_check.h"
|
||||
#include "cipher.h"
|
||||
#include "crypt.h"
|
||||
/*
|
||||
function name: crypt_malloc_zero
|
||||
description: Distribute internal memory
|
||||
arguments: An integer that designates the size of internal memory distributed
|
||||
return value: A pointer of type void*
|
||||
Note:If the size of internal memory distributed is zero, it's unreasonable. The size should be greater than zero.
|
||||
At the same time, if malloc fails, program would exit.
|
||||
*/
|
||||
|
||||
void* crypt_malloc_zero(size_t size)
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
|
|
|||
|
|
@ -34,14 +34,6 @@
|
|||
static int check_key_num(const char* password);
|
||||
static void create_child_dir(const char* pathdir);
|
||||
|
||||
|
||||
/*
|
||||
function name: check_path
|
||||
description: Check if the string delivered has the character that should not be included
|
||||
arguments: A pointer to string that its type is const char
|
||||
return value: void
|
||||
Note:none
|
||||
*/
|
||||
void check_path(const char *path_name)
|
||||
{
|
||||
const char* danger_character_list[] = {"|",
|
||||
|
|
@ -77,14 +69,6 @@ void check_path(const char *path_name)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function name: check_key_num
|
||||
description: Check if the password is a null string, if so, then the password is invalid.
|
||||
At the same time, the function check if the length of password exceeds MAX_CRYPT_LEN, if so, print the error.
|
||||
arguments: A pointer to string that its type is const char
|
||||
return value: An integer that its type is static int
|
||||
Note:The length of password should not be zero, and never exceeds MAX_CRYPT_LEN
|
||||
*/
|
||||
static int check_key_num(const char* password)
|
||||
{
|
||||
int key_len = 0;
|
||||
|
|
|
|||
|
|
@ -1229,12 +1229,6 @@ parse_next_sync_groups(char **pgroup, char *result)
|
|||
static int
|
||||
transform_az_name(char *config_value, char *allAZString, int allAZStringBufLen, const char *data_dir)
|
||||
{
|
||||
if (strcmp(config_value, "''") == 0) {
|
||||
errno_t rc = strncpy_s(allAZString, allAZStringBufLen, config_value, strlen(config_value));
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char *azString = NULL;
|
||||
char *buf = allAZString;
|
||||
int buflen = allAZStringBufLen;
|
||||
|
|
|
|||
|
|
@ -5923,7 +5923,7 @@ int main(int argc, char** argv)
|
|||
&option_index)) != -1)
|
||||
#endif
|
||||
#else
|
||||
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:C:dqL:T:Q:", long_options,
|
||||
while ((c = getopt_long(argc, argv, "b:cD:e:fi:G:l:m:M:N:o:O:p:P:r:R:v:x:sS:t:u:U:wWZ:dqL:T:Q:", long_options,
|
||||
&option_index)) != -1)
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ char* all_data_nodename_list = NULL;
|
|||
const uint32 USTORE_UPGRADE_VERSION = 92368;
|
||||
const uint32 PACKAGE_ENHANCEMENT = 92444;
|
||||
const uint32 SUBSCRIPTION_VERSION = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
|
||||
#ifdef DUMPSYSLOG
|
||||
char* syslogpath = NULL;
|
||||
|
|
@ -4454,7 +4454,16 @@ void getSubscriptions(Archive *fout)
|
|||
}
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
res = ExecuteSqlQuery(fout,
|
||||
"SELECT count(*) FROM pg_subscription "
|
||||
"WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database"
|
||||
" WHERE datname = current_database())",
|
||||
PGRES_TUPLES_OK);
|
||||
uint64 n = (res != NULL) ? strtoul(PQgetvalue(res, 0, 0), NULL, 10) : 0;
|
||||
if (n > 0) {
|
||||
write_msg(NULL, "WARNING: subscriptions not dumped because current user is not a superuser\n");
|
||||
}
|
||||
PQclear(res);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -10786,11 +10795,6 @@ static void dumpDirectory(Archive* fout)
|
|||
char* dirpath = NULL;
|
||||
char* diracl = NULL;
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: directory not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure we are in proper schema */
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
|
||||
|
|
@ -21400,11 +21404,6 @@ static void dumpSynonym(Archive* fout)
|
|||
PQExpBuffer q;
|
||||
PQExpBuffer delq;
|
||||
|
||||
if (!isExecUserSuperRole(fout)) {
|
||||
write_msg(NULL, "WARNING: synonym not dumped because current user is not a superuser\n");
|
||||
return;
|
||||
}
|
||||
|
||||
selectSourceSchema(fout, "pg_catalog");
|
||||
query = createPQExpBuffer();
|
||||
printfPQExpBuffer(query,
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@
|
|||
it will be backuped up in external dirs */
|
||||
parray *pgdata_nobackup_dir = NULL;
|
||||
|
||||
/* list of logical replication slots */
|
||||
parray *logical_replslot = NULL;
|
||||
|
||||
static int standby_message_timeout_local = 10 ; /* 10 sec = default */
|
||||
static XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;
|
||||
static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;
|
||||
|
|
@ -92,11 +89,10 @@ static void backup_cleanup(bool fatal, void *userdata);
|
|||
|
||||
static void *backup_files(void *arg);
|
||||
|
||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs,
|
||||
bool backup_replslots);
|
||||
static void do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs);
|
||||
|
||||
static void pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots);
|
||||
PGNodeInfo *nodeInfo, PGconn *conn);
|
||||
static void pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, PGNodeInfo *nodeInfo);
|
||||
static int checkpoint_timeout(PGconn *backup_conn);
|
||||
|
||||
|
|
@ -562,7 +558,7 @@ static void sync_files(parray *database_map, const char *database_path, parray *
|
|||
* Move files from 'pgdata' to a subdirectory in 'backup_path'.
|
||||
*/
|
||||
static void
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||
do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool backup_logs)
|
||||
{
|
||||
int i;
|
||||
char database_path[MAXPGPATH];
|
||||
|
|
@ -595,7 +591,7 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
|||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
/* Call pg_start_backup function in openGauss connect */
|
||||
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn, backup_replslots);
|
||||
pg_start_backup(label, smooth_checkpoint, ¤t, nodeInfo, backup_conn);
|
||||
|
||||
/* Obtain current timeline */
|
||||
#if PG_VERSION_NUM >= 90600
|
||||
|
|
@ -628,10 +624,10 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
|||
/* list files with the logical path. omit $PGDATA */
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
fio_list_dir(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, true, 0, backup_replslots);
|
||||
true, true, false, backup_logs, true, 0);
|
||||
else
|
||||
dir_list_file(backup_files_list, instance_config.pgdata,
|
||||
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST, backup_replslots);
|
||||
true, true, false, backup_logs, true, 0, FIO_LOCAL_HOST);
|
||||
|
||||
/*
|
||||
* Get database_map (name to oid) for use in partial restore feature.
|
||||
|
|
@ -753,11 +749,6 @@ do_backup_instance(PGconn *backup_conn, PGNodeInfo *nodeInfo, bool no_sync, bool
|
|||
}
|
||||
pgdata_nobackup_dir = NULL;
|
||||
|
||||
if (logical_replslot) {
|
||||
free_dir_list(logical_replslot);
|
||||
}
|
||||
logical_replslot = NULL;
|
||||
|
||||
/* Cleanup */
|
||||
if (backup_list)
|
||||
{
|
||||
|
|
@ -858,7 +849,7 @@ static void do_after_backup()
|
|||
*/
|
||||
int
|
||||
do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots)
|
||||
bool no_validate, bool no_sync, bool backup_logs)
|
||||
{
|
||||
PGconn *backup_conn = NULL;
|
||||
PGNodeInfo nodeInfo;
|
||||
|
|
@ -934,7 +925,7 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
|||
add_note(¤t, set_backup_params->note);
|
||||
|
||||
/* backup data */
|
||||
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs, backup_replslots);
|
||||
do_backup_instance(backup_conn, &nodeInfo, no_sync, backup_logs);
|
||||
pgut_atexit_pop(backup_cleanup, NULL);
|
||||
|
||||
/* compute size of wal files of this backup stored in the archive */
|
||||
|
|
@ -1043,15 +1034,13 @@ confirm_block_size(PGconn *conn, const char *name, int blcksz)
|
|||
*/
|
||||
static void
|
||||
pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
||||
PGNodeInfo *nodeInfo, PGconn *conn, bool backup_replslots)
|
||||
PGNodeInfo *nodeInfo, PGconn *conn)
|
||||
{
|
||||
PGresult *res;
|
||||
const char *params[2];
|
||||
uint32 lsn_hi;
|
||||
uint32 lsn_lo;
|
||||
int ret;
|
||||
int i;
|
||||
XLogRecPtr startLsn;
|
||||
|
||||
params[0] = label;
|
||||
|
||||
|
|
@ -1079,33 +1068,7 @@ pg_start_backup(const char *label, bool smooth, pgBackup *backup,
|
|||
XLogDataFromLSN(ret, PQgetvalue(res, 0, 0), &lsn_hi, &lsn_lo);
|
||||
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||
/* Calculate LSN */
|
||||
startLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
|
||||
if (backup_replslots) {
|
||||
logical_replslot = parray_new();
|
||||
/* query for logical replication slots of subscriptions */
|
||||
res = pgut_execute(conn,
|
||||
"SELECT slot_name, restart_lsn FROM pg_catalog.pg_get_replication_slots()"
|
||||
"WHERE slot_type = 'logical' AND plugin = 'pgoutput'", 0, NULL);
|
||||
if (PQntuples(res) == 0) {
|
||||
elog(LOG, "logical replication slots for subscriptions not found");
|
||||
} else {
|
||||
XLogRecPtr repslotLsn;
|
||||
|
||||
for (i = 0; i < PQntuples(res); i++) {
|
||||
XLogDataFromLSN(ret, PQgetvalue(res, i, 1), &lsn_hi, &lsn_lo);
|
||||
securec_check_for_sscanf_s(ret, 2, "\0", "\0");
|
||||
repslotLsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
startLsn = Min(startLsn, repslotLsn);
|
||||
|
||||
char* slotname = pg_strdup(PQgetvalue(res, i, 0));
|
||||
parray_append(logical_replslot, slotname);
|
||||
}
|
||||
elog(WARNING, "logical replication slots for subscriptions will be backed up. "
|
||||
"If don't use them after restoring, please drop them to avoid affecting xlog recycling.");
|
||||
}
|
||||
}
|
||||
backup->start_lsn = startLsn;
|
||||
backup->start_lsn = ((uint64) lsn_hi )<< 32 | lsn_lo;
|
||||
|
||||
PQclear(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ const char *pgdata_exclude_dir[] =
|
|||
(const char *)"pg_stat_tmp",
|
||||
(const char *)"pgsql_tmp",
|
||||
|
||||
/*
|
||||
* It is generally not useful to backup the contents of this directory even
|
||||
* if the intention is to restore to another master. See backup.sgml for a
|
||||
* more detailed description.
|
||||
*/
|
||||
(const char *)"pg_replslot",
|
||||
|
||||
/* Contents removed on startup, see dsm_cleanup_for_mmap(). */
|
||||
(const char *)"pg_dynshmem",
|
||||
|
||||
|
|
@ -61,7 +68,7 @@ const char *pgdata_exclude_dir[] =
|
|||
(const char *)"pg_subtrans",
|
||||
|
||||
/* end of list */
|
||||
NULL, /* pg_log and pg_replslot will be set later */
|
||||
NULL, /* pg_log will be set later */
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -121,20 +128,17 @@ may be removed int the future */
|
|||
|
||||
static int pgCompareString(const void *str1, const void *str2);
|
||||
|
||||
static char dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots);
|
||||
static char dir_check_file(pgFile *file, bool backup_logs);
|
||||
static char check_in_tablespace(pgFile *file, bool in_tablespace);
|
||||
static char check_db_dir(pgFile *file);
|
||||
static char check_digit_file(pgFile *file);
|
||||
static char check_nobackup_dir(pgFile *file);
|
||||
static void dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||
bool exclude, bool follow_symlink, bool backup_logs,
|
||||
bool skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots);
|
||||
bool skip_hidden, int external_dir_num, fio_location location);
|
||||
static void opt_path_map(ConfigOption *opt, const char *arg,
|
||||
TablespaceList *list, const char *type);
|
||||
|
||||
char check_logical_replslot_dir(const char *rel_path);
|
||||
|
||||
/* Tablespace mapping */
|
||||
static TablespaceList tablespace_dirs = {NULL, NULL};
|
||||
/* Extra directories mapping */
|
||||
|
|
@ -534,7 +538,7 @@ db_map_entry_free(void *entry)
|
|||
void
|
||||
dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
||||
fio_location location, bool backup_replslots)
|
||||
fio_location location)
|
||||
{
|
||||
pgFile *file;
|
||||
|
||||
|
|
@ -561,7 +565,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
|
|||
parray_append(files, file);
|
||||
|
||||
dir_list_file_internal(files, file, root, exclude, follow_symlink,
|
||||
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||
backup_logs, skip_hidden, external_dir_num, location);
|
||||
|
||||
if (!add_root)
|
||||
pgFileFree(file);
|
||||
|
|
@ -585,7 +589,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool follow_symlink
|
|||
* - datafiles
|
||||
*/
|
||||
static char
|
||||
dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
|
||||
dir_check_file(pgFile *file, bool backup_logs)
|
||||
{
|
||||
int i;
|
||||
int sscanf_res;
|
||||
|
|
@ -648,29 +652,6 @@ dir_check_file(pgFile *file, bool backup_logs, bool backup_replslots)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Backup pg_replslot if it is specified.
|
||||
* It is generally not useful to backup the contents of this directory even
|
||||
* if the intention is to restore to another master. See backup.sgml for a
|
||||
* more detailed description.
|
||||
*/
|
||||
if (!backup_replslots) {
|
||||
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) == 0) {
|
||||
/* Skip */
|
||||
elog(VERBOSE, "Excluding directory content: %s", file->rel_path);
|
||||
return CHECK_EXCLUDE_FALSE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Check file that under pg_replslot and judge whether it
|
||||
* belonged to logical replication slots for subscriptions.
|
||||
*/
|
||||
if (strcmp(file->rel_path, PG_REPLSLOT_DIR) != 0 &&
|
||||
path_is_prefix_of_path(PG_REPLSLOT_DIR, file->rel_path)) {
|
||||
return check_logical_replslot_dir(file->rel_path);
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_nobackup_dir(file);
|
||||
if (ret != -1) { /* -1 means need backup */
|
||||
return ret;
|
||||
|
|
@ -768,35 +749,6 @@ static char check_nobackup_dir(pgFile *file)
|
|||
return ret;
|
||||
}
|
||||
|
||||
char check_logical_replslot_dir(const char *rel_path)
|
||||
{
|
||||
char ret = CHECK_FALSE;
|
||||
int i = 0;
|
||||
char *tmp = pg_strdup(rel_path);
|
||||
char *p;
|
||||
#define DIRECTORY_DELIMITER "/"
|
||||
|
||||
if (logical_replslot) {
|
||||
/* extract slot name from rel_path, such as sub1 from pg_replslot/sub1/snap */
|
||||
p = strtok(tmp, DIRECTORY_DELIMITER);
|
||||
if (p != NULL) {
|
||||
p = strtok(NULL, DIRECTORY_DELIMITER);
|
||||
}
|
||||
|
||||
for (i = 0; p != NULL && i < (int)parray_num(logical_replslot); i++) {
|
||||
char *slotName = (char *)parray_get(logical_replslot, i);
|
||||
if (strcmp(p, slotName) == 0) {
|
||||
pfree(tmp);
|
||||
return CHECK_TRUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = CHECK_TRUE;
|
||||
}
|
||||
pfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char check_db_dir(pgFile *file)
|
||||
{
|
||||
char ret = -1;
|
||||
|
|
@ -937,8 +889,7 @@ bool SkipSomeDirFile(pgFile *file, struct dirent *dent, bool skipHidden)
|
|||
static void
|
||||
dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
||||
bool exclude, bool follow_symlink, bool backup_logs,
|
||||
bool skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots)
|
||||
bool skip_hidden, int external_dir_num, fio_location location)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
|
|
@ -986,7 +937,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
|||
|
||||
if (exclude)
|
||||
{
|
||||
check_res = dir_check_file(file, backup_logs, backup_replslots);
|
||||
check_res = dir_check_file(file, backup_logs);
|
||||
if (check_res == CHECK_FALSE)
|
||||
{
|
||||
/* Skip */
|
||||
|
|
@ -1012,7 +963,7 @@ dir_list_file_internal(parray *files, pgFile *parent, const char *parent_dir,
|
|||
*/
|
||||
if (S_ISDIR(file->mode))
|
||||
dir_list_file_internal(files, file, child, exclude, follow_symlink,
|
||||
backup_logs, skip_hidden, external_dir_num, location, backup_replslots);
|
||||
backup_logs, skip_hidden, external_dir_num, location);
|
||||
}
|
||||
|
||||
if (errno && errno != ENOENT)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ typedef struct
|
|||
bool exclusive_backup;
|
||||
bool skip_hidden;
|
||||
int external_dir_num;
|
||||
bool backup_replslots;
|
||||
} fio_list_dir_request;
|
||||
|
||||
typedef struct
|
||||
|
|
@ -1795,7 +1794,7 @@ cleanup:
|
|||
/* Compile the array of files located on remote machine in directory root */
|
||||
void fio_list_dir(parray *files, const char *root, bool exclude,
|
||||
bool follow_symlink, bool add_root, bool backup_logs,
|
||||
bool skip_hidden, int external_dir_num, bool backup_replslots)
|
||||
bool skip_hidden, int external_dir_num)
|
||||
{
|
||||
fio_header hdr;
|
||||
fio_list_dir_request req;
|
||||
|
|
@ -1812,7 +1811,6 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
|
|||
req.exclusive_backup = exclusive_backup;
|
||||
req.skip_hidden = skip_hidden;
|
||||
req.external_dir_num = external_dir_num;
|
||||
req.backup_replslots = backup_replslots;
|
||||
|
||||
hdr.cop = FIO_LIST_DIR;
|
||||
hdr.size = sizeof(req);
|
||||
|
|
@ -1872,14 +1870,7 @@ void fio_list_dir(parray *files, const char *root, bool exclude,
|
|||
securec_check_ss_c(nRet, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* Check file that under pg_replslot and judge whether it
|
||||
* belonged to logical replication slots for subscriptions.
|
||||
*/
|
||||
if (backup_replslots && strcmp(buf, PG_REPLSLOT_DIR) != 0 &&
|
||||
path_is_prefix_of_path(PG_REPLSLOT_DIR, buf) && check_logical_replslot_dir(file->rel_path) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
parray_append(files, file);
|
||||
}
|
||||
|
|
@ -1923,7 +1914,7 @@ static void fio_list_dir_impl(int out, char* buf)
|
|||
|
||||
dir_list_file(file_files, req->path, req->exclude, req->follow_symlink,
|
||||
req->add_root, req->backup_logs, req->skip_hidden,
|
||||
req->external_dir_num, FIO_LOCAL_HOST, req->backup_replslots);
|
||||
req->external_dir_num, FIO_LOCAL_HOST);
|
||||
|
||||
/* send information about files to the main process */
|
||||
for (i = 0; i < (int)parray_num(file_files); i++)
|
||||
|
|
|
|||
|
|
@ -163,7 +163,5 @@ extern z_off_t fio_gzseek(gzFile f, z_off_t offset, int whence);
|
|||
extern const char* fio_gzerror(gzFile file, int *errnum);
|
||||
#endif
|
||||
|
||||
extern char check_logical_replslot_dir(const char *rel_path);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,6 @@ void help_pg_probackup(void)
|
|||
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
||||
printf(_(" [--remote-libpath=libpath]\n"));
|
||||
printf(_(" [--ttl=interval] [--expire-time=time]\n"));
|
||||
printf(_(" [--backup-pg-replslot]\n"));
|
||||
printf(_(" [--help]\n"));
|
||||
|
||||
printf(_("\n %s restore -B backup-path --instance=instance_name\n"), PROGRAM_NAME);
|
||||
|
|
@ -421,7 +420,6 @@ static void help_backup(void)
|
|||
printf(_(" [--remote-port=port] [--ssh-options=ssh_options]\n"));
|
||||
printf(_(" [--remote-libpath=libpath]\n"));
|
||||
printf(_(" [--ttl=interval] [--expire-time=time]\n\n"));
|
||||
printf(_(" [--backup-pg-replslot]\n"));
|
||||
|
||||
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
|
||||
printf(_(" --instance=instance_name name of the instance\n"));
|
||||
|
|
@ -443,7 +441,6 @@ static void help_backup(void)
|
|||
printf(_(" --note=text add note to backup\n"));
|
||||
printf(_(" (example: --note='backup before app update to v13.1')\n"));
|
||||
printf(_(" --archive-timeout=timeout wait timeout for WAL segment archiving (default: 5min)\n"));
|
||||
printf(_(" --backup-pg-replslot] backup of '%s' directory\n"), PG_REPLSLOT_DIR);
|
||||
|
||||
printf(_("\n Logging options:\n"));
|
||||
printf(_(" --log-level-console=log-level-console\n"));
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ int rw_timeout = 0;
|
|||
|
||||
/* backup options */
|
||||
bool backup_logs = false;
|
||||
bool backup_replslots = false;
|
||||
bool smooth_checkpoint;
|
||||
char *remote_agent;
|
||||
static char *backup_note = NULL;
|
||||
|
|
@ -187,7 +186,6 @@ static ConfigOption cmd_options[] =
|
|||
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
|
||||
{ 'b', 146, "expired", &delete_expired, SOURCE_CMD_STRICT },
|
||||
{ 's', 172, "status", &delete_status, SOURCE_CMD_STRICT },
|
||||
{ 'b', 186, "backup-pg-replslot", &backup_replslots, SOURCE_CMD_STRICT},
|
||||
|
||||
{ 'b', 147, "force", &force, SOURCE_CMD_STRICT },
|
||||
{ 'b', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
|
||||
|
|
@ -552,7 +550,7 @@ static int do_actual_operate()
|
|||
elog(ERROR, "required parameter not specified: BACKUP_MODE "
|
||||
"(-b, --backup-mode)");
|
||||
|
||||
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
|
||||
return do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs);
|
||||
}
|
||||
case RESTORE_CMD:
|
||||
return do_restore_or_validate(current.backup_id,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ extern const char *PROGRAM_FULL_PATH;
|
|||
#define HEADER_MAP "page_header_map"
|
||||
#define HEADER_MAP_TMP "page_header_map_tmp"
|
||||
#define PG_RELATIVE_TBLSPC_DIR "pg_location"
|
||||
#define PG_REPLSLOT_DIR "pg_replslot"
|
||||
|
||||
/* Timeout defaults */
|
||||
#define ARCHIVE_TIMEOUT_DEFAULT 300
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@ extern bool smooth_checkpoint;
|
|||
it will be backuped up in external dirs */
|
||||
extern parray *pgdata_nobackup_dir;
|
||||
|
||||
/* list of logical replication slots */
|
||||
extern parray *logical_replslot;
|
||||
|
||||
/* remote probackup options */
|
||||
extern char* remote_agent;
|
||||
|
||||
|
|
@ -92,7 +89,7 @@ extern const char *pgdata_exclude_dir[];
|
|||
|
||||
/* in backup.c */
|
||||
extern int do_backup(time_t start_time, pgSetBackupParams *set_backup_params,
|
||||
bool no_validate, bool no_sync, bool backup_logs, bool backup_replslots);
|
||||
bool no_validate, bool no_sync, bool backup_logs);
|
||||
extern BackupMode parse_backup_mode(const char *value);
|
||||
extern const char *deparse_backup_mode(BackupMode mode);
|
||||
extern void process_block_change(ForkNumber forknum, const RelFileNode rnode,
|
||||
|
|
@ -242,8 +239,7 @@ extern const char* deparse_compress_alg(int alg);
|
|||
/* in dir.c */
|
||||
extern void dir_list_file(parray *files, const char *root, bool exclude,
|
||||
bool follow_symlink, bool add_root, bool backup_logs,
|
||||
bool skip_hidden, int external_dir_num, fio_location location,
|
||||
bool backup_replslots = false);
|
||||
bool skip_hidden, int external_dir_num, fio_location location);
|
||||
|
||||
extern void create_data_directories(parray *dest_files,
|
||||
const char *data_dir,
|
||||
|
|
@ -436,8 +432,7 @@ extern int fio_send_file(const char *from_fullpath, const char *to_fullpath, FIL
|
|||
pgFile *file, char **errormsg);
|
||||
|
||||
extern void fio_list_dir(parray *files, const char *root, bool exclude, bool follow_symlink,
|
||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num,
|
||||
bool backup_replslots = false);
|
||||
bool add_root, bool backup_logs, bool skip_hidden, int external_dir_num);
|
||||
|
||||
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
||||
|
||||
|
|
|
|||
|
|
@ -8641,7 +8641,7 @@
|
|||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wal_senders", 1,
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(21, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 25), _22(21, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(21, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
AddBuiltinFunc(_0(3099), _1("pg_stat_get_wal_senders"), _2(0), _3(false), _4(true), _5(pg_stat_get_wal_senders), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(10), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(22, 20, 23, 25, 25, 25, 25, 1184, 1184, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 23, 25, 25), _22(22, 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(22, "pid", "sender_pid", "local_role", "peer_role", "peer_state", "state", "catchup_start", "catchup_end", "sender_sent_location", "sender_write_location", "sender_flush_location", "sender_replay_location", "receiver_received_location", "receiver_write_location", "receiver_flush_location", "receiver_replay_location", "sync_percent", "sync_state", "sync_group", "sync_priority", "sync_most_available", "channel"), _24(NULL), _25("pg_stat_get_wal_senders"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("statistics: information about currently active replication"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0))
|
||||
),
|
||||
AddFuncGroup(
|
||||
"pg_stat_get_wlm_ec_operator_info", 1,
|
||||
|
|
|
|||
|
|
@ -6230,7 +6230,7 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple)
|
|||
return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs));
|
||||
}
|
||||
|
||||
Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
|
||||
Oid AddNewIntervalPartition(Relation rel, void* insertTuple)
|
||||
{
|
||||
Relation pgPartRel = NULL;
|
||||
Oid newPartOid = InvalidOid;
|
||||
|
|
@ -6327,13 +6327,7 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL)
|
|||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* If add interval partition in the DDL, do not need to change the csn
|
||||
* because the scn has been changed in the DDL.
|
||||
*/
|
||||
if (!isDDL) {
|
||||
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
|
||||
}
|
||||
UpdatePgObjectChangecsn(RelationGetRelid(rel), rel->rd_rel->relkind);
|
||||
|
||||
return newPartOid;
|
||||
}
|
||||
|
|
@ -7119,7 +7113,7 @@ int lookupHBucketid(oidvector *buckets, int low, int2 bktId)
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
|
||||
Oid heapTupleGetPartitionId(Relation rel, void *tuple)
|
||||
{
|
||||
Oid partitionid = InvalidOid;
|
||||
|
||||
|
|
@ -7146,7 +7140,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL)
|
|||
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("inserted partition key does not map to any table partition")));
|
||||
} break;
|
||||
case PART_AREA_INTERVAL: {
|
||||
return AddNewIntervalPartition(rel, tuple, isDDL);
|
||||
return AddNewIntervalPartition(rel, tuple);
|
||||
} break;
|
||||
case PART_AREA_LIST: {
|
||||
ereport(ERROR,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ static void InternalAggIsSupported(const char *aggName)
|
|||
"json_agg",
|
||||
"json_object_agg",
|
||||
"st_summarystatsagg",
|
||||
"st_union",
|
||||
"wm_concat"
|
||||
"st_union"
|
||||
};
|
||||
|
||||
uint len = lengthof(supportList);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ static_assert(sizeof(false) == sizeof(char), "illegal bool size");
|
|||
static struct HTAB* nameHash = NULL;
|
||||
static struct HTAB* oidHash = NULL;
|
||||
|
||||
/* for dolphin */
|
||||
/* for b_sql_plugin */
|
||||
struct HTAB* b_nameHash = NULL;
|
||||
struct HTAB* b_oidHash = NULL;
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ static const FuncGroup* NameHashTableAccess(HASHACTION action, const char* name,
|
|||
|
||||
Assert(name != NULL);
|
||||
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_nameHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
result = (HashEntryNameToFuncGroup *)hash_search(b_nameHash, &temp_name, action, &found);
|
||||
} else {
|
||||
result = (HashEntryNameToFuncGroup *)hash_search(nameHash, &temp_name, action, &found);
|
||||
|
|
@ -144,7 +144,7 @@ static const Builtin_func* OidHashTableAccess(HASHACTION action, Oid oid, const
|
|||
bool found = false;
|
||||
Assert(oid > 0);
|
||||
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.dolphin) {
|
||||
if (DB_IS_CMPT(B_FORMAT) && b_oidHash != NULL && u_sess->attr.attr_sql.b_sql_plugin) {
|
||||
result = (HashEntryOidToBuiltinFunc *)hash_search(b_oidHash, &oid, action, &found);
|
||||
} else {
|
||||
result = (HashEntryOidToBuiltinFunc *)hash_search(oidHash, &oid, action, &found);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
/* Get slotname */
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subslotname, &isnull);
|
||||
if (!isnull) {
|
||||
if (unlikely(isnull)) {
|
||||
sub->slotname = pstrdup(NameStr(*DatumGetName(datum)));
|
||||
} else {
|
||||
sub->slotname = NULL;
|
||||
|
|
@ -92,10 +92,10 @@ Subscription *GetSubscription(Oid subid, bool missing_ok)
|
|||
|
||||
datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup, Anum_pg_subscription_subbinary, &isnull);
|
||||
if (unlikely(isnull)) {
|
||||
sub->binary = false;
|
||||
} else {
|
||||
sub->binary = DatumGetBool(datum);
|
||||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
||||
errmsg("null binary for subscription %u", subid)));
|
||||
}
|
||||
sub->binary = DatumGetBool(datum);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
|
|
|
|||
|
|
@ -309,7 +309,6 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
|
|||
{
|
||||
size_t passwd_len = strlen(passwd);
|
||||
errno_t rc = EOK;
|
||||
/* the length of salt and password is <= SIZE_MAX */
|
||||
#ifndef WIN32
|
||||
if (unlikely(passwd_len >= SIZE_MAX - salt_len)) {
|
||||
return false;
|
||||
|
|
@ -323,7 +322,6 @@ bool pg_md5_encrypt(const char* passwd, const char* salt, size_t salt_len, char*
|
|||
char* crypt_buf = (char*)malloc(passwd_len + salt_len + 1);
|
||||
bool ret = false;
|
||||
|
||||
/* the buffer is not exist */
|
||||
if (crypt_buf == NULL)
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -772,15 +772,6 @@ bool pg_sha256_encrypt_for_md5(const char* password, const char* salt, size_t sa
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: calculate the encrypted password for GsSm3.
|
||||
* @const char* password : the password need be encrypted.
|
||||
* @const char* salt_s : the content fo the slat.
|
||||
* @size_t salt_len : the length fo the slat.
|
||||
* @char* buf : the buffer to store the encrypted key with GsSm3.
|
||||
* @char* client_key_buf : the buffer to store the key of client.
|
||||
* @int iteration_count : to record the number of the iteration.
|
||||
*/
|
||||
bool GsSm3Encrypt(
|
||||
const char* password, const char* salt_s, size_t salt_len, char* buf, char* client_key_buf, int iteration_count)
|
||||
{
|
||||
|
|
@ -808,7 +799,6 @@ bool GsSm3Encrypt(
|
|||
}
|
||||
|
||||
password_len = strlen(password);
|
||||
/* Tranform string(64Bytes) to binary(32Bytes) */
|
||||
sha_hex_to_bytes32(salt, (char*)salt_s);
|
||||
/* calculate k */
|
||||
pkcs_ret = PKCS5_PBKDF2_HMAC((char*)password,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@
|
|||
|
||||
THR_LOCAL bool skip_read_extern_fields = false;
|
||||
|
||||
#define IS_DATANODE_BUT_NOT_SINGLENODE (IS_PGXC_DATANODE && !IS_SINGLE_NODE)
|
||||
/*
|
||||
* Macros to simplify reading of different kinds of fields. Use these
|
||||
* wherever possible to reduce the chance for silly typos. Note that these
|
||||
|
|
@ -402,27 +401,24 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); /* skip :fldname */ \
|
||||
local_node->fldname = _readBitmapset()
|
||||
|
||||
#define READ_TYPEINFO_FIELD(fldname) \
|
||||
do { \
|
||||
if (local_node->fldname >= FirstBootstrapObjectId) { \
|
||||
IF_EXIST(exprtypename) \
|
||||
{ \
|
||||
char* exprtypename = NULL; \
|
||||
char* exprtypenamespace = NULL; \
|
||||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
exprtypename = nullable_string(token, length); \
|
||||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
exprtypenamespace = nullable_string(token, length); \
|
||||
/* No need to reset field on CN or singlenode, keep pg_strtok() for forward compatibility */ \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
|
||||
} \
|
||||
pfree_ext(exprtypename); \
|
||||
pfree_ext(exprtypenamespace); \
|
||||
} \
|
||||
} \
|
||||
#define READ_TYPEINFO_FIELD(fldname) \
|
||||
do { \
|
||||
if (local_node->fldname >= FirstBootstrapObjectId) { \
|
||||
IF_EXIST(exprtypename) \
|
||||
{ \
|
||||
char* exprtypename = NULL; \
|
||||
char* exprtypenamespace = NULL; \
|
||||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
exprtypename = nullable_string(token, length); \
|
||||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
exprtypenamespace = nullable_string(token, length); \
|
||||
local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \
|
||||
pfree_ext(exprtypename); \
|
||||
pfree_ext(exprtypenamespace); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define READ_TYPEINFO(typePtr) \
|
||||
|
|
@ -497,30 +493,9 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
funcnamespace = nullable_string(token, length); \
|
||||
bool notfound = false; \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { \
|
||||
Oid funcoid = InvalidOid; \
|
||||
do { \
|
||||
Oid nspid = get_namespace_oid(funcnamespace, true); \
|
||||
if (!OidIsValid(nspid)) { \
|
||||
notfound = true; \
|
||||
break; \
|
||||
} \
|
||||
funcoid = get_func_oid(funcname, nspid, (Expr*)local_node); \
|
||||
} while (0); \
|
||||
if (notfound || !OidIsValid(funcoid)) { \
|
||||
ereport(ERROR, \
|
||||
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT), \
|
||||
errmsg("Cannot identify function %s.%s while deserializing field.", \
|
||||
funcname, funcnamespace), \
|
||||
errdetail("Function with oid %u or its namespace may be renamed", \
|
||||
local_node->fldname), \
|
||||
errhint("Please rebuild column defalt expression, views etc. that are" \
|
||||
" related to this renamed object."), \
|
||||
errcause("Object renamed after recorded as nodetree."), \
|
||||
erraction("Rebuild relevant object."))); \
|
||||
} \
|
||||
local_node->fldname = funcoid; \
|
||||
if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \
|
||||
local_node->fldname = \
|
||||
get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \
|
||||
} \
|
||||
pfree_ext(funcname); \
|
||||
pfree_ext(funcnamespace); \
|
||||
|
|
@ -550,7 +525,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
oprrightname = nullable_string(token, length); \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||
if (IS_PGXC_DATANODE) { \
|
||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||
oprright = oprleft; \
|
||||
|
|
@ -593,7 +568,7 @@ THR_LOCAL bool skip_read_extern_fields = false;
|
|||
token = pg_strtok(&length); \
|
||||
token = pg_strtok(&length); \
|
||||
oprrightname = nullable_string(token, length); \
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE) { \
|
||||
if (IS_PGXC_DATANODE) { \
|
||||
namespaceId = get_namespace_oid(opnamespace, false); \
|
||||
oprleft = get_typeoid(namespaceId, oprleftname); \
|
||||
oprright = oprleft; \
|
||||
|
|
@ -2151,21 +2126,14 @@ static FuncExpr* _readFuncExpr(void)
|
|||
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()")));
|
||||
}
|
||||
|
||||
if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) {
|
||||
if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) {
|
||||
Oid seqid = get_valid_relname_relid(seqNamespace, seqName);
|
||||
|
||||
Oid seqid = get_valid_relname_relid(seqNamespace, seqName, true);
|
||||
Const* firstArg = (Const*)linitial(local_node->args);
|
||||
if (OidIsValid(seqid)) {
|
||||
Const* firstArg = (Const*)linitial(local_node->args);
|
||||
if (firstArg != NULL) {
|
||||
firstArg->constvalue = ObjectIdGetDatum(seqid);
|
||||
}
|
||||
} else {
|
||||
ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("Cannot identify sequence %s.%s while deserializing field.", seqNamespace, seqName),
|
||||
errdetail("Sequence with oid %u or its namespace may be renamed",
|
||||
DatumGetObjectId(firstArg->constvalue)),
|
||||
errhint("Please rebuild column defalt expression, views etc. that are related to this sequence"),
|
||||
errcause("Object renamed after recorded as nodetree."), erraction("Rebuild relevant object.")));
|
||||
}
|
||||
}
|
||||
pfree_ext(seqName);
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
analyze.cpp
|
||||
CMakeLists.txt
|
||||
gram.xml
|
||||
gram.y
|
||||
hint_gram.y
|
||||
hint_scan.l
|
||||
keywords.cpp
|
||||
kwlookup.cpp
|
||||
LIST.TXT
|
||||
Makefile
|
||||
parser.cpp
|
||||
parse_agg.cpp
|
||||
parse_clause.cpp
|
||||
parse_coerce.cpp
|
||||
parse_collate.cpp
|
||||
parse_compatibility.cpp
|
||||
parse_cte.cpp
|
||||
parse_expr.cpp
|
||||
parse_func.cpp
|
||||
parse_hint.cpp
|
||||
parse_merge.cpp
|
||||
parse_node.cpp
|
||||
parse_oper.cpp
|
||||
parse_param.cpp
|
||||
parse_relation.cpp
|
||||
parse_startwith.cpp
|
||||
parse_target.cpp
|
||||
parse_type.cpp
|
||||
parse_utilcmd.cpp
|
||||
README
|
||||
scan.l
|
||||
scansup.cpp
|
||||
新建文本文档.bat
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1454,14 +1454,7 @@ FuncCandidateList sort_candidate_func_list(FuncCandidateList oldCandidates)
|
|||
}
|
||||
candidates[smallestIndex] = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (candidates[i] != NULL) {
|
||||
lastCandidate->next = candidates[i];
|
||||
lastCandidate = lastCandidate->next;
|
||||
}
|
||||
}
|
||||
lastCandidate->next = NULL;
|
||||
|
||||
pfree(candidates);
|
||||
return sortedCandidates;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
DIR *.* /B >LIST.TXT
|
||||
|
|
@ -61,7 +61,7 @@ void GlobalBaseDefCache::RemoveElemFromBucket(GlobalBaseEntry *base)
|
|||
if (is_relation) {
|
||||
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
||||
uint64 rel_size = GetRelEstimateSize(entry);
|
||||
pg_atomic_fetch_sub_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||
pg_atomic_fetch_sub_u64(&m_base_space, rel_size);
|
||||
m_db_entry->MemoryEstimateSub(rel_size);
|
||||
} else {
|
||||
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
||||
|
|
@ -77,7 +77,7 @@ void GlobalBaseDefCache::AddHeadToBucket(Index hash_index, GlobalBaseEntry *base
|
|||
if (is_relation) {
|
||||
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
||||
uint64 rel_size = GetRelEstimateSize(entry);
|
||||
pg_atomic_fetch_add_u64(&m_base_space, AllocSetContextUsedSpace(((AllocSet)entry->rel_mem_manager)));
|
||||
pg_atomic_fetch_add_u64(&m_base_space, rel_size);
|
||||
m_db_entry->MemoryEstimateAdd(rel_size);
|
||||
} else {
|
||||
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
||||
|
|
@ -400,4 +400,4 @@ GlobalBaseDefCache::GlobalBaseDefCache(Oid db_oid, bool is_shared, GlobalSysDBCa
|
|||
m_base_space = 0;
|
||||
m_obj_locks = NULL;
|
||||
m_db_entry = entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -659,27 +659,7 @@ void GlobalSysDBCache::InitSysCacheRelIds()
|
|||
*/
|
||||
void GlobalSysDBCache::RefreshHotStandby()
|
||||
{
|
||||
if (!EnableGlobalSysCache()) {
|
||||
return;
|
||||
}
|
||||
hot_standby = (t_thrd.postmaster_cxt.HaShmData->current_mode != STANDBY_MODE || XLogStandbyInfoActive());
|
||||
if (hot_standby || !m_is_inited) {
|
||||
return;
|
||||
}
|
||||
/* clean all */
|
||||
for (int hash_index = 0; hash_index < m_nbuckets; hash_index ++) {
|
||||
PthreadRWlockRdlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
|
||||
for (Dlelem * elt = DLGetTail(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
|
||||
GlobalSysDBCacheEntry *entry = (GlobalSysDBCacheEntry *)DLE_VAL(elt);
|
||||
elt = DLGetPred(elt);
|
||||
entry->ResetDBCache<true>();
|
||||
}
|
||||
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, &m_db_locks[hash_index]);
|
||||
}
|
||||
if (m_global_shared_db_entry != NULL) {
|
||||
m_global_shared_db_entry->ResetDBCache<true>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GlobalSysDBCache::Init(MemoryContext parent)
|
||||
|
|
@ -1292,18 +1272,9 @@ int ResizeHashBucket(int origin_nbucket, DynamicHashBucketStrategy strategy)
|
|||
return cc_nbuckets;
|
||||
}
|
||||
|
||||
void NotifyGscRecoveryStarted()
|
||||
{
|
||||
if (!EnableGlobalSysCache()) {
|
||||
return;
|
||||
}
|
||||
g_instance.global_sysdbcache.recovery_finished = false;
|
||||
|
||||
}
|
||||
|
||||
void NotifyGscRecoveryFinished()
|
||||
{
|
||||
if (EnableGlobalSysCache()) {
|
||||
g_instance.global_sysdbcache.recovery_finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ void GlobalSysTabCache::InvalidTuples(int cache_id, uint32 hash_value, bool rese
|
|||
|
||||
/* maybe upgrade from version before v5r2c00, the cacheid is out of order
|
||||
* whatever, we cache nothing except relmap, so just ignore the catcache invalmsg */
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished && m_global_systupcaches[cache_id] == NULL)) {
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished) && m_global_systupcaches[cache_id] == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Partition LocalPartDefCache::SearchPartitionFromGlobalCopy(Oid part_oid)
|
|||
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||
return NULL;
|
||||
}
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
return NULL;
|
||||
}
|
||||
uint32 hash_value = oid_hash((void *)&(part_oid), sizeof(Oid));
|
||||
|
|
@ -165,7 +165,7 @@ static bool IsPartOidStoreInGlobal(Oid part_oid)
|
|||
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||
return false;
|
||||
}
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
return false;
|
||||
}
|
||||
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
||||
|
|
@ -456,4 +456,4 @@ Partition LocalPartDefCache::PartitionIdGetPartition(Oid part_oid, StorageType s
|
|||
}
|
||||
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
|
|
@ -433,7 +433,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobal(Datum *arguments, uint32 h
|
|||
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||
m_global_systupcache->enable_rls ||
|
||||
!g_instance.global_sysdbcache.hot_standby ||
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
||||
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
||||
global_ct = m_global_systupcache->SearchTupleFromFile(hash_value, arguments, true);
|
||||
} else {
|
||||
|
|
@ -585,7 +585,7 @@ LocalCatCList *LocalSysTupCache::SearchListFromGlobal(int nkeys, Datum *argument
|
|||
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||
m_global_systupcache->enable_rls ||
|
||||
!g_instance.global_sysdbcache.hot_standby ||
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
||||
GlobalCatCList *global_cl;
|
||||
if (invalid_entries.ExistList() || bypass_gsc) {
|
||||
global_cl = m_global_systupcache->SearchListFromFile(hash_value, nkeys, arguments, true);
|
||||
|
|
@ -703,7 +703,7 @@ LocalCatCTup *LocalSysTupCache::SearchTupleFromGlobalForProcAllArgs(
|
|||
bool bypass_gsc = HistoricSnapshotActive() ||
|
||||
m_global_systupcache->enable_rls ||
|
||||
!g_instance.global_sysdbcache.hot_standby ||
|
||||
unlikely(!IsPrimaryRecoveryFinished());
|
||||
unlikely(!g_instance.global_sysdbcache.recovery_finished);
|
||||
if (invalid_entries.ExistTuple(hash_value) || bypass_gsc) {
|
||||
global_ct = m_global_systupcache->SearchTupleFromFileWithArgModes(hash_value, arguments, argModes, true);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ Relation LocalTabDefCache::SearchRelationFromGlobalCopy(Oid rel_oid)
|
|||
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||
return NULL;
|
||||
}
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
return NULL;
|
||||
}
|
||||
uint32 hash_value = oid_hash((void *)&(rel_oid), sizeof(Oid));
|
||||
|
|
@ -190,7 +190,7 @@ static bool IsRelOidStoreInGlobal(Oid rel_oid)
|
|||
if (!g_instance.global_sysdbcache.hot_standby) {
|
||||
return false;
|
||||
}
|
||||
if (unlikely(!IsPrimaryRecoveryFinished())) {
|
||||
if (unlikely(!g_instance.global_sysdbcache.recovery_finished)) {
|
||||
return false;
|
||||
}
|
||||
if (g_instance.global_sysdbcache.StopInsertGSC()) {
|
||||
|
|
@ -1137,4 +1137,4 @@ void LocalTabDefCache::ResetInitFlag()
|
|||
m_is_inited_phase3 = false;
|
||||
|
||||
m_db_id = InvalidOid;
|
||||
}
|
||||
}
|
||||
|
|
@ -1723,7 +1723,7 @@ char* get_relname_relid_extend(
|
|||
extern bool StreamTopConsumerAmI();
|
||||
|
||||
/* same as get_relname_relid except we check for cache invalidation here */
|
||||
Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok)
|
||||
Oid get_valid_relname_relid(const char* relnamespace, const char* relname)
|
||||
{
|
||||
Oid nspid = InvalidOid;
|
||||
Oid oldnspid = InvalidOid;
|
||||
|
|
@ -1747,10 +1747,7 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool
|
|||
if (EnableLocalSysCache()) {
|
||||
thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter;
|
||||
}
|
||||
nspid = get_namespace_oid(relnamespace, nsp_missing_ok);
|
||||
if (!OidIsValid(nspid)) {
|
||||
return InvalidOid;
|
||||
}
|
||||
nspid = get_namespace_oid(relnamespace, false);
|
||||
relid = get_relname_relid(relname, nspid);
|
||||
/*
|
||||
* In bootstrap processing mode, we don't bother with locking
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ bool open_join_children = true;
|
|||
bool will_shutdown = false;
|
||||
|
||||
/* hard-wired binary version number */
|
||||
const uint32 GRAND_VERSION_NUM = 92606;
|
||||
const uint32 GRAND_VERSION_NUM = 92607;
|
||||
|
||||
const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522;
|
||||
const uint32 UPSERT_WHERE_VERSION_NUM = 92514;
|
||||
|
|
@ -101,7 +101,7 @@ const uint32 PRIVS_DIRECTORY_VERSION_NUM = 92460;
|
|||
const uint32 COMMENT_RECORD_PARAM_VERSION_NUM = 92484;
|
||||
const uint32 SCAN_BATCH_MODE_VERSION_NUM = 92568;
|
||||
const uint32 PUBLICATION_VERSION_NUM = 92580;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92606;
|
||||
const uint32 SUBSCRIPTION_BINARY_VERSION_NUM = 92607;
|
||||
|
||||
/* Version number of the guc parameter backend_version added in V500R001C20 */
|
||||
const uint32 V5R1C20_BACKEND_VERSION_NUM = 92305;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
#include "tsdb/compaction/compaction_entry.h"
|
||||
#endif /* ENABLE_MULTIPLE_NODES */
|
||||
#endif /* ENABLE_MULTIPLE_NODES */
|
||||
#include "access/ustore/knl_undoworker.h"
|
||||
|
||||
#define DIRECTORY_LOCK_FILE "postmaster.pid"
|
||||
|
|
@ -84,31 +84,47 @@ Alarm alarmItemTooManyDbUserConn[1] = {ALM_AI_Unknown, ALM_AS_Normal, 0, 0, 0, 0
|
|||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void ReportAlarmTooManyDbUserConn(const char *roleName)
|
||||
void ReportAlarmTooManyDbUserConn(const char* roleName)
|
||||
{
|
||||
AlarmAdditionalParam tempAdditionalParam;
|
||||
|
||||
// Initialize the alarm item
|
||||
AlarmItemInitialize(alarmItemTooManyDbUserConn, ALM_AI_TooManyDbUserConn, alarmItemTooManyDbUserConn->stat, NULL,
|
||||
alarmItemTooManyDbUserConn->lastReportTime, alarmItemTooManyDbUserConn->reportCount);
|
||||
AlarmItemInitialize(alarmItemTooManyDbUserConn,
|
||||
ALM_AI_TooManyDbUserConn,
|
||||
alarmItemTooManyDbUserConn->stat,
|
||||
NULL,
|
||||
alarmItemTooManyDbUserConn->lastReportTime,
|
||||
alarmItemTooManyDbUserConn->reportCount);
|
||||
// fill the alarm message
|
||||
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "AllDatabases",
|
||||
const_cast<char *>(roleName), alarmItemTooManyDbUserConn, ALM_AT_Fault,
|
||||
const_cast<char *>(roleName));
|
||||
WriteAlarmAdditionalInfo(&tempAdditionalParam,
|
||||
g_instance.attr.attr_common.PGXCNodeName,
|
||||
"AllDatabases",
|
||||
const_cast<char*>(roleName),
|
||||
alarmItemTooManyDbUserConn,
|
||||
ALM_AT_Fault,
|
||||
const_cast<char*>(roleName));
|
||||
// report the alarm
|
||||
AlarmReporter(alarmItemTooManyDbUserConn, ALM_AT_Fault, &tempAdditionalParam);
|
||||
}
|
||||
|
||||
void ReportResumeTooManyDbUserConn(const char *roleName)
|
||||
void ReportResumeTooManyDbUserConn(const char* roleName)
|
||||
{
|
||||
AlarmAdditionalParam tempAdditionalParam;
|
||||
|
||||
// Initialize the alarm item
|
||||
AlarmItemInitialize(alarmItemTooManyDbUserConn, ALM_AI_TooManyDbUserConn, alarmItemTooManyDbUserConn->stat, NULL,
|
||||
alarmItemTooManyDbUserConn->lastReportTime, alarmItemTooManyDbUserConn->reportCount);
|
||||
AlarmItemInitialize(alarmItemTooManyDbUserConn,
|
||||
ALM_AI_TooManyDbUserConn,
|
||||
alarmItemTooManyDbUserConn->stat,
|
||||
NULL,
|
||||
alarmItemTooManyDbUserConn->lastReportTime,
|
||||
alarmItemTooManyDbUserConn->reportCount);
|
||||
// fill the resume message
|
||||
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "AllDatabases",
|
||||
const_cast<char *>(roleName), alarmItemTooManyDbUserConn, ALM_AT_Resume);
|
||||
WriteAlarmAdditionalInfo(&tempAdditionalParam,
|
||||
g_instance.attr.attr_common.PGXCNodeName,
|
||||
"AllDatabases",
|
||||
const_cast<char*>(roleName),
|
||||
alarmItemTooManyDbUserConn,
|
||||
ALM_AT_Resume);
|
||||
// report the alarm
|
||||
AlarmReporter(alarmItemTooManyDbUserConn, ALM_AT_Resume, &tempAdditionalParam);
|
||||
}
|
||||
|
|
@ -121,8 +137,13 @@ void ReportAlarmDataInstLockFileExist()
|
|||
// Initialize the alarm item
|
||||
AlarmItemInitialize(alarmItem, ALM_AI_DataInstLockFileExist, 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);
|
||||
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);
|
||||
}
|
||||
|
|
@ -135,8 +156,8 @@ void ReportResumeDataInstLockFileExist()
|
|||
// Initialize the alarm item
|
||||
AlarmItemInitialize(alarmItem, ALM_AI_DataInstLockFileExist, ALM_AS_Normal, NULL);
|
||||
// fill the alarm message
|
||||
WriteAlarmAdditionalInfo(&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem,
|
||||
ALM_AT_Resume);
|
||||
WriteAlarmAdditionalInfo(
|
||||
&tempAdditionalParam, g_instance.attr.attr_common.PGXCNodeName, "", "", alarmItem, ALM_AT_Resume);
|
||||
// report the alarm
|
||||
AlarmReporter(alarmItem, ALM_AT_Resume, &tempAdditionalParam);
|
||||
}
|
||||
|
|
@ -146,28 +167,29 @@ void ReportResumeDataInstLockFileExist()
|
|||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void SetDatabasePath(const char *path)
|
||||
void SetDatabasePath(const char* path)
|
||||
{
|
||||
/* This should happen only once per process */
|
||||
Assert(!u_sess->proc_cxt.DatabasePath);
|
||||
u_sess->proc_cxt.DatabasePath = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), path);
|
||||
u_sess->proc_cxt.DatabasePath =
|
||||
MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set data directory, but make sure it's an absolute path. Use this,
|
||||
* never set t_thrd.proc_cxt.DataDir directly.
|
||||
*/
|
||||
void SetDataDir(const char *dir)
|
||||
void SetDataDir(const char* dir)
|
||||
{
|
||||
AssertArg(dir);
|
||||
|
||||
/* If presented path is relative, convert to absolute */
|
||||
char *newm = make_absolute_path(dir);
|
||||
char* newm = make_absolute_path(dir);
|
||||
char real_newm[PATH_MAX + 1] = {'\0'};
|
||||
char *DataDir = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), MAXPGPATH);
|
||||
char* DataDir = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), MAXPGPATH);
|
||||
|
||||
if (realpath(newm, real_newm) == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), errmsg("invalid path:%s", dir)));
|
||||
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED),errmsg("invalid path:%s", dir)));
|
||||
}
|
||||
errno_t rc = strncpy_s(DataDir, MAXPGPATH, real_newm, MAXPGPATH - 1);
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
|
@ -195,8 +217,8 @@ void ChangeToDataDir(void)
|
|||
AssertState(t_thrd.proc_cxt.DataDir);
|
||||
|
||||
if (chdir(t_thrd.proc_cxt.DataDir) < 0)
|
||||
ereport(FATAL, (errcode_for_file_access(),
|
||||
errmsg("could not change directory to \"%s\": %m", t_thrd.proc_cxt.DataDir)));
|
||||
ereport(FATAL,
|
||||
(errcode_for_file_access(), errmsg("could not change directory to \"%s\": %m", t_thrd.proc_cxt.DataDir)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -209,9 +231,9 @@ void ChangeToDataDir(void)
|
|||
* should happen before doing ChangeToDataDir(), else the user will probably
|
||||
* not like the results.
|
||||
*/
|
||||
char *make_absolute_path(const char *path)
|
||||
char* make_absolute_path(const char* path)
|
||||
{
|
||||
char *newm = NULL;
|
||||
char* newm = NULL;
|
||||
size_t tmplen;
|
||||
|
||||
/* Returning null for null input is convenient for some callers */
|
||||
|
|
@ -220,16 +242,16 @@ char *make_absolute_path(const char *path)
|
|||
}
|
||||
|
||||
if (!is_absolute_path(path)) {
|
||||
char *buf = NULL;
|
||||
char* buf = NULL;
|
||||
size_t buflen;
|
||||
|
||||
buflen = MAXPGPATH;
|
||||
|
||||
for (;;) {
|
||||
#ifdef FRONTEND
|
||||
buf = (char *)malloc(buflen);
|
||||
buf = (char*)malloc(buflen);
|
||||
#else
|
||||
buf = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), buflen);
|
||||
buf = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), buflen);
|
||||
#endif
|
||||
|
||||
if (buf == NULL)
|
||||
|
|
@ -237,7 +259,8 @@ char *make_absolute_path(const char *path)
|
|||
|
||||
if (getcwd(buf, buflen) != NULL) {
|
||||
break;
|
||||
} else if (errno == ERANGE) {
|
||||
}
|
||||
else if (errno == ERANGE) {
|
||||
#ifdef FRONTEND
|
||||
free(buf);
|
||||
#else
|
||||
|
|
@ -257,9 +280,9 @@ char *make_absolute_path(const char *path)
|
|||
|
||||
tmplen = strlen(buf) + strlen(path) + 2;
|
||||
#ifdef FRONTEND
|
||||
newm = (char *)malloc(tmplen);
|
||||
newm = (char*)malloc(tmplen);
|
||||
#else
|
||||
newm = (char *)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), tmplen);
|
||||
newm = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), tmplen);
|
||||
#endif
|
||||
|
||||
if (newm == NULL)
|
||||
|
|
@ -298,16 +321,12 @@ Oid GetAuthenticatedUserId(void)
|
|||
*
|
||||
* Note: there's no SetUserId() anymore; use SetUserIdAndSecContext().
|
||||
*/
|
||||
/*
|
||||
*功能: 这个函数用于获取当前会话的用户标识符(Oid),以便在数据库操作中标识当前用户的身份。
|
||||
*/
|
||||
Oid GetUserId(void)
|
||||
{
|
||||
// 检查当前用户标识符是否有效
|
||||
if (!OidIsValid(u_sess->misc_cxt.CurrentUserId)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Current user id is invalid. Please try later.")));
|
||||
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("Current user id is invalid. Please try later.")));
|
||||
}
|
||||
// 返回当前用户标识符
|
||||
return u_sess->misc_cxt.CurrentUserId;
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +438,7 @@ bool exist_logic_cluster()
|
|||
* show_nodegroup_mode - return node group mode as sting.
|
||||
* The function is only used in guc.cpp.
|
||||
*/
|
||||
const char *show_nodegroup_mode(void)
|
||||
const char* show_nodegroup_mode(void)
|
||||
{
|
||||
modify_nodegroup_mode();
|
||||
|
||||
|
|
@ -465,7 +484,7 @@ const int GetCustomParserId()
|
|||
* get_current_lcgroup_name - get current logic group name.
|
||||
* The function return NULL in datanode because datanode don't see pgxc_group.
|
||||
*/
|
||||
const char *get_current_lcgroup_name()
|
||||
const char* get_current_lcgroup_name()
|
||||
{
|
||||
if (IS_PGXC_COORDINATOR && u_sess->attr.attr_common.current_logic_cluster_name == NULL &&
|
||||
OidIsValid(u_sess->misc_cxt.current_logic_cluster) && t_thrd.proc_cxt.postgres_initialized) {
|
||||
|
|
@ -474,8 +493,8 @@ const char *get_current_lcgroup_name()
|
|||
|
||||
if (HeapTupleIsValid(groupTup)) {
|
||||
rform = (Form_pgxc_group)GETSTRUCT(groupTup);
|
||||
u_sess->attr.attr_common.current_logic_cluster_name =
|
||||
MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), NameStr(rform->group_name));
|
||||
u_sess->attr.attr_common.current_logic_cluster_name = MemoryContextStrdup(
|
||||
SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), NameStr(rform->group_name));
|
||||
ReleaseSysCache(groupTup);
|
||||
}
|
||||
}
|
||||
|
|
@ -502,9 +521,9 @@ static void set_current_lcgroup_oid(Oid group_oid)
|
|||
* show_show_lcgroup_name - show current logic group name.
|
||||
* The function is only used in guc.cpp.
|
||||
*/
|
||||
const char *show_lcgroup_name()
|
||||
const char* show_lcgroup_name()
|
||||
{
|
||||
const char *name = get_current_lcgroup_name();
|
||||
const char* name = get_current_lcgroup_name();
|
||||
return (name == NULL) ? "" : name;
|
||||
}
|
||||
|
||||
|
|
@ -595,7 +614,7 @@ Oid get_pgxc_logic_groupoid(Oid roleid)
|
|||
* Obtain PGXC Logic Group Oid for rolename
|
||||
* Return Invalid Oid if group does not exist
|
||||
*/
|
||||
Oid get_pgxc_logic_groupoid(const char *rolename)
|
||||
Oid get_pgxc_logic_groupoid(const char* rolename)
|
||||
{
|
||||
bool isNull = false;
|
||||
Datum aclDatum;
|
||||
|
|
@ -674,7 +693,7 @@ static void RegisterNodeGroupCacheCallback()
|
|||
* and perhaps restored is indeed invalid. We have to be able to get
|
||||
* through AbortTransaction without asserting in case InitPostgres fails.
|
||||
*/
|
||||
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
|
||||
void GetUserIdAndSecContext(Oid* userid, int* sec_context)
|
||||
{
|
||||
*userid = u_sess->misc_cxt.CurrentUserId;
|
||||
*sec_context = u_sess->misc_cxt.SecurityRestrictionContext;
|
||||
|
|
@ -708,7 +727,7 @@ bool InSecurityRestrictedOperation(void)
|
|||
* pljava. We allow the userid to be set, but only when not inside a
|
||||
* security restriction context.
|
||||
*/
|
||||
void GetUserIdAndContext(Oid *userid, bool *sec_def_context)
|
||||
void GetUserIdAndContext(Oid* userid, bool* sec_def_context)
|
||||
{
|
||||
*userid = u_sess->misc_cxt.CurrentUserId;
|
||||
*sec_def_context = InLocalUserIdChange();
|
||||
|
|
@ -718,8 +737,9 @@ void SetUserIdAndContext(Oid userid, bool sec_def_context)
|
|||
{
|
||||
/* We throw the same error SET ROLE would. */
|
||||
if (InSecurityRestrictedOperation())
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("cannot set parameter \"%s\" within security-restricted operation", "role")));
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("cannot set parameter \"%s\" within security-restricted operation", "role")));
|
||||
|
||||
u_sess->misc_cxt.CurrentUserId = userid;
|
||||
|
||||
|
|
@ -780,7 +800,7 @@ static void DecreaseUserCountReuse(Oid roleid, bool ispoolerreuse)
|
|||
/*
|
||||
* Initialize user identity during normal backend startup
|
||||
*/
|
||||
void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid useroid)
|
||||
void InitializeSessionUserId(const char* rolename, bool ispoolerreuse, Oid useroid)
|
||||
{
|
||||
HeapTuple roleTup;
|
||||
Form_pg_authid rform;
|
||||
|
|
@ -793,7 +813,8 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
* exist yet, and they should be owned by openGauss anyway.
|
||||
*/
|
||||
if (IsBootstrapProcessingMode()) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("IsBootstrapProcessingMode")));
|
||||
ereport(
|
||||
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("IsBootstrapProcessingMode")));
|
||||
}
|
||||
|
||||
/* In pooler stateless reuse mode, to reset session userid */
|
||||
|
|
@ -803,10 +824,10 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
if (!isUserOidInvalid) {
|
||||
AssertState(false);
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("Abnormal process. UserOid has been reseted. Current userOid[%u], reset username is %s,"
|
||||
"useroid is %u",
|
||||
u_sess->misc_cxt.AuthenticatedUserId, rolename, useroid)));
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("Abnormal process. UserOid has been reseted. Current userOid[%u], reset username is %s,"
|
||||
"useroid is %u",
|
||||
u_sess->misc_cxt.AuthenticatedUserId, rolename, useroid)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -829,7 +850,7 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR));
|
||||
if (u_sess->proc_cxt.MyProcPort->user_name)
|
||||
pfree_ext(u_sess->proc_cxt.MyProcPort->user_name);
|
||||
u_sess->proc_cxt.MyProcPort->user_name = pstrdup((char *)GetSuperUserName((char *)userName));
|
||||
u_sess->proc_cxt.MyProcPort->user_name = pstrdup((char*)GetSuperUserName((char*)userName));
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
rolename = u_sess->proc_cxt.MyProcPort->user_name;
|
||||
}
|
||||
|
|
@ -842,20 +863,23 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
securec_check_ss(rc, "", "");
|
||||
rolename = roleIdStr;
|
||||
}
|
||||
/*
|
||||
* Audit user login
|
||||
* it's unsafe to deal with plugins hooks as dynamic lib may be released
|
||||
/*
|
||||
* Audit user login
|
||||
* it's unsafe to deal with plugins hooks as dynamic lib may be released
|
||||
*/
|
||||
if (!(g_instance.status > NoShutdown) && user_login_hook) {
|
||||
user_login_hook(u_sess->proc_cxt.MyProcPort->database_name, rolename, false, true);
|
||||
}
|
||||
int rcs = snprintf_truncated_s(details, sizeof(details), "login db(%s) failed-the role(%s)does not exist",
|
||||
u_sess->proc_cxt.MyProcPort->database_name, rolename);
|
||||
int rcs = snprintf_truncated_s(details,
|
||||
sizeof(details),
|
||||
"login db(%s) failed-the role(%s)does not exist",
|
||||
u_sess->proc_cxt.MyProcPort->database_name,
|
||||
rolename);
|
||||
securec_check_ss(rcs, "\0", "\0");
|
||||
pgaudit_user_login(FALSE, u_sess->proc_cxt.MyProcPort->database_name, details);
|
||||
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("Invalid username/password,login denied.")));
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("Invalid username/password,login denied.")));
|
||||
}
|
||||
|
||||
rform = (Form_pg_authid)GETSTRUCT(roleTup);
|
||||
|
|
@ -901,8 +925,9 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
}
|
||||
|
||||
if (!rform->rolcanlogin)
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("role \"%s\" is not permitted to login", rolename)));
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("role \"%s\" is not permitted to login", rolename)));
|
||||
|
||||
/*
|
||||
* Check connection limit for this role.
|
||||
|
|
@ -918,7 +943,7 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
CountUserBackends(roleid) > rform->rolconnlimit) {
|
||||
ReportAlarmTooManyDbUserConn(rolename);
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for role \"%s\"", rolename)));
|
||||
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for role \"%s\"", rolename)));
|
||||
} else if (!u_sess->misc_cxt.AuthenticatedUserIsSuperuser) {
|
||||
ReportResumeTooManyDbUserConn(rolename);
|
||||
}
|
||||
|
|
@ -926,8 +951,8 @@ void InitializeSessionUserId(const char *rolename, bool ispoolerreuse, Oid usero
|
|||
|
||||
/* Record username and superuser status as GUC settings too */
|
||||
SetConfigOption("session_authorization", rolename, PGC_BACKEND, PGC_S_OVERRIDE);
|
||||
SetConfigOption("is_sysadmin", u_sess->misc_cxt.AuthenticatedUserIsSuperuser ? "on" : "off", PGC_INTERNAL,
|
||||
PGC_S_OVERRIDE);
|
||||
SetConfigOption(
|
||||
"is_sysadmin", u_sess->misc_cxt.AuthenticatedUserIsSuperuser ? "on" : "off", PGC_INTERNAL, PGC_S_OVERRIDE);
|
||||
|
||||
ReleaseSysCache(roleTup);
|
||||
}
|
||||
|
|
@ -943,15 +968,14 @@ void InitializeSessionUserIdStandalone(void)
|
|||
*/
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsJobSchedulerProcess() || IsJobWorkerProcess() ||
|
||||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
|
||||
CompactionProcess::IsTsCompactionProcess() || IsRbCleanerProcess() || IsRbWorkerProcess() ||
|
||||
t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
|
||||
#else /* ENABLE_MULTIPLE_NODES */
|
||||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
|
||||
CompactionProcess::IsTsCompactionProcess() || IsRbCleanerProcess() || IsRbWorkerProcess() ||
|
||||
t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
|
||||
#else /* ENABLE_MULTIPLE_NODES */
|
||||
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsJobSchedulerProcess() || IsJobWorkerProcess() ||
|
||||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() ||
|
||||
IsRbCleanerProcess() || IsRbWorkerProcess() || t_thrd.role == PARALLEL_DECODE ||
|
||||
t_thrd.role == LOGICAL_READ_RECORD);
|
||||
#endif /* ENABLE_MULTIPLE_NODES */
|
||||
AM_WAL_SENDER || IsTxnSnapCapturerProcess() || IsTxnSnapWorkerProcess() || IsUndoWorkerProcess() || IsRbCleanerProcess() ||
|
||||
IsRbWorkerProcess() || t_thrd.role == PARALLEL_DECODE || t_thrd.role == LOGICAL_READ_RECORD);
|
||||
#endif /* ENABLE_MULTIPLE_NODES */
|
||||
|
||||
/* In pooler stateless reuse mode, to reset session userid */
|
||||
if (!ENABLE_STATELESS_REUSE) {
|
||||
|
|
@ -987,8 +1011,8 @@ void SetSessionAuthorization(Oid userid, bool is_superuser)
|
|||
|
||||
if (!t_thrd.xact_cxt.bInAbortTransaction && userid != u_sess->misc_cxt.AuthenticatedUserId &&
|
||||
!u_sess->misc_cxt.AuthenticatedUserIsSuperuser && !superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set session authorization")));
|
||||
ereport(
|
||||
ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set session authorization")));
|
||||
|
||||
SetSessionUserId(userid, is_superuser);
|
||||
|
||||
|
|
@ -1053,10 +1077,10 @@ void SetCurrentRoleId(Oid roleid, bool is_superuser)
|
|||
/*
|
||||
* Get user name from user oid
|
||||
*/
|
||||
char *GetUserNameFromId(Oid roleid)
|
||||
char* GetUserNameFromId(Oid roleid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
char *result = NULL;
|
||||
char* result = NULL;
|
||||
|
||||
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
|
||||
|
||||
|
|
@ -1069,10 +1093,10 @@ char *GetUserNameFromId(Oid roleid)
|
|||
return result;
|
||||
}
|
||||
|
||||
char *GetUserNameById(Oid roleid)
|
||||
char* GetUserNameById(Oid roleid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
char *result = NULL;
|
||||
char* result = NULL;
|
||||
|
||||
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
|
||||
|
||||
|
|
@ -1085,6 +1109,7 @@ char *GetUserNameById(Oid roleid)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Interlock-file support
|
||||
*
|
||||
|
|
@ -1105,7 +1130,7 @@ char *GetUserNameById(Oid roleid)
|
|||
*/
|
||||
static void UnlinkLockFile(int status, Datum filename)
|
||||
{
|
||||
char *fname = (char *)DatumGetPointer(filename);
|
||||
char* fname = (char*)DatumGetPointer(filename);
|
||||
|
||||
if (fname != NULL) {
|
||||
if (unlink(fname) != 0) {
|
||||
|
|
@ -1129,7 +1154,7 @@ static void UnLockPidLockFile(int status, Datum fileDes)
|
|||
}
|
||||
}
|
||||
|
||||
static void CreatePidLockFile(const char *filename)
|
||||
static void CreatePidLockFile(const char* filename)
|
||||
{
|
||||
int fd = -1;
|
||||
char pid_lock_file[MAXPGPATH] = {0};
|
||||
|
|
@ -1137,8 +1162,7 @@ static void CreatePidLockFile(const char *filename)
|
|||
securec_check_ss(rc, "", "");
|
||||
|
||||
if ((fd = open(pid_lock_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
|
||||
ereport(FATAL,
|
||||
(errcode_for_file_access(), errmsg("could not create or open lock file \"%s\": %m", pid_lock_file)));
|
||||
ereport(FATAL, (errcode_for_file_access(), errmsg("could not create or open lock file \"%s\": %m", pid_lock_file)));
|
||||
}
|
||||
|
||||
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
|
||||
|
|
@ -1156,7 +1180,7 @@ static void CreatePidLockFile(const char *filename)
|
|||
* amPostmaster is used to determine how to encode the output PID.
|
||||
* isDDLock and refName are used to determine what error message to produce.
|
||||
*/
|
||||
static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLock, const char *refName)
|
||||
static void CreateLockFile(const char* filename, bool amPostmaster, bool isDDLock, const char* refName)
|
||||
{
|
||||
int fd = -1;
|
||||
char buffer[MAXPGPATH * 2 + 256];
|
||||
|
|
@ -1165,7 +1189,7 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
int encoded_pid;
|
||||
pid_t other_pid;
|
||||
pid_t my_pid, my_p_pid, my_gp_pid;
|
||||
const char *envvar = NULL;
|
||||
const char* envvar = NULL;
|
||||
|
||||
/* Grab a file lock to establish our priority to process postmaster.pid */
|
||||
if (isDDLock) {
|
||||
|
|
@ -1266,15 +1290,18 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
if (lstat(filename, &filenameStat) >= 0) {
|
||||
if (0 == filenameStat.st_size) {
|
||||
if (remove(filename) < 0)
|
||||
ereport(FATAL, (errcode_for_file_access(),
|
||||
errmsg("bogus lock file \"%s\",could not unlink it : %m", filename)));
|
||||
ereport(FATAL,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("bogus lock file \"%s\",could not unlink it : %m", filename)));
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ereport(FATAL, (errmsg("bogus data in lock file \"%s\": \"%s\", please kill the "
|
||||
"instance process, than remove the damaged lock file",
|
||||
filename, buffer)));
|
||||
ereport(FATAL,
|
||||
(errmsg("bogus data in lock file \"%s\": \"%s\", please kill the "
|
||||
"instance process, than remove the damaged lock file",
|
||||
filename,
|
||||
buffer)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1311,20 +1338,24 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
{
|
||||
ReportAlarmDataInstLockFileExist();
|
||||
|
||||
ereport(
|
||||
FATAL,
|
||||
(errcode(ERRCODE_LOCK_FILE_EXISTS), errmsg("lock file \"%s\" already exists", filename),
|
||||
isDDLock
|
||||
? ((encoded_pid < 0)
|
||||
? errhint("Is another openGauss (PID %d) running in data directory \"%s\"?",
|
||||
(int)other_pid, refName)
|
||||
: errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
|
||||
(int)other_pid, refName))
|
||||
: ((encoded_pid < 0) ? errhint("Is another openGauss (PID %d) \
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_LOCK_FILE_EXISTS),
|
||||
errmsg("lock file \"%s\" already exists", filename),
|
||||
isDDLock
|
||||
? ((encoded_pid < 0)
|
||||
? errhint("Is another openGauss (PID %d) running in data directory \"%s\"?",
|
||||
(int)other_pid,
|
||||
refName)
|
||||
: errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
|
||||
(int)other_pid,
|
||||
refName))
|
||||
: ((encoded_pid < 0) ? errhint("Is another openGauss (PID %d) \
|
||||
using socket file \"%s\"?",
|
||||
(int)other_pid, refName)
|
||||
: errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
|
||||
(int)other_pid, refName))));
|
||||
(int)other_pid,
|
||||
refName)
|
||||
: errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
|
||||
(int)other_pid,
|
||||
refName))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1341,7 +1372,7 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
* error.
|
||||
*/
|
||||
if (isDDLock != false) {
|
||||
char *ptr = buffer;
|
||||
char* ptr = buffer;
|
||||
unsigned long id1, id2;
|
||||
int lineno;
|
||||
|
||||
|
|
@ -1354,15 +1385,17 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
|
||||
if (ptr != NULL && sscanf_s(ptr, "%lu %lu", &id1, &id2) == 2) {
|
||||
if (PGSharedMemoryIsInUse(id1, id2)) {
|
||||
ereport(FATAL, (errcode(ERRCODE_LOCK_FILE_EXISTS),
|
||||
errmsg("pre-existing shared memory block "
|
||||
"(key %lu, ID %lu) is still in use",
|
||||
id1, id2),
|
||||
errhint("If you're sure there are no old "
|
||||
"server processes still running, remove "
|
||||
"the shared memory block "
|
||||
"or just delete the file \"%s\".",
|
||||
filename)));
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_LOCK_FILE_EXISTS),
|
||||
errmsg("pre-existing shared memory block "
|
||||
"(key %lu, ID %lu) is still in use",
|
||||
id1,
|
||||
id2),
|
||||
errhint("If you're sure there are no old "
|
||||
"server processes still running, remove "
|
||||
"the shared memory block "
|
||||
"or just delete the file \"%s\".",
|
||||
filename)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1373,10 +1406,12 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
* would-be creators.
|
||||
*/
|
||||
if (unlink(filename) < 0)
|
||||
ereport(FATAL, (errcode_for_file_access(), errmsg("could not remove old lock file \"%s\": %m", filename),
|
||||
errhint("The file seems accidentally left over, but "
|
||||
"it could not be removed. Please remove the file "
|
||||
"by hand and try again.")));
|
||||
ereport(FATAL,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not remove old lock file \"%s\": %m", filename),
|
||||
errhint("The file seems accidentally left over, but "
|
||||
"it could not be removed. Please remove the file "
|
||||
"by hand and try again.")));
|
||||
}
|
||||
|
||||
ReportResumeDataInstLockFileExist();
|
||||
|
|
@ -1387,8 +1422,8 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
* both datadir and socket lockfiles; although more stuff may get added to
|
||||
* the datadir lockfile later.
|
||||
*/
|
||||
char *unixSocketDir = NULL;
|
||||
char *pghost = gs_getenv_r("PGHOST");
|
||||
char* unixSocketDir = NULL;
|
||||
char* pghost = gs_getenv_r("PGHOST");
|
||||
if (pghost != NULL) {
|
||||
check_backend_env(pghost);
|
||||
}
|
||||
|
|
@ -1403,13 +1438,18 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
}
|
||||
}
|
||||
|
||||
int rc = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d\n%s\n%ld\n%d\n%s\n",
|
||||
amPostmaster ? (int)my_pid : -((int)my_pid), t_thrd.proc_cxt.DataDir,
|
||||
(long)t_thrd.proc_cxt.MyStartTime, g_instance.attr.attr_network.PostPortNumber,
|
||||
int rc = snprintf_s(buffer,
|
||||
sizeof(buffer),
|
||||
sizeof(buffer) - 1,
|
||||
"%d\n%s\n%ld\n%d\n%s\n",
|
||||
amPostmaster ? (int)my_pid : -((int)my_pid),
|
||||
t_thrd.proc_cxt.DataDir,
|
||||
(long)t_thrd.proc_cxt.MyStartTime,
|
||||
g_instance.attr.attr_network.PostPortNumber,
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
unixSocketDir
|
||||
unixSocketDir
|
||||
#else
|
||||
""
|
||||
""
|
||||
#endif
|
||||
);
|
||||
|
||||
|
|
@ -1427,13 +1467,13 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
pgstat_report_waitevent(WAIT_EVENT_LOCK_FILE_CREATE_WRITE);
|
||||
if (strlen(buffer) > 0) {
|
||||
if ((unsigned int)(write(fd, buffer, strlen(buffer))) != strlen(buffer)) {
|
||||
int save_errno = errno;
|
||||
int save_errno = errno;
|
||||
|
||||
close(fd);
|
||||
(void)unlink(filename);
|
||||
/* if write didn't set errno, assume problem is no disk space */
|
||||
errno = save_errno ? save_errno : ENOSPC;
|
||||
ereport(FATAL, (errcode_for_file_access(), errmsg("could not write lock file \"%s\": %m", filename)));
|
||||
close(fd);
|
||||
(void)unlink(filename);
|
||||
/* if write didn't set errno, assume problem is no disk space */
|
||||
errno = save_errno ? save_errno : ENOSPC;
|
||||
ereport(FATAL, (errcode_for_file_access(), errmsg("could not write lock file \"%s\": %m", filename)));
|
||||
}
|
||||
}
|
||||
pgstat_report_waitevent(WAIT_EVENT_END);
|
||||
|
|
@ -1461,7 +1501,7 @@ static void CreateLockFile(const char *filename, bool amPostmaster, bool isDDLoc
|
|||
* Arrange for automatic removal of lockfile at proc_exit.
|
||||
*/
|
||||
{
|
||||
char *ptr = NULL;
|
||||
char* ptr = NULL;
|
||||
ptr = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), filename);
|
||||
on_proc_exit(UnlinkLockFile, PointerGetDatum(ptr));
|
||||
}
|
||||
|
|
@ -1482,7 +1522,7 @@ void CreateDataDirLockFile(bool amPostmaster)
|
|||
/*
|
||||
* Create a lockfile for the specified Unix socket file.
|
||||
*/
|
||||
void CreateSocketLockFile(const char *socketfile, bool amPostmaster, bool is_create_psql_sock)
|
||||
void CreateSocketLockFile(const char* socketfile, bool amPostmaster, bool is_create_psql_sock)
|
||||
{
|
||||
char lockfile[MAXPGPATH];
|
||||
|
||||
|
|
@ -1492,7 +1532,8 @@ void CreateSocketLockFile(const char *socketfile, bool amPostmaster, bool is_cre
|
|||
CreateLockFile(lockfile, amPostmaster, false, socketfile);
|
||||
/* Save name of lockfile for TouchSocketLockFile */
|
||||
errno_t rcs = strcpy_s((is_create_psql_sock ? u_sess->misc_cxt.socketLockFile : u_sess->misc_cxt.hasocketLockFile),
|
||||
MAXPGPATH, lockfile);
|
||||
MAXPGPATH,
|
||||
lockfile);
|
||||
securec_check_c(rcs, "\0", "\0");
|
||||
}
|
||||
|
||||
|
|
@ -1504,7 +1545,7 @@ void CreateSocketLockFile(const char *socketfile, bool amPostmaster, bool is_cre
|
|||
* from being removed by overenthusiastic /tmp-directory-cleaner daemons.
|
||||
* (Another reason we should never have put the socket file in /tmp...)
|
||||
*/
|
||||
void TouchSocketLockFileInternel(const char *socketLockFile)
|
||||
void TouchSocketLockFileInternel(const char* socketLockFile)
|
||||
{
|
||||
/* Do nothing if we did not create a socket... */
|
||||
if (socketLockFile[0] != '\0') {
|
||||
|
|
@ -1548,12 +1589,12 @@ void TouchSocketLockFile(void)
|
|||
* Caution: this erases all following lines. In current usage that is OK
|
||||
* because lines are added in order. We could improve it if needed.
|
||||
*/
|
||||
void AddToDataDirLockFile(int target_line, const char *str)
|
||||
void AddToDataDirLockFile(int target_line, const char* str)
|
||||
{
|
||||
int fd = -1;
|
||||
int len;
|
||||
int lineno;
|
||||
char *ptr = NULL;
|
||||
char* ptr = NULL;
|
||||
char buffer[BLCKSZ];
|
||||
|
||||
fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
|
||||
|
|
@ -1582,8 +1623,11 @@ void AddToDataDirLockFile(int target_line, const char *str)
|
|||
|
||||
for (lineno = 1; lineno < target_line; lineno++) {
|
||||
if ((ptr = strchr(ptr, '\n')) == NULL) {
|
||||
ereport(LOG, (errmsg("incomplete data in \"%s\": found only %d newlines while trying to add line %d",
|
||||
DIRECTORY_LOCK_FILE, lineno - 1, target_line)));
|
||||
ereport(LOG,
|
||||
(errmsg("incomplete data in \"%s\": found only %d newlines while trying to add line %d",
|
||||
DIRECTORY_LOCK_FILE,
|
||||
lineno - 1,
|
||||
target_line)));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1639,15 +1683,15 @@ void AddToDataDirLockFile(int target_line, const char *str)
|
|||
*
|
||||
* If compatible, return. Otherwise, ereport(FATAL).
|
||||
*/
|
||||
void ValidatePgVersion(const char *path)
|
||||
void ValidatePgVersion(const char* path)
|
||||
{
|
||||
char full_path[MAXPGPATH];
|
||||
FILE *file = NULL;
|
||||
FILE* file = NULL;
|
||||
int ret;
|
||||
long file_major, file_minor;
|
||||
long my_major = 0, my_minor = 0;
|
||||
char *endptr = NULL;
|
||||
const char *version_string = PG_VERSION;
|
||||
char* endptr = NULL;
|
||||
const char* version_string = PG_VERSION;
|
||||
errno_t rc;
|
||||
|
||||
my_major = strtol(version_string, &endptr, 10);
|
||||
|
|
@ -1663,8 +1707,9 @@ void ValidatePgVersion(const char *path)
|
|||
if (file == NULL) {
|
||||
if (errno == ENOENT)
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not a valid data directory", path),
|
||||
errdetail("File \"%s\" is missing.", full_path)));
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("\"%s\" is not a valid data directory", path),
|
||||
errdetail("File \"%s\" is missing.", full_path)));
|
||||
else
|
||||
ereport(FATAL, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", full_path)));
|
||||
}
|
||||
|
|
@ -1672,17 +1717,23 @@ void ValidatePgVersion(const char *path)
|
|||
ret = fscanf_s(file, "%ld.%ld", &file_major, &file_minor);
|
||||
|
||||
if (ret != 2)
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not a valid data directory", path),
|
||||
errdetail("File \"%s\" does not contain valid data.", full_path),
|
||||
errhint("You might need to initdb.")));
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("\"%s\" is not a valid data directory", path),
|
||||
errdetail("File \"%s\" does not contain valid data.", full_path),
|
||||
errhint("You might need to initdb.")));
|
||||
|
||||
FreeFile(file);
|
||||
|
||||
if (my_major != file_major || my_minor != file_minor)
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("database files are incompatible with server"),
|
||||
errdetail("The data directory was initialized by PostgreSQL version %ld.%ld, "
|
||||
"which is not compatible with this version %s.",
|
||||
file_major, file_minor, version_string)));
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("database files are incompatible with server"),
|
||||
errdetail("The data directory was initialized by PostgreSQL version %ld.%ld, "
|
||||
"which is not compatible with this version %s.",
|
||||
file_major,
|
||||
file_minor,
|
||||
version_string)));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
|
|
@ -1695,12 +1746,12 @@ void ValidatePgVersion(const char *path)
|
|||
* 'gucname': name of GUC variable, for error reports
|
||||
* 'restricted': if true, force libraries to be in $libdir/plugins/
|
||||
*/
|
||||
static void load_libraries(const char *libraries, const char *gucname, bool restricted)
|
||||
static void load_libraries(const char* libraries, const char* gucname, bool restricted)
|
||||
{
|
||||
char *rawstring = NULL;
|
||||
List *elemlist = NULL;
|
||||
char* rawstring = NULL;
|
||||
List* elemlist = NULL;
|
||||
int elevel;
|
||||
ListCell *l = NULL;
|
||||
ListCell* l = NULL;
|
||||
|
||||
if (libraries == NULL || libraries[0] == '\0') {
|
||||
return; /* nothing to do */
|
||||
|
|
@ -1732,21 +1783,21 @@ static void load_libraries(const char *libraries, const char *gucname, bool rest
|
|||
elevel = LOG;
|
||||
|
||||
foreach (l, elemlist) {
|
||||
char *tok = (char *)lfirst(l);
|
||||
char *filename = NULL;
|
||||
char* tok = (char*)lfirst(l);
|
||||
char* filename = NULL;
|
||||
errno_t rc;
|
||||
|
||||
filename = pstrdup(tok);
|
||||
if (strcmp(filename, "security_plugin") == 0 && WorkingGrandVersionNum < 92076) {
|
||||
if (strcmp(filename, "security_plugin") == 0 && WorkingGrandVersionNum < 92076) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
canonicalize_path(filename);
|
||||
|
||||
/* If restricting, insert $libdir/plugins if not mentioned already */
|
||||
if (restricted && first_dir_separator(filename) == NULL) {
|
||||
char *expanded = NULL;
|
||||
char* expanded = NULL;
|
||||
|
||||
expanded = (char *)palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
|
||||
expanded = (char*)palloc(strlen("$libdir/plugins/") + strlen(filename) + 1);
|
||||
rc = strcpy_s(expanded, strlen("$libdir/plugins/") + strlen(filename) + 1, "$libdir/plugins/");
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
rc = strcat_s(expanded, strlen("$libdir/plugins/") + strlen(filename) + 1, filename);
|
||||
|
|
@ -1767,11 +1818,12 @@ static void load_libraries(const char *libraries, const char *gucname, bool rest
|
|||
/*
|
||||
* process shared preloaded libraries internal
|
||||
*/
|
||||
void process_shared_preload_libraries_internal(void)
|
||||
void
|
||||
process_shared_preload_libraries_internal(void)
|
||||
{
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
if (is_streaming_engine_available()) {
|
||||
load_libraries("streaming", "shared_preload_libraries", false);
|
||||
load_libraries("streaming", "shared_preload_libraries", false);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
|
|
@ -1796,7 +1848,7 @@ void process_local_preload_libraries(void)
|
|||
load_libraries(u_sess->attr.attr_common.local_preload_libraries_string, "local_preload_libraries", true);
|
||||
}
|
||||
|
||||
void pg_bindtextdomain(const char *domain)
|
||||
void pg_bindtextdomain(const char* domain)
|
||||
{
|
||||
#ifdef ENABLE_NLS
|
||||
|
||||
|
|
@ -1823,8 +1875,7 @@ void Reset_Pseudo_CurrentUserId(void)
|
|||
* During connection obtaining, the agent_send_connection_params_parallel function
|
||||
* is used to synchronize the version number.
|
||||
*/
|
||||
void register_backend_version(uint32 backend_version)
|
||||
{
|
||||
void register_backend_version(uint32 backend_version){
|
||||
if (IsBootstrapProcessingMode() || IsInitProcessingMode() || !IS_PGXC_COORDINATOR) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1839,7 +1890,7 @@ void register_backend_version(uint32 backend_version)
|
|||
ereport(ERROR, (errcode(ERRCODE_SET_QUERY), errmsg("backend_version is a error value: %d", backend_version)));
|
||||
}
|
||||
securec_check_ss_c(ret, "\0", "\0");
|
||||
if (PoolManagerSetCommand(POOL_CMD_GLOBAL_SET, sql_tmp, "backend_version") < 0) {
|
||||
if (PoolManagerSetCommand(POOL_CMD_GLOBAL_SET, sql_tmp, "backend_version") < 0){
|
||||
ereport(ERROR, (errmodule(MOD_TRANS_HANDLE), errcode(ERRCODE_SET_QUERY), errmsg("ERROR SET backend_version")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1847,8 +1898,8 @@ void register_backend_version(uint32 backend_version)
|
|||
/*
|
||||
* Check whether the version contains the backend_version parameter.
|
||||
*/
|
||||
bool contain_backend_version(uint32 version_number)
|
||||
{
|
||||
return ((version_number >= V5R1C20_BACKEND_VERSION_NUM && version_number < V5R2C00_START_VERSION_NUM) ||
|
||||
bool contain_backend_version(uint32 version_number) {
|
||||
return ((version_number >= V5R1C20_BACKEND_VERSION_NUM &&
|
||||
version_number < V5R2C00_START_VERSION_NUM) ||
|
||||
(version_number >= V5R2C00_BACKEND_VERSION_NUM));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2712,8 +2712,8 @@ void PostgresInitializer::InitExtensionVariable()
|
|||
}
|
||||
|
||||
/* check whether the extension has been created */
|
||||
const char* dolphin = "dolphin";
|
||||
u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists(dolphin);
|
||||
const char* b_sql_plugin = "b_sql_plugin";
|
||||
u_sess->attr.attr_sql.b_sql_plugin = CheckIfExtensionExists(b_sql_plugin);
|
||||
}
|
||||
|
||||
void PostgresInitializer::FinishInit()
|
||||
|
|
|
|||
|
|
@ -10340,16 +10340,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
|
|||
|
||||
oldCxt = MemoryContextSwitchTo(u_sess->plsql_cxt.curr_compile_context->compile_tmp_cxt);
|
||||
u_sess->plsql_cxt.plpgsql_yylloc = plpgsql_yylloc;
|
||||
RawParserHook parser_hook= raw_parser;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
if (u_sess->attr.attr_sql.dolphin) {
|
||||
int id = GetCustomParserId();
|
||||
if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) {
|
||||
parser_hook = (RawParserHook)g_instance.raw_parser_hook[id];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
(void)parser_hook(stmt, NULL);
|
||||
(void) raw_parser(stmt);
|
||||
MemoryContextSwitchTo(oldCxt);
|
||||
|
||||
/* Restore former ereport callback */
|
||||
|
|
|
|||
|
|
@ -44,22 +44,11 @@ static int g_iPosBlackList = 0;
|
|||
/* array store for black list */
|
||||
static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX];
|
||||
|
||||
/*
|
||||
function name: BBOX_DetermineMsb
|
||||
description: The function should judge the mode that PC uses to store data is Big-endian/Little-endian.
|
||||
arguments: void
|
||||
return value: An integer that indicates the mode is Big-endian/Little-endian,
|
||||
if it is ELFDATA2LSB, the mode is Little-endian,
|
||||
if it is ELFDATA2MSB, the mode is Big-endian.
|
||||
note:The way that this function judge the mode that PC uses to store data is through a union variable unProbe,
|
||||
at first we give its first member variable sShortInt a value BBOX_MSB_LSB_INT of type short, then its second
|
||||
member variable cSplit[sizeof(short)] equaling to cSplit[2] would have the equal value of the first. Finally we
|
||||
just need to compare BBOX_LITTER_BITS and BBOX_HIGH_BITS, namely the low byte and high byte of
|
||||
BBOX_MSB_LSB_INT, with unProbe.cSplit[0] and unProbe.cSplit[1], if they are correspondingly equal, the mode is
|
||||
Little-endian, else is the Big-endian.
|
||||
date: 2022/8/2
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
/*
|
||||
* Determines whether the byte order of the local machine is large or small
|
||||
* return : ELFDATA2LSB - large
|
||||
* : ELFDATA2MSB - small
|
||||
*/
|
||||
int BBOX_DetermineMsb(void)
|
||||
{
|
||||
union INT_PROBE {
|
||||
|
|
|
|||
|
|
@ -51,19 +51,8 @@ struct PIPE_IDS {
|
|||
static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS];
|
||||
|
||||
/*
|
||||
function name: bbox_strncmp
|
||||
description: To compare two substrings, the pointers pszSrc and pszTarget store their host strings'addresses.
|
||||
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
|
||||
An integer indicates the number of characters at the former of two strings that
|
||||
will be compared.
|
||||
return value: Type s32, an interger.
|
||||
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
|
||||
else it indicates the difference between the first two characters that these two
|
||||
strings can't match.
|
||||
note:The two pointers shouldn't be null. The last argument shouldn't less than zero.
|
||||
date: 2022/8/2
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* compare string pszSrc and pszTarget
|
||||
*/
|
||||
s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
|
||||
{
|
||||
signed char cRes = 0;
|
||||
|
|
@ -79,20 +68,8 @@ s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_strcmp
|
||||
description: compare two strings, the pointer pszSrc and pszTarget store their addresses.
|
||||
arguments: Two pointers of type const char*, pointing to two strings needed to be compared.
|
||||
An integer indicates the number of characters at the former of two strings that
|
||||
will be compared.
|
||||
return value: Type s32, an interger.
|
||||
If it's zero, then the former substrings of string pszSrc and pszTarget are same,
|
||||
else if it's 1, then it indicates between first two characters that these two
|
||||
strings can't match, the character of first string that pszSrc points is greater,
|
||||
else if it's -1, the character of second string that pszTarget points is greater.
|
||||
note:The two pointers shouldn't be null. The last argument shouldn't less than zero.
|
||||
date: 2022/8/2
|
||||
contact tel:same
|
||||
*/
|
||||
* compare string pszSrc and pszTarget
|
||||
*/
|
||||
s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
|
||||
{
|
||||
unsigned char c1, c2;
|
||||
|
|
@ -113,15 +90,8 @@ s32 bbox_strcmp(const char* pszSrc, const char* pszTarget)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_strlen
|
||||
description: Calculate the length of string.
|
||||
arguments: An pointer that indicates the address of a string.
|
||||
return value: Type s32, an integer indicating the length of string.
|
||||
note: the length of string=(address of the last character not '\0'-address of the first character)/sizeof(char), and sizeof(char)
|
||||
equals to 1, so the length of string=(address of the last character not '\0'-address of the first character).
|
||||
date: 2022/8/2
|
||||
contact tel:same
|
||||
*/
|
||||
* get the length of string pszString
|
||||
*/
|
||||
s32 bbox_strlen(const char* pszString)
|
||||
{
|
||||
const char* pszTemp = NULL;
|
||||
|
|
@ -135,16 +105,8 @@ s32 bbox_strlen(const char* pszString)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_strnlen
|
||||
description: Calculate the length of string, but having some restrictive conditions.
|
||||
arguments: An pointer that indicates the address of a string.
|
||||
And an integer that indicates the maxlenth.
|
||||
return value: Type s32, an integer indicating the length of string.
|
||||
note: If the length of string exceed the argument count, then return the length of string,
|
||||
else return the argument count.
|
||||
date: 2022/8/2
|
||||
contact tel:same
|
||||
*/
|
||||
* get the length of string pszString
|
||||
*/
|
||||
s32 bbox_strnlen(const char* pszString, s32 count)
|
||||
{
|
||||
const char* pszTemp = NULL;
|
||||
|
|
@ -157,16 +119,8 @@ s32 bbox_strnlen(const char* pszString, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_atoi
|
||||
description: Convert a string that includes continuous digital characters to an integer,
|
||||
if the first character of the string is '-', then we will return a negative result.
|
||||
arguments: An pointer that indicates the address of a string.
|
||||
return value: Type s32, an integer indicating the result of string converted.
|
||||
note: I think the function isn't perfect, though it's not a core function. For example, what about
|
||||
the condition that the first character of the string is '+'?
|
||||
date: 2022/8/2
|
||||
contact tel:same
|
||||
*/
|
||||
* convert a string to interger
|
||||
*/
|
||||
s32 bbox_atoi(const char* pszString)
|
||||
{
|
||||
s32 n = 0;
|
||||
|
|
@ -186,18 +140,10 @@ s32 bbox_atoi(const char* pszString)
|
|||
|
||||
return iNeg ? -n : n;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: bbox_memcmp
|
||||
description: Compare former count bytes in ASCII of data stored in two areas that pointers cs and ct direct.
|
||||
arguments: Two pointers to areas of memory, and an integer indicating the max counts compared.
|
||||
return value: Type s32, an integer.
|
||||
If the value returned is 0, then the data stored in two areas destined are same,
|
||||
else if is 1, then between two first data in ASCII of byte different, cs's is greater,
|
||||
else if is -1, then ct's is greater.
|
||||
note: The two pointers should not be null, it's dangerous.
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* compare memory
|
||||
*/
|
||||
s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
|
||||
{
|
||||
const unsigned char *su1 = NULL;
|
||||
|
|
@ -213,18 +159,8 @@ s32 bbox_memcmp(const void* cs, const void* ct, s32 count)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_strstr
|
||||
description: Judge if the string s2 directs is substring of string s1 directs.
|
||||
arguments: Two pointers of type const char*, pointing to two strings.
|
||||
return value: Type char*, a pointer. Actually it's a address, if s2 directs a
|
||||
null string, then return the address of the first character of s1,
|
||||
if the string s2 directs isn't substring of string s1 directs, return
|
||||
null, if the string s2 directs is substring of string s1 directs, then return
|
||||
the address of first character matched.
|
||||
note: The two pointers should not be null, it's dangerous.
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* search string l2 in l1
|
||||
*/
|
||||
char* bbox_strstr(const char* s1, const char* s2)
|
||||
{
|
||||
int l1, l2;
|
||||
|
|
@ -246,17 +182,8 @@ char* bbox_strstr(const char* s1, const char* s2)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_mkdir
|
||||
description: We distinguish parent directory and child directory through character '/',
|
||||
normally through a for loop, we can make sure all directories above the directory
|
||||
we want to creat exist, finally we will creat the flag directory after its parent.
|
||||
arguments: A pointers of type const char*, pointing to one strings, which indicates the filename and its full path.
|
||||
return value: An integer of type s32, if it's RET_ERR, then we fail to make a directory, else if it's RET_OK then we succeed.
|
||||
note: Take care the last non-null character of the string needed to be '/', and once if flag directory's
|
||||
ancestors aren't exist, the function return RET_ERR.
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* make a directory
|
||||
*/
|
||||
s32 bbox_mkdir(const char* pszDir)
|
||||
{
|
||||
char szDirName[BBOX_TMP_LEN_32 * 16];
|
||||
|
|
@ -301,16 +228,8 @@ s32 bbox_mkdir(const char* pszDir)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_GetFreePid
|
||||
description: Through a for loop, we search a free pipe in a structure array, to an array element if its
|
||||
member variable isUsed's value is 0, we return the array element's another member variable
|
||||
stPid's address.
|
||||
arguments: void
|
||||
return value: An pointer of type struct PIPE_ID* or NULL.
|
||||
note: none
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* search free pipe id
|
||||
*/
|
||||
struct PIPE_ID* bbox_GetFreePid(void)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -326,14 +245,8 @@ struct PIPE_ID* bbox_GetFreePid(void)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_PutPid
|
||||
description: Release the occupied pipe.
|
||||
arguments: A pointer of type struct PIPE_ID*.
|
||||
return value: void
|
||||
note: If the argument pointer is null, then there is no need to free the storage, the function ends.
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* Release the occupied pipeid
|
||||
*/
|
||||
void bbox_PutPid(struct PIPE_ID* pstPid)
|
||||
{
|
||||
struct PIPE_IDS* pstPids = NULL;
|
||||
|
|
@ -348,16 +261,8 @@ void bbox_PutPid(struct PIPE_ID* pstPid)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_FindPid
|
||||
description: In all occupied pipes, the function search the flag pipe through compare all structure
|
||||
array elements's member variable stPid's member variable iFd with the function
|
||||
argument iFd, if they are equal, then return the addres of this array elements.
|
||||
arguments: An integer that indicates a file's file handle.
|
||||
return value: A pointer of type struct PIPE_ID* or NULL.
|
||||
note: none
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* find available pipe id by file handle
|
||||
*/
|
||||
struct PIPE_ID* bbox_FindPid(int iFd)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -376,17 +281,8 @@ struct PIPE_ID* bbox_FindPid(int iFd)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: sys_popen
|
||||
description: The function gets a free pipe by function bbox_GetFreePid, if normally, then creat a pipe
|
||||
through sys_pipe, andcreat a child process through function sys_fork, execute a shell command
|
||||
to run a process.
|
||||
arguments: One pointer to a string that represents command line, another pointer of type const char*
|
||||
indicates that the file file handle directs is used in the this mode.
|
||||
return value: A pointer of type struct PIPE_ID* or NULL.
|
||||
note: The string that indicates pszMode should only be "r" or "w",
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* run popen
|
||||
*/
|
||||
s32 sys_popen(char* pszCmd, const char* pszMode)
|
||||
{
|
||||
struct PIPE_ID* volatile stCurPid = NULL;
|
||||
|
|
@ -491,15 +387,8 @@ s32 sys_popen(char* pszCmd, const char* pszMode)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: sys_pclose
|
||||
description: The function has an contrary action to function sys_popen, it close the pipe
|
||||
that sys_popen open.
|
||||
arguments: iFd, an integer that indicates a file handle.
|
||||
return value: An integer that indicates the final status of the process working before.
|
||||
note: none
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* close file handle
|
||||
*/
|
||||
int sys_pclose(s32 iFd)
|
||||
{
|
||||
struct PIPE_ID* pstCur = NULL;
|
||||
|
|
@ -522,17 +411,8 @@ int sys_pclose(s32 iFd)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_listdir
|
||||
description: The function list all files below this path in directory.
|
||||
arguments: The first argument is a pointer to a string representing a file path, all files below
|
||||
this path will be listed in directory. The second argument is a pointer to a callback
|
||||
function. The last is a pointer of type void*, it indicates a command line.
|
||||
return value: An integer that indicates the result of function, if normal, it's RET_OK, else
|
||||
it's RET_ERR.
|
||||
note: The path that the first argument represents should be absolute path, take care.
|
||||
date: 2022/8/2
|
||||
contact tel: same
|
||||
*/
|
||||
* list file in directory
|
||||
*/
|
||||
s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs)
|
||||
{
|
||||
struct linux_dirent* pstEntry = NULL;
|
||||
|
|
|
|||
|
|
@ -57,37 +57,23 @@ void bbox_initlog(int iLogScreen)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_itoc
|
||||
description: Convert an integer to a character.
|
||||
arguments: An integer needed to be converted.
|
||||
return value: An character that corresponds to the function's integer argument.
|
||||
note: The integer argument can be converted in radices more than decimalism.
|
||||
date: 2022/8/2
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* convert int to string
|
||||
*/
|
||||
inline char bbox_itoc(u8 sNum)
|
||||
{
|
||||
return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87));
|
||||
}
|
||||
|
||||
/*
|
||||
function name: bbox_put_dox
|
||||
description: Conversion of number systems.
|
||||
arguments: The first argument pCallback is a pointer to a callback function, we
|
||||
use it to reverse the final result. The second argument is a pointer of
|
||||
type void* used as a argument of function pCallback. The third argument
|
||||
piCount is a pointer of type int, an offset pointer, also be used as a argument
|
||||
of pCallback. The fourth argument is an integer of 32 bits, it indicates the buffer
|
||||
size pCallback uses.The fifth argument uNum is a decimal integer that will
|
||||
be converted to an integer in another radix. The sixth argument is used as
|
||||
base to conversion of number systems. The last argument indicates the integer
|
||||
after converted is a negative integer or not.
|
||||
return value: An integer, indicating if the function pCallback work successfully.
|
||||
note: The argument uNum should be a positive integer, after conversion of number systems
|
||||
the sign will be appended to string's tail.
|
||||
date: 2022/8/2
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* convert int to string
|
||||
* in : pCallback - call back function
|
||||
* ptr - private data to call this function
|
||||
* piCount - offset pointer
|
||||
* iSize - buffer size
|
||||
* uNum - the variable to convert
|
||||
* sSys - type of variable
|
||||
* isNeg - is negative
|
||||
* return : need call back
|
||||
*/
|
||||
s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg)
|
||||
{
|
||||
s64 i = 0;
|
||||
|
|
@ -122,21 +108,15 @@ s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iS
|
|||
|
||||
return iRet;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: bbox_vsnprintf
|
||||
description: The function is used to print string in corresponding array.
|
||||
arguments: The first argument is a pointer to a callback function, the next is a
|
||||
pointer to private data to call this function, also to buffer.
|
||||
The third is used to destine buffer size. The forth is used to destine
|
||||
the print format of deferent string, the last is a pointer to variable parameter list.
|
||||
return value: An integer, if iSize is big enough, then the return value is the length of
|
||||
string been written in destined memory successfully, not include '\0',
|
||||
if function makes errors, the return value is a negative integer.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* simple signal-safe function vsnprintf
|
||||
* in : pCallback - call back function
|
||||
* ptr - private data to call this function
|
||||
* iSize - buffer size
|
||||
* pFmt - format type
|
||||
* ap - parameter list pointer¸ñʽ
|
||||
* return : length of string
|
||||
*/
|
||||
s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap)
|
||||
{
|
||||
|
||||
|
|
@ -255,20 +235,13 @@ s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const c
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_SnprintCallback
|
||||
description: The function is used to print string in corresponding array, usually
|
||||
used as the first argument of function bbox_vsnprintf.
|
||||
arguments: The first argument is a character waited to be written into buffer that
|
||||
pPtr directs, the second argument directs a buffer area, the third is a
|
||||
pointer to an integera used to record the count to call this callback function,
|
||||
at the same time, it represents the count of characters written into buffer, it's
|
||||
a pointer so that we can conveniently modify data storedin it. The last
|
||||
argument destines the size of buffer, it represents the limit of length.
|
||||
return value: An integer, if written successfully, it's RET_OK, else it's RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* call back function of snprintf_s
|
||||
* in : c - string to calculate
|
||||
* pPtr - pointer to buffer
|
||||
* piCount - count of character
|
||||
* iSize - limit of length
|
||||
* return : length of string
|
||||
*/
|
||||
s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize)
|
||||
{
|
||||
char** pszBuff = (char**)pPtr;
|
||||
|
|
|
|||
|
|
@ -64,14 +64,8 @@ u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */
|
|||
BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */
|
||||
|
||||
/*
|
||||
function name: BBOX_ReserveZeroStack
|
||||
description: The function creat a empty stack, and its size depend on argument count.
|
||||
arguments: An integer of type s32, namely int, it destines the storage of stack.
|
||||
return value: void
|
||||
note: The stack this function creats is actually a character array.
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* reserved count bytes on current stack, and set 0
|
||||
*/
|
||||
void BBOX_ReserveZeroStack(s32 count)
|
||||
{
|
||||
char buff[count];
|
||||
|
|
@ -101,14 +95,8 @@ s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_GetTaskNumber
|
||||
description: When get a path to specific process, this function will return count of threads below it.
|
||||
arguments: A pointer of type char*, including a path to specific process.
|
||||
return value: An integer that indicates the count of threads below specific process.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* get count of thread
|
||||
*/
|
||||
s32 BBOX_GetTaskNumber(char* szTaskPath)
|
||||
{
|
||||
struct kernel_stat stProcSB = {0};
|
||||
|
|
@ -142,17 +130,8 @@ s32 BBOX_GetTaskNumber(char* szTaskPath)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_GetTaskId
|
||||
description: When get a path to specific process, this function will return count of threads below it.
|
||||
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
|
||||
we use it as a structure array to store requisite thread infomation, the next argument destines
|
||||
the max size of the array that the first argument destines. The last argument is a pointer of type
|
||||
char*, including a path to specific process.
|
||||
return value: An integer that indicates the count of threads stored in structure array.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* get thread pid
|
||||
*/
|
||||
s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath)
|
||||
{
|
||||
s32 iProc = -1;
|
||||
|
|
@ -235,19 +214,13 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_PtraceAttachPid
|
||||
description: The function is used to check the process whose id stored in structure array pstTaskInfo work normally.
|
||||
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
|
||||
it is used as a structure array that has stored requisite thread infomation, the next argument destines
|
||||
the size of the array that the first argument destines, namely how many elements the array has.
|
||||
The last argument is an integer to decide if need to check if the trace to destined process
|
||||
work normally, if normal, corresponding element of array pstTaskInfo's member variable cIsAttached
|
||||
will change from 0 to 1.
|
||||
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* a ptrace debug thread
|
||||
* in : TASK_ATTACH_INFO - thread information
|
||||
* iPidCount - count of thread information
|
||||
* iDoPtraceCheck - check if ptrace success
|
||||
* return : 0 - success
|
||||
* err code - failed
|
||||
*/
|
||||
s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -299,18 +272,13 @@ s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s3
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_DetachAllThread
|
||||
description: The function is used to cancel checking the process whose id stored in structure array pstTaskInfo
|
||||
work normally, "work normally" means in array pstTaskInfo corresponding element's member
|
||||
variable cIsAttached's value is 1.
|
||||
arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*,
|
||||
it is used as a structure array that has stored requisite thread infomation, the next argument destines
|
||||
the size of the array that the first argument destines, namely how many elements the array has.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* cancel ptrace debug thread
|
||||
* in : TASK_ATTACH_INFO - thread information
|
||||
* iPidCount - count of thread information
|
||||
* iDoPtraceCheck - check if ptrace success
|
||||
* return : 0 - success
|
||||
* err code - failed
|
||||
*/
|
||||
void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -355,18 +323,12 @@ void BBOX_CheckResumeThread(void* pArgs)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_PtraceAndRun
|
||||
description: When get a path to specific process, this function will trace the threads below it, and get the
|
||||
information for example how many threads work normally then store it in pstArgs.
|
||||
arguments: The first argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
|
||||
what matters is its member variable callback function pointer, the next argument destines
|
||||
the max count of the thread. The last argument is a pointer of type char*, including a path
|
||||
to specific process.
|
||||
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* ptrace thread and run function.
|
||||
* in : pstArgs - information of callback function
|
||||
* iMaxThreadCount - max count of thread
|
||||
* pszProcSelfTask - /proc/[pid]/task of current tracked thread.
|
||||
* return 0 if success else err code.
|
||||
*/
|
||||
s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask)
|
||||
{
|
||||
struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount];
|
||||
|
|
@ -445,15 +407,8 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_PrintFailedLog
|
||||
description: Write log infomation into specific file, if errors arise, print the infomation about errors.
|
||||
arguments: The only argument is a pointer of type const char* to a filename string, if this file doesn't
|
||||
exist, we will creat a new file named it.
|
||||
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* print log information if export failed.
|
||||
*/
|
||||
void BBOX_PrintFailedLog(const char* pFileName)
|
||||
{
|
||||
ssize_t iRet = 0;
|
||||
|
|
@ -482,15 +437,8 @@ void BBOX_PrintFailedLog(const char* pFileName)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_ListThread
|
||||
description: Export thread information.
|
||||
arguments: The only argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
|
||||
what matters is its member variable callback function pointer and thread infomation.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* export thread information.
|
||||
*/
|
||||
void BBOX_ListThread(struct BBOX_ListParams* pstArgs)
|
||||
{
|
||||
pid_t ppid = 0;
|
||||
|
|
@ -597,18 +545,12 @@ errout:
|
|||
}
|
||||
|
||||
/*
|
||||
function name: BBOX_GetClonePidResult
|
||||
description: The function get the status of child process at first, then according to it assign pstArgs's
|
||||
member variables iError and iResult appropriate values.
|
||||
arguments: The first argument is a integer named iClonePid, it represents the pid of child process.
|
||||
The second argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*,
|
||||
what matters is its member variable callback function pointer and thread infomation.
|
||||
The third argument is a integer indicating error code.
|
||||
return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/3
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* get return value of child process
|
||||
* in : iClonePid - PID of child process
|
||||
* pstArgs - parameter
|
||||
* iCloneErrno - err code
|
||||
* return 0 if success else failed.
|
||||
*/
|
||||
s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno)
|
||||
{
|
||||
s32 iStatus = 0;
|
||||
|
|
|
|||
|
|
@ -57,22 +57,6 @@ BlacklistItem g_blacklist_items[] = {
|
|||
{DATA_WRITER_QUEUE, "DATA_WRITER_QUEUE", false}
|
||||
};
|
||||
|
||||
/*
|
||||
function name: coredump_handler
|
||||
description: When a program is abnormal, but the exception appears in the core of process and wasn't caught,
|
||||
The function will generate a file to store the information about memory of process, status of register
|
||||
and running stack.
|
||||
arguments: The first argument is an integer indicating signal code that usually used in program of processing
|
||||
signal as variable.
|
||||
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
|
||||
directs stores comprehensive information about signal, for example, which process sends
|
||||
and which user sends.
|
||||
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
static void coredump_handler(int sig, siginfo_t *si, void *uc)
|
||||
{
|
||||
static volatile int64 first_tid = INVALID_TID;
|
||||
|
|
@ -100,19 +84,8 @@ static void coredump_handler(int sig, siginfo_t *si, void *uc)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: bbox_handler
|
||||
description: Handle signal conditions for bbox.
|
||||
arguments: The first argument is an integer indicating signal code that usually used in program of processing
|
||||
signal as variable.
|
||||
The second argument is a structure pointer of type siginfo_t*, the memory that this pointer
|
||||
directs stores comprehensive information about signal, for example, which process sends
|
||||
and which user sends.
|
||||
The third argument is a pointer of type void*, other kinds of pointers can directly used here.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* bbox_handler - handle signal conditions for bbox
|
||||
*/
|
||||
static void bbox_handler(int sig, siginfo_t *si, void *uc)
|
||||
{
|
||||
static volatile int64 first_tid = INVALID_TID;
|
||||
|
|
@ -152,16 +125,8 @@ static void bbox_handler(int sig, siginfo_t *si, void *uc)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: get_bbox_coredump_pattern_path
|
||||
description: Get the core dump file's path from the file "/proc/sys/kernel/core_pattern".
|
||||
arguments: The first argument is a pointer to string, we use it to store core dump file's path acquired
|
||||
from the file "/proc/sys/kernel/core_pattern", the next argument is the number of characters
|
||||
reading from the file "/proc/sys/kernel/core_pattern", all len-1 characters or less if appear '\n'.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* get_bbox_coredump_pattern_path - get the core dump path from the file "/proc/sys/kernel/core_pattern"
|
||||
*/
|
||||
static void get_bbox_coredump_pattern_path(char* path, Size len)
|
||||
{
|
||||
FILE* fp = NULL;
|
||||
|
|
@ -191,17 +156,7 @@ static void get_bbox_coredump_pattern_path(char* path, Size len)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function name: build_bbox_corepath
|
||||
description: Get the core dump file's path.
|
||||
arguments: The first argument is a pointer to string, we use it to store core dump file's path,
|
||||
the next argument is the size of the path's name, the last argument is a pointer
|
||||
to string that indicates maybe store a path to configure the core dump file.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
/* compute directory into which bbox dump core files are saved. */
|
||||
static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
|
@ -277,15 +232,6 @@ void assign_bbox_corepath(const char* newval, void* extra)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: show_bbox_dump_path
|
||||
description: Get the dump file's path.
|
||||
arguments: void
|
||||
return value: A pointer of type const char*, directing the path to dump or NULL.
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
const char* show_bbox_dump_path(void)
|
||||
{
|
||||
const char* path = g_bbox_dump_path;
|
||||
|
|
@ -293,15 +239,6 @@ const char* show_bbox_dump_path(void)
|
|||
return (path != NULL) ? path : "";
|
||||
}
|
||||
|
||||
/*
|
||||
function name: split_string_into_blacklist
|
||||
description: Get all strings been divided into character ',' in source string.
|
||||
arguments: A pointer of type const char*, directing the source string.
|
||||
return value: A pointer of type static List*.
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
static List* split_string_into_blacklist(const char* source)
|
||||
{
|
||||
List *result = NIL;
|
||||
|
|
@ -327,6 +264,7 @@ static List* split_string_into_blacklist(const char* source)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool check_bbox_blacklist(char** newval, void** extra, GucSource source)
|
||||
{
|
||||
if (t_thrd.proc_cxt.MyProcPid != PostmasterPid)
|
||||
|
|
@ -464,15 +402,10 @@ void bbox_blacklist_remove(BlacklistIndex item, void* addr)
|
|||
}
|
||||
|
||||
/*
|
||||
function name: CheckFilenameValid
|
||||
description: Check if the filename is in line with norms, or if dangerous characters appear
|
||||
the filename is invalid.
|
||||
arguments: A pointer to string indicating filename.
|
||||
return value: An integer, if function works normally, the value is RET_OK, else it's RET_ERR.
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
* @Description: check the value from environment variablethe to prevent command injection.
|
||||
* @in input_env_value : the input value need be checked.
|
||||
*
|
||||
*/
|
||||
int CheckFilenameValid(const char* inputEnvValue)
|
||||
{
|
||||
const int maxLen = 1024;
|
||||
|
|
|
|||
|
|
@ -45,15 +45,6 @@
|
|||
|
||||
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port);
|
||||
|
||||
/*
|
||||
function name: SetCPUAffinity
|
||||
description: The function set the affinity of CPU or CPUs destined by argument cpu_id.
|
||||
arguments: An integer representing the id of one CPU or more.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/5
|
||||
contact: 18720816902
|
||||
*/
|
||||
void SetCPUAffinity(int cpu_id)
|
||||
{
|
||||
cpu_set_t mask;
|
||||
|
|
@ -279,15 +270,6 @@ IPAddrType CommLibNetGetIPType(unsigned int ip)
|
|||
#define CMD_STR_MAX 512
|
||||
#define CMD_OUTPUT_BUFFER_SIZE 1024
|
||||
|
||||
/*
|
||||
function name: CommCheckLtranProcess
|
||||
description: The function check if the process currently working has loaded transactions.
|
||||
arguments: void
|
||||
return value: 0 or 1, if 1, then at least one loaded transcation exists, if 0, no one.
|
||||
note: none
|
||||
date: 2022/8/5
|
||||
contact: 18720816902
|
||||
*/
|
||||
int CommCheckLtranProcess()
|
||||
{
|
||||
AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt);
|
||||
|
|
@ -405,21 +387,6 @@ static T GetCommProxySubParameter(const char* str_attr, const char* key)
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: ParseCommProxyNumaBind
|
||||
description: Get the ids of CPU to bind process with specific CPU.
|
||||
arguments: The first argument is a pointer of type const char* to a string that indicating
|
||||
the id of CPUs below NUMA, not necessarily all CPUs.
|
||||
The second argument is an integer telling us we will get CPUs' id from which position
|
||||
of array str_attr.
|
||||
The third argument tells us the number of NUMA system framework.
|
||||
The fourth argument is a pointer to an integer array used to store CPUs' id gotten
|
||||
from string str_attr, we can use these ids to bind specific CPU.
|
||||
return value: void
|
||||
note: none
|
||||
date: 2022/8/5
|
||||
contact: 18720816902
|
||||
*/
|
||||
static void ParseCommProxyNumaBind(
|
||||
const char* str_attr, const int pos, const int numa_num, int* numa_bind)
|
||||
{
|
||||
|
|
@ -520,22 +487,6 @@ bool ParseCommProxyAttr(CommProxyConfig* config)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: CommCheckFilterMatch
|
||||
description: This function compare the ip and port allowed with ip and port gotten from
|
||||
Filter, if they are correspondingly same, it will return true value.
|
||||
arguments: The first argument is a pointer of type const char* to a string that indicating
|
||||
the id and port of the request been sent to Filter, the id and port have been
|
||||
separated by character ':'.
|
||||
The second argument is an integer telling us we the length of the string first
|
||||
argument directs.
|
||||
The third argument tells us the ip allowed.
|
||||
The fourth argument tells us the port allowed.
|
||||
return value: static bool
|
||||
note: none
|
||||
date: 2022/8/4
|
||||
contact: 18720816902
|
||||
*/
|
||||
static bool CommCheckFilterMatch(const char *filter, int len, const char *ip, int port)
|
||||
{
|
||||
char *str_ip = NULL;
|
||||
|
|
|
|||
|
|
@ -175,24 +175,6 @@ void UpdateTxRxStats(int msg_level)
|
|||
last_rx_nbytes = current_rx_nbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: parse_monitor_sock_queue
|
||||
description: Compare the string recv_buffer with "sockqueue fd:fd", the "fd"
|
||||
after character ':' is an integer indicating file descriptor. If recv_buffer
|
||||
accords with the format, the function will takes next action to see if
|
||||
fd is 0, which represents stdin, so the function ends with returned value 0.
|
||||
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
|
||||
if equal, then get a structure variable including socket descriptor
|
||||
destined by the fd gotten from the first argument, if it's NULL, we can
|
||||
write "fd:[%d], type:[normal fd], no sock queue" into send_buffer.
|
||||
arguments: The first argument is a pointer to a string indicating request infomation.
|
||||
The second argument is a pointer to a string to store sent infomation.
|
||||
The third argument tells the kind of socket request.
|
||||
return value: 0 or 1.
|
||||
note: none
|
||||
date: 2022/8/5
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorType type)
|
||||
{
|
||||
int length;
|
||||
|
|
@ -223,25 +205,6 @@ int parse_monitor_sock_queue(char* recv_buffer, char* send_buffer, ParseMonitorT
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: parse_monitor_fd
|
||||
description: Compare the string recv_buffer with "query fd:fd", the "fd"
|
||||
after character ':' is an integer indicating file descriptor. If recv_buffer
|
||||
accords with the format, the function will takes next action to see if
|
||||
fd is 0, which represents stdin, so the function ends with returned value 0.
|
||||
If fd isn't 0, compare the third argument type with ParseMonitorTypeSet,
|
||||
if equal, then get a structure variable including socket descriptor
|
||||
destined by the fd gotten from the first argument, if it's NULL, we can
|
||||
write "fd:[%d], type:[normal fd]"(%d--fd) into send_buffer, else write
|
||||
"fd:[%d], type:[%d]"(%d--fd,%d--sock_desc->m_fd_type).
|
||||
arguments: The first argument is a pointer to a string indicating request infomation.
|
||||
The second argument is a pointer to a string to store sent infomation.
|
||||
The third argument tells the kind of socket request.
|
||||
return value: 0 or 1.
|
||||
note: none
|
||||
date: 2022/8/5
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int parse_monitor_fd(char* recv_buffer, char* send_buffer, ParseMonitorType type)
|
||||
{
|
||||
int length;
|
||||
|
|
|
|||
|
|
@ -53,24 +53,6 @@ static void comm_wait_broadcast_end(SocketRequest** req_arr, int num);
|
|||
* export function definition
|
||||
************************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
function name: comm_proxy_socket
|
||||
description: This function creates a socket file descriptor whose protocol family is
|
||||
domain, protocol type is type, and protocol number is protocol. If the
|
||||
function call is successful, it will return a file descriptor that identifies
|
||||
the socket. If it fails, it will return - 1.
|
||||
arguments: The first argument specifies the protocol family, it's used as domain to
|
||||
set up network communication.
|
||||
The second argument is used to set the type of socket communication.
|
||||
The third argument is used to specify a specific type of a protocol, which
|
||||
is a type in the second argument types' type.
|
||||
return value: If the function call is successful, it will return a file descriptor that
|
||||
identifies the socket. If it fails, it will return - 1.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_socket(int domain, int type, int protocol)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -172,16 +154,6 @@ ssize_t comm_proxy_addr_recv(int sockfd, void *buf, size_t len, int flags)
|
|||
return comm_proxy_recv(sockfd, buf, len, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_close
|
||||
description: The function is used to release the resources allocated
|
||||
to the socket by the system.
|
||||
arguments: The argument is the socket file descriptor to be closed.
|
||||
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_close(int fd)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -233,18 +205,6 @@ int comm_proxy_close(int fd)
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_shutdown
|
||||
description: The function is used to release the resources allocated
|
||||
to the socket by the system.
|
||||
arguments: The first argument is a descriptor used to identify a socket.
|
||||
The second argument is used to describe which operations
|
||||
are prohibited, which determines the behavior of the function.
|
||||
return value: If the call is successful, return 0; otherwise, return - 1 and set errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_shutdown(int fd, int how)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -299,21 +259,6 @@ int comm_proxy_shutdown(int fd, int how)
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_accept
|
||||
description: This function extracts the first connection from the waiting connection queue of S, creates
|
||||
a new socket interface similar to s and returns a handle.
|
||||
arguments: The first argument is a socket descriptor, which listens for connection after comm_proxy_listen().
|
||||
The second argument is a optional pointer pointing to a buffer where the address of the
|
||||
connection entity known to the communication layer is received. The actual format of the
|
||||
addr argument is determined by the address family generated when the socket is created.
|
||||
The third argument is a optional pointer, used together with addr, pointing to the integer
|
||||
number with the length of addr address.
|
||||
return value: The return value is a new socket descriptor, which represents a new connection with the client.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -398,17 +343,6 @@ int comm_proxy_accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, in
|
|||
return comm_proxy_accept(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_connect
|
||||
description: This function is used to establish a connection with a specified socket.
|
||||
arguments: The first argument is used to identify an unconnected socket.
|
||||
The second argument is a pointer to the sockaddr structure to socket will be connected.
|
||||
The third argument is byte length of sockaddr structure.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -460,17 +394,6 @@ int comm_proxy_connect(int sockfd, const struct sockaddr *addr, socklen_t addrle
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_bind
|
||||
description: This function binds a local address with a set of interfaces.
|
||||
arguments: The first argument indicates the socket descriptor that has been established.
|
||||
The second argument is a pointer to the sockaddr structure to socket.
|
||||
The third argument is byte length of sockaddr structure.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t addrlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -498,16 +421,6 @@ int comm_proxy_bind(int sockfd, const struct sockaddr* ServerAddr, socklen_t add
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_listen
|
||||
description: This function creates a socket interface and listens for the requested connection.
|
||||
arguments: The first argument is a descriptor used to identify a bundled but unconnected socket.
|
||||
The second argument indicates the maximum length of waiting for connection queue
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_listen(int sockfd, int backlog)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -534,19 +447,6 @@ int comm_proxy_listen(int sockfd, int backlog)
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_setsockopt
|
||||
description: The function is used to set option values for sockets of any type and any state.
|
||||
arguments: The first argument is a descriptor that identifies a socket interface.
|
||||
The second argument indicates the level defined by the option.
|
||||
The third argument specifies the option to be set.
|
||||
The fourth argument is a pointer to the buffer where the new value of the option to be set is stored.
|
||||
The fifth argument indicates optval buffer length.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -576,19 +476,6 @@ int comm_proxy_setsockopt(int sockfd, int level, int optname, const void* optval
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_getsockopt
|
||||
description: The function is used to obtain the current value of the option of any type and any state socket, and store the result in optval.
|
||||
arguments: The first argument is a descriptor that identifies a socket interface.
|
||||
The second argument indicates the level defined by the option.
|
||||
The third argument specifies the socket options to be obtained.
|
||||
The fourth argument is a pointer to the buffer where the obtained option value is stored.
|
||||
The fifth argument is a pointer to the length value of optval buffer.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -617,18 +504,6 @@ int comm_proxy_getsockopt(int sockfd, int level, int optname, void* optval, sock
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_getsockname
|
||||
description: The function is used to get the name of a socket. It is used for a bundled or
|
||||
connected socket, and the local address will be returned.
|
||||
arguments: The first argument is a descriptor that identifies a socket interface.
|
||||
The second argument indicates the address of the receiving socket.
|
||||
The third argument specifies the length of the name buffer.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -655,17 +530,6 @@ int comm_proxy_getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_getpeername
|
||||
description: The function is used to obtain the foreign protocol address associated with a socket.
|
||||
arguments: The first argument is a descriptor that identifies a socket interface.
|
||||
The second argument indicates the name structure of the receiver address.
|
||||
The third argument specifies the length of the name structure.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -692,19 +556,6 @@ int comm_proxy_getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_fcntl
|
||||
description: The function can change the nature of the opened file, it provides control over descriptors.
|
||||
The argument sockfd is a descriptor operated by the argument cmd. For the value of cmd,
|
||||
fcntl can accept the third argument arg, which is a variable argument.
|
||||
arguments: The first argument is a descriptor that identifies a socket interface.
|
||||
The second argument represents the instruction to be operated.
|
||||
The third argument is a variable argument
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_fcntl(int sockfd, int cmd, ...)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -771,17 +622,6 @@ int comm_proxy_fcntl(int sockfd, int cmd, ...)
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_poll
|
||||
description: The function is used to hang the current file pointer to the waiting queue.
|
||||
arguments: The first argument is an array of struct pollfd structure type, used to store the socket descriptor whose state needs to be detected.
|
||||
The second argument is used to mark the total number of structural elements in the array fdarray;
|
||||
The third argument is the blocking time of the comm_proxy_poll function call.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
|
||||
{
|
||||
CommWaitPollParam param;
|
||||
|
|
@ -818,15 +658,6 @@ int comm_proxy_poll(struct pollfd* fdarray, unsigned long nfds, int timeout)
|
|||
return param.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_epoll_create
|
||||
description: The function is used to create a handle to epoll.
|
||||
arguments: The only argument size is used to tell the kernel how many listeners there are.
|
||||
return value: Returns a file descriptor that points to the newly created epoll instance
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_epoll_create(int size)
|
||||
{
|
||||
/*
|
||||
|
|
@ -856,21 +687,6 @@ int comm_proxy_epoll_create1(int flag)
|
|||
return comm_proxy_epoll_create(1);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_epoll_ctl
|
||||
description: This system call performs control operations on the epoll instance referenced
|
||||
by the file descriptor epfd. It requires the operation op to execute the target
|
||||
file descriptor fd. It's used as epoll's event registration function, it adds,
|
||||
modifies, or deletes events of interest to the epoll object.
|
||||
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
|
||||
The second argument indicates the actions to be taken, such as registering events.
|
||||
The third argument is associated file descriptor.
|
||||
The fourth argument is a pointer of type struct epoll_event, used to tell the kernel what events and actions to listen for.
|
||||
return value: The return value is 0 if succeed, - 1 is returned for failure and error reason is stored in errno.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
|
||||
{
|
||||
SocketRequest req;
|
||||
|
|
@ -1110,23 +926,6 @@ int comm_proxy_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
|
|||
return result.s_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: comm_proxy_epoll_wait
|
||||
description: Wait for IO events on the specified epoll file descriptor.
|
||||
arguments: The first argument is a specific file descriptor for epoll generated by epoll_ create.
|
||||
The second argument is a pointer to type epoll_ event structure, but it is now used
|
||||
as a container to get the collection of events from the kernel.
|
||||
The third argument is used to tell how large the container is (number of event
|
||||
array members), that is, the number of events that can be processed each time.
|
||||
The fourth argument is the timeout value for waiting for IO events.
|
||||
return value: When successful, comm_proxy_epoll_wait() returns the number of file descriptors
|
||||
ready for the requested IO. Returns zero if no file descriptor is ready within the
|
||||
requested timeout milliseconds. When an error occurs, comm_proxy_epoll_wait()
|
||||
returns - 1 and sets errno correctly.
|
||||
note: none
|
||||
date: 2022/8/8
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int comm_proxy_epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)
|
||||
{
|
||||
CommWaitEpollWaitParam param;
|
||||
|
|
|
|||
|
|
@ -81,27 +81,6 @@ void mc_tcp_set_keepalive(int fd)
|
|||
mc_tcp_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (char*)&count, sizeof(count));
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_get_peer_name
|
||||
description: This function is used to obtain the host IP and port number of the host bound to the specific socket.
|
||||
arguments: The first argument is a descriptor to a specified socket.
|
||||
The second argument is used to store the host IP address bound to the socket determined by the first parameter, in dotted decimal.
|
||||
The third parameter is used to store the port number bound to a specific socket, in the order of host bytes.
|
||||
return value: Return 0 if the function runs successfully.
|
||||
When the call to the getpeername() function fails:
|
||||
1、Return EBADF if the socket argument is not a valid file descriptor.
|
||||
2、Return EINVAL if the socket has been shut down.
|
||||
3、Return ENOTCONN if the socket is not connected or otherwise has not had the peer pre-specified.
|
||||
4、Return ENOTSOCK if the socket argument does not refer to a socket.
|
||||
5、Return EOPNOTSUPP if the operation is not supported for the socket protocol.
|
||||
6、Return ENOBUFS if insufficient resources were available in the system to complete the call.
|
||||
Return -2 when the host IP address belongs to IPv4 type, it fails to convert it to dotted decimal.
|
||||
Return -3 when the host IP address belongs to IPv6 type, it fails to convert it to dotted decimal.
|
||||
Return -4 when the error type is not any of the above.
|
||||
note: Allocate a certain amount of memory space for the host and port pointers respectively in advance.
|
||||
date: 2022/8/9
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_get_peer_name(int fd, char* host, int* port)
|
||||
{
|
||||
struct sockaddr peeraddr = {0};
|
||||
|
|
@ -142,17 +121,6 @@ int mc_tcp_set_cloexec(int fd)
|
|||
return set_socketopt(fd, 1, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_accept
|
||||
description: This function will block the process by default until a client connection is established and returns a new available socket.
|
||||
arguments: The first argument is a socket descriptor to a specific socket.
|
||||
The second argument is a result parameter, which is used to accept a return value that specifies the address of the client.
|
||||
The third argument is also a result argument, which is used to accept the size of the sockaddr structure. It indicates the number of bytes occupied by the sockaddr structure.
|
||||
return value: Return a value less than 0 if an error occurred when call the function accept4(), else return the new fd of socket.
|
||||
note: none
|
||||
date: 2022/8/9
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
|
||||
{
|
||||
int new_fd;
|
||||
|
|
@ -179,17 +147,6 @@ again:
|
|||
return (new_fd);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_bind
|
||||
description: This function binds the specified socket to a specific IP address and port.
|
||||
arguments: The first argument indicates the socket descriptor that has been established.
|
||||
The second argument is a pointer to the sockaddr structure to socket.
|
||||
The third argument is byte length of sockaddr structure.
|
||||
return value: Return errno, the return value is 0 if succeed, else one of other error types is returned for failure.
|
||||
note: none
|
||||
date: 2022/8/9
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_bind(int fd, const struct sockaddr* sa, socklen_t salen)
|
||||
{
|
||||
int error = -1;
|
||||
|
|
@ -234,20 +191,6 @@ static void mc_tcp_do_listen(int fd, int backlog)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_read_block
|
||||
description: This function receives data from the other end of TCP in a blocking manner, the receiving
|
||||
process will not end until the data of size byte length is successfully received or a real error occurs in the receiving process
|
||||
arguments: The first argument indicates the specific socket that has been established.
|
||||
The second argument is a pointer to memory area, we use it to store received data.
|
||||
The third argument is byte length of the memory area pointed to by the data pointer.
|
||||
The fourth argument specifies additional operations in addition to the read operation.
|
||||
return value: If there is no error, it returns the byte length of the successfully read data. If an error
|
||||
occurs, return - 1.
|
||||
note: When the data is read successfully, the byte length of the data is greater than 0.
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_read_block(int fd, void* data, int size, int flags)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
@ -318,20 +261,6 @@ int mc_tcp_read_block(int fd, void* data, int size, int flags)
|
|||
return (size_t)nbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_read_nonblock
|
||||
description: This function receives data from the other end of TCP in a non blocking manner,
|
||||
the data receiving process is only performed once.
|
||||
arguments: The first argument indicates the specific socket that has been established.
|
||||
The second argument is a pointer to memory area, we use it to store received data.
|
||||
The third argument is byte length of the memory area pointed to by the data pointer.
|
||||
The fourth argument specifies additional operations in addition to the read operation.
|
||||
return value: If the error type is one of the errors represented by EAGAIN, EWOULDBLOCK and EINTR, it returns 0;
|
||||
other error types return - 1; if there is no error, it returns the byte length of the successfully read data.
|
||||
note: When the data is read successfully, the byte length of the data is greater than 0.
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
@ -377,17 +306,6 @@ int mc_tcp_read_nonblock(int fd, void* data, int size, int flags)
|
|||
return (size_t)nbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_check_socket
|
||||
description: This function binds the specified socket to a specific IP address and port.
|
||||
arguments: The only argument indicates the specific socket that has been established.
|
||||
return value: Return -1 if when the recv function wait for the protocol to receive data,
|
||||
the other end of TCP closes the connection or a real error occurred while
|
||||
reading data. In other cases, 0 is returned.
|
||||
note: none
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_check_socket(int sock)
|
||||
{
|
||||
char temp_buf[IOV_DATA_SIZE] = {0};
|
||||
|
|
@ -450,19 +368,6 @@ int mc_tcp_check_socket(int sock)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_write_block
|
||||
description: This function writes data to the specified socket in blocking mode, the sending process
|
||||
will not end until all the data are successfully sent or a real error occurs during the sending process
|
||||
arguments: The first argument indicates the specific socket that has been established.
|
||||
The second argument is a pointer to memory area, we use it to store data to be sent.
|
||||
The third argument is byte length of data to be sent.
|
||||
return value: If there is no error, it returns the byte length of the successfully sent data. If an error
|
||||
occurs, return - 1.
|
||||
note: none
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_write_block(int fd, const void* data, int size)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
@ -521,21 +426,6 @@ int mc_tcp_write_block(int fd, const void* data, int size)
|
|||
return (size_t)nSend;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_write_noblock
|
||||
description: This function writes data to the specified socket in non blocking mode,
|
||||
the data transmission process is only performed once.
|
||||
arguments: The first argument indicates the specific socket that has been established.
|
||||
The second argument is a pointer to memory area, we use it to store data to be sent.
|
||||
The third argument is byte length of data to be sent.
|
||||
return value: If the sending fails but the failure reason is one of the error types represented by EAGAIN、
|
||||
EWOULDBLOCK、EINTR 和ENOBUFS, then 0 is returned; if the error type is other, then - 1
|
||||
is returned; If the transmission is successful, the byte length of the successfully transmitted
|
||||
data is returned
|
||||
note: none
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_write_noblock(int fd, const void* data, int size)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
@ -629,17 +519,6 @@ int mc_tcp_addr_init(const char* host, int port, struct sockaddr_storage* ss, in
|
|||
return (error == 1) ? 0 : error;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_connect_nonblock
|
||||
description: This function is used to create a socket and establish a connection with the port of the specified host
|
||||
in non blocking mode.
|
||||
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
|
||||
return value: If the connection is successfully established, the file descriptor of the socket connected to the port of the
|
||||
specified host is returned; otherwise, - 1 is returned.
|
||||
note: none
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_connect_nonblock(const char* host, int port)
|
||||
{
|
||||
int sockfd, n;
|
||||
|
|
@ -687,18 +566,6 @@ int mc_tcp_connect_nonblock(const char* host, int port)
|
|||
return sockfd;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_connect
|
||||
description: This function first obtains the ports of other hosts with the same domain name stored through
|
||||
the ports of specific hosts, and creates a socket to establish a connection with an appropriate
|
||||
one of these ports.
|
||||
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
|
||||
return value: The key is to successfully establish a connection with a port in the linked list. If the connection is successful, the
|
||||
socket file descriptor connected to it will be returned. Otherwise, it will return - 1.
|
||||
note: We finally get the infomation of the ports of other hosts through a linked list.
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_connect(const char* host, int port)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
@ -789,18 +656,6 @@ retry:
|
|||
return (sockfd);
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_tcp_listen
|
||||
description: This function first obtains the ports of other hosts with the same domain name stored through
|
||||
the ports of specific hosts, and creates a socket to bind with an appropriate one of these ports.
|
||||
arguments: The first parameter specifies a specific host, and the second parameter specifies a specific port of the host.
|
||||
The third is used to store size of protocol address.
|
||||
return value: The key lies in the successful binding with a port in the linked list. If the binding is successful, the socket file
|
||||
descriptor connected to it will be returned. Otherwise, it will return - 1.
|
||||
note: We finally get the infomation of the ports of other hosts through a linked list.
|
||||
date: 2022/8/10
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_tcp_listen(const char* host, int port, socklen_t* addrlenp)
|
||||
{
|
||||
#ifdef LIBCOMM_FAULT_INJECTION_ENABLE
|
||||
|
|
|
|||
|
|
@ -232,21 +232,6 @@ static int gs_tcp_write_noblock(int node_idx, int sock, const char* msg, int msg
|
|||
|
||||
return send_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: libcomm_tcp_send
|
||||
description: This function is used to send the message including message head and message body, to
|
||||
a specific socket.
|
||||
arguments: send_ info is a pointer of LibcommRecvInfo* type, pointing to the memory storing the data
|
||||
waiting to be sent.
|
||||
return value: Data will be sent twice in total. Before sending data, if it is found that the socket to receive
|
||||
data is not matched with the specified socket, then - 1 will be returned; If the sending of
|
||||
message head or message body fails, return - 1; If the function runs successfully, the byte
|
||||
length of the message body sent successfully is returned.
|
||||
note: none
|
||||
date: 2022/8/11
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
static int libcomm_tcp_send(LibcommSendInfo* send_info)
|
||||
{
|
||||
int sock = send_info->socket;
|
||||
|
|
@ -337,19 +322,6 @@ static int libcomm_tcp_send(LibcommSendInfo* send_info)
|
|||
return send_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: libcomm_tcp_recv_noidx
|
||||
description: This function is used to store the message transmitted from the sender, specifically to obtain
|
||||
the message from a specific socket.
|
||||
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
|
||||
received.
|
||||
return value: If it fails to allocate memory for iov_ Item, return RECV_MEM_ERROR;
|
||||
If it fails to obtain data, no matter it is a message header or a message body, from the specified socket in blocking mode, return RECV_NET_ERROR;
|
||||
If the function runs successfully, the byte length of the read message body is returned.
|
||||
note: none
|
||||
date: 2022/8/11
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
|
||||
{
|
||||
int sock = recv_info->socket;
|
||||
|
|
@ -399,23 +371,6 @@ static int libcomm_tcp_recv_noidx(LibcommRecvInfo* recv_info)
|
|||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: libcomm_tcp_recv
|
||||
description: This function is used to store the message transmitted from the sender, specifically to obtain
|
||||
the message from a specific socket.
|
||||
arguments: recv_ info is a pointer of LibcommRecvInfo* type, pointing to the memory to store the data
|
||||
received.
|
||||
return value: If the receiver has not been determined, call libcomm_tcp_recv_noidx() and take the return value
|
||||
of (libcomm_tcp_recv_noidx (recv_info)); Return RECV_NET_ERROR if there is an error in the
|
||||
process of reading the message heade or message body; If there is no data readable in the
|
||||
receiving buffer of the specified socket at this time or the number of bytes of the data that
|
||||
has been read is not enough, it returns RECV_NEED_RETRY; If iov_item is NULL, it returns
|
||||
RECV_MEM_ERROR if it fails to allocate space for it; If the function runs successfully, then
|
||||
the byte length of the read message head and message body is returned.
|
||||
note: none
|
||||
date: 2022/8/11
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int libcomm_tcp_recv(LibcommRecvInfo* recv_info)
|
||||
{
|
||||
MsgHead* msg_head = NULL;
|
||||
|
|
|
|||
|
|
@ -137,16 +137,6 @@ static int LibCommClientSSLDHVerifyCb(const SSL* s, const SSL_CTX* ctx,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: ssl_cipher_list2string
|
||||
description: This function converts the two-dimensional character array storing the key into a one-dimensional character array.
|
||||
arguments: The first argument represents the two-dimensional character array to be converted.
|
||||
The second argument indicates the number of one-dimensional arrays contained in this two-dimensional array.
|
||||
return value: Returns a pointer to the one-dimensional character array that has been successfully converted. If the conversion fails, NULL is returned.
|
||||
note: none
|
||||
date: 2022/8/12
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
static char* ssl_cipher_list2string(const char* ciphers[], const int num) {
|
||||
int i;
|
||||
int catlen = 0;
|
||||
|
|
@ -237,20 +227,7 @@ char* LibCommErrMessage(void) {
|
|||
return errBuf;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: LibCommClientSSLPasswd
|
||||
description: As a client, this function is used to detect whether there is a file with a valid key in the specified
|
||||
directory and whether there is permission to operate it. If so, the password will be decrypted by
|
||||
using the file.
|
||||
arguments: The first parameter is a pointer of type (SSL *).
|
||||
The second parameter is used to obtain the absolute path of the certificate file.
|
||||
The third parameter represents the user name.
|
||||
The fourth parameter is a pointer of type (libcommconn *), whose member variable contains the ciphertext to be decrypted.
|
||||
return value: If the path is empty or does not have operation permission to the directory where the certificate file is located, a non-1 value is returned; otherwise, 0 is returned.
|
||||
note: none
|
||||
date: 2022/8/12
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
|
||||
int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * userName, LibCommConn * conn) {
|
||||
char* CertFilesDir = NULL;
|
||||
char CertFilesPath[MAXPATH] = {0};
|
||||
|
|
@ -274,8 +251,8 @@ int LibCommClientSSLPasswd(SSL* pstContext, const char * path, const char * user
|
|||
|
||||
/*check whether the cipher and rand files begins with userName exist.
|
||||
if exist, decrypt it.
|
||||
if not,decrypt the default cipher and rand files begins with client.
|
||||
Because,for every client user may own certification and private key*/
|
||||
if not,decrypt the default cipher and rand files begins with client%.
|
||||
Because,for every client user mayown certification and private key*/
|
||||
if (NULL == userName) {
|
||||
retval = LibCommClientCheckPermissionCipherFile(CertFilesDir, conn, NULL);
|
||||
if (retval != 1)
|
||||
|
|
|
|||
|
|
@ -33,16 +33,6 @@ inline int mc_lqueue_item_size(struct mc_lqueue_item* q_item)
|
|||
return q_item->element.data->iov_len;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_lqueue_add
|
||||
description: Add an element to a specific queue.
|
||||
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
|
||||
The second parameter points to the element to be added to the queue.
|
||||
return value: Returns 1 if the element is successfully added to the queue, otherwise returns - 1.
|
||||
note: none
|
||||
date: 2022/8/13
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
|
||||
{
|
||||
if (q == NULL || q_item == NULL) {
|
||||
|
|
@ -70,17 +60,6 @@ int mc_lqueue_add(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_lqueue_remove
|
||||
description: Remove the head element in a specific queue.
|
||||
arguments: The first parameter is a pointer of type (mc_lqueue *), whose member variable list points to the target queue.
|
||||
The second parameter points to the queue head element used to store the removal from the queue.
|
||||
return value: Return NULL if an error occurs during the removal of the queue head element, otherwise a pointer to
|
||||
the successfully removed queue head element is returned.
|
||||
note: none
|
||||
date: 2022/8/13
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_item* q_item)
|
||||
{
|
||||
if (q == NULL) {
|
||||
|
|
@ -110,18 +89,6 @@ struct mc_lqueue_item* mc_lqueue_remove(struct mc_lqueue* q, struct mc_lqueue_it
|
|||
return q_item;
|
||||
}
|
||||
|
||||
/*
|
||||
function name: mc_lqueue_init
|
||||
description: This function is used to open an area in the memory area. One part of the area is used to store a queue with
|
||||
a certain specification, and the other part is used to store the information of the queue, such as the specification
|
||||
and the number of elements. Finally, a pointer to the area is returned.
|
||||
arguments: This parameter specifies that the maximum number of elements that the queue can hold is size, but this does
|
||||
not mean that the size of the queue is so large at the beginning.
|
||||
return value: If the function runs successfully, it returns a pointer to the opened memory area; otherwise, it returns NULL.
|
||||
note: none
|
||||
date: 2022/8/13
|
||||
contact tel: 18720816902
|
||||
*/
|
||||
struct mc_lqueue* mc_lqueue_init(unsigned long size)
|
||||
{
|
||||
if (size == 0) {
|
||||
|
|
|
|||
|
|
@ -222,7 +222,6 @@ NON_EXEC_STATIC void PercentileMain()
|
|||
g_instance.stat_cxt.force_process = false;
|
||||
sleep(SLEEP_INTERVAL);
|
||||
}
|
||||
elog(LOG, "instrumention percentile ended");
|
||||
gs_thread_exit(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,23 +14,11 @@ import os
|
|||
|
||||
from . import feature_mapping
|
||||
from . import features
|
||||
# To import file feature_mapping and features from parent folder
|
||||
|
||||
#function name: load_feature_lib
|
||||
#description: Print the variable FEATURE_LIB in the file-- features
|
||||
#return value: The value of FEATURE_LIB
|
||||
#date: 2022/8/2
|
||||
#contact: 1865997821
|
||||
|
||||
def load_feature_lib():
|
||||
return features.FEATURE_LIB
|
||||
|
||||
#function name: get_feature_mapper
|
||||
#description: Get the item and value of a dictionary type in the file-- feature_mapping and output it as a generator.
|
||||
#return value: The item and value in _dict_ variable
|
||||
#note:Dictionary key-value pairs must start with C then the item and value will be return.
|
||||
#date: 2022/8/2
|
||||
#contact: 1865997821
|
||||
|
||||
def get_feature_mapper():
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -11,27 +11,22 @@
|
|||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
# See the Mulan PSL v2 for more details.
|
||||
import csv
|
||||
#import csv packet
|
||||
from collections import defaultdict
|
||||
from typing import List
|
||||
# To import defaultdict in the parent floder collections and List in the parent floder typing
|
||||
|
||||
import numpy as np
|
||||
# import numpy packet as the name np
|
||||
|
||||
from ..analyzer import _euclid_distance as euclid_distance
|
||||
from dbmind.common.utils import ExceptionCatch
|
||||
#To import private function-- _euclid_distance as euclid_distance
|
||||
|
||||
#function name: calculate_weight
|
||||
#description: This function will output feature_weight (= residual_vector / the sum of residual_vector)
|
||||
#The data used for the calculation is from the features_labels_dict, and the key value pairs of the features_labels_dict are filtered
|
||||
#arguments: np.ndarray and np.ndarray
|
||||
#return value: weight_matrix
|
||||
#date: 2022/8/2
|
||||
#contact: 1865997821
|
||||
|
||||
def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
|
||||
"""
|
||||
Calculate weight matrix based on feature set
|
||||
:param features: feature set
|
||||
:param labels: label set
|
||||
:return: weight_matrix
|
||||
"""
|
||||
normalize_features, normalize_labels = [], []
|
||||
features_labels_dict = defaultdict(list)
|
||||
for i in range(len(labels)):
|
||||
|
|
@ -61,16 +56,6 @@ def calculate_weight(features: np.ndarray, labels: np.ndarray) -> List:
|
|||
return weight_matrix
|
||||
|
||||
|
||||
# function name: build_model
|
||||
# description: Create two variables-- features and labels.There are refer to two numpy array(all elements are zero)
|
||||
# The features array's size is feature_number and dimension is feature_dimension
|
||||
# This function will read the two arrays and write it as a matrix in a csv file(the save path is './features_new.npz')
|
||||
# And then it will call the function calculate_weight to calculate the matrix
|
||||
# arguments: feature_path, feature_number, feature_dimension
|
||||
# return value: None
|
||||
# note:A ExceptionCatch function modifier is used
|
||||
# date: 2022/8/2
|
||||
#contact: 1865997821
|
||||
@ExceptionCatch(strategy='exit', name='FEATURE')
|
||||
def build_model(feature_path: str, feature_number: int, feature_dimension: int,
|
||||
save_path: str = './features_new.npz') -> None:
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@
|
|||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
# See the Mulan PSL v2 for more details.
|
||||
|
||||
#function name: detect
|
||||
#description: if the method is "bool" type, then call the functions sum_detect、avg_detect、ks_detect to diagnose errors
|
||||
#These functions are in the parent slow_sql/significance_detection
|
||||
#arguments: data1(array), data2(array), method
|
||||
#return value: bool type
|
||||
#date: 2022/8/2
|
||||
#contact: 1865997821
|
||||
|
||||
def detect(data1, data2, method='bool', threshold=0.01, p_value=0.5):
|
||||
if method == 'bool':
|
||||
|
|
|
|||
|
|
@ -12,16 +12,17 @@
|
|||
# See the Mulan PSL v2 for more details.
|
||||
|
||||
alpha = 1e-10
|
||||
#Define a minimum number of errors
|
||||
|
||||
#function name: detect
|
||||
#description: Calculate whether the data has abrupt changes based on the average value
|
||||
#arguments: data1, data2, threshold,method
|
||||
#return value: bool
|
||||
#date: 2022/8/
|
||||
#contact: 1865997821
|
||||
|
||||
def detect(data1, data2, threshold=0.5, method='bool'):
|
||||
"""
|
||||
Calculate whether the data has abrupt changes based on the average value
|
||||
:param data1: input data array
|
||||
:param data2: input data array
|
||||
:param threshold: Mutation rate
|
||||
:param method: The way to calculate the mutation
|
||||
:return: bool
|
||||
"""
|
||||
if not isinstance(data1, list) or not isinstance(data2, list):
|
||||
raise TypeError("The format of the input data is wrong.")
|
||||
avg1 = sum(data1) / len(data1) if data1 else 0
|
||||
|
|
|
|||
|
|
@ -13,14 +13,8 @@
|
|||
import sys
|
||||
|
||||
from .cli import DBMindRun
|
||||
#To import DBMindRun method from the parent file cli
|
||||
|
||||
#function name: main
|
||||
#description: Get the system command parameters, pass to the DBMindRun and call this function,if an InterruptedError is reported, the program will exit( sys.exit(1)).
|
||||
#arguments: None
|
||||
#return value: None
|
||||
#date: 2022/8/3
|
||||
#contact: 1865997821
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
DBMindRun(sys.argv[1:])
|
||||
|
|
|
|||
|
|
@ -55,12 +55,7 @@ CONFIG_OPTIONS = {
|
|||
'LOG-level': ['DEBUG', 'INFO', 'WARNING', 'ERROR']
|
||||
}
|
||||
|
||||
#function name: check_config_validity
|
||||
#description: Checks the validity of the passed parameter
|
||||
#arguments: section, option, value
|
||||
#return value: bool and string
|
||||
#date: 2022/8/
|
||||
#contact: 1865997821
|
||||
|
||||
def check_config_validity(section, option, value):
|
||||
config_item = '%s-%s' % (section, option)
|
||||
# exceptional cases:
|
||||
|
|
@ -92,16 +87,6 @@ def check_config_validity(section, option, value):
|
|||
return True, None
|
||||
|
||||
|
||||
|
||||
#function name: load_sys_configs
|
||||
#description: Create and load the modification file
|
||||
#arguments: The configuration to modify
|
||||
#return value: a new configuration file
|
||||
#note:To facilitate the user to modify the configuration items through the
|
||||
#configuration file easily, we add inline comments to the file, but we need to remove the inline comments while parsing.
|
||||
#Otherwise, it will cause the read configuration items to be wrong.
|
||||
#date: 2022/8/
|
||||
#contact: 1865997821
|
||||
def load_sys_configs(confile):
|
||||
# Note: To facilitate the user to modify the configuration items through the
|
||||
# configuration file easily, we add inline comments to the file, but we need
|
||||
|
|
@ -111,8 +96,6 @@ def load_sys_configs(confile):
|
|||
with open(file=confile, mode='r') as fp:
|
||||
configs.read_file(fp)
|
||||
|
||||
|
||||
# Define a class that encapsulates the modification item
|
||||
class ConfigWrapper(object):
|
||||
def __getattribute__(self, name):
|
||||
try:
|
||||
|
|
@ -139,7 +122,7 @@ def load_sys_configs(confile):
|
|||
|
||||
return ConfigWrapper()
|
||||
|
||||
# Defines a class that updates the encapsulated modification file
|
||||
|
||||
class ConfigUpdater:
|
||||
def __init__(self, filepath):
|
||||
self.config = ConfigParser(inline_comment_prefixes=None)
|
||||
|
|
@ -187,7 +170,7 @@ class ConfigUpdater:
|
|||
self.fp.flush()
|
||||
self.fp.close()
|
||||
|
||||
# Defines a class that dynamically displays a modified item
|
||||
|
||||
class DynamicConfig:
|
||||
@staticmethod
|
||||
def get(*args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ except ImportError:
|
|||
|
||||
SKIP_LIST = ('COMMENT', 'LOG')
|
||||
|
||||
# The global variable acts as a switch that controls whether the program runs
|
||||
dbmind_master_should_exit = False
|
||||
|
||||
|
||||
|
|
@ -58,16 +57,8 @@ def _process_clean(force=False):
|
|||
global_vars.worker.terminate(cancel_futures=force)
|
||||
TimedTaskManager.stop()
|
||||
|
||||
#function name: signal_handler
|
||||
#description: The function processes the received signal parameters, reassigns variable x according to different signals
|
||||
#or calls other functions to complete the content indicated by signals
|
||||
#arguments: signum, frame
|
||||
#return value: bool (dbmind_master_should_exit)
|
||||
#date: 2022/8/3
|
||||
#contact: 1865997821
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
# The global variable dbmind_master_should_exit can be modified in this function to continue to play a control role
|
||||
global dbmind_master_should_exit
|
||||
|
||||
if signum == signal.SIGINT or signum == signal.SIGHUP:
|
||||
|
|
@ -157,12 +148,10 @@ class DBMindMain(Daemon):
|
|||
time.sleep(1)
|
||||
logging.info('DBMind will close.')
|
||||
|
||||
# Emptying the execution pool
|
||||
def clean(self):
|
||||
if os.path.exists(self.pid_file):
|
||||
os.unlink(self.pid_file)
|
||||
|
||||
# Reload the execution pool and solve the error
|
||||
|
||||
def reload(self):
|
||||
pid = read_dbmind_pid_file(self.pid_file)
|
||||
if pid > 0:
|
||||
|
|
|
|||
|
|
@ -27,17 +27,6 @@ def do_after(rt_result):
|
|||
def do_exception(exception):
|
||||
"""Nothing"""
|
||||
|
||||
|
||||
#function name: around
|
||||
#description: Preserve the function properties and prevent an error from terminating the program
|
||||
#arguments: One or more functions
|
||||
#return value: none
|
||||
#note: Decorators are implemented in such a way that the function being decorated is actually another function (the function name and other properties change).
|
||||
#To avoid this, Python's FuncTools package provides a decorator called wraps to remove such side effects.
|
||||
#When writing a decorator, it is a good idea to wrap FuncTools before implementing it.
|
||||
#It preserves the name and properties of the original function
|
||||
#date: 2022/8/4
|
||||
#contact: 1865997821
|
||||
def around(func, *args, **kw):
|
||||
@wraps(func)
|
||||
def wrapper():
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ from typing import Optional, Iterable, Union
|
|||
from .root_cause import RootCause
|
||||
from .enumerations import ALARM_TYPES, ALARM_LEVEL
|
||||
|
||||
#Define an Alarm class that takes the error parameters entered by the user and displays the error content and cause
|
||||
#method:Display the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
|
||||
#note:The property decorator turns a method into a property call.(root_causes、suggestions)
|
||||
#date:2022/8/4
|
||||
#contact:18365997821
|
||||
|
||||
class Alarm:
|
||||
def __init__(self,
|
||||
host: Union[str],
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@
|
|||
# See the Mulan PSL v2 for more details.
|
||||
from .root_cause import RootCause
|
||||
|
||||
#Define anSlowQuery class thatSlow query accepts user input commands and performs operations on the database
|
||||
#method:Display the error content and suggestions, and retrieve suggestions provided by the system. If there are no suggestions, return “ no suggestions”
|
||||
#note:The property decorator turns a method into a property call.(root_causes、suggestions)
|
||||
#date:2022/8/4
|
||||
#contact:18365997821
|
||||
|
||||
class SlowQuery:
|
||||
def __init__(self, db_host, db_port, db_name, schema_name, query, start_timestamp, duration_time,
|
||||
hit_rate=None, fetch_rate=None, cpu_time=None, data_io_time=None, template_id=None, sort_count=None,
|
||||
|
|
|
|||
|
|
@ -18,19 +18,13 @@ import psycopg2
|
|||
from .execute_factory import ExecuteFactory
|
||||
from .execute_factory import IndexInfo
|
||||
|
||||
#class name: DriverExecute (Inherits from the parent class ExecuteFactory)
|
||||
#description: The SQL statement performs the operations associated with the call
|
||||
#date: 2022/8/10
|
||||
#contact: 1865997821
|
||||
|
||||
class DriverExecute(ExecuteFactory):
|
||||
def __init__(self, *arg):
|
||||
#Call the arguments of the parent class __init__ method
|
||||
super(DriverExecute, self).__init__(*arg)
|
||||
self.conn = None
|
||||
self.cur = None
|
||||
|
||||
#Connecting to the database
|
||||
def init_conn_handle(self):
|
||||
self.conn = psycopg2.connect(dbname=self.dbname,
|
||||
user=self.user,
|
||||
|
|
@ -39,7 +33,6 @@ class DriverExecute(ExecuteFactory):
|
|||
port=self.port)
|
||||
self.cur = self.conn.cursor()
|
||||
|
||||
#If an error occurs after the SQL statement is executed, the error information is reported to the user
|
||||
def execute(self, sql):
|
||||
try:
|
||||
self.cur.execute(sql)
|
||||
|
|
@ -48,13 +41,11 @@ class DriverExecute(ExecuteFactory):
|
|||
except Exception:
|
||||
self.conn.commit()
|
||||
|
||||
#Disconnecting from the database
|
||||
def close_conn(self):
|
||||
if self.conn and self.cur:
|
||||
self.cur.close()
|
||||
self.conn.close()
|
||||
|
||||
#Check whether multiple nodes exist
|
||||
def is_multi_node(self):
|
||||
self.init_conn_handle()
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -13,11 +13,6 @@
|
|||
|
||||
import re
|
||||
|
||||
#class name: IndexInfo
|
||||
#description: Define information about table indexes
|
||||
#methods: __init__
|
||||
#date: 2022/8/10
|
||||
#contact: 1865997821
|
||||
|
||||
class IndexInfo:
|
||||
def __init__(self, schema, table, indexname, columns, indexdef):
|
||||
|
|
@ -29,9 +24,7 @@ class IndexInfo:
|
|||
self.primary_key = False
|
||||
self.redundant_obj = []
|
||||
|
||||
#class name: ExecuteFactory
|
||||
#date: 2022/8/10
|
||||
#contact: 1865997821
|
||||
|
||||
class ExecuteFactory:
|
||||
def __init__(self, dbname, user, password, host, port, schema, multi_node, max_index_storage):
|
||||
self.dbname = dbname
|
||||
|
|
@ -43,11 +36,11 @@ class ExecuteFactory:
|
|||
self.max_index_storage = max_index_storage
|
||||
self.multi_node = multi_node
|
||||
|
||||
# Record redundant indexes
|
||||
@staticmethod
|
||||
def record_redundant_indexes(cur_table_indexes, redundant_indexes):
|
||||
cur_table_indexes = sorted(cur_table_indexes,
|
||||
key=lambda index_obj: len(index_obj.columns.split(',')))
|
||||
# record redundant indexes
|
||||
for pos, index in enumerate(cur_table_indexes[:-1]):
|
||||
is_redundant = False
|
||||
for candidate_index in cur_table_indexes[pos + 1:]:
|
||||
|
|
@ -59,7 +52,6 @@ class ExecuteFactory:
|
|||
if is_redundant:
|
||||
redundant_indexes.append(index)
|
||||
|
||||
#Match the name of the table against the index of the query
|
||||
@staticmethod
|
||||
def match_table_name(table_name, query_index_dict):
|
||||
for elem in query_index_dict.keys():
|
||||
|
|
@ -74,7 +66,6 @@ class ExecuteFactory:
|
|||
return False, table_name
|
||||
return True, table_name
|
||||
|
||||
#Retrieves a valid index based on the regular expression, adding the corresponding index and empty element if none exists
|
||||
@staticmethod
|
||||
def get_valid_indexes(record, hypoid_table_column, valid_indexes):
|
||||
tokens = record.split(' ')
|
||||
|
|
@ -97,7 +88,6 @@ class ExecuteFactory:
|
|||
if columns not in valid_indexes[table_name]:
|
||||
valid_indexes[table_name].append((columns, index_type))
|
||||
|
||||
#Record invalid SQL statements and returns the corresponding help information that matches the corresponding SQL statement
|
||||
@staticmethod
|
||||
def record_ineffective_negative_sql(candidate_index, obj, ind):
|
||||
cur_table = candidate_index.table
|
||||
|
|
@ -135,7 +125,6 @@ class ExecuteFactory:
|
|||
candidate_index.ineffective_pos.append(ind)
|
||||
candidate_index.total_sql_num += obj.frequency
|
||||
|
||||
#Returns the last input and the corresponding result
|
||||
@staticmethod
|
||||
def match_last_result(table_name, index_column, history_indexes, history_invalid_indexes):
|
||||
for column in history_indexes.get(table_name, dict()):
|
||||
|
|
@ -153,7 +142,6 @@ class ExecuteFactory:
|
|||
if not history_indexes[table_name]:
|
||||
del history_indexes[table_name]
|
||||
|
||||
#Correcting SQL statements
|
||||
@staticmethod
|
||||
def make_single_advisor_sql(ori_sql):
|
||||
sql = 'select gs_index_advise(\''
|
||||
|
|
|
|||
|
|
@ -23,16 +23,12 @@ from .execute_factory import IndexInfo
|
|||
|
||||
BASE_CMD = None
|
||||
|
||||
#class name: GSqlExecute
|
||||
#description: Solve the optimization problem of GSQL statement execution
|
||||
#date: 2022/8/11
|
||||
#contact: 1865997821
|
||||
|
||||
class GSqlExecute(ExecuteFactory):
|
||||
def __init__(self, *args):
|
||||
super(GSqlExecute, self).__init__(*args)
|
||||
|
||||
def init_conn_handle(self):
|
||||
#define a global variable BASE_CMD,it is a connection command statement
|
||||
global BASE_CMD
|
||||
BASE_CMD = 'gsql -p ' + str(self.port) + ' -d ' + self.dbname
|
||||
if self.host:
|
||||
|
|
@ -42,7 +38,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
if self.password:
|
||||
BASE_CMD += ' -W ' + self.password
|
||||
|
||||
#Run the shell command in BASE_CMD
|
||||
def run_shell_cmd(self, target_sql_list):
|
||||
cmd = BASE_CMD + ' -c \"'
|
||||
if self.schema:
|
||||
|
|
@ -52,7 +47,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
cmd += '\"'
|
||||
proc = subprocess.Popen(
|
||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
#Read data from stdout and stderr,If an error message is displayed, an error message is displayed
|
||||
(stdout, stderr) = proc.communicate()
|
||||
stdout, stderr = stdout.decode(), stderr.decode()
|
||||
if 'gsql: FATAL:' in stderr or 'failed to connect' in stderr:
|
||||
|
|
@ -80,7 +74,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
print(e.output.decode(), file=sys.stderr)
|
||||
return int(ret.decode().strip().split()[2]) > 0
|
||||
|
||||
#Parse the recommended result returned
|
||||
@staticmethod
|
||||
def parse_single_advisor_result(res, table_index_dict):
|
||||
if len(res) > 2 and res[0:2] == ' (':
|
||||
|
|
@ -190,7 +183,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
total_cost = 0
|
||||
found_plan = False
|
||||
hypo_index = False
|
||||
# create hypo-indexes
|
||||
for line in res:
|
||||
if 'QUERY PLAN' in line:
|
||||
found_plan = True
|
||||
|
|
@ -230,7 +222,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
i += 1
|
||||
return total_cost
|
||||
|
||||
#Production workflows consume report files
|
||||
def estimate_workload_cost_file(self, workload, index_config=None, ori_indexes_name=None):
|
||||
sql_file = str(time.time()) + '.sql'
|
||||
is_computed = False
|
||||
|
|
@ -273,7 +264,6 @@ class GSqlExecute(ExecuteFactory):
|
|||
|
||||
return total_cost
|
||||
|
||||
#Check for empty indexes and note them to optimize the table structure
|
||||
def check_useless_index(self, history_indexes, history_invalid_indexes):
|
||||
schemas = [elem.lower()
|
||||
for elem in filter(None, self.schema.split(','))]
|
||||
|
|
|
|||
|
|
@ -26,11 +26,9 @@ import logging
|
|||
try:
|
||||
from .dao.gsql_execute import GSqlExecute
|
||||
from .dao.execute_factory import ExecuteFactory
|
||||
from .mcts import MCTS
|
||||
except ImportError:
|
||||
from dao.gsql_execute import GSqlExecute
|
||||
from dao.execute_factory import ExecuteFactory
|
||||
from mcts import MCTS
|
||||
|
||||
ENABLE_MULTI_NODE = False
|
||||
SAMPLE_NUM = 5
|
||||
|
|
@ -194,12 +192,9 @@ class IndexAdvisor:
|
|||
self.workload_used_index))
|
||||
if DRIVER:
|
||||
self.db.close_conn()
|
||||
if MAX_INDEX_STORAGE:
|
||||
opt_config = MCTS(self.workload_info[0], atomic_config_total, candidate_indexes,
|
||||
MAX_INDEX_STORAGE, MAX_INDEX_NUM)
|
||||
else:
|
||||
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
|
||||
candidate_indexes, self.index_cost_total[0])
|
||||
|
||||
opt_config = greedy_determine_opt_config(self.workload_info[0], atomic_config_total,
|
||||
candidate_indexes, self.index_cost_total[0])
|
||||
self.retain_lower_cost_index(candidate_indexes)
|
||||
if len(opt_config) == 0:
|
||||
print("No optimal indexes generated!")
|
||||
|
|
@ -948,7 +943,7 @@ def check_parameter(args):
|
|||
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
|
||||
args.max_index_num)
|
||||
if args.max_index_storage is not None and args.max_index_storage <= 0:
|
||||
raise argparse.ArgumentTypeError("%s is an invalid positive float value" %
|
||||
raise argparse.ArgumentTypeError("%s is an invalid positive int value" %
|
||||
args.max_index_storage)
|
||||
JSON_TYPE = args.json
|
||||
MAX_INDEX_NUM = args.max_index_num
|
||||
|
|
@ -976,7 +971,7 @@ def main(argv):
|
|||
arg_parser.add_argument(
|
||||
"--max_index_num", help="Maximum number of suggested indexes", type=int)
|
||||
arg_parser.add_argument("--max_index_storage",
|
||||
help="Maximum storage of suggested indexes/MB", type=float)
|
||||
help="Maximum storage of suggested indexes/MB", type=int)
|
||||
arg_parser.add_argument("--multi_iter_mode", action='store_true',
|
||||
help="Whether to use multi-iteration algorithm", default=False)
|
||||
arg_parser.add_argument("--multi_node", action='store_true',
|
||||
|
|
|
|||
|
|
@ -1,397 +0,0 @@
|
|||
import sys
|
||||
import math
|
||||
import random
|
||||
import copy
|
||||
|
||||
STORAGE_THRESHOLD = 0
|
||||
AVAILABLE_CHOICES = None
|
||||
ATOMIC_CHOICES = None
|
||||
WORKLOAD_INFO = None
|
||||
MAX_INDEX_NUM = 0
|
||||
|
||||
|
||||
def is_same_index(index, compared_index):
|
||||
return index.table == compared_index.table and \
|
||||
index.columns == compared_index.columns and \
|
||||
index.index_type == compared_index.index_type
|
||||
|
||||
|
||||
def atomic_config_is_valid(atomic_config, config):
|
||||
# if candidate indexes contains all atomic index of current config1, then record it
|
||||
for atomic_index in atomic_config:
|
||||
is_exist = False
|
||||
for index in config:
|
||||
if is_same_index(index, atomic_index):
|
||||
index.storage = atomic_index.storage
|
||||
is_exist = True
|
||||
break
|
||||
if not is_exist:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def find_subsets_num(choice):
|
||||
atomic_subsets_num = []
|
||||
for pos, atomic in enumerate(ATOMIC_CHOICES):
|
||||
if not atomic or len(atomic) > len(choice):
|
||||
continue
|
||||
# find valid atomic index
|
||||
if atomic_config_is_valid(atomic, choice):
|
||||
atomic_subsets_num.append(pos)
|
||||
# find the same atomic index as the candidate index
|
||||
if len(atomic) == 1 and (is_same_index(choice[-1], atomic[0])):
|
||||
choice[-1].atomic_pos = pos
|
||||
return atomic_subsets_num
|
||||
|
||||
|
||||
def find_best_benefit(choice):
|
||||
atomic_subsets_num = find_subsets_num(choice)
|
||||
total_benefit = 0
|
||||
for ind, obj in enumerate(WORKLOAD_INFO):
|
||||
# calculate the best benefit for the current sql
|
||||
max_benefit = 0
|
||||
for pos in atomic_subsets_num:
|
||||
if (obj.cost_list[0] - obj.cost_list[pos]) > max_benefit:
|
||||
max_benefit = obj.cost_list[0] - obj.cost_list[pos]
|
||||
total_benefit += max_benefit
|
||||
return total_benefit
|
||||
|
||||
|
||||
def get_diff(available_choices, choices):
|
||||
except_choices = copy.copy(available_choices)
|
||||
for i in available_choices:
|
||||
for j in choices:
|
||||
if is_same_index(i, j):
|
||||
except_choices.remove(i)
|
||||
return except_choices
|
||||
|
||||
|
||||
class State(object):
|
||||
"""
|
||||
The game state of the Monte Carlo tree search,
|
||||
the state data recorded under a certain Node node,
|
||||
including the current game score, the current number of game rounds,
|
||||
and the execution record from the beginning to the current.
|
||||
|
||||
It is necessary to realize whether the current state has reached the end of the game state,
|
||||
and support the operation of randomly fetching from the Action collection.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.current_storage = 0.0
|
||||
self.current_benefit = 0.0
|
||||
# record the sum of choices up to the current state
|
||||
self.accumulation_choices = []
|
||||
# record available choices of current state
|
||||
self.available_choices = []
|
||||
self.displayable_choices = []
|
||||
|
||||
def get_available_choices(self):
|
||||
return self.available_choices
|
||||
|
||||
def set_available_choices(self, choices):
|
||||
self.available_choices = choices
|
||||
|
||||
def get_current_storage(self):
|
||||
return self.current_storage
|
||||
|
||||
def set_current_storage(self, value):
|
||||
self.current_storage = value
|
||||
|
||||
def get_current_benefit(self):
|
||||
return self.current_benefit
|
||||
|
||||
def set_current_benefit(self, value):
|
||||
self.current_benefit = value
|
||||
|
||||
def get_accumulation_choices(self):
|
||||
return self.accumulation_choices
|
||||
|
||||
def set_accumulation_choices(self, choices):
|
||||
self.accumulation_choices = choices
|
||||
|
||||
def is_terminal(self):
|
||||
# the current node is a leaf node
|
||||
return len(self.accumulation_choices) == MAX_INDEX_NUM
|
||||
|
||||
def compute_benefit(self):
|
||||
return self.current_benefit
|
||||
|
||||
def get_next_state_with_random_choice(self):
|
||||
# ensure that the choices taken are not repeated
|
||||
if not self.available_choices:
|
||||
return None
|
||||
random_choice = random.choice([choice for choice in self.available_choices])
|
||||
self.available_choices.remove(random_choice)
|
||||
choice = copy.copy(self.accumulation_choices)
|
||||
choice.append(random_choice)
|
||||
benefit = find_best_benefit(choice)
|
||||
# if current choice not satisfy restrictions, then continue get next choice
|
||||
if benefit <= self.current_benefit or \
|
||||
self.current_storage + random_choice.storage > STORAGE_THRESHOLD:
|
||||
return self.get_next_state_with_random_choice()
|
||||
|
||||
next_state = State()
|
||||
# initialize the properties of the new state
|
||||
next_state.set_accumulation_choices(choice)
|
||||
next_state.set_current_benefit(benefit)
|
||||
next_state.set_current_storage(self.current_storage + random_choice.storage)
|
||||
next_state.set_available_choices(get_diff(AVAILABLE_CHOICES, choice))
|
||||
return next_state
|
||||
|
||||
def __repr__(self):
|
||||
self.displayable_choices = ['{}: {}'.format(choice.table, choice.columns)
|
||||
for choice in self.accumulation_choices]
|
||||
return "reward: {}, storage :{}, choices: {}".format(
|
||||
self.current_benefit, self.current_storage, self.displayable_choices)
|
||||
|
||||
|
||||
class Node(object):
|
||||
"""
|
||||
The Node of the Monte Carlo tree search tree contains the parent node and
|
||||
current point information,
|
||||
which is used to calculate the traversal times and quality value of the UCB,
|
||||
and the State of the Node selected by the game.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.visit_number = 0
|
||||
self.quality = 0.0
|
||||
|
||||
self.parent = None
|
||||
self.children = []
|
||||
self.state = None
|
||||
|
||||
def get_parent(self):
|
||||
return self.parent
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def get_children(self):
|
||||
return self.children
|
||||
|
||||
def expand_child(self, node):
|
||||
node.set_parent(self)
|
||||
self.children.append(node)
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def get_visit_number(self):
|
||||
return self.visit_number
|
||||
|
||||
def set_visit_number(self, number):
|
||||
self.visit_number = number
|
||||
|
||||
def update_visit_number(self):
|
||||
self.visit_number += 1
|
||||
|
||||
def get_quality_value(self):
|
||||
return self.quality
|
||||
|
||||
def set_quality_value(self, value):
|
||||
self.quality = value
|
||||
|
||||
def update_quality_value(self, reward):
|
||||
self.quality += reward
|
||||
|
||||
def is_all_expand(self):
|
||||
return len(self.children) == \
|
||||
len(AVAILABLE_CHOICES) - len(self.get_state().get_accumulation_choices())
|
||||
|
||||
def __repr__(self):
|
||||
return "Node: {}, Q/N: {}/{}, State: {}".format(
|
||||
hash(self), self.quality, self.visit_number, self.state)
|
||||
|
||||
|
||||
def tree_policy(node):
|
||||
"""
|
||||
In the Selection and Expansion stages of Monte Carlo tree search,
|
||||
the node that needs to be searched (such as the root node) is passed in,
|
||||
and the best node that needs to be expanded is returned
|
||||
according to the exploration/exploitation algorithm.
|
||||
Note that if the node is a leaf node, it will be returned directly.
|
||||
|
||||
The basic strategy is to first find the child nodes that have not been selected at present,
|
||||
and select them randomly if there are more than one. If both are selected,
|
||||
find the one with the largest UCB value that has weighed exploration/exploitation,
|
||||
and randomly select if the UCB values are equal.
|
||||
"""
|
||||
|
||||
# check if the current node is leaf node
|
||||
while node and not node.get_state().is_terminal():
|
||||
|
||||
if node.is_all_expand():
|
||||
node = best_child(node, True)
|
||||
else:
|
||||
# return the new sub node
|
||||
sub_node = expand(node)
|
||||
# when there is no node that satisfies the condition in the remaining nodes,
|
||||
# this state is empty
|
||||
if sub_node.get_state():
|
||||
return sub_node
|
||||
|
||||
# return the leaf node
|
||||
return node
|
||||
|
||||
|
||||
def default_policy(node):
|
||||
"""
|
||||
In the Simulation stage of Monte Carlo tree search, input a node that needs to be expanded,
|
||||
create a new node after random operation, and return the reward of the new node.
|
||||
Note that the input node should not be a child node,
|
||||
and there are unexecuted Actions that can be expendable.
|
||||
|
||||
The basic strategy is to choose the Action at random.
|
||||
"""
|
||||
|
||||
# get the state of the game
|
||||
current_state = copy.deepcopy(node.get_state())
|
||||
|
||||
# run until the game over
|
||||
while not current_state.is_terminal():
|
||||
# pick one random action to play and get next state
|
||||
next_state = current_state.get_next_state_with_random_choice()
|
||||
if not next_state:
|
||||
break
|
||||
current_state = next_state
|
||||
|
||||
final_state_reward = current_state.compute_benefit()
|
||||
return final_state_reward
|
||||
|
||||
|
||||
def expand(node):
|
||||
"""
|
||||
Enter a node, expand a new node on the node, use the random method to execute the Action,
|
||||
and return the new node. Note that it is necessary to ensure that the newly
|
||||
added nodes are different from other node Action
|
||||
"""
|
||||
|
||||
new_state = node.get_state().get_next_state_with_random_choice()
|
||||
sub_node = Node()
|
||||
sub_node.set_state(new_state)
|
||||
node.expand_child(sub_node)
|
||||
|
||||
return sub_node
|
||||
|
||||
|
||||
def best_child(node, is_exploration):
|
||||
"""
|
||||
Using the UCB algorithm,
|
||||
select the child node with the highest score after weighing the exploration and exploitation.
|
||||
Note that if it is the prediction stage,
|
||||
the current Q-value score with the highest score is directly selected.
|
||||
"""
|
||||
|
||||
best_score = -sys.maxsize
|
||||
best_sub_node = None
|
||||
|
||||
# travel all sub nodes to find the best one
|
||||
for sub_node in node.get_children():
|
||||
# The children nodes of the node contains the children node whose state is empty,
|
||||
# this kind of node comes from the node that does not meet the conditions.
|
||||
if not sub_node.get_state():
|
||||
continue
|
||||
# ignore exploration for inference
|
||||
if is_exploration:
|
||||
C = 1 / math.sqrt(2.0)
|
||||
else:
|
||||
C = 0.0
|
||||
|
||||
# UCB = quality / times + C * sqrt(2 * ln(total_times) / times)
|
||||
left = sub_node.get_quality_value() / sub_node.get_visit_number()
|
||||
right = 2.0 * math.log(node.get_visit_number()) / sub_node.get_visit_number()
|
||||
score = left + C * math.sqrt(right)
|
||||
# get the maximum score, while filtering nodes that do not meet the space constraints and
|
||||
# nodes that have no revenue
|
||||
if score > best_score \
|
||||
and sub_node.get_state().get_current_storage() <= STORAGE_THRESHOLD \
|
||||
and sub_node.get_state().get_current_benefit() > 0:
|
||||
best_sub_node = sub_node
|
||||
best_score = score
|
||||
|
||||
return best_sub_node
|
||||
|
||||
|
||||
def backpropagate(node, reward):
|
||||
"""
|
||||
In the Backpropagation stage of Monte Carlo tree search,
|
||||
input the node that needs to be expended and the reward of the newly executed Action,
|
||||
feed it back to the expend node and all upstream nodes,
|
||||
and update the corresponding data.
|
||||
"""
|
||||
|
||||
# update util the root node
|
||||
while node is not None:
|
||||
# update the visit number
|
||||
node.update_visit_number()
|
||||
|
||||
# update the quality value
|
||||
node.update_quality_value(reward)
|
||||
|
||||
# change the node to the parent node
|
||||
node = node.parent
|
||||
|
||||
|
||||
def monte_carlo_tree_search(node):
|
||||
"""
|
||||
Implement the Monte Carlo tree search algorithm, pass in a root node,
|
||||
expand new nodes and update data according to the
|
||||
tree structure that has been explored before in a limited time,
|
||||
and then return as long as the child node with the highest exploitation.
|
||||
|
||||
When making predictions,
|
||||
you only need to select the node with the largest exploitation according to the Q value,
|
||||
and find the next optimal node.
|
||||
"""
|
||||
|
||||
computation_budget = len(AVAILABLE_CHOICES) * 3
|
||||
|
||||
# run as much as possible under the computation budget
|
||||
for i in range(computation_budget):
|
||||
# 1. find the best node to expand
|
||||
expand_node = tree_policy(node)
|
||||
if not expand_node:
|
||||
# when it is None, it means that all nodes are added but no nodes meet the space limit
|
||||
break
|
||||
# 2. random get next action and get reward
|
||||
reward = default_policy(expand_node)
|
||||
|
||||
# 3. update all passing nodes with reward
|
||||
backpropagate(expand_node, reward)
|
||||
|
||||
# get the best next node
|
||||
best_next_node = best_child(node, False)
|
||||
|
||||
return best_next_node
|
||||
|
||||
|
||||
def MCTS(workload_info, atomic_choices, available_choices, storage_threshold, max_index_num):
|
||||
global ATOMIC_CHOICES, STORAGE_THRESHOLD, WORKLOAD_INFO, AVAILABLE_CHOICES, MAX_INDEX_NUM
|
||||
WORKLOAD_INFO = workload_info
|
||||
AVAILABLE_CHOICES = available_choices
|
||||
ATOMIC_CHOICES = atomic_choices
|
||||
STORAGE_THRESHOLD = storage_threshold
|
||||
MAX_INDEX_NUM = max_index_num if max_index_num else len(available_choices)
|
||||
|
||||
# create the initialized state and initialized node
|
||||
init_state = State()
|
||||
choices = copy.copy(available_choices)
|
||||
init_state.set_available_choices(choices)
|
||||
init_node = Node()
|
||||
init_node.set_state(init_state)
|
||||
current_node = init_node
|
||||
|
||||
opt_config = []
|
||||
# set the rounds to play
|
||||
for i in range(len(AVAILABLE_CHOICES)):
|
||||
if current_node:
|
||||
current_node = monte_carlo_tree_search(current_node)
|
||||
if current_node:
|
||||
opt_config = current_node.state.accumulation_choices
|
||||
else:
|
||||
break
|
||||
return opt_config
|
||||
|
|
@ -539,14 +539,13 @@ class RnnModel():
|
|||
keras.backend.clear_session()
|
||||
set_session(self.session)
|
||||
with self.graph.as_default():
|
||||
# Judge whether the model needs to be initialized according to the changes of the model input and output dimensions.
|
||||
feature, label, need_init = self.parse(filename)
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
epsilon = self.model_info.make_epsilon()
|
||||
if need_init:# Cold start training
|
||||
if need_init:
|
||||
epoch_start = 0
|
||||
self.model = self._build_model(epsilon)
|
||||
else:# Incremental training
|
||||
else:
|
||||
epoch_start = int(self.model_info.last_epoch)
|
||||
ratio_error = ratio_error_loss_wrapper(epsilon)
|
||||
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
|
||||
|
|
@ -557,16 +556,12 @@ class RnnModel():
|
|||
log_path = os.path.realpath(os.path.join(settings.PATH_LOG, self.model_info.model_name + '_log.json'))
|
||||
if not os.path.exists(log_path):
|
||||
os.mknod(log_path, mode=0o600)
|
||||
# Training logging callback function
|
||||
json_logging_callback = LossHistory(log_path, self.model_info.model_name, self.model_info.last_epoch)
|
||||
# Data segmentation
|
||||
X_train, X_val, y_train, y_val = \
|
||||
train_test_split(feature, label, test_size=0.1)
|
||||
# model training
|
||||
self.model.fit(X_train, y_train, epochs=self.model_info.last_epoch,
|
||||
batch_size=int(self.model_info.batch_size), validation_data=(X_val, y_val),
|
||||
verbose=0, initial_epoch=epoch_start, callbacks=[json_logging_callback])
|
||||
# save model
|
||||
self.model.save(self.model_info.model_path)
|
||||
val_pred = self.model.predict(X_val)
|
||||
val_re = get_ratio_errors_general(val_pred, y_val, epsilon)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from . import AbstractModel
|
|||
|
||||
|
||||
class TemplateModel(AbstractModel):
|
||||
# Initialize algorithm parameters
|
||||
def __init__(self, params):
|
||||
super().__init__(params)
|
||||
self.bias = 1e-5
|
||||
|
|
|
|||
|
|
@ -173,16 +173,11 @@ def procedure_main(mode, db_info, config):
|
|||
def rl_model(mode, env, config):
|
||||
# Lazy loading. Because loading Tensorflow takes a long time.
|
||||
from tuner.algorithms.rl_agent import RLAgent
|
||||
# Start reinforcement learning agent class.
|
||||
rl = RLAgent(env, alg=config['rl_algorithm'])
|
||||
# The two modes of training and tuning correspond to different execution processes.
|
||||
# The model needs to be trained before it can be used for tuning. The output of the training and tuning process is the list of parameters to be tuned. Because they share a set of models, it is required that the list of parameters to be tuned must be consistent in the two modes, otherwise exceptions with different output dimensions will be thrown.
|
||||
if mode == 'train':
|
||||
logging.warning('The list of tuned knobs in the training mode '
|
||||
'based on the reinforcement learning algorithm must be the same as '
|
||||
'that in the tuning mode. ')
|
||||
# The key parameter is the maximum iteration round rl_ steps, theoretically, the longer the more accurate, but also more time-consuming.
|
||||
# max_episode_steps is the maximum number of rounds in each round of reinforcement learning algorithm. In the implementation of x-tuner, this parameter is weakened, and it is generally default.
|
||||
rl.fit(config['rl_steps'], nb_max_episode_steps=config['max_episode_steps'])
|
||||
rl.save(config['rl_model_path'])
|
||||
logging.info('Saved reinforcement learning model at %s.', config['rl_model_path'])
|
||||
|
|
@ -205,7 +200,6 @@ def rl_model(mode, env, config):
|
|||
|
||||
def global_search(env, config):
|
||||
method = config['gop_algorithm']
|
||||
# Determine which algorithm to use.
|
||||
if method == 'bayes':
|
||||
from bayes_opt import BayesianOptimization
|
||||
|
||||
|
|
@ -213,13 +207,6 @@ def global_search(env, config):
|
|||
pbound = {name: (0, 1) for name in env.db.ordered_knob_list}
|
||||
|
||||
def performance_function(**params):
|
||||
"""
|
||||
function name: performance_function
|
||||
description: Define a black box function to adapt to the interface of the third-party library.
|
||||
author: Li Xinran
|
||||
date: 2022/8/4
|
||||
contact: 19154068808
|
||||
"""
|
||||
if not len(params) == env.nb_actions:
|
||||
raise AssertionError('Failed to check the input feature dimension.')
|
||||
|
||||
|
|
@ -235,21 +222,12 @@ def global_search(env, config):
|
|||
pbounds=pbound
|
||||
)
|
||||
optimizer.maximize(
|
||||
# The larger the maximum iteration round, the more accurate the result is, but it is also more time-consuming.
|
||||
n_iter=config['max_iterations']
|
||||
)
|
||||
elif method == 'pso':
|
||||
from tuner.algorithms.pso import Pso
|
||||
|
||||
def performance_function(v):
|
||||
"""
|
||||
function name: performance_function
|
||||
description: Find the global minimum value.
|
||||
note: Because the implementation of PSO algorithm is to find the global minimum value, take the opposite number here, so we need to change to take the global maximum value.
|
||||
author: Li Xinran
|
||||
date: 2022/8/4
|
||||
contact: 19154068808
|
||||
"""
|
||||
s, r, d, _ = env.step(v)
|
||||
return -r # Use -reward because PSO wishes to minimize.
|
||||
|
||||
|
|
@ -259,7 +237,6 @@ def global_search(env, config):
|
|||
particle_nums=config['particle_nums'],
|
||||
# max_iterations on the PSO indicates the maximum number of iterations per particle,
|
||||
# so it must be divided by the number of particles to be consistent with Bayes.
|
||||
# The larger the maximum iteration round is, the more accurate the result is, but also the more time-consuming.
|
||||
max_iteration=config['max_iterations'] // config['particle_nums'],
|
||||
x_min=0, x_max=1, max_vel=0.5
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from collections.abc import Iterable
|
|||
from collections import defaultdict
|
||||
|
||||
import index_advisor_workload as iaw
|
||||
import mcts
|
||||
|
||||
|
||||
def hash_any(obj):
|
||||
|
|
@ -228,32 +227,6 @@ select * from student_range_part1 where credit=1;
|
|||
|
||||
class IndexAdvisorTester(unittest.TestCase):
|
||||
|
||||
def test_mcts(self):
|
||||
storage_threshold = 12
|
||||
index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
|
||||
index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
|
||||
index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
|
||||
index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
|
||||
|
||||
atomic_index1 = iaw.IndexItem('public.a', 'col1', index_type='global')
|
||||
atomic_index2 = iaw.IndexItem('public.b', 'col1', index_type='global')
|
||||
atomic_index3 = iaw.IndexItem('public.c', 'col1', index_type='global')
|
||||
atomic_index4 = iaw.IndexItem('public.d', 'col1', index_type='global')
|
||||
|
||||
atomic_index1.storage = 10
|
||||
atomic_index2.storage = 4
|
||||
atomic_index3.storage = 7
|
||||
available_choices = [index1, index2, index3, index4]
|
||||
atomic_choices = [[], [atomic_index2], [atomic_index1], [atomic_index3],
|
||||
[atomic_index2, atomic_index3], [atomic_index4]]
|
||||
query = iaw.QueryItem('select * from gia_01', 1)
|
||||
query.cost_list = [10, 7, 5, 9, 4, 11]
|
||||
workload_info = [query]
|
||||
|
||||
results = mcts.MCTS(workload_info, atomic_choices, available_choices, storage_threshold, 2)
|
||||
self.assertLessEqual([index1.atomic_pos, index2.atomic_pos, index3.atomic_pos], [2, 1, 3])
|
||||
self.assertSetEqual({results[0].table, results[1].table}, {'public.b', 'public.c'})
|
||||
|
||||
def test_get_indexable_columns(self):
|
||||
tables = 'table1 table2 table2 table3 table3 table3'.split()
|
||||
columns = 'col1,col2 col2 col3 col1,col2 col2,col3 col2,col5'.split()
|
||||
|
|
|
|||
|
|
@ -43,47 +43,47 @@
|
|||
#include "catalog/pg_proc_fn.h"
|
||||
|
||||
/*
|
||||
DefineAggregate
|
||||
"oldstyle" 表示旧式聚合函数定义风格,
|
||||
其中聚合函数的输入类型由参数中的 BASETYPE 元素指定。
|
||||
否则,"args" 定义了输入类型
|
||||
* DefineAggregate
|
||||
*
|
||||
* "oldstyle" signals the old (pre-8.2) style where the aggregate input type
|
||||
* is specified by a BASETYPE element in the parameters. Otherwise,
|
||||
* "args" defines the input type(s).
|
||||
*/
|
||||
void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
||||
{
|
||||
char* aggName = NULL;//字符型指针,用于存储聚合函数的名称
|
||||
Oid aggNamespace;//对象标识符类型,表示聚合函数所属的命名空间
|
||||
AclResult aclresult;//aclresult 是一个枚举值,表示访问控制的结果
|
||||
List* transfuncName = NIL;//链表类型,用于存储聚合函数的过渡函数的名称
|
||||
List* finalfuncName = NIL;//用于存储聚合函数的最终函数的名称
|
||||
List* sortoperatorName = NIL;//用于存储排序操作符的名称
|
||||
TypeName* baseType = NULL;//一个指向 TypeName 结构的指针,表示聚合函数的基础类型
|
||||
TypeName* transType = NULL;//也是TypeName结构的指针,表示聚合函数的过渡类型
|
||||
char* initval = NULL;//用于存储聚合函数的初始值
|
||||
char* aggName = NULL;
|
||||
Oid aggNamespace;
|
||||
AclResult aclresult;
|
||||
List* transfuncName = NIL;
|
||||
List* finalfuncName = NIL;
|
||||
List* sortoperatorName = NIL;
|
||||
TypeName* baseType = NULL;
|
||||
TypeName* transType = NULL;
|
||||
char* initval = NULL;
|
||||
#ifdef PGXC
|
||||
List* collectfuncName = NIL;
|
||||
char* initcollect = NULL;
|
||||
#endif
|
||||
//定义了用于收集数据的函数名称和初始值
|
||||
Oid* aggArgTypes = NULL;//表示聚合函数的参数类型
|
||||
int numArgs;//表示聚合函数的参数数量
|
||||
Oid transTypeId;//表示聚合函数的过渡类型的标识符
|
||||
ListCell* pl = NULL;//循环中间变量
|
||||
Oid* aggArgTypes = NULL;
|
||||
int numArgs;
|
||||
Oid transTypeId;
|
||||
ListCell* pl = NULL;
|
||||
|
||||
//有序集合聚合函数的属性或特征
|
||||
/* attribute for ordered set aggregate */
|
||||
char aggKind = AGGKIND_NORMAL;
|
||||
|
||||
//将一组名称列表转换为名称和命名空间
|
||||
/* Convert list of names to a name and namespace */
|
||||
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
|
||||
|
||||
//检查是否具有在目标命名空间中创建的权限
|
||||
/* Check we have creation rights in target namespace */
|
||||
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
|
||||
if (u_sess->attr.attr_sql.enforce_a_behavior) {
|
||||
Oid proowner = InvalidOid;
|
||||
/*
|
||||
如果 isalter 为真,则将对象的所有者更改为命名空间的所有者,
|
||||
但前提是命名空间的所有者与命名空间本身具有相同的名称
|
||||
* isalter is true, change the owner of the objects as the owner of the
|
||||
* namespace, if the owner of the namespce has the same name as the namescpe
|
||||
*/
|
||||
bool isalter = false;
|
||||
proowner = GetUserIdFromNspId(aggNamespace);
|
||||
|
|
@ -102,8 +102,10 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
foreach (pl, parameters) {
|
||||
DefElem* defel = (DefElem*)lfirst(pl);
|
||||
|
||||
//sfunc1、stype1 和 initcond1 被认为 是sfunc、stype 和 initcond 的过时拼写方式
|
||||
|
||||
/*
|
||||
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
|
||||
* for sfunc, stype, initcond.
|
||||
*/
|
||||
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
|
||||
transfuncName = defGetQualifiedName(defel);
|
||||
else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
|
||||
|
|
@ -122,10 +124,6 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
initval = defGetString(defel);
|
||||
else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
|
||||
initval = defGetString(defel);
|
||||
/*
|
||||
上述使用一个循环遍历 parameters 列表中的每个元素(DefElem 结构),
|
||||
然后根据元素的 defname 字段(参数名称)的值执行不同的操作
|
||||
*/
|
||||
#ifdef PGXC
|
||||
else if (pg_strcasecmp(defel->defname, "cfunc") == 0)
|
||||
collectfuncName = defGetQualifiedName(defel);
|
||||
|
|
@ -137,23 +135,29 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("aggregate attribute \"%s\" not recognized", defel->defname)));
|
||||
}
|
||||
|
||||
|
||||
//确保我们有所需的定义变量
|
||||
|
||||
/*
|
||||
* make sure we have our required definitions
|
||||
*/
|
||||
if (transType == NULL)
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate stype must be specified")));
|
||||
if (transfuncName == NIL)
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate sfunc must be specified")));
|
||||
|
||||
/*
|
||||
* look up the aggregate's input datatype(s).
|
||||
*/
|
||||
if (oldstyle) {
|
||||
/*
|
||||
Old style支持零个或一个输入的聚合函数,其中输入类型 ANY 表示零个输入
|
||||
以往我们允许命令看起来像 basetype = 'ANY',因此我们必须对名称 ANY 进行不区分大小写的比较
|
||||
* Old style: use basetype parameter. This supports aggregates of
|
||||
* zero or one input, with input type ANY meaning zero inputs.
|
||||
*
|
||||
* Historically we allowed the command to look like basetype = 'ANY'
|
||||
* so we must do a case-insensitive comparison for the name ANY. Ugh.
|
||||
*/
|
||||
if (baseType == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate input type must be specified")));
|
||||
//baseType参数的值不能为空,聚合函数的输入类型必须指定
|
||||
|
||||
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0) {
|
||||
numArgs = 0;
|
||||
aggArgTypes = NULL;
|
||||
|
|
@ -163,6 +167,9 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
aggArgTypes[0] = typenameTypeId(NULL, baseType);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* New style: args is a list of TypeNames (possibly zero of 'em).
|
||||
*/
|
||||
ListCell* lc = NULL;
|
||||
int i = 0;
|
||||
|
||||
|
|
@ -171,31 +178,32 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("basetype is redundant with aggregate input type specification")));
|
||||
|
||||
/* 在使用没有直接参数的有序集合聚合函数时,aggr_args 变量会在 gram.y 文件中进行修改。
|
||||
因此,解析 aggr_args 的过程应该进行相应的更改。
|
||||
*/
|
||||
/* Given ordered set aggregate with no direct args, aggr_args variable is modified in gram.y.
|
||||
So the parse of aggr_args should be changed. See gram.y for detail. */
|
||||
numArgs = list_length((List*)linitial(args));
|
||||
// 获取传递给聚合函数的参数数量
|
||||
aggArgTypes = (Oid*)palloc(sizeof(Oid) * numArgs);
|
||||
// 为聚合函数的参数类型标识符分配内存
|
||||
foreach (lc, (List*)linitial(args))
|
||||
{//// 遍历传递给聚合函数的参数列表
|
||||
foreach (lc, (List*)linitial(args)) {
|
||||
TypeName* curTypeName = (TypeName*)lfirst(lc);
|
||||
|
||||
aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
|
||||
// 获取当前参数的类型标识符并存储到数组中
|
||||
}
|
||||
|
||||
/* Set aggKind to AGGKIND_ORDERED_SET if second arg of aggr_args is 0. */
|
||||
if (intVal(lsecond(args)) == 0) {
|
||||
aggKind = AGGKIND_ORDERED_SET;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
下面查找聚合函数的 transtype
|
||||
transtype 不能是伪类型,因为我们需要能够存储 transtype 的值。
|
||||
然而,在某些情况下,我们可以允许多态的 transtype(AggregateCreate 函数将会检查这一点)。
|
||||
此外,我们允许使用 "internal",用于那些希望传递指向私有数据结构的指针的函数
|
||||
* look up the aggregate's transtype.
|
||||
*
|
||||
* transtype can't be a pseudo-type, since we need to be able to store
|
||||
* values of the transtype. However, we can allow polymorphic transtype
|
||||
* in some cases (AggregateCreate will check). Also, we allow "internal"
|
||||
* for functions that want to pass pointers to private data structures;
|
||||
* but allow that only to superusers, since you could crash the system (or
|
||||
* worse) by connecting up incompatible internal-using functions in an
|
||||
* aggregate.
|
||||
*/
|
||||
transTypeId = typenameTypeId(NULL, transType);
|
||||
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId)
|
||||
|
|
@ -204,44 +212,45 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("aggregate transition data type cannot be %s", format_type_be(transTypeId))));
|
||||
}
|
||||
|
||||
//大部分的参数检查都在 AggregateCreate 函数内部完成
|
||||
AggregateCreate(aggName, /* 聚合函数的名称 */
|
||||
aggNamespace, /* 命名空间 */
|
||||
aggKind, /* 聚合种类 */
|
||||
aggArgTypes, /* 输入类型种类 */
|
||||
numArgs, /* 输入参数数量 */
|
||||
transfuncName, /* 过渡函数名称 */
|
||||
/*
|
||||
* Most of the argument-checking is done inside of AggregateCreate
|
||||
*/
|
||||
AggregateCreate(aggName, /* aggregate name */
|
||||
aggNamespace, /* namespace */
|
||||
aggKind, /* agg kind */
|
||||
aggArgTypes, /* input data type(s) */
|
||||
numArgs,
|
||||
transfuncName, /* step function name */
|
||||
#ifdef PGXC
|
||||
collectfuncName, /* 收集函数名称 */
|
||||
collectfuncName, /* collect function name */
|
||||
#endif
|
||||
finalfuncName, /* 最终函数名称 */
|
||||
sortoperatorName, /* 排序操作符名称 */
|
||||
transTypeId, /* 过渡数据类型 */
|
||||
finalfuncName, /* final function name */
|
||||
sortoperatorName, /* sort operator name */
|
||||
transTypeId, /* transition data type */
|
||||
#ifdef PGXC
|
||||
initval, /* 初始条件 */
|
||||
initcollect); /* 收集函数的初始条件 */
|
||||
initval, /* initial condition */
|
||||
initcollect); /* initial condition for collection function */
|
||||
#else
|
||||
initval); /* 初始条件 */
|
||||
initval); /* initial condition */
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenameAggregate(List* name, List* args, const char* newname)
|
||||
{
|
||||
Oid procOid; /* 聚合函数的 Oid */
|
||||
Oid namespaceOid; /* 命名空间的 Oid */
|
||||
HeapTuple tup; /* HeapTuple 结构,用于存储元组 */
|
||||
Form_pg_proc procForm; /* pg_proc 表中的元组结构 */
|
||||
Relation rel; /* pg_proc 表的 Relation 对象 */
|
||||
AclResult aclresult; /* AclResult 枚举,用于存储访问控制的结果 */
|
||||
bool isNull = false; /* 布尔变量,表示是否为 NULL */
|
||||
rel = heap_open(ProcedureRelationId, RowExclusiveLock); /* 打开 pg_proc 表 */
|
||||
Oid procOid;
|
||||
Oid namespaceOid;
|
||||
HeapTuple tup;
|
||||
Form_pg_proc procForm;
|
||||
Relation rel;
|
||||
AclResult aclresult;
|
||||
bool isNull = false;
|
||||
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
|
||||
/* 查询函数并确保他是聚合的*/
|
||||
/* Look up function and make sure it's an aggregate */
|
||||
procOid = LookupAggNameTypeNames(name, args, false);
|
||||
|
||||
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
|
||||
if (!HeapTupleIsValid(tup)) /* 如果运行正常这是不会出现的 */
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid)));
|
||||
procForm = (Form_pg_proc)GETSTRUCT(tup);
|
||||
|
||||
|
|
@ -256,14 +265,13 @@ void RenameAggregate(List* name, List* args, const char* newname)
|
|||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
Datum allargtypes = ProcedureGetAllArgTypes(tup, &isNull);
|
||||
Datum argmodes = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proargmodes, &isNull);
|
||||
// 在系统缓存中搜索具有相同参数的函数
|
||||
/* make sure the new name doesn't exist */
|
||||
if (SearchSysCacheForProcAllArgs(
|
||||
CStringGetDatum(newname),
|
||||
allargtypes,
|
||||
ObjectIdGetDatum(namespaceOid),
|
||||
ObjectIdGetDatum(packageoid),
|
||||
argmodes))
|
||||
// 如果找到相同参数的函数,报告错误
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
||||
errmsg("function %s already exists in schema \"%s\"",
|
||||
|
|
@ -280,16 +288,16 @@ void RenameAggregate(List* name, List* args, const char* newname)
|
|||
funcname_signature_string(newname, procForm->pronargs, NIL, proargs->values),
|
||||
get_namespace_name(namespaceOid))));
|
||||
#endif
|
||||
// 检查当前用户是否是聚合函数的所有者,如果不是,则报告错误
|
||||
/* must be owner */
|
||||
if (!pg_proc_ownercheck(procOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name));
|
||||
|
||||
// 检查当前用户是否有在命名空间中创建对象的权限
|
||||
/* must have CREATE privilege on namespace */
|
||||
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid));
|
||||
|
||||
//重命名
|
||||
/* rename */
|
||||
(void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);
|
||||
simple_heap_update(rel, &tup->t_self, tup);
|
||||
CatalogUpdateIndexes(rel, tup);
|
||||
|
|
@ -299,15 +307,15 @@ void RenameAggregate(List* name, List* args, const char* newname)
|
|||
}
|
||||
|
||||
/*
|
||||
下面函数用来更改聚合函数的所有者
|
||||
* Change aggregate owner
|
||||
*/
|
||||
void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)
|
||||
{
|
||||
Oid procOid;
|
||||
|
||||
/* 查找函数并确保它是一个聚合函数。 */
|
||||
/* Look up function and make sure it's an aggregate */
|
||||
procOid = LookupAggNameTypeNames(name, args, false);
|
||||
|
||||
/* 其余部分与普通函数类似 */
|
||||
/* The rest is just like a function */
|
||||
AlterFunctionOwner_oid(procOid, newOwnerId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,10 +90,7 @@ static void DropExtensionInListIsSupported(List* objname)
|
|||
}
|
||||
}
|
||||
|
||||
/* Enable DROP operation of the above objects during inplace upgrade or support_extended_features is true */
|
||||
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !g_instance.attr.attr_common.support_extended_features) {
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
|
||||
}
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXTENSION is not yet supported.")));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1175,7 +1175,7 @@ void CreateExtension(CreateExtensionStmt* stmt)
|
|||
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
|
||||
}
|
||||
|
||||
if (pg_strcasecmp(stmt->extname, "dolphin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
||||
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0 && !DB_IS_CMPT(B_FORMAT)) {
|
||||
ereport(ERROR,
|
||||
(errmsg("please create extension \"%s\" with B type DBCOMPATIBILITY", stmt->extname)));
|
||||
}
|
||||
|
|
@ -1418,8 +1418,8 @@ void CreateExtension(CreateExtensionStmt* stmt)
|
|||
|
||||
u_sess->exec_cxt.extension_is_valid = true;
|
||||
|
||||
if (pg_strcasecmp(stmt->extname, "dolphin") == 0) {
|
||||
u_sess->attr.attr_sql.dolphin = true;
|
||||
if (pg_strcasecmp(stmt->extname, "b_sql_plugin") == 0) {
|
||||
u_sess->attr.attr_sql.b_sql_plugin = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -23175,7 +23175,7 @@ static void checkValidationForExchangeTable(Relation partTableRel, Relation ordT
|
|||
int2 bucketId = InvalidBktId;
|
||||
|
||||
// get right partition oid for the tuple
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple)tuple, true);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (HeapTuple) tuple);
|
||||
|
||||
searchFakeReationForPartitionOid(
|
||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||
|
|
@ -24797,8 +24797,7 @@ static Oid AddTemporaryPartitionForAlterPartitions(const AlterTableCmd* cmd, Rel
|
|||
destPartOid = AddTemporaryHashPartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||
break;
|
||||
}
|
||||
case PART_TYPE_RANGE:
|
||||
case PART_TYPE_INTERVAL: {
|
||||
case PART_TYPE_RANGE: {
|
||||
destPartOid = AddTemporaryRangePartitionForAlterPartitions(cmd, partTableRel, partSeq, renameTargetPart);
|
||||
break;
|
||||
}
|
||||
|
|
@ -25099,11 +25098,11 @@ static void readTuplesAndInsertInternal(Relation tempTableRel, Relation partTabl
|
|||
|
||||
/* tableam_tops_copy_tuple is not ready so we add UStore hack path */
|
||||
copyTuple = tableam_tops_copy_tuple(tuple);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple, true);
|
||||
targetPartOid = heapTupleGetPartitionId(partTableRel, (void *)tuple);
|
||||
searchFakeReationForPartitionOid(
|
||||
partRelHTAB, CurrentMemoryContext, partTableRel, targetPartOid, partRel, part, RowExclusiveLock);
|
||||
if (RelationIsSubPartitioned(partTableRel)) {
|
||||
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple, true);
|
||||
targetPartOid = heapTupleGetPartitionId(partRel, (void *)tuple);
|
||||
searchFakeReationForPartitionOid(partRelHTAB, CurrentMemoryContext, partRel, targetPartOid, subPartRel,
|
||||
subPart, RowExclusiveLock);
|
||||
partRel = subPartRel;
|
||||
|
|
|
|||
|
|
@ -5911,7 +5911,6 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
|
|||
errno_t rc = EOK;
|
||||
|
||||
/* For PG ecological compatibility, we stored both sha256 and md5 password. */
|
||||
/* the encrypted method of sha256 */
|
||||
if (!pg_sha256_encrypt(password,
|
||||
salt_string,
|
||||
strlen(salt_string),
|
||||
|
|
@ -5922,7 +5921,7 @@ Datum calculate_encrypted_combined_password(const char* password, const char* ro
|
|||
securec_check(rc, "\0", "\0");
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("first stage encryption password failed")));
|
||||
}
|
||||
/* the encrypted method of md5 */
|
||||
|
||||
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {
|
||||
rc = memset_s(encrypted_md5_password, MD5_PASSWD_LEN + 1, 0, MD5_PASSWD_LEN + 1);
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
|
@ -6053,7 +6052,6 @@ static Datum gs_calculate_encrypted_sm3_password(const char* password, const cha
|
|||
Datum calculate_encrypted_password(bool is_encrypted, const char* password, const char* rolname,
|
||||
const char* salt_string)
|
||||
{
|
||||
/* If the password is '\0' or not exist */
|
||||
if (password == NULL || password[0] == '\0') {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("The password could not be NULL.")));
|
||||
}
|
||||
|
|
@ -6061,7 +6059,6 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
|
|||
char encrypted_md5_password[MD5_PASSWD_LEN + 1] = {0};
|
||||
Datum datum_value;
|
||||
|
||||
/* If the password has encrypted */
|
||||
if (!is_encrypted || isPWDENCRYPTED(password)) {
|
||||
return CStringGetTextDatum(password);
|
||||
}
|
||||
|
|
@ -6071,7 +6068,6 @@ Datum calculate_encrypted_password(bool is_encrypted, const char* password, cons
|
|||
* if Password_encryption_type is 0, the encrypted password is md5.
|
||||
* if Password_encryption_type is 1, the encrypted password is sha256 + md5.
|
||||
* if Password_encryption_type is 2, the encrypted password is sha256.
|
||||
* if Password_encryption_type is 3, the encrypted password is SM3.
|
||||
*/
|
||||
if (u_sess->attr.attr_security.Password_encryption_type == 0) {
|
||||
if (!pg_md5_encrypt(password, rolname, strlen(rolname), encrypted_md5_password)) {
|
||||
|
|
|
|||
|
|
@ -1181,17 +1181,6 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn
|
|||
return jtnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* We must flatten any join alias Vars in the subquery's targetlist,
|
||||
* because pulling up the subquery's subqueries might have changed their
|
||||
* expansions into arbitrary expressions, which could affect
|
||||
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
|
||||
* are needed for tlist entries. (Likely it'd be better to do
|
||||
* flatten_join_alias_vars on the whole query tree at some earlier stage,
|
||||
* maybe even in the rewriter; but for now let's just fix this case here.)
|
||||
*/
|
||||
subquery->targetList = (List *) flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
|
||||
|
||||
/*
|
||||
* Adjust level-0 varnos in subquery so that we can append its rangetable
|
||||
* to upper query's. We have to fix the subquery's append_rel_list as
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -38,102 +38,63 @@
|
|||
#include "utils/plancache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
/*
|
||||
* 功能:检查共享缓存失效消息是否符合条件
|
||||
*
|
||||
* 参数列表:
|
||||
* msg:指向 SharedInvalidationMessage 结构的指针,表示共享缓存失效消息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果共享缓存失效消息符合条件,返回 true;否则返回 false
|
||||
*/
|
||||
bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg)
|
||||
{
|
||||
if (msg->id >= 0) {
|
||||
// 如果消息的 id 大于等于 0
|
||||
if (msg->cc.id == PROCOID || msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) {
|
||||
// 如果消息的 cc.id 是 PROCOID、NAMESPACEOID、OPOID 或 AMOPOPID 中的任何一个,返回 true
|
||||
return true;
|
||||
}
|
||||
} else if (msg->id == SHAREDINVALRELCACHE_ID || msg->id == SHAREDINVALPARTCACHE_ID) {
|
||||
// 如果消息的 id 是 SHAREDINVALRELCACHE_ID 或 SHAREDINVALPARTCACHE_ID,返回 true
|
||||
return true;
|
||||
}
|
||||
// 如果以上条件都不满足,返回 false
|
||||
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* 功能:根据本地失效消息判断是否需要丢弃计划缓存项
|
||||
*
|
||||
* 参数列表:
|
||||
* plansource:指向 CachedPlanSource 结构的指针,表示计划源对象
|
||||
* tot:失效消息的总数
|
||||
* idx:失效消息的索引数组
|
||||
* msgs:指向 SharedInvalidationMessage 结构的指针,表示失效消息数组
|
||||
*
|
||||
* 返回值:
|
||||
* 如果需要丢弃计划缓存项,返回 true;否则返回 false
|
||||
*/
|
||||
bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource *plansource, int tot, const int *idx,
|
||||
const SharedInvalidationMessage *msgs)
|
||||
|
||||
bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs)
|
||||
{
|
||||
// 获取计划源对象所属的数据库 ID
|
||||
Oid database_id = plansource->gpc.key->env.plainenv.database_id;
|
||||
|
||||
for (int j = 0; j < tot; j++) {
|
||||
const SharedInvalidationMessage *msg = &msgs[idx[j]];
|
||||
// 如果计划源对象具有原始解析树,并且原始解析树的类型是 TransactionStmt,则跳过该消息的处理
|
||||
if ((plansource)->raw_parse_tree && IsA((plansource)->raw_parse_tree, TransactionStmt))
|
||||
continue;
|
||||
|
||||
if (msg->id >= 0) {
|
||||
// 如果消息的 id 大于等于 0
|
||||
|
||||
if (msg->cc.dbId == database_id || msg->cc.dbId == InvalidOid) {
|
||||
if (msg->cc.id == PROCOID) {
|
||||
// 检查计划缓存项的失效项依赖性,并更新
|
||||
CheckInvalItemDependency(plansource, msg->cc.id, msg->cc.hashValue);
|
||||
} else if (msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) {
|
||||
// 重置计划缓存项
|
||||
ResetPlanCache(plansource);
|
||||
}
|
||||
}
|
||||
} else if (msg->id == SHAREDINVALRELCACHE_ID) {
|
||||
// 如果消息的 id 是 SHAREDINVALRELCACHE_ID
|
||||
if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid) {
|
||||
// 检查计划缓存项与关系依赖性,并更新
|
||||
if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid)
|
||||
{
|
||||
CheckRelDependency(plansource, msg->rc.relId);
|
||||
}
|
||||
} else if (msg->id == SHAREDINVALPARTCACHE_ID) {
|
||||
// 如果消息的 id 是 SHAREDINVALPARTCACHE_ID
|
||||
if (msg->pc.dbId == database_id || msg->pc.dbId == InvalidOid) {
|
||||
// 检查计划缓存项与分区依赖性,并更新
|
||||
CheckRelDependency(plansource, msg->pc.partId);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果计划源对象需要丢弃共享 GPC,则返回 true
|
||||
if (plansource->gpc.status.NeedDropSharedGPC()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不需要丢弃计划缓存项,返回 false
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
* 功能:根据失效消息进行计划缓存项的失效处理
|
||||
*
|
||||
* 参数列表:
|
||||
* msgs:指向 SharedInvalidationMessage 结构的指针,表示失效消息数组
|
||||
* n:失效消息的数量
|
||||
*/
|
||||
|
||||
void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
|
||||
{
|
||||
// 分配并初始化一个索引数组
|
||||
int *idx = (int *)palloc0(n * sizeof(int));
|
||||
int tot = 0;
|
||||
int *idx = (int *)palloc0(n * sizeof(int));
|
||||
int tot = 0;
|
||||
|
||||
// 遍历失效消息数组,筛选出需要处理的消息
|
||||
for (int i = 0; i < n; i++) {
|
||||
const SharedInvalidationMessage *msg = &msgs[i];
|
||||
|
||||
|
|
@ -142,21 +103,20 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
|
|||
}
|
||||
}
|
||||
|
||||
// 如果没有需要处理的消息,释放索引数组并返回
|
||||
if (tot == 0) {
|
||||
pfree_ext(idx);
|
||||
return;
|
||||
return ;
|
||||
}
|
||||
|
||||
/* Go through each bucket in the GPC HTAB and do some invalidation depending on the GPCInvalInfo we got.*/
|
||||
for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id++) {
|
||||
for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id ++) {
|
||||
/* Ok so bucket is not empty. Get the bucket S-lock so we can iterate through it. */
|
||||
int lock_id = m_array[bucket_id].lockId;
|
||||
LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE);
|
||||
MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context);
|
||||
|
||||
/* Check the number of entries in the bucket again.
|
||||
* GPC Eviction might have removed the last entry while we were waiting for the shared lock. */
|
||||
|
||||
/* Check the number of entries in the bucket again.
|
||||
* GPC Eviction might have removed the last entry while we were waiting for the shared lock. */
|
||||
int bucketEntriesCount = m_array[bucket_id].count;
|
||||
if (0 == bucketEntriesCount) {
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
|
@ -169,7 +129,7 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
|
|||
GPCEntry *entry = NULL;
|
||||
|
||||
while ((entry = (GPCEntry *)hash_seq_search(&hash_seq)) != NULL) {
|
||||
Assert(entry->val.plansource != NULL);
|
||||
Assert (entry->val.plansource != NULL);
|
||||
/* for standby mode, Invalid Msg send by xlog thread, but xlog thread didn't set db id into MyDatabaseId.
|
||||
So we need check each plan's db id by gpc'key in NeedDropEntryByLocalMsg latter */
|
||||
if (pmState == PM_RUN &&
|
||||
|
|
@ -178,7 +138,7 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
|
|||
}
|
||||
|
||||
/* Atomic read the number of CachedEnvironment in this entry */
|
||||
if (NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) {
|
||||
if(NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) {
|
||||
RemoveEntry(bucket_id, entry);
|
||||
}
|
||||
}
|
||||
|
|
@ -187,6 +147,5 @@ void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n)
|
|||
LWLockRelease(GetMainLWLockByIndex(lock_id));
|
||||
}
|
||||
|
||||
// 释放索引数组
|
||||
pfree_ext(idx);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -23,10 +23,10 @@
|
|||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/dbe_scheduler.h"
|
||||
#include "postgres.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/dbe_scheduler.h"
|
||||
|
||||
/*
|
||||
* repeat_interval = frequency_clause
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
*/
|
||||
|
||||
/* Initialize calendaring fields */
|
||||
static bool IsLegalIntervalStr(const char *str, bool numeric_only = false);
|
||||
static bool IsLegalIntervalStr(const char* str, bool numeric_only = false);
|
||||
static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only = false);
|
||||
static char **tokenize_str(char *src, const char *delims, int fields);
|
||||
static int validate_field_names(char **toks);
|
||||
|
|
@ -66,9 +66,9 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens);
|
|||
static void get_calendar_n_interval(Calendar calendar, char **tokens);
|
||||
static char *get_calendar_bymonth_val(Calendar calendar, char **tokens);
|
||||
static void get_calendar_bymonth(Calendar calendar, char **tokens);
|
||||
static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */
|
||||
static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */
|
||||
static void get_calendar_byyearday(Calendar calendar, char **tokens); /* unsupported */
|
||||
static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */
|
||||
static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */
|
||||
static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens);
|
||||
static void get_calendar_bymonthday(Calendar calendar, char **tokens);
|
||||
static void get_calendar_byday(Calendar calendar, char **tokens); /* unsupported */
|
||||
|
|
@ -97,10 +97,10 @@ static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline,
|
|||
/* Calendaring Interval Calculator */
|
||||
static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline);
|
||||
static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt);
|
||||
static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
|
||||
static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
|
||||
static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */
|
||||
static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt);
|
||||
static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
|
||||
static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
|
||||
static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
|
||||
static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */
|
||||
static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline,
|
||||
|
|
@ -115,7 +115,7 @@ static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz sta
|
|||
* @return true legal
|
||||
* @return false illegal
|
||||
*/
|
||||
static bool IsLegalIntervalStr(const char *str, bool numeric_only)
|
||||
static bool IsLegalIntervalStr(const char* str, bool numeric_only)
|
||||
{
|
||||
size_t NBytes = (unsigned int)strlen(str);
|
||||
if (NBytes > (MAX_CALENDAR_FIELD_LEN)) {
|
||||
|
|
@ -141,6 +141,7 @@ static bool IsLegalIntervalStr(const char *str, bool numeric_only)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief get_calendar_clause
|
||||
* Get calendar clause and return its value;
|
||||
|
|
@ -153,7 +154,7 @@ static char *get_calendar_clause_val(char **tokens, const char *clause, bool num
|
|||
char *val = NULL;
|
||||
for (int i = 0; i < MAX_CALENDAR_FIELDS; i += 2) {
|
||||
if (tokens[i] != NULL && pg_strcasecmp(tokens[i], clause) == 0) {
|
||||
val = tokens[i + 1]; /* get clause's value */
|
||||
val = tokens[i + 1]; /* get clause's value */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -162,10 +163,10 @@ static char *get_calendar_clause_val(char **tokens, const char *clause, bool num
|
|||
}
|
||||
|
||||
if (!IsLegalIntervalStr(val, numeric_only)) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
|
@ -204,10 +205,10 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens)
|
|||
calendar->frequency = SECONDLY;
|
||||
} else {
|
||||
pfree_ext(tokens);
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -222,7 +223,7 @@ static bool get_calendar_freqency(Calendar calendar, char **tokens)
|
|||
*/
|
||||
static void get_calendar_n_interval(Calendar calendar, char **tokens)
|
||||
{
|
||||
calendar->interval = 1; /* we ALWAYS set interval to 1 */
|
||||
calendar->interval = 1; /* we ALWAYS set interval to 1 */
|
||||
char *val = get_calendar_clause_val(tokens, "interval", true);
|
||||
if (val == NULL) {
|
||||
return;
|
||||
|
|
@ -231,10 +232,10 @@ static void get_calendar_n_interval(Calendar calendar, char **tokens)
|
|||
int num = atoi(val);
|
||||
if (num < 1 || num > MAX_CALENDAR_INTERVAL_NUM) {
|
||||
pfree_ext(tokens);
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
calendar->interval = num;
|
||||
}
|
||||
|
|
@ -305,10 +306,10 @@ static void get_calendar_bymonth(Calendar calendar, char **tokens)
|
|||
}
|
||||
}
|
||||
if (month == 0) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid month token \'%s\'.", val),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid month token \'%s\'.", val), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
} else {
|
||||
/* numeric in */
|
||||
|
|
@ -351,10 +352,10 @@ static void get_calendar_byweekno(Calendar calendar, char **tokens)
|
|||
{
|
||||
char *val = get_calendar_clause_val(tokens, "byweekno", true);
|
||||
if (val != NULL) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -372,10 +373,10 @@ static void get_calendar_byyearday(Calendar calendar, char **tokens)
|
|||
{
|
||||
char *val = get_calendar_clause_val(tokens, "byyearday", true);
|
||||
if (val != NULL) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -392,26 +393,16 @@ static void get_calendar_bydate(Calendar calendar, char **tokens)
|
|||
{
|
||||
char *val = get_calendar_clause_val(tokens, "byweekno", true);
|
||||
if (val != NULL) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYDATE clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYDATE clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取日历规则中的 "byhour" 子句的值
|
||||
*
|
||||
* 参数列表:
|
||||
* calendar:日历规则
|
||||
* tokens:日历规则中的标记
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在 "byhour" 子句,则返回其值;否则返回 NULL。
|
||||
*/
|
||||
|
||||
static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens)
|
||||
{
|
||||
// 获取 "byhour" 子句的值
|
||||
char *val = get_calendar_clause_val(tokens, "bymonthday", true);
|
||||
if (val != NULL) {
|
||||
/* apply bymonthday rule if bymonthday is specified */
|
||||
|
|
@ -430,10 +421,10 @@ static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens)
|
|||
calendar->monthday_len = 0;
|
||||
}
|
||||
/* We cannot optimize any further since monthday/yearday are not perfectly periodic */
|
||||
// 没有指定 "byhour" 子句,返回 NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief get_calendar_bymonth
|
||||
* Get bymonthday_clause.
|
||||
|
|
@ -458,10 +449,10 @@ static void get_calendar_bymonthday(Calendar calendar, char **tokens)
|
|||
while (tok != NULL) {
|
||||
int monthday = atoi(tok);
|
||||
if (monthday < -(DAYS_PER_MONTH + 1) || monthday > (DAYS_PER_MONTH + 1) || monthday == 0) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid monthday \'%d\'.", monthday),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid monthday \'%d\'.", monthday), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
tok = strtok_s(NULL, ",", &context);
|
||||
|
||||
|
|
@ -514,13 +505,14 @@ static void get_calendar_byday(Calendar calendar, char **tokens)
|
|||
{
|
||||
char *val = get_calendar_clause_val(tokens, "byday", true);
|
||||
if (val != NULL) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYDAY clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("BYDAY clause is currently unsupported."), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *get_calendar_byhour_val(Calendar calendar, char **tokens)
|
||||
{
|
||||
char *val = get_calendar_clause_val(tokens, "byhour", true);
|
||||
|
|
@ -576,10 +568,10 @@ static void get_calendar_byhour(Calendar calendar, char **tokens)
|
|||
while (tok != NULL) {
|
||||
int hour = atoi(tok);
|
||||
if (hour < 0 || hour >= HOURS_PER_DAY) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."), errdetail("Invalid time \'%d\' o\' clock.", hour),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Invalid time \'%d\' o\' clock.", hour), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
tok = strtok_s(NULL, ",", &context);
|
||||
|
||||
|
|
@ -598,19 +590,9 @@ static void get_calendar_byhour(Calendar calendar, char **tokens)
|
|||
calendar->byfields |= INTERVAL_BYHOUR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取日历规则中的 "byminute" 子句的值
|
||||
*
|
||||
* 参数列表:
|
||||
* calendar:日历规则
|
||||
* tokens:日历规则中的标记
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在 "byminute" 子句,则返回其值;否则返回 NULL。
|
||||
*/
|
||||
|
||||
static char *get_calendar_byminute_val(Calendar calendar, char **tokens)
|
||||
{
|
||||
// 获取 "byminute" 子句的值
|
||||
char *val = get_calendar_clause_val(tokens, "byminute", true);
|
||||
if (val != NULL) {
|
||||
/* apply byminute rule if byminute is specified */
|
||||
|
|
@ -639,7 +621,6 @@ static char *get_calendar_byminute_val(Calendar calendar, char **tokens)
|
|||
calendar->minute_len *= -1;
|
||||
calendar->byminute[0] = mod;
|
||||
}
|
||||
// 没有指定 "byminute" 子句,返回 NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -686,26 +667,15 @@ static void get_calendar_byminute(Calendar calendar, char **tokens)
|
|||
calendar->time_depth *= (calendar->minute_len == 0) ? 1 : calendar->minute_len;
|
||||
calendar->byfields |= INTERVAL_BYMINUTE;
|
||||
}
|
||||
/*
|
||||
* 功能:获取日历规则中的 "bysecond" 子句的值
|
||||
*
|
||||
* 参数列表:
|
||||
* calendar:日历规则
|
||||
* tokens:日历规则中的标记
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在 "bysecond" 子句,则返回其值;否则返回 NULL。
|
||||
*/
|
||||
|
||||
static char *get_calendar_bysecond_val(Calendar calendar, char **tokens)
|
||||
{
|
||||
// 获取 "bysecond" 子句的值
|
||||
char *val = get_calendar_clause_val(tokens, "bysecond", true);
|
||||
if (val != NULL) {
|
||||
/* apply bysecond rule if bysecond is specified */
|
||||
return val;
|
||||
}
|
||||
|
||||
// 断言,确保频率不高于 SECONDLY
|
||||
Assert(calendar->frequency <= SECONDLY);
|
||||
/* Even higher frequency is unavailable */
|
||||
if (calendar->frequency < SECONDLY) {
|
||||
|
|
@ -723,7 +693,6 @@ static char *get_calendar_bysecond_val(Calendar calendar, char **tokens)
|
|||
calendar->second_len *= -1;
|
||||
calendar->bysecond[0] = mod;
|
||||
}
|
||||
// 没有指定 "bysecond" 子句,返回 NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -771,6 +740,7 @@ static void get_calendar_bysecond(Calendar calendar, char **tokens)
|
|||
calendar->byfields |= INTERVAL_BYSECOND;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief tokenize_str
|
||||
* Tokenize string with given delimiters.
|
||||
|
|
@ -796,21 +766,13 @@ static char **tokenize_str(char *src, const char *delims, int fields)
|
|||
}
|
||||
return tokens;
|
||||
}
|
||||
/*
|
||||
* 功能:验证日历规则中的字段名是否有效
|
||||
*
|
||||
* 参数列表:
|
||||
* toks:日历规则中的标记数组
|
||||
*
|
||||
* 返回值:
|
||||
* 如果字段名有效,则返回 -1;否则返回字段名在标记数组中的位置。
|
||||
*/
|
||||
|
||||
static int validate_field_names(char **toks)
|
||||
{
|
||||
bool valid = false;
|
||||
const int name_pos_step = 2;
|
||||
const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday",
|
||||
"byhour", "byminute", "bysecond"};
|
||||
const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday", "byhour",
|
||||
"byminute", "bysecond"};
|
||||
bool fields_used[SUPPORTED_FIELDS] = {0};
|
||||
for (int i = 0; i < MAX_CALENDAR_FIELDS; i += name_pos_step) {
|
||||
for (int j = 0; j < SUPPORTED_FIELDS; j++) {
|
||||
|
|
@ -829,7 +791,7 @@ static int validate_field_names(char **toks)
|
|||
fields_used[j] = true;
|
||||
valid = true;
|
||||
}
|
||||
break; /* here is way pass guarding condition, break it */
|
||||
break; /* here is way pass guarding condition, break it */
|
||||
}
|
||||
if (!valid) {
|
||||
return i;
|
||||
|
|
@ -853,18 +815,19 @@ Calendar interpret_calendar_interval(char *calendar_str)
|
|||
/* Make token lists */
|
||||
char **str_toks = tokenize_str(calendar_str, " =;", MAX_CALENDAR_FIELDS);
|
||||
if (str_toks == NULL) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Unable to parse calendaring string."), errcause("Calendaring string is too long/invalid"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Unable to parse calendaring string."),
|
||||
errcause("Calendaring string is too long/invalid"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
|
||||
int pos = validate_field_names(str_toks);
|
||||
if (pos >= 0) {
|
||||
ereport(ERROR,
|
||||
(errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Fail to evaluate calendaring string."),
|
||||
errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"),
|
||||
erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
|
||||
/* Make Calendar */
|
||||
|
|
@ -1065,7 +1028,7 @@ static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timelin
|
|||
*cnt = 0;
|
||||
int tz = 0;
|
||||
fsec_t fsec;
|
||||
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
|
||||
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
|
||||
copy_calendar_dates(timeline, calendar->monthday_len, chunk);
|
||||
for (int i = 0; i < calendar->monthday_len; i++) {
|
||||
if (calendar->bymonthday[i] < 0) {
|
||||
|
|
@ -1259,7 +1222,8 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da
|
|||
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), PointerGetDatum(period));
|
||||
Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"),
|
||||
PointerGetDatum(period));
|
||||
int pace = (int)DatumGetFloat8(pace_datum);
|
||||
pfree_ext(period);
|
||||
|
||||
|
|
@ -1273,8 +1237,8 @@ static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_da
|
|||
Interval *ff_span = get_calendar_period(calendar, num_of_periods);
|
||||
if (ff_span == NULL) {
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."),
|
||||
errcause("N/A"), erraction("Please modify the calendaring string.")));
|
||||
}
|
||||
new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span));
|
||||
pfree_ext(ff_span);
|
||||
|
|
@ -1433,7 +1397,7 @@ static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, Ti
|
|||
}
|
||||
|
||||
fsec_t fsec;
|
||||
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
|
||||
struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */
|
||||
int tz;
|
||||
if (timestamp2tm(base_date, &tz, tm, &fsec, NULL, NULL) != 0) {
|
||||
ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
|
|
@ -1467,6 +1431,7 @@ static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_
|
|||
return base_date;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief evaluate_calendar_interval
|
||||
* Calculate next date base on start date.
|
||||
|
|
@ -1551,9 +1516,9 @@ Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_a
|
|||
*/
|
||||
Datum evaluate_calendar_string_internal(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum string = PG_GETARG_DATUM(0); /* calendar string */
|
||||
Datum start_date = PG_GETARG_DATUM(1); /* start date */
|
||||
Datum date_after = PG_GETARG_DATUM(2); /* return date after */
|
||||
Datum string = PG_GETARG_DATUM(0); /* calendar string */
|
||||
Datum start_date = PG_GETARG_DATUM(1); /* start date */
|
||||
Datum date_after = PG_GETARG_DATUM(2); /* return date after */
|
||||
Datum new_next_date = evaluate_repeat_interval(string, start_date, date_after);
|
||||
PG_RETURN_DATUM(new_next_date);
|
||||
}
|
||||
|
|
@ -61,63 +61,60 @@ THR_LOCAL bool IsInitdb = false;
|
|||
|
||||
size_t mmap_threshold = (size_t)0xffffffff;
|
||||
|
||||
const char *progname = NULL;
|
||||
const char* progname = NULL;
|
||||
|
||||
static void startup_hacks(const char *progname);
|
||||
static void help(const char *progname);
|
||||
static void check_root(const char *progname);
|
||||
static char *get_current_username(const char *progname);
|
||||
static void startup_hacks(const char* progname);
|
||||
static void help(const char* progname);
|
||||
static void check_root(const char* progname);
|
||||
static char* get_current_username(const char* progname);
|
||||
static void syscall_lock_init(void);
|
||||
|
||||
extern int encrypte_main(int argc, char *const argv[]);
|
||||
extern int encrypte_main(int argc, char* const argv[]);
|
||||
|
||||
/*
|
||||
* Any openGauss server process begins execution here.
|
||||
*/
|
||||
/*
|
||||
* 功能:GaussDB 主函数
|
||||
*
|
||||
* 参数列表:
|
||||
* argc:命令行参数的数量
|
||||
* argv:命令行参数的字符串数组
|
||||
*
|
||||
* 注意:
|
||||
* 该函数负责 GaussDB 的启动和主要控制流程。它会根据命令行参数分发到不同的子程序,如
|
||||
* initdb、Postmaster、GucInfoMain等。 在启动过程中,会进行一系列初始化操作,包括内存管理、信号处理、地区设置等。
|
||||
* 根据命令行参数,可能执行初始化数据库、显示配置信息或启动主 Postmaster 进程。
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char *mmap_env = NULL;
|
||||
syscall_lock_init(); // 初始化系统调用锁
|
||||
char* mmap_env = NULL;
|
||||
syscall_lock_init();
|
||||
|
||||
mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD");
|
||||
if (mmap_env != NULL) {
|
||||
check_backend_env(mmap_env); // 检查后端环境变量
|
||||
mmap_threshold = (size_t)atol(mmap_env); // 设置内存映射阈值
|
||||
check_backend_env(mmap_env);
|
||||
mmap_threshold = (size_t)atol(mmap_env);
|
||||
}
|
||||
|
||||
knl_instance_init(); // 初始化内核实例
|
||||
knl_instance_init();
|
||||
|
||||
// 创建增量检查点上下文
|
||||
g_instance.increCheckPoint_context = AllocSetContextCreate(
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), "IncreCheckPointContext", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE),
|
||||
"IncreCheckPointContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
|
||||
// 创建帐户上下文
|
||||
g_instance.account_context =
|
||||
AllocSetContextCreate(g_instance.instance_context, "StandbyAccontContext", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
|
||||
g_instance.account_context = AllocSetContextCreate(g_instance.instance_context,
|
||||
"StandbyAccontContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
|
||||
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);
|
||||
|
||||
// 创建通信全局内存上下文
|
||||
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);
|
||||
|
||||
// 创建内置过程全局内存上下文
|
||||
g_instance.builtin_proc_context =
|
||||
AllocSetContextCreate(g_instance.instance_context, "builtin_procGlobalMemoryContext", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
|
||||
g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context,
|
||||
"builtin_procGlobalMemoryContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
/*
|
||||
* Fire up essential subsystems: error and memory management
|
||||
*
|
||||
|
|
@ -129,9 +126,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
PmTopMemoryContext = t_thrd.top_mem_cxt;
|
||||
|
||||
knl_thread_init(MASTER_THREAD); // 初始化内核线程
|
||||
knl_thread_init(MASTER_THREAD);
|
||||
|
||||
// 创建伪会话上下文
|
||||
t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0);
|
||||
t_thrd.fake_session->status = KNL_SESS_FAKE;
|
||||
|
||||
|
|
@ -141,21 +137,19 @@ int main(int argc, char *argv[])
|
|||
|
||||
MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT));
|
||||
|
||||
progname = get_progname(argv[0]); // 获取程序名
|
||||
progname = get_progname(argv[0]);
|
||||
|
||||
/*
|
||||
* Platform-specific startup hacks
|
||||
*/
|
||||
// 平台特定的启动操作
|
||||
startup_hacks(progname);
|
||||
|
||||
/* if gaussdb's name is gs_encrypt, so run in encrypte_main() */
|
||||
// 如果程序名是 "gs_encrypt",则执行 encrypte_main() 函数
|
||||
if (!strcmp(progname, "gs_encrypt")) {
|
||||
return encrypte_main(argc, argv);
|
||||
}
|
||||
|
||||
init_plog_global_mem(); // 初始化全局日志内存
|
||||
init_plog_global_mem();
|
||||
|
||||
/*
|
||||
* Remember the physical location of the initially given argv[] array for
|
||||
|
|
@ -197,7 +191,7 @@ int main(int argc, char *argv[])
|
|||
* environment. If there is nothing there we fall back on the codepage.
|
||||
*/
|
||||
{
|
||||
char *env_locale = NULL;
|
||||
char* env_locale = NULL;
|
||||
|
||||
if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) {
|
||||
check_backend_env(env_locale);
|
||||
|
|
@ -264,8 +258,8 @@ int main(int argc, char *argv[])
|
|||
pgwin32_signal_initialize();
|
||||
#endif
|
||||
|
||||
t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate(
|
||||
t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) {
|
||||
ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. ")));
|
||||
proc_exit(1);
|
||||
|
|
@ -316,7 +310,7 @@ int main(int argc, char *argv[])
|
|||
* is too brain-dead to provide a standard C execution environment
|
||||
* without help. Avoid adding more here, if you can.
|
||||
*/
|
||||
static void startup_hacks(const char *progname)
|
||||
static void startup_hacks(const char* progname)
|
||||
{
|
||||
/*
|
||||
* On some platforms, unaligned memory accesses result in a kernel trap;
|
||||
|
|
@ -369,8 +363,7 @@ static void startup_hacks(const char *progname)
|
|||
* Help display should match the options accepted by PostmasterMain()
|
||||
* and PostgresMain().
|
||||
*/
|
||||
// 此函数用于对它支持的命令行选项和用法进行说明。这有助于理解代码的功能和如何使用这些选项来运行程序。
|
||||
static void help(const char *progname)
|
||||
static void help(const char* progname)
|
||||
{
|
||||
printf(_("%s is the gaussdb server.\n\n"), progname);
|
||||
printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
|
||||
|
|
@ -469,7 +462,7 @@ static void help(const char *progname)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void check_root(const char *progname)
|
||||
static void check_root(const char* progname)
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (geteuid() == 0) {
|
||||
|
|
@ -503,49 +496,29 @@ static void check_root(const char *progname)
|
|||
}
|
||||
#endif /* WIN32 */
|
||||
}
|
||||
/*
|
||||
* 功能:获取当前操作系统用户的用户名
|
||||
*
|
||||
* 参数列表:
|
||||
* progname:程序的名称,用于错误消息
|
||||
*
|
||||
* 返回值:
|
||||
* 当前用户的用户名,以字符串形式返回
|
||||
*
|
||||
* 注意:
|
||||
* 该函数在不同的操作系统下使用不同的方法获取用户名。
|
||||
* 在 Unix-like 系统下,使用 getpwuid 函数获取用户名。
|
||||
* 在 Windows 系统下,使用 GetUserName 函数获取用户名。
|
||||
* 函数内部包含线程安全措施,以确保在多线程环境中的正确性。
|
||||
*/
|
||||
static char *get_current_username(const char *progname)
|
||||
|
||||
static char* get_current_username(const char* progname)
|
||||
{
|
||||
#ifndef WIN32
|
||||
struct passwd *pw = NULL;
|
||||
char *pRet = NULL;
|
||||
struct passwd* pw = NULL;
|
||||
char* pRet = NULL;
|
||||
|
||||
/* 获取 getpwuid 函数的锁,以确保线程安全 */
|
||||
(void)syscalllockAcquire(&getpwuid_lock);
|
||||
/* 获取当前用户的密码项 */
|
||||
pw = getpwuid(geteuid());
|
||||
if (pw == NULL) {
|
||||
/* 释放锁并报告错误,如果获取密码项失败 */
|
||||
(void)syscalllockRelease(&getpwuid_lock);
|
||||
write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid());
|
||||
exit(1);
|
||||
}
|
||||
/* Allocate new memory because later getpwuid() calls can overwrite it. */
|
||||
pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name);
|
||||
/* 释放锁并返回用户名 */
|
||||
(void)syscalllockRelease(&getpwuid_lock);
|
||||
return pRet;
|
||||
#else
|
||||
unsigned long namesize = 256 /* UNLEN */ + 1;
|
||||
char *name = NULL;
|
||||
char* name = NULL;
|
||||
|
||||
/* 在内存上分配空间以存储用户名 */
|
||||
name = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize);
|
||||
/* 尝试获取 Windows 用户名 */
|
||||
if (!GetUserName(name, &namesize)) {
|
||||
write_stderr("%s: could not determine user name (GetUserName failed)\n", progname);
|
||||
exit(1);
|
||||
|
|
@ -554,29 +527,12 @@ static char *get_current_username(const char *progname)
|
|||
return name;
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* 功能:初始化系统调用锁
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于初始化多个系统调用锁,以确保在多线程环境中的正确性。
|
||||
* 每个锁用于保护不同的系统调用,以避免并发调用时的竞争条件。
|
||||
*/
|
||||
|
||||
static void syscall_lock_init(void)
|
||||
{
|
||||
/* 初始化获取用户密码项的锁 */
|
||||
syscalllockInit(&getpwuid_lock);
|
||||
|
||||
/* 初始化环境变量锁 */
|
||||
syscalllockInit(&env_lock);
|
||||
|
||||
/* 初始化 dlerror 函数的锁 */
|
||||
syscalllockInit(&dlerror_lock);
|
||||
|
||||
/* 初始化 Kerberos 连接锁 */
|
||||
syscalllockInit(&kerberos_conn_lock);
|
||||
|
||||
/* 初始化读取加密数据的锁 */
|
||||
syscalllockInit(&read_cipher_lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"utility": "cpp"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -22,7 +22,6 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
// 该文件实现了openGauss的报警检查线程功能,主要用于检查数据库运行过程中的异常情况并进行相应的报警处理
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
|
|
@ -49,77 +48,64 @@
|
|||
#include "replication/walsender.h"
|
||||
|
||||
// declare the global variable of alarm module
|
||||
// 声明用于控制报警模块行为的全局变量
|
||||
int g_alarmReportInterval; // 报警上报的时间间隔(单位:秒)
|
||||
char g_alarmComponentPath[MAXPGPATH]; // 报警组件路径,存储报警信息的组件的路径(长度为MAXPGPATH)
|
||||
int g_alarmReportMaxCount; // 最大报警上报次数
|
||||
int g_alarmReportInterval;
|
||||
char g_alarmComponentPath[MAXPGPATH];
|
||||
int g_alarmReportMaxCount;
|
||||
/* seconds, interval of alarm check loop. */
|
||||
static const int AlarmCheckInterval = 1; // 报警检查循环的时间间隔,初始设置为1秒
|
||||
static const int AlarmCheckInterval = 1;
|
||||
|
||||
bool enable_alarm = false; // 表示是否启用报警功能。初始值为false,通过设置为true来启用报警功能
|
||||
bool enable_alarm = false;
|
||||
|
||||
static Alarm* DataInstAlarmList = NULL; // 指向 Alarm 结构体的指针,表示报警项的列表。报警项是用于检测不同类型报警的配置信息和处理函数的集合。
|
||||
static Alarm* DataInstAlarmList = NULL;
|
||||
|
||||
static int DataInstAlarmListSize = 0; // 报警项列表的大小,即列表中报警项的数量
|
||||
static int DataInstAlarmListSize = 0;
|
||||
|
||||
// 函数原型声明
|
||||
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);//报警检查函数,用于检查数据目录或重做日志目录是否存在
|
||||
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
|
||||
static void DataInstAlarmItemInitialize(void);
|
||||
|
||||
static void DataInstAlarmItemInitialize(void); // 报警项初始化函数,用于初始化数据实例的报警项列表。
|
||||
static void acSighupHandler(SIGNAL_ARGS);
|
||||
static void acSigquitHandler(SIGNAL_ARGS);
|
||||
|
||||
static void acSighupHandler(SIGNAL_ARGS); // SIGHUP信号处理函数,用于在收到SIGHUP信号时设置相关标志
|
||||
static void acSigquitHandler(SIGNAL_ARGS); // SIGQUIT信号处理函数的原型,用于在收到SIGQUIT信号时设置相关标志。
|
||||
// 数据实例归档检查函数,用于检查数据实例的归档状态
|
||||
extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
// 连接认证方法检查函数,用于检查连接的认证方法是否异常
|
||||
extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
// 数据实例连接到GTM的检查函数,用于检查数据实例连接到GTM的状态
|
||||
extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
extern AlarmCheckResult DataInstConnToGTMChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
|
||||
// 初始化数据实例报警列表
|
||||
void DataInstAlarmItemInitialize(void)
|
||||
{
|
||||
// 设置数据实例报警项的数量为6
|
||||
DataInstAlarmListSize = 6;
|
||||
// 分配内存来存储数据实例报警项
|
||||
DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize);
|
||||
// 检查内存分配是否成功,如果失败则记录错误日志并退出程序
|
||||
if (NULL == DataInstAlarmList) {
|
||||
AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed.");
|
||||
exit(1);
|
||||
}
|
||||
// 初始化各个数据实例报警项 参数列表中后三个分别为报警项的类型、严重性级别,以及对应的检查函数
|
||||
// ALM_AI_MissingDataInstDataOrRedoDir 报警项
|
||||
// ALM_AI_MissingDataInstDataOrRedoDir
|
||||
AlarmItemInitialize(
|
||||
&(DataInstAlarmList[0]), ALM_AI_MissingDataInstDataOrRedoDir, ALM_AS_Normal, DataOrRedoDirNotExistChecker);
|
||||
// ALM_AI_MissingDataInstWalSegmt 报警项
|
||||
// ALM_AI_MissingDataInstWalSegmt
|
||||
AlarmItemInitialize(
|
||||
&(DataInstAlarmList[1]), ALM_AI_MissingDataInstWalSegmt, ALM_AS_Normal, WalSegmentsRemovedChecker);
|
||||
// ALM_AI_TooManyDataInstConn 报警项
|
||||
// ALM_AI_TooManyDataInstConn
|
||||
AlarmItemInitialize(&(DataInstAlarmList[2]), ALM_AI_TooManyDataInstConn, ALM_AS_Normal, ConnectionOverloadChecker);
|
||||
// ALM_AI_AbnormalDataInstArch 报警项
|
||||
// ALM_AI_AbnormalDataInstArch
|
||||
AlarmItemInitialize(&(DataInstAlarmList[3]), ALM_AI_AbnormalDataInstArch, ALM_AS_Normal, DataInstArchChecker);
|
||||
// ALM_AI_AbnormalDataInstConnAuthMethod 报警项
|
||||
// ALM_AI_AbnormalDataInstConnAuthMethod
|
||||
AlarmItemInitialize(
|
||||
&(DataInstAlarmList[4]), ALM_AI_AbnormalDataInstConnAuthMethod, ALM_AS_Normal, ConnAuthMethodChecker);
|
||||
// ALM_AI_AbnormalDataInstConnToGTM 报警项
|
||||
// ALM_AI_AbnormalDataInstConnToGTM
|
||||
AlarmItemInitialize(
|
||||
&(DataInstAlarmList[5]), ALM_AI_AbnormalDataInstConnToGTM, ALM_AS_Normal, DataInstConnToGTMChecker);
|
||||
}
|
||||
|
||||
// 特定条件下启动报警检查线程,以便定期检查系统状态并进行报警处理。
|
||||
ThreadId startAlarmChecker(void)
|
||||
{
|
||||
// 如果不是在Postmaster环境下或者报警功能被禁用,则直接返回0,表示未启动报警检查线程
|
||||
if (!IsPostmasterEnvironment || !enable_alarm) {
|
||||
return 0;
|
||||
}
|
||||
// 否则,调用initialize_util_thread函数启动报警检查线程,并返回线程ID
|
||||
|
||||
return initialize_util_thread(ALARMCHECK);
|
||||
}
|
||||
|
||||
// 维护一个周期性的报警检查线程,用于及时发现系统异常情况并进行相应的处理。
|
||||
NON_EXEC_STATIC void AlarmCheckerMain()
|
||||
{
|
||||
|
||||
|
|
@ -127,23 +113,21 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
IsUnderPostmaster = true;
|
||||
|
||||
/* reset t_thrd.proc_cxt.MyProcPid */
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); //将当前线程的系统级线程ID分配给MyProcPid,以标识当前线程
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
|
||||
/* record Start Time for logging */
|
||||
t_thrd.proc_cxt.MyStartTime = time(NULL); //获取当前的系统时间,即记录线程的启动时间。
|
||||
t_thrd.proc_cxt.MyStartTime = time(NULL);
|
||||
|
||||
/* reord my name */
|
||||
t_thrd.proc_cxt.MyProgName = "AlarmChecker"; // 设置线程的名称,用于标识当前线程的名称
|
||||
t_thrd.proc_cxt.MyProgName = "AlarmChecker";
|
||||
|
||||
/* Identify myself via ps */
|
||||
init_ps_display("AlarmChecker", "", "", ""); // 设置线程在进程状态(ps)显示中的标识为 "AlarmChecker"
|
||||
init_ps_display("AlarmChecker", "", "", "");
|
||||
|
||||
AlarmLog(ALM_LOG, "alarm checker started."); // 记录报警检查线程启动信息
|
||||
AlarmLog(ALM_LOG, "alarm checker started.");
|
||||
|
||||
// 初始化Latch支持,用于等待Latch的触发
|
||||
InitializeLatchSupport(); /* needed for latch waits */
|
||||
|
||||
// 初始化用于信号处理的私有Latch,以便在信号到达时唤醒线程执行相应的处理
|
||||
/* Initialize private latch for use by signal handlers */
|
||||
InitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
|
||||
|
|
@ -155,15 +139,10 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
* want to wait for the backends to exit, whereupon the postmaster will
|
||||
* tell us it's okay to shut down (via SIGUSR2).
|
||||
*/
|
||||
// 处理了信号的设置和忽略,确保报警检查线程能够正确响应或忽略不同的信号
|
||||
// 将SIGHUP信号的处理函数设置为acSighupHandler。当收到SIGHUP信号时,会触发该信号处理函数,用于读取配置文件的标志位。
|
||||
(void)gspqsignal(SIGHUP, acSighupHandler); /* set flag to read config file */
|
||||
// 将SIGINT和SIGTERM信号的处理设置为忽略状态,当收到这两个信号时,不会触发任何处理。
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
(void)gspqsignal(SIGTERM, SIG_IGN);
|
||||
// 将SIGQUIT信号的处理函数设置为acSigquitHandler。当收到SIGQUIT信号时,会触发该信号处理函数,用于执行快速退出操作。
|
||||
(void)gspqsignal(SIGQUIT, acSigquitHandler);
|
||||
// 将SIGALRM、SIGPIPE、SIGUSR1、SIGUSR2信号的处理设置为忽略状态,即当收到以上信号时,不会触发任何处理。
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, SIG_IGN);
|
||||
|
|
@ -172,57 +151,46 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
// 重置一些信号的默认处理方式,以确保报警检查线程不会干扰其他信号的处理
|
||||
// 将信号的处理设置为默认处理方式 SIG_DFL
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 子进程状态变化信号
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL); // 后台进程试图从终端读取时发送的信号
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL); // 后台进程试图向终端写入时发送的信号
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL); // 用于继续停止的进程的信号
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL); // 终端窗口大小发生变化时发送的信号
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(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(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT));
|
||||
// 调用函数,初始化数据实例的报警列表
|
||||
|
||||
DataInstAlarmItemInitialize();
|
||||
|
||||
// 报警检查线程的主要工作循环,用于持续进行报警检查和处理
|
||||
for (;;) {
|
||||
/* Clear any already-pending wakeups */
|
||||
//将报警检查线程的私有Latch重置为未触发状态,防止在等待期间可能发生的竞争条件或意外触发。
|
||||
ResetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
|
||||
/* the normal shutdown case */
|
||||
// 如果收到了终止信号,退出循环
|
||||
if (t_thrd.alarm_cxt.gotSigdie)
|
||||
break;
|
||||
|
||||
/*
|
||||
* reload the postgresql.conf
|
||||
*/
|
||||
// 如果收到了重新加载配置文件的信号
|
||||
if (t_thrd.alarm_cxt.gotSighup) {
|
||||
// 设置为 false,表示报警检查线程不再需要重新加载配置文件。
|
||||
// 用于处理 SIGHUP 信号执行操作,标记已经理重新加载配置文件的请求,以便线程在下次循环迭代时不会再次触发重新加载
|
||||
t_thrd.alarm_cxt.gotSighup = false;
|
||||
ProcessConfigFile(PGC_SIGHUP); // 调用ProcessConfigFile函数重新加载配置文件
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
}
|
||||
// 调用AlarmCheckerLoop函数进行报警检查
|
||||
|
||||
AlarmCheckerLoop(DataInstAlarmList, DataInstAlarmListSize);
|
||||
|
||||
/*
|
||||
* Sleep until there's something to do
|
||||
*/
|
||||
// 等待一段时间,等待Latch被设置或超时
|
||||
(void)WaitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch, WL_LATCH_SET | WL_TIMEOUT, AlarmCheckInterval * 1000);
|
||||
}
|
||||
// 记录日志,标识报警检查线程正在关闭
|
||||
|
||||
AlarmLog(ALM_LOG, "alarm checker shutting down...");
|
||||
// 调用 proc_exit 函数终止线程执行。参数 0 表示正常退出,线程将在此处终止并释放相关资源
|
||||
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
|
|
@ -235,16 +203,15 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
// 信号处理函数,用于处理 SIGHUP 信号,并设置相应的标志
|
||||
static void acSighupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前 errno 的值,以便后续恢复
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.alarm_cxt.gotSighup = true; // 将线程上下文中的 gotSighup 标志设置为 true,表示收到了 SIGHUP 信号
|
||||
t_thrd.alarm_cxt.gotSighup = true;
|
||||
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); // 使用 SetLatch 函数触发线程的私有 Latch,以便唤醒线程并处理信号
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
|
||||
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 errno 的操作
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -253,96 +220,84 @@ static void acSighupHandler(SIGNAL_ARGS)
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
// 信号处理函数,用于处理 SIGTERM 或 SIGINT 信号,并设置相应的标志
|
||||
static void acSigquitHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;// 保存当前 errno 的值,以便后续恢复
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.alarm_cxt.gotSigdie = true;// 将线程上下文中的 gotSigdie 标志设置为 true,表示收到了 SIGTERM 或 SIGINT 信号
|
||||
t_thrd.alarm_cxt.gotSigdie = true;
|
||||
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);// 使用 SetLatch 函数触发线程的私有 Latch,以便唤醒线程并处理信号
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
|
||||
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 errno 的操作
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
// 用于检查目录是否存在,以及目录是否具有合适的属性和权限,即检查目录的有效性。
|
||||
bool isDirExist(const char* dir)
|
||||
{
|
||||
// 创建一个用于存放文件/目录属性信息的结构体
|
||||
struct stat stat_buf;
|
||||
// 使用 stat 函数获取目录的状态信息,如果返回值不等于0,则表示目录不存在
|
||||
|
||||
if (stat(dir, &stat_buf) != 0)
|
||||
return false;
|
||||
// 使用 S_ISDIR 宏判断目录的文件类型是否为目录,如果不是目录类型,则返回 false
|
||||
|
||||
if (!S_ISDIR(stat_buf.st_mode))
|
||||
return false;
|
||||
|
||||
// 如果不在 Windows 平台且不在 Cygwin 环境中
|
||||
#if !defined(WIN32) && !defined(__CYGWIN__)
|
||||
// 检查目录的拥有者是否为当前用户,如果不是则返回 false
|
||||
|
||||
if (stat_buf.st_uid != geteuid())
|
||||
return false;
|
||||
// 检查目录的权限是否为用户可读、写和执行权限,如果不是则返回 false
|
||||
|
||||
if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU)
|
||||
return false;
|
||||
|
||||
#endif
|
||||
// 如果以上条件都满足,则返回 true,表示目录存在且符合要求
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam)
|
||||
{
|
||||
// 检查 data 目录和 pg_xlog 目录是否存在
|
||||
if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) {
|
||||
// fill the alarm message
|
||||
// 填写报警信息
|
||||
WriteAlarmAdditionalInfo(additionalParam, //additionalParam:报警的附加参数,用于存储报警信息的详细内容
|
||||
g_instance.attr.attr_common.PGXCNodeName, // 数据库实例的名称,用于标识报警发生的实例
|
||||
"",
|
||||
"",
|
||||
alarm, //报警的类型或描述,用于标识具体的报警原因或问题
|
||||
ALM_AT_Resume,// 设置报警动作为“恢复”,表示报警条件已经解决
|
||||
g_instance.attr.attr_common.PGXCNodeName); //数据库实例的名称,用于填写报警信息
|
||||
return ALM_ACR_Normal;// 返回报警检查结果为“正常”
|
||||
} else {
|
||||
// fill the alarm message
|
||||
// 填写报警信息
|
||||
WriteAlarmAdditionalInfo(additionalParam,
|
||||
g_instance.attr.attr_common.PGXCNodeName,
|
||||
"",
|
||||
"",
|
||||
alarm,
|
||||
ALM_AT_Fault, // 设置报警动作为“故障”
|
||||
ALM_AT_Resume,
|
||||
g_instance.attr.attr_common.PGXCNodeName);
|
||||
return ALM_ACR_Abnormal;// 返回报警检查结果为“异常”
|
||||
return ALM_ACR_Normal;
|
||||
} else {
|
||||
// fill the alarm message
|
||||
WriteAlarmAdditionalInfo(additionalParam,
|
||||
g_instance.attr.attr_common.PGXCNodeName,
|
||||
"",
|
||||
"",
|
||||
alarm,
|
||||
ALM_AT_Fault,
|
||||
g_instance.attr.attr_common.PGXCNodeName);
|
||||
return ALM_ACR_Abnormal;
|
||||
}
|
||||
}
|
||||
|
||||
/* implementation of alarm module. */
|
||||
// 用于释放动态分配的内存,避免内存泄漏
|
||||
void AlarmFree(void* pointer)
|
||||
{
|
||||
if (pointer != NULL)// 检查指针是否非空
|
||||
pfree(pointer); // 使用 pfree 函数释放内存
|
||||
if (pointer != NULL)
|
||||
pfree(pointer);
|
||||
}
|
||||
|
||||
// 用于分配指定大小的内存块
|
||||
void* AlarmAlloc(size_t size)
|
||||
{
|
||||
return palloc(size);// 调用 palloc 函数分配指定大小的内存块,并返回分配的内存块指针
|
||||
return palloc(size);
|
||||
}
|
||||
|
||||
// 日志输出函数,用于在不同级别输出报警信息
|
||||
// 输入参数分别为报警级别、前缀和报警文本,根据需要输出不同级别的报警信息以进行监控和调试。
|
||||
void AlarmLogImplementation(int level, const char* prefix, const char* logtext)
|
||||
{
|
||||
// 使用 switch 语句根据不同的级别选择不同的日志输出函数并输出信息
|
||||
switch (level) {
|
||||
case ALM_DEBUG:// 在 DEBUG3 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
|
||||
case ALM_DEBUG:
|
||||
ereport(DEBUG3, (errmsg("%s%s", prefix, logtext)));
|
||||
break;
|
||||
case ALM_LOG:// 在 LOG 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
|
||||
case ALM_LOG:
|
||||
ereport(LOG, (errmsg("%s%s", prefix, logtext)));
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -52,72 +52,60 @@
|
|||
|
||||
#define atolsn(x) ((XLogRecPtr)strtoul((x), NULL, 0))
|
||||
|
||||
// 将barrier_id 写入到OBS(华为云对象存储)中,用于实现数据存档
|
||||
static void write_barrier_id_to_obs(const char* barrier_name, ArchiveConfig *archive_obs)
|
||||
{
|
||||
errno_t rc = 0; // 用于存储函数调用的返回值,以便检查错误
|
||||
ArchiveConfig obsConfig; // 存储OBS配置的临时变量
|
||||
char pathPrefix[MAXPGPATH] = {0}; // 用于存储OBS路径前缀的临时变量
|
||||
errno_t rc = 0;
|
||||
ArchiveConfig obsConfig;
|
||||
char pathPrefix[MAXPGPATH] = {0};
|
||||
|
||||
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name))); // 输出日志,表示开始写入BarrierID到OBS
|
||||
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name)));
|
||||
|
||||
/* copy OBS configs to temporary variable for customising file path */
|
||||
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig)); // 将OBS配置复制到临时变量,以便自定义文件路径
|
||||
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig));
|
||||
securec_check(rc, "", "");
|
||||
|
||||
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
|
||||
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix); // 将OBS路径前缀复制到pathPrefix中
|
||||
if (!IS_PGXC_COORDINATOR) {
|
||||
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix);
|
||||
securec_check(rc, "\0", "\0");
|
||||
// 查找路径中的最后一个 '/'
|
||||
char *p = strrchr(pathPrefix, '/');
|
||||
char *p = strrchr(pathPrefix, '/');
|
||||
if (p == NULL) {
|
||||
// 如果找不到最后一个 '/',则输出错误并终止
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("Obs path prefix is invalid")));
|
||||
}
|
||||
// 将找到的最后一个 '/' 替换为字符串结束符,以截取路径前缀
|
||||
*p = '\0';
|
||||
// 将修改后的路径前缀赋值回obsConfig
|
||||
obsConfig.archive_prefix = pathPrefix;
|
||||
}
|
||||
|
||||
// 调用ArchiveWrite函数,将BarrierID写入OBS
|
||||
ArchiveWrite(BARRIER_FILE, barrier_name, MAX_BARRIER_ID_LENGTH - 1, &obsConfig);
|
||||
}
|
||||
|
||||
// 等待Barrier归档操作,直到达到指定的LSN
|
||||
static void WaitBarrierArch(XLogRecPtr barrierLsn, const char *slotName)
|
||||
{
|
||||
// 输出日志,表示开始等待Barrier归档操作
|
||||
ereport(LOG,
|
||||
(errmsg("WaitBarrierArch start: 0x%lx", barrierLsn)));
|
||||
|
||||
int cnt = 0; // 用于计数等待次数
|
||||
const int interval = 100; // 定义计数间隔
|
||||
int cnt = 0;
|
||||
const int interval = 100;
|
||||
do {
|
||||
ArchiveTaskStatus *archive_task_status = NULL; // 用于存储归档任务状态的指针
|
||||
archive_task_status = find_archive_task_status(slotName); // 根据slotName查找归档任务状态
|
||||
ArchiveTaskStatus *archive_task_status = NULL;
|
||||
archive_task_status = find_archive_task_status(slotName);
|
||||
if (NULL == archive_task_status) {
|
||||
// 如果找不到归档任务状态,输出错误信息并终止
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_NOT_SUPPORTED), errmsg("Obs slot <%s> not exist.", slotName)));
|
||||
}
|
||||
|
||||
// 比较barrierLsn和当前已归档的LSN,如果达到了要求则跳出循环
|
||||
if (XLByteLE(pg_atomic_read_u64(&barrierLsn),
|
||||
pg_atomic_read_u64(&archive_task_status->archived_lsn))) {
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
/* Also check stop flag */
|
||||
if (t_thrd.barrier_arch.ready_to_stop) {
|
||||
// 如果已经准备停止,则输出错误信息并终止
|
||||
ereport(ERROR, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("[BarrierArch] terminating barrier arch"
|
||||
" due to administrator command")));
|
||||
}
|
||||
pg_usleep(100000L); // 等待100000微秒(0.1秒)
|
||||
pg_usleep(100000L);
|
||||
if (t_thrd.barrier_arch.lastArchiveLoc == pg_atomic_read_u64(&archive_task_status->archived_lsn)) {
|
||||
// 如果归档位置没有发生变化,计数加一,并在一定间隔输出警告信息
|
||||
cnt++;
|
||||
if ((cnt % interval) == 0) {
|
||||
ereport(WARNING, (errmsg("[WaitBarrierArch] arch thread now archived"
|
||||
|
|
@ -125,27 +113,23 @@ static void WaitBarrierArch(XLogRecPtr barrierLsn, const char *slotName)
|
|||
(uint32)t_thrd.barrier_arch.lastArchiveLoc, cnt)));
|
||||
}
|
||||
} else {
|
||||
cnt = 0; // 如果归档位置发生变化,计数清零
|
||||
cnt = 0;
|
||||
}
|
||||
// 更新归档位置,并检查是否超过了等待超时
|
||||
t_thrd.barrier_arch.lastArchiveLoc = pg_atomic_read_u64(&archive_task_status->archived_lsn);
|
||||
if (cnt > WAIT_ARCHIVE_TIMEOUT) {
|
||||
// 如果等待次数超过了设定的超时次数,输出错误信息并终止
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_NOT_SUPPORTED), errmsg("Wait archived timeout.")));
|
||||
}
|
||||
} while (1); // 无限循环,直到达到要求的LSN
|
||||
} while (1);
|
||||
|
||||
ereport(LOG, (errmsg("WaitBarrierArch archive lsn end"))); // 输出日志,表示等待归档操作结束
|
||||
ereport(LOG, (errmsg("WaitBarrierArch archive lsn end")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the current BARRIER WAL record has been archived.If not, wait until
|
||||
* the BARRIER WAL record has been archived.
|
||||
*/
|
||||
// 确保当前的BARRIER WAL记录已被归档
|
||||
void ProcessBarrierQueryArchive(char* id)
|
||||
{
|
||||
// 输出日志,表示收到BARRIER QUERY消息
|
||||
ereport(LOG,
|
||||
(errmsg("Receive BARRIER <%s> QUERY message on Coordinator or Datanode", id)));
|
||||
|
||||
|
|
@ -153,99 +137,84 @@ void ProcessBarrierQueryArchive(char* id)
|
|||
char *slotName;
|
||||
char *lsn;
|
||||
|
||||
lsn = strtok_r(id, ":", &slotName); // 使用":"分割id,获取LSN和slotName
|
||||
lsn = strtok_r(id, ":", &slotName);
|
||||
if (lsn == NULL) {
|
||||
// 如果LSN为空,输出错误信息并终止
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
|
||||
errmsg("The BARRIER QUERY ARCHIVE target lsn is null")));
|
||||
}
|
||||
// 将LSN转换为XLogRecPtr类型
|
||||
XLogRecPtr barrierTargetLsn = atolsn(lsn);
|
||||
// 输出日志,表示收到指定LSN消息
|
||||
ereport(LOG,
|
||||
(errmsg("Receive LSN <%lx> message on Coordinator or Datanode", barrierTargetLsn)));
|
||||
|
||||
// 输出日志,表示收到BARRIER QUERY消息的slotName
|
||||
ereport(LOG,
|
||||
(errmsg("Receive BARRIER QUERY message slotname: %s", slotName)));
|
||||
|
||||
// 如果不是从协调器连接,输出错误信息并终止
|
||||
if (!IsConnFromCoord())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
|
||||
errmsg("The BARRIER QUERY ARCHIVE message is expected to "
|
||||
"arrive from a Coordinator")));
|
||||
|
||||
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
|
||||
WaitBarrierArch(barrierTargetLsn, slotName); // 等待达到指定的LSN
|
||||
if (!IS_PGXC_COORDINATOR) {
|
||||
WaitBarrierArch(barrierTargetLsn, slotName);
|
||||
}
|
||||
|
||||
pq_beginmessage(&buf, 'b'); // 启动一个'b'类型的消息
|
||||
pq_sendstring(&buf, id); // 向消息中添加id字符串
|
||||
pq_endmessage(&buf); // 结束消息构建
|
||||
pq_flush(); // 刷新消息 发送给客户端
|
||||
pq_beginmessage(&buf, 'b');
|
||||
pq_sendstring(&buf, id);
|
||||
pq_endmessage(&buf);
|
||||
pq_flush();
|
||||
}
|
||||
|
||||
// 处理用于终止Barrier归档的信号
|
||||
static void BarrierArchWakenStop(SIGNAL_ARGS)
|
||||
{
|
||||
t_thrd.barrier_arch.ready_to_stop = true; // 设置标志,准备终止归档
|
||||
t_thrd.barrier_arch.ready_to_stop = true;
|
||||
}
|
||||
|
||||
// 处理收到SIGHUP信号的情况
|
||||
static void BarrierArchSighupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
t_thrd.barrier_arch.got_SIGHUP = true; // 设置标志,表示收到了SIGHUP信号
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
int save_errno = errno;
|
||||
t_thrd.barrier_arch.got_SIGHUP = true;
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* Reset some signals that are accepted by postmaster but not here */
|
||||
// 设置不同信号的处理方式,确保程序在收到不同的信号时能够正确地处理
|
||||
static void BarrierArchSetupSignalHook(void)
|
||||
{
|
||||
(void)gspqsignal(SIGHUP, BarrierArchSighupHandler); // 设置SIGHUP的处理函数为BarrierArchSighupHandler
|
||||
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
|
||||
(void)gspqsignal(SIGTERM, die); // 设置SIGTERM的处理函数为die
|
||||
(void)gspqsignal(SIGQUIT, quickdie); // 设置SIGQUIT的处理函数为quickdie
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
|
||||
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 设置SIGUSR1的处理函数为procsignal_sigusr1_handler
|
||||
(void)gspqsignal(SIGUSR2, BarrierArchWakenStop); // 设置SIGUSR2的处理函数为BarrierArchWakenStop
|
||||
(void)gspqsignal(SIGHUP, BarrierArchSighupHandler);
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
(void)gspqsignal(SIGTERM, die);
|
||||
(void)gspqsignal(SIGQUIT, quickdie);
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
||||
(void)gspqsignal(SIGUSR2, BarrierArchWakenStop);
|
||||
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 将SIGCHLD的处理函数设置为默认值
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL); // 将SIGTTIN的处理函数设置为默认值
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL); // 将SIGTTOU的处理函数设置为默认值
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL); // 将SIGCONT的处理函数设置为默认值
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL); // 将SIGWINCH的处理函数设置为默认值
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL);
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从阻塞信号集中移除SIGQUIT信号
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 当宏 ENABLE_MULTIPLE_NODES 被定义时,编译以下代码块
|
||||
|
||||
// 获取所有节点的连接句柄
|
||||
static PGXCNodeAllHandles* GetAllNodesHandles()
|
||||
{
|
||||
// 获取所有数据节点和协调节点的列表
|
||||
List* barrierDataNodeList = GetAllDataNodes();
|
||||
List* barrierCoordList = GetAllCoordNodes();
|
||||
PGXCNodeAllHandles* conn_handles = NULL;
|
||||
|
||||
// 获取连接句柄
|
||||
conn_handles = get_handles(barrierDataNodeList, barrierCoordList, false);
|
||||
|
||||
// 释放节点列表内存
|
||||
list_free(barrierCoordList);
|
||||
list_free(barrierDataNodeList);
|
||||
|
||||
return conn_handles; // 返回连接句柄
|
||||
return conn_handles;
|
||||
}
|
||||
|
||||
// 向所有节点发送Barrier归档请求
|
||||
static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count, ArchiveBarrierLsnInfo *barrierLsnInfo)
|
||||
{
|
||||
int conn;
|
||||
|
|
@ -254,26 +223,23 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
|
|||
errno_t rc;
|
||||
char barrierInfo[BARRIER_ARCH_INFO_LEN];
|
||||
|
||||
// 输出日志,表示开始向所有节点发送Barrier归档请求
|
||||
ereport(LOG, (errmsg("Start to send barrier arch request to all nodes.")));
|
||||
|
||||
for (conn = 0; conn < count; conn++) { // 循环遍历所有连接,向每个连接发送请求
|
||||
for (conn = 0; conn < count; conn++) {
|
||||
PGXCNodeHandle* handle = NULL;
|
||||
|
||||
// 根据连接类型获取连接句柄
|
||||
if (conn < handles->co_conn_count)
|
||||
handle = handles->coord_handles[conn];
|
||||
else
|
||||
handle = handles->datanode_handles[conn - handles->co_conn_count];
|
||||
|
||||
/* Invalid connection state, return error */
|
||||
if (handle->state != DN_CONNECTION_STATE_IDLE) { // 如果连接状态无效,输出错误信息并终止
|
||||
if (handle->state != DN_CONNECTION_STATE_IDLE) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("Failed to send BARRIER request to the node")));
|
||||
}
|
||||
|
||||
// 遍历所有barrierLsnInfo,找到匹配节点的信息
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (barrierLsnInfo[i].nodeoid == handle->nodeoid) {
|
||||
rc = snprintf_s(barrierInfo, BARRIER_ARCH_INFO_LEN, BARRIER_ARCH_INFO_LEN - 1, "0x%lx:%s",
|
||||
|
|
@ -283,15 +249,13 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
|
|||
|
||||
barrier_idlen = strlen(barrierInfo) + 1;
|
||||
|
||||
// 计算消息的总长度
|
||||
msglen = 4; /* for the length itself */
|
||||
msglen += barrier_idlen;
|
||||
msglen += 1; /* for barrier command itself */
|
||||
|
||||
/* msgType + msgLen */
|
||||
ensure_out_buffer_capacity(1 + msglen, handle); // 确保输出缓冲区足够容纳消息
|
||||
ensure_out_buffer_capacity(1 + msglen, handle);
|
||||
|
||||
// 添加消息类型 'b'
|
||||
Assert(handle->outBuffer != NULL);
|
||||
handle->outBuffer[handle->outEnd++] = 'b';
|
||||
msglen = htonl(msglen);
|
||||
|
|
@ -299,147 +263,123 @@ static void SendBarrierArchRequest(const PGXCNodeAllHandles* handles, int count,
|
|||
securec_check(rc, "\0", "\0");
|
||||
handle->outEnd += 4;
|
||||
|
||||
// 添加Barrier归档命令 BARRIER_QUERY_ARCHIVE
|
||||
handle->outBuffer[handle->outEnd++] = BARRIER_QUERY_ARCHIVE;
|
||||
|
||||
// 添加Barrier信息
|
||||
rc = memcpy_s(handle->outBuffer + handle->outEnd, handle->outSize - handle->outEnd, barrierInfo, barrier_idlen);
|
||||
securec_check(rc, "\0", "\0");
|
||||
handle->outEnd += barrier_idlen;
|
||||
|
||||
// 设置连接状态为查询状态
|
||||
handle->state = DN_CONNECTION_STATE_QUERY;
|
||||
|
||||
// 刷新连接的输出缓冲区
|
||||
pgxc_node_flush(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查在所有节点上执行BARRIER ARCH查询命令的状态
|
||||
static void CheckBarrierArchCommandStatus(const PGXCNodeAllHandles* conn_handles, int count, const char *id)
|
||||
{
|
||||
int conn;
|
||||
RemoteQueryState* combiner = NULL;
|
||||
|
||||
// 输出调试日志,表示正在检查BARRIER ARCH查询命令状态
|
||||
ereport(DEBUG1, (errmsg("Check BARRIER ARCH QUERY <%s> command status", id)));
|
||||
|
||||
// 创建一个响应组合器,用于合并来自多个节点的响应
|
||||
combiner = CreateResponseCombiner(count, COMBINE_TYPE_NONE);
|
||||
|
||||
// 循环遍历所有连接,检查每个连接的响应
|
||||
for (conn = 0; conn < count; conn++) {
|
||||
PGXCNodeHandle* handle = NULL;
|
||||
|
||||
// 根据连接类型获取连接句柄
|
||||
if (conn < conn_handles->co_conn_count)
|
||||
handle = conn_handles->coord_handles[conn];
|
||||
else
|
||||
handle = conn_handles->datanode_handles[conn - conn_handles->co_conn_count];
|
||||
|
||||
// 接收来自节点的响应
|
||||
if (pgxc_node_receive(1, &handle, NULL))
|
||||
ereport(
|
||||
ERROR, (errcode(ERRCODE_OPERATE_FAILED), errmsg("Failed to receive response from the remote side")));
|
||||
// 处理响应并检查执行状态
|
||||
if (handle_response(handle, combiner) != RESPONSE_BARRIER_OK)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_FAILED),
|
||||
errmsg("BARRIER ARCH QUERY failed with error %s", handle->error)));
|
||||
}
|
||||
// 关闭响应组合器
|
||||
CloseCombiner(combiner);
|
||||
|
||||
// 输出日志,表示在所有节点上成功完成了BARRIER ARCH查询命令
|
||||
ereport(LOG,
|
||||
(errmsg("Successfully completed BARRIER ARCH QUERY <%s> command on "
|
||||
"all nodes",
|
||||
id)));
|
||||
}
|
||||
|
||||
// 执行归档相关的任务
|
||||
static void QueryBarrierArch(PGXCNodeAllHandles* handles, ArchiveConfig *archive_obs)
|
||||
{
|
||||
int connCnt = handles->co_conn_count + handles->dn_conn_count;
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
// 获取最大节点数量
|
||||
int archivMaxNodeCnt = g_instance.archive_obs_cxt.max_node_cnt;
|
||||
// 如果连接数超过了最大节点数
|
||||
if (connCnt >= archivMaxNodeCnt) {
|
||||
// 释放全局锁
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
// 输出调试信息,表示当前连接数大于最大节点数
|
||||
ereport(DEBUG2, (errmsg("current cn get connCnt: <%d> max than cluster connCnt: <%d>",
|
||||
connCnt, archivMaxNodeCnt)));
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiveBarrierLsnInfo barrierLsnInfo[g_instance.archive_obs_cxt.max_node_cnt];
|
||||
// 如果当前的Barrier名称和已存储的名称相同,直接返回
|
||||
|
||||
if (strncmp(t_thrd.barrier_arch.barrierName, g_instance.archive_obs_cxt.barrierName,
|
||||
strlen(g_instance.archive_obs_cxt.barrierName)) == 0) {
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
return;
|
||||
}
|
||||
// 复制当前的Barrier名称到全局变量
|
||||
|
||||
errno_t errorno = memcpy_s(t_thrd.barrier_arch.barrierName, MAX_BARRIER_ID_LENGTH,
|
||||
g_instance.archive_obs_cxt.barrierName,
|
||||
sizeof(g_instance.archive_obs_cxt.barrierName));
|
||||
securec_check(errorno, "\0", "\0");
|
||||
|
||||
// 检查是否所有节点的barrierLsn都不为0,如果有一个为0,直接返回
|
||||
for (int i = 0; i < connCnt + 1; i++) {
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info[i].barrierLsn == 0x0) {
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 复制barrierLsn信息到局部变量
|
||||
errorno = memcpy_s(&barrierLsnInfo, sizeof(ArchiveBarrierLsnInfo) * g_instance.archive_obs_cxt.max_node_cnt,
|
||||
g_instance.archive_obs_cxt.barrier_lsn_info,
|
||||
sizeof(ArchiveBarrierLsnInfo) * g_instance.archive_obs_cxt.max_node_cnt);
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
securec_check(errorno, "\0", "\0");
|
||||
// 发送Barrier归档请求
|
||||
|
||||
SendBarrierArchRequest(handles, connCnt, barrierLsnInfo);
|
||||
// 检查Barrier归档命令的状态
|
||||
|
||||
CheckBarrierArchCommandStatus(handles, connCnt, t_thrd.barrier_arch.barrierName);
|
||||
// 等待Barrier归档完成
|
||||
|
||||
WaitBarrierArch(barrierLsnInfo[connCnt].barrierLsn, t_thrd.barrier_arch.slot_name);
|
||||
// 将Barrier名称写入归档存储
|
||||
|
||||
write_barrier_id_to_obs(t_thrd.barrier_arch.barrierName, archive_obs);
|
||||
}
|
||||
// 如果未定义ENABLE_MULTIPLE_NODES,则执行以下代码块
|
||||
|
||||
#else
|
||||
// 用于在单个节点上执行Barrier归档操作,等待特定的LSN完成后将Barrier名称写入归档存储
|
||||
static void SingleBarrierArch(ArchiveConfig *archive_obs)
|
||||
{
|
||||
XLogRecPtr barrierLsn;
|
||||
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局锁
|
||||
// 检查当前的Barrier名称是否和已存储的名称相同,如果相同则释放锁并返回
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
if (strncmp(t_thrd.barrier_arch.barrierName, g_instance.archive_obs_cxt.barrierName,
|
||||
strlen(g_instance.archive_obs_cxt.barrierName)) == 0) {
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
return;
|
||||
}
|
||||
// 复制当前的Barrier名称到线程局部变量
|
||||
|
||||
errno_t errorno = memcpy_s(t_thrd.barrier_arch.barrierName, MAX_BARRIER_ID_LENGTH,
|
||||
g_instance.archive_obs_cxt.barrierName,
|
||||
sizeof(g_instance.archive_obs_cxt.barrierName));
|
||||
securec_check(errorno, "\0", "\0");
|
||||
// 复制BarrierLSN到局部变量
|
||||
|
||||
barrierLsn = g_instance.archive_obs_cxt.barrierLsn;
|
||||
// 释放全局锁
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
// 等待Barrier归档完成
|
||||
|
||||
WaitBarrierArch(barrierLsn, t_thrd.barrier_arch.slot_name);
|
||||
// 将Barrier名称写入归档存储
|
||||
|
||||
write_barrier_id_to_obs(t_thrd.barrier_arch.barrierName, archive_obs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 归档Barrier线程的入口函数
|
||||
NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
|
||||
{
|
||||
ArchiveSlotConfig *obsArchiveSlot = NULL;
|
||||
|
|
@ -448,30 +388,25 @@ NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
|
|||
char username[NAMEDATALEN];
|
||||
char *dbname = (char *)pstrdup(DEFAULT_DATABASE);
|
||||
|
||||
SetProcessingMode(InitProcessing); // 设置当前线程的处理模式为初始化模式
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
// 设置线程的相关信息配置
|
||||
t_thrd.role = BARRIER_ARCH;
|
||||
t_thrd.proc_cxt.MyProgName = "BarrierArch";
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
t_thrd.barrier_arch.slot_name = pstrdup((char *)arg->payload);
|
||||
u_sess->attr.attr_common.application_name = pstrdup("BarrierArch");
|
||||
|
||||
// 输出日志,表示归档Barrier线程启动
|
||||
ereport(LOG, (errmsg("[BarrierArch] barrier arch thread starts. slot name: %s", t_thrd.barrier_arch.slot_name)));
|
||||
|
||||
// 在进程退出时调用 PGXCNodeCleanAndRelease 函数
|
||||
|
||||
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
|
||||
|
||||
// 设置信号处理函数
|
||||
BarrierArchSetupSignalHook();
|
||||
|
||||
// 初始化
|
||||
|
||||
BaseInit();
|
||||
// 设置数据库和用户信息
|
||||
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username);
|
||||
t_thrd.proc_cxt.PostInit->InitBarrierCreator();
|
||||
// 创建一个内存上下文用于执行工作
|
||||
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "BarrierArch",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
|
||||
|
|
@ -492,139 +427,119 @@ NON_EXEC_STATIC void BarrierArchMain(knl_thread_arg* arg)
|
|||
* If an exception is encountered, processing resumes here.
|
||||
* See notes in postgres.c about the design of this coding.
|
||||
*/
|
||||
// 捕获异常 以及进行异常处理
|
||||
int curTryCounter;
|
||||
int* oldTryCounter = NULL;
|
||||
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
|
||||
/* Since not using PG_TRY, must reset error stack by hand */
|
||||
t_thrd.log_cxt.error_context_stack = NULL; // 清理错误上下文
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
/* Prevent interrupts while cleaning up */
|
||||
HOLD_INTERRUPTS(); // 阻止中断信号
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/* Report the error to the server log */
|
||||
EmitErrorReport(); // 将错误信息写入日志
|
||||
EmitErrorReport();
|
||||
|
||||
// 释放资源
|
||||
/* release resource held by lsc */
|
||||
AtEOXact_SysDBCache(false);
|
||||
AtEOXact_SysDBCache(false);
|
||||
|
||||
/* release resource */
|
||||
LWLockReleaseAll();
|
||||
LWLockReleaseAll();
|
||||
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
// 切换回初始内存上下文,清空错误状态
|
||||
MemoryContextSwitchTo(barrierArchContext);
|
||||
FlushErrorState();
|
||||
MemoryContextResetAndDeleteChildren(barrierArchContext);
|
||||
|
||||
/* Now we can allow interrupts again */
|
||||
RESUME_INTERRUPTS(); // 恢复中断处理
|
||||
RESUME_INTERRUPTS();
|
||||
|
||||
/*
|
||||
* Sleep at least 1 second after any error. A write error is likely
|
||||
* to be repeated, and we don't want to be filling the error logs as
|
||||
* fast as we can.
|
||||
*/
|
||||
// 等待一秒,以防止错误信息频繁写入日志
|
||||
pg_usleep(1000000L);
|
||||
}
|
||||
destroy_handles(); // 销毁连接句柄
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 设置捕获异常的计数器,以便后续异常处理
|
||||
destroy_handles();
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
/* We can now handle ereport(ERROR) */
|
||||
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf; // 将当前的异常捕获状态保存到线程的异常堆栈上
|
||||
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf;
|
||||
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us)
|
||||
*/
|
||||
// 解除在 BarrierArchSetupSignalHook 函数中阻塞的一些信号
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
// 将处理模式设置为正常处理模式,表明线程正在正常运行并处理任务
|
||||
|
||||
SetProcessingMode(NormalProcessing);
|
||||
// 等待1 秒
|
||||
|
||||
pg_usleep_retry(1000000L, 0);
|
||||
|
||||
obsArchiveSlot = getArchiveReplicationSlotWithName(t_thrd.barrier_arch.slot_name); // 获取与给定名称匹配的归档复制槽
|
||||
if (obsArchiveSlot == NULL) { // 如果找不到匹配的槽
|
||||
t_thrd.barrier_arch.ready_to_stop = true; // 准备停止线程
|
||||
ereport(WARNING, (errmsg("[BarrierArch] obs slot not created."))); // 输出警告消息
|
||||
obsArchiveSlot = getArchiveReplicationSlotWithName(t_thrd.barrier_arch.slot_name);
|
||||
if (obsArchiveSlot == NULL) {
|
||||
t_thrd.barrier_arch.ready_to_stop = true;
|
||||
ereport(WARNING, (errmsg("[BarrierArch] obs slot not created.")));
|
||||
return;
|
||||
}
|
||||
|
||||
exec_init_poolhandles(); // 初始化连接池句柄
|
||||
exec_init_poolhandles();
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 开启多节点编译选项时执行以下代码块
|
||||
do {
|
||||
// 如果当前节点不是第一个协调器节点,跳出循环
|
||||
if (IsFirstCn())
|
||||
break;
|
||||
// 输出日志,显示当前节点不是第一个协调器节点
|
||||
|
||||
ereport(DEBUG1, (errmsg("[BarrierArch] Current node is not first node: %s",
|
||||
g_instance.attr.attr_common.PGXCNodeName)));
|
||||
if (IsGotPoolReload()) {
|
||||
// 如果收到了连接池重载标志,执行连接池重载操作
|
||||
processPoolerReload();
|
||||
// 重置连接池重载标志为 false
|
||||
ResetGotPoolReload(false);
|
||||
}
|
||||
// 检查是否有中断请求
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
// 暂停 10 秒
|
||||
pg_usleep(10000000L);
|
||||
} while (1);
|
||||
// 获取一个名为 barrier_lock 的自旋锁,用于保护关键资源
|
||||
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
// 如果 barrier_lsn_info 为空,或者 max_node_cnt 为 0
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL ||
|
||||
g_instance.archive_obs_cxt.max_node_cnt == 0) {
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放自旋锁
|
||||
// 输出警告消息,提示 barrier_lsn_info 未分配
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrier_lsn_info not alloc.")));
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrier_lsn_info not alloc.")));
|
||||
return;
|
||||
}
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放自旋锁
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
#endif
|
||||
// 结束多节点编译选项的条件编译
|
||||
// 输出日志,显示正在初始化与协调器和数据节点的连接,以及数据节点和协调器的数量
|
||||
ereport(DEBUG1,
|
||||
(errmsg("[BarrierArch] Init connections with CN/DN, dn count : %d, cn count : %d",
|
||||
u_sess->pgxc_cxt.NumDataNodes, u_sess->pgxc_cxt.NumCoords)));
|
||||
|
||||
// 进入循环,只要 ready_to_stop 标志不为真,就一直执行循环体
|
||||
while (!t_thrd.barrier_arch.ready_to_stop) {
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
|
||||
// 如果 barrierName 为空或长度为 0
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
if (g_instance.archive_obs_cxt.barrierName == NULL || strlen(g_instance.archive_obs_cxt.barrierName) == 0) {
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null."))); // 输出警告消息,提示 barrierName 为空
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null.")));
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 开启了多节点编译选项时执行以下代码块
|
||||
// 如果收到了连接池重载标志
|
||||
if (IsGotPoolReload()) {
|
||||
processPoolerReload(); // 执行连接池重载操作
|
||||
ResetGotPoolReload(false); // 重置连接池重载标志为 false
|
||||
if (!IsFirstCn()) // 如果当前节点不是第一个协调器节点,跳出循环
|
||||
processPoolerReload();
|
||||
ResetGotPoolReload(false);
|
||||
if (!IsFirstCn())
|
||||
break;
|
||||
}
|
||||
// 获取所有节点的连接句柄
|
||||
PGXCNodeAllHandles* handles = GetAllNodesHandles();
|
||||
// 调用 QueryBarrierArch 函数处理Barrier归档
|
||||
|
||||
PGXCNodeAllHandles* handles = GetAllNodesHandles();
|
||||
|
||||
QueryBarrierArch(handles, &obsArchiveSlot->archive_config);
|
||||
// 释放所有节点的连接句柄内存
|
||||
|
||||
pfree_pgxc_all_handles(handles);
|
||||
#else
|
||||
// 没有开启多节点编译选项时执行以下代码块
|
||||
// 调用 SingleBarrierArch 函数处理Barrie归档
|
||||
SingleBarrierArch(&obsArchiveSlot->archive_config);
|
||||
#endif
|
||||
}
|
||||
// 输出日志,显示障碍归档线程已退出
|
||||
ereport(LOG, (errmsg("[BarrierArch] barrier arch thread exits.")));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,116 +52,100 @@ const int BARRIER_NAME_LEN = 40;
|
|||
const char* CSN_BARRIER_PATTREN_STR = "csn_%021lu_%013ld";
|
||||
const char* CSN_SWITCHOVER_BARRIER_PATTREN_STR = "csn_%021lu_dr_switchover";
|
||||
|
||||
/*
|
||||
作用: 生成用于Barrier归档的名称
|
||||
参数: barrierRet:字符数组指针,存储生成的 Barrier 名称
|
||||
isSwitchoverBarrier:表示是否为切换 Barrier
|
||||
*/
|
||||
void GetCsnBarrierName(char* barrierRet, bool isSwitchoverBarrier)
|
||||
{
|
||||
struct timeval tv; // 用于存储时间的结构体变量
|
||||
int rc; // 用于存储函数返回值的变量
|
||||
CommitSeqNo csn; // 用于存储事务提交序列号的变量
|
||||
struct timeval tv;
|
||||
int rc;
|
||||
CommitSeqNo csn;
|
||||
|
||||
// 如果处于GTM模式,获取全局事务管理器的事务提交序列号
|
||||
if (GTM_MODE)
|
||||
csn = GetCSNGTM();
|
||||
else
|
||||
csn = CommitCSNGTM(false); // 否则获取本地事务提交序列号
|
||||
csn = CommitCSNGTM(false);
|
||||
|
||||
gettimeofday(&tv, NULL); // 获取当前时间信息
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
if (isSwitchoverBarrier) { // 如果要切换Barrier
|
||||
// 构造用于切换Barrier的名称,将CSN插入到特定的格式字符串中
|
||||
if (isSwitchoverBarrier) {
|
||||
rc = snprintf_s(barrierRet, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_SWITCHOVER_BARRIER_PATTREN_STR, csn);
|
||||
} else {
|
||||
// 构造普通Barrier的名称,将CSN和时间戳信息插入到格式字符串中
|
||||
rc = snprintf_s(barrierRet, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_PATTREN_STR, csn,
|
||||
TIME_GET_MILLISEC(tv));
|
||||
}
|
||||
securec_check_ss_c(rc, "\0", "\0"); // 检查格式化操作的返回值,确保操作成功
|
||||
// 记录调试日志,输出生成的Barrier名称和对应的CSN
|
||||
securec_check_ss_c(rc, "\0", "\0");
|
||||
elog(DEBUG1, "GetCsnBarrierName csn = %lu, barrier_name = %s", csn, barrierRet);
|
||||
}
|
||||
/* 根据传入的CSN Barrier名称,解析出其中的CSN值,并返回
|
||||
参数: csnBarrier 表示 CSN Barrier 的名称
|
||||
返回值:解析出的 CSN 值(解析成功)
|
||||
*/
|
||||
|
||||
CommitSeqNo CsnBarrierNameGetCsn(const char *csnBarrier)
|
||||
{
|
||||
CommitSeqNo csn;
|
||||
long ts = 0;
|
||||
// 使用格式化字符串解析CSN Barrier名称,提取其中的CSN值和时间戳(如果有)
|
||||
if ((strstr(csnBarrier, "_dr_switchover") != NULL &&
|
||||
sscanf_s(csnBarrier, CSN_SWITCHOVER_BARRIER_PATTREN_STR, &csn) == 1) ||
|
||||
sscanf_s(csnBarrier, CSN_BARRIER_PATTREN_STR, &csn, &ts) == 2) {
|
||||
return csn;
|
||||
}
|
||||
return 0; // 解析失败时返回0
|
||||
return 0;
|
||||
}
|
||||
// 根据传入的CSN Barrier名称,解析出其中的时间戳值,并返回
|
||||
|
||||
int64 CsnBarrierNameGetTimeStamp(const char *csnBarrier)
|
||||
{
|
||||
CommitSeqNo csn;
|
||||
int64 ts = 0;
|
||||
// 使用格式化字符串解析CSN Barrier名称,提取其中的CSN值和时间戳(如果有)
|
||||
if (sscanf_s(csnBarrier, CSN_BARRIER_PATTREN_STR, &csn, &ts) == 2) {
|
||||
return ts;
|
||||
}
|
||||
return 0; // 解析失败时返回0
|
||||
return 0;
|
||||
}
|
||||
// 判断传入的CSN Barrier名称是否为切换Barrier
|
||||
|
||||
bool IsSwitchoverBarrier(const char *csnBarrier)
|
||||
{
|
||||
// 判断CSN Barrier名称是否符合要求,且是否包含 "_dr_switchover" 子串
|
||||
if (!IS_CSN_BARRIER(csnBarrier) || (strstr(csnBarrier, "_dr_switchover") == NULL)) {
|
||||
return false;
|
||||
}
|
||||
return true; // 如果符合要求,则认为是切换Barrier
|
||||
return true;
|
||||
}
|
||||
// 判断当前节点是否为第一个执行的协调器节点
|
||||
|
||||
bool IsFirstCn()
|
||||
{
|
||||
char *firstExecNode = find_first_exec_cn(); // 查找第一个执行的协调器节点
|
||||
// 将当前节点的名称与firstExecNode进行比较,如果两者相同,则返回true,表示当前节点是第一个执行的协调器节点;否则返回false
|
||||
char *firstExecNode = find_first_exec_cn();
|
||||
return (strcmp(firstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
|
||||
}
|
||||
// 关闭Barrier创建线程
|
||||
|
||||
void barrier_creator_thread_shutdown(void)
|
||||
{
|
||||
g_instance.barrier_creator_cxt.stop = true; // 设置标志,表示停止线程
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread shutting down."))); // 输出日志
|
||||
g_instance.barrier_creator_cxt.stop = true;
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread shutting down.")));
|
||||
}
|
||||
// SIGHUP信号处理函数,用于重新加载配置
|
||||
|
||||
static void barrier_creator_sighup_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前errno
|
||||
t_thrd.barrier_creator_cxt.got_SIGHUP = true; // 设置标志,表示收到SIGHUP信号
|
||||
errno = save_errno; // 恢复errno
|
||||
}
|
||||
int save_errno = errno;
|
||||
t_thrd.barrier_creator_cxt.got_SIGHUP = true;
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* Reset some signals that are accepted by postmaster but not here */
|
||||
static void barrier_creator_setup_signal_hook(void)
|
||||
{
|
||||
(void)gspqsignal(SIGHUP, barrier_creator_sighup_handler); // 设置SIGHUP信号处理函数
|
||||
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
|
||||
(void)gspqsignal(SIGTERM, die); // 设置SIGTERM信号处理函数为die
|
||||
(void)gspqsignal(SIGQUIT, quickdie); // 设置SIGQUIT信号处理函数为quickdie
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
|
||||
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 设置SIGUSR1信号处理函数为procsignal_sigusr1_handler
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
|
||||
(void)gspqsignal(SIGHUP, barrier_creator_sighup_handler);
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
(void)gspqsignal(SIGTERM, die);
|
||||
(void)gspqsignal(SIGQUIT, quickdie);
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN);
|
||||
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 设置SIGCHLD信号处理函数为默认
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL); // 设置SIGTTIN信号处理函数为默认
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL); // 设置SIGTTOU信号处理函数为默认
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL); // 设置SIGCONT信号处理函数为默认
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL); // 设置SIGWINCH信号处理函数为默认
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL);
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许随时处理SIGQUIT(quickdie)信号
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
}
|
||||
// 从OBS中读取Barrier标识
|
||||
|
||||
static uint64_t read_barrier_id_from_obs(const char *slotName, long *currBarrierTime)
|
||||
{
|
||||
char barrier_name[BARRIER_NAME_LEN];
|
||||
|
|
@ -169,153 +153,146 @@ static uint64_t read_barrier_id_from_obs(const char *slotName, long *currBarrier
|
|||
uint64_t barrier_id;
|
||||
|
||||
if (ArchiveReplicationReadFile(BARRIER_FILE, (char *)barrier_name, MAX_BARRIER_ID_LENGTH, slotName)) {
|
||||
barrier_name[BARRIER_NAME_LEN - 1] = '\0'; // 将读取的数据末尾设置为字符串结束符
|
||||
// 输出日志,表示从归档存储中读取了barrier ID
|
||||
barrier_name[BARRIER_NAME_LEN - 1] = '\0';
|
||||
ereport(LOG, (errmsg("[BarrierCreator] read barrier id from obs %s", barrier_name)));
|
||||
} else {
|
||||
// 输出日志,表示从归档存储中读取barrier ID失败,将从0开始
|
||||
ereport(LOG, (errmsg("[BarrierCreator] failed to read barrier id from obs, start barrier from 0")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 根据不同的编译选项解析Barrier名称,更新barrier_id和currBarrierTime
|
||||
ret = sscanf_s(barrier_name, "csn_%021" PRIu64 "_%013ld", &barrier_id, currBarrierTime);
|
||||
#else
|
||||
ret = sscanf_s(barrier_name, "hadr_%020" PRIu64 "_%013ld", &barrier_id, currBarrierTime);
|
||||
#endif
|
||||
// 如果解析成功,更新barrier_id并返回
|
||||
|
||||
if (ret == 2) {
|
||||
barrier_id++;
|
||||
return barrier_id;
|
||||
}
|
||||
return 0; // 解析失败,返回0
|
||||
return 0;
|
||||
}
|
||||
// 获取归档槽中的最大barrier索引和最新的barrier时间
|
||||
|
||||
uint64_t GetObsBarrierIndex(const List *archiveSlotNames, long *last_barrier_time)
|
||||
{
|
||||
uint64_t maxIndex = 0; // 最大barrier索引
|
||||
long maxBarrierTime = 0; // 最大barrier时间
|
||||
foreach_cell(cell, archiveSlotNames) { // 遍历归档槽名称列表
|
||||
long currBarrierTime = 0; // 当前barrier时间
|
||||
char* slotName = (char*)lfirst(cell); // 获取归档槽名称
|
||||
if (slotName == NULL || strlen(slotName) == 0) { // 如果归档槽名称为空,跳过本次循环
|
||||
uint64_t maxIndex = 0;
|
||||
long maxBarrierTime = 0;
|
||||
foreach_cell(cell, archiveSlotNames) {
|
||||
long currBarrierTime = 0;
|
||||
char* slotName = (char*)lfirst(cell);
|
||||
if (slotName == NULL || strlen(slotName) == 0) {
|
||||
continue;
|
||||
}
|
||||
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime); // 从归档存储中读取barrier索引和时间
|
||||
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex; // 更新最大barrier索引
|
||||
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime; // 更新最大barrier时间
|
||||
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime);
|
||||
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex;
|
||||
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime;
|
||||
}
|
||||
*last_barrier_time = maxBarrierTime; // 将最大barrier时间赋值给指针参数
|
||||
*last_barrier_time = maxBarrierTime;
|
||||
|
||||
return maxIndex; // 返回最大barrier索引
|
||||
return maxIndex;
|
||||
}
|
||||
// 获取归档槽中第一个协调器节点的barrier时间线
|
||||
|
||||
uint64 GetObsFirstCNBarrierTimeline(const List *archiveSlotNames)
|
||||
{
|
||||
uint64 timeline = 0; // 时间线初始值为0
|
||||
uint64 timeline = 0;
|
||||
|
||||
foreach_cell(cell, archiveSlotNames) { // 遍历归档槽名称列表
|
||||
char* slotName = (char*)lfirst(cell); // 获取归档槽名称
|
||||
if (slotName == NULL || strlen(slotName) == 0) { // 如果归档槽名称为空,跳过本次循环
|
||||
foreach_cell(cell, archiveSlotNames) {
|
||||
char* slotName = (char*)lfirst(cell);
|
||||
if (slotName == NULL || strlen(slotName) == 0) {
|
||||
continue;
|
||||
}
|
||||
timeline = ReadBarrierTimelineRecordFromObs(slotName); // 从归档存储中读取协调器节点的barrier时间线
|
||||
break; // 跳出循环,只获取第一个归档槽的时间线
|
||||
timeline = ReadBarrierTimelineRecordFromObs(slotName);
|
||||
break;
|
||||
}
|
||||
return timeline; // 返回获取的时间线
|
||||
return timeline;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 分配和初始化BarrierLsnInfo数组
|
||||
static void AllocBarrierLsnInfo(int nodeSize)
|
||||
{
|
||||
int rc;
|
||||
g_instance.archive_obs_cxt.barrier_lsn_info = (ArchiveBarrierLsnInfo *)palloc0(
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize); // 分配内存
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize);
|
||||
rc = memset_s(g_instance.archive_obs_cxt.barrier_lsn_info,
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize, 0,
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize); // 将分配的内存内容初始化为0
|
||||
securec_check(rc, "", ""); // 检查内存操作是否成功,如果不成功,输出错误信息
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize);
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// BarrierCreator连接池重新加载
|
||||
static void BarrierCreatorPoolerReload(void)
|
||||
{
|
||||
destroy_handles(); // 销毁连接句柄
|
||||
processPoolerReload(); // 重新加载连接池
|
||||
destroy_handles();
|
||||
processPoolerReload();
|
||||
ereport(LOG,
|
||||
(errmsg("[BarrierCreatorPoolerReload] Reload connections with CN/DN, dn count : %d, cn count : %d",
|
||||
u_sess->pgxc_cxt.NumDataNodes,
|
||||
u_sess->pgxc_cxt.NumCoords)));
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num == 0) {
|
||||
return; // 如果没有归档槽,直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
int maxNodeCnt = *t_thrd.pgxc_cxt.shmemNumCoords + *t_thrd.pgxc_cxt.shmemNumDataNodes;
|
||||
if (maxNodeCnt > g_instance.archive_obs_cxt.max_node_cnt) { // 如果节点数量超过最大值
|
||||
if (maxNodeCnt > g_instance.archive_obs_cxt.max_node_cnt) {
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0; // 清零最大节点数量
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0;
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
int nodeSize = maxNodeCnt;
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info != NULL) {
|
||||
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info); // 释放旧的BarrierLsnInfo数组内存
|
||||
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info);
|
||||
}
|
||||
AllocBarrierLsnInfo(nodeSize); // 重新分配和初始化BarrierLsnInfo数组
|
||||
AllocBarrierLsnInfo(nodeSize);
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
g_instance.archive_obs_cxt.max_node_cnt = nodeSize; // 更新最大节点数量
|
||||
g_instance.archive_obs_cxt.max_node_cnt = nodeSize;
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// 用于释放之前分配的存储归档操作的Barrier LSN信息的内存
|
||||
|
||||
static void FreeBarrierLsnInfo()
|
||||
{
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局锁
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0; // 将最大节点数设置为0
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放全局锁
|
||||
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info); // 释放分配的内存
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0;
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info);
|
||||
}
|
||||
// 创建不同类型的 Barrier,以实现数据一致性和灾备需求
|
||||
|
||||
void barrier_creator_main(void)
|
||||
{
|
||||
uint64_t index = 0; // 初始化用于记录创建Barrier的索引
|
||||
long last_barrier_time = 0; // 初始化上一个Barrier的时间
|
||||
struct timeval tv; // 用于获取当前时间
|
||||
int rc; // 用于记录函数返回值
|
||||
char barrier_name[BARRIER_NAME_LEN]; // 用于存储Barrier名称的字符数组
|
||||
List* archiveSlotNames; // 存储归档槽名的列表
|
||||
MemoryContext barrier_creator_context; // 用于存储Barrier Creator线程的内存上下文
|
||||
sigjmp_buf local_sigjmp_buf; // 用于实现异常处理跳转
|
||||
t_thrd.barrier_creator_cxt.is_first_barrier = true; // 标识是否是首次创建Barrier
|
||||
char username[NAMEDATALEN]; // 存储用户名的字符数组
|
||||
char *dbname = (char *)pstrdup(DEFAULT_DATABASE); // 存储默认数据库名称的指针
|
||||
bool startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier; // 表示是否启用自动CSN Barrier的标志
|
||||
uint64_t index = 0;
|
||||
long last_barrier_time = 0;
|
||||
struct timeval tv;
|
||||
int rc;
|
||||
char barrier_name[BARRIER_NAME_LEN];
|
||||
List* archiveSlotNames;
|
||||
MemoryContext barrier_creator_context;
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
t_thrd.barrier_creator_cxt.is_first_barrier = true;
|
||||
char username[NAMEDATALEN];
|
||||
char *dbname = (char *)pstrdup(DEFAULT_DATABASE);
|
||||
bool startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier;
|
||||
// use InnerMaintenanceTools mode to avoid deadlock with thread pool
|
||||
u_sess->proc_cxt.IsInnerMaintenanceTools = true; // 使用InnerMaintenanceTools模式,以避免与线程池产生死锁
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator started"))); // 输出日志,表示Barrier Creator线程已启动
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0; // 初始化最大节点数为0
|
||||
SetProcessingMode(InitProcessing); // 设置处理模式为InitProcessing
|
||||
u_sess->proc_cxt.IsInnerMaintenanceTools = true;
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator started")));
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0;
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
t_thrd.role = BARRIER_CREATOR; // 设置线程的角色为BARRIER_CREATOR
|
||||
t_thrd.proc_cxt.MyProgName = "BarrierCreator"; // 设置线程的程序名称为"BarrierCreator"
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); // 设置线程的进程ID为当前线程ID
|
||||
u_sess->attr.attr_common.application_name = pstrdup("BarrierCreator"); // 设置应用程序名称为"BarrierCreator"
|
||||
g_instance.barrier_creator_cxt.stop = false; // 初始化停止标志为false,表示Barrier Creator线程不停止
|
||||
t_thrd.role = BARRIER_CREATOR;
|
||||
t_thrd.proc_cxt.MyProgName = "BarrierCreator";
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
u_sess->attr.attr_common.application_name = pstrdup("BarrierCreator");
|
||||
g_instance.barrier_creator_cxt.stop = false;
|
||||
|
||||
on_shmem_exit(PGXCNodeCleanAndRelease, 0); // 注册一个在进程退出时执行的回调函数,用于清理PGXC节点资源
|
||||
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
|
||||
|
||||
barrier_creator_setup_signal_hook(); // 设置信号处理函数
|
||||
barrier_creator_setup_signal_hook();
|
||||
|
||||
BaseInit(); // 初始化
|
||||
BaseInit();
|
||||
|
||||
// 设置当前线程的数据库和用户信息
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username);
|
||||
// 初始化Barrier Creator模块的上下文
|
||||
t_thrd.proc_cxt.PostInit->InitBarrierCreator();
|
||||
// 创建一个新的资源拥有者,用于管理Barrier Creator线程的资源
|
||||
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "BarrierCreator",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
|
||||
|
|
@ -325,7 +302,6 @@ void barrier_creator_main(void)
|
|||
* possible memory leaks. Formerly this code just ran in
|
||||
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
|
||||
*/
|
||||
// 创建内存上下文
|
||||
barrier_creator_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"BarrierCreator",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -337,92 +313,83 @@ void barrier_creator_main(void)
|
|||
* If an exception is encountered, processing resumes here.
|
||||
* See notes in postgres.c about the design of this coding.
|
||||
*/
|
||||
// 如果遇到异常,将从这里恢复
|
||||
int curTryCounter;
|
||||
int *oldTryCounter = NULL;
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
destroy_handles(); // 销毁句柄
|
||||
destroy_handles();
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
|
||||
/* Since not using PG_TRY, must reset error stack by hand */
|
||||
// 重置错误堆栈
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
t_thrd.log_cxt.call_stack = NULL;
|
||||
|
||||
/* Prevent interrupts while cleaning up */
|
||||
HOLD_INTERRUPTS(); // 在清理期间阻止中断
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/* Report the error to the server log */
|
||||
EmitErrorReport(); // 将错误报告记录到服务器日志中
|
||||
EmitErrorReport();
|
||||
|
||||
/* release resource held by lsc */
|
||||
AtEOXact_SysDBCache(false); // 释放lsc持有的资源
|
||||
AtEOXact_SysDBCache(false);
|
||||
|
||||
/* release resource */
|
||||
// 释放资源
|
||||
LWLockReleaseAll(); // 释放所有的轻量级锁
|
||||
LWLockReleaseAll();
|
||||
|
||||
FreeBarrierLsnInfo(); // 释放Barrier信息结构体的内存
|
||||
FreeBarrierLsnInfo();
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
MemoryContextSwitchTo(barrier_creator_context); // 切换到 barrier_creator_context 内存上下文
|
||||
FlushErrorState(); // 清除错误状态信息
|
||||
MemoryContextResetAndDeleteChildren(barrier_creator_context); // 重置并删除 barrier_creator_context 内存上下文的子节点
|
||||
MemoryContextSwitchTo(barrier_creator_context);
|
||||
FlushErrorState();
|
||||
MemoryContextResetAndDeleteChildren(barrier_creator_context);
|
||||
|
||||
/* Now we can allow interrupts again */
|
||||
RESUME_INTERRUPTS(); // 恢复中断处理
|
||||
RESUME_INTERRUPTS();
|
||||
return;
|
||||
}
|
||||
// 进入异常处理尝试块,保存旧的异常处理计数器并获取当前计数器
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
/* We can now handle ereport(ERROR) */
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; // 将本地的跳转缓冲区设置为异常处理的跳转缓冲区
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us)
|
||||
*/
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除先前阻塞的信号
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除对 SIGUSR2 信号的阻塞
|
||||
SetProcessingMode(NormalProcessing); // 设置处理模式为正常处理模式
|
||||
exec_init_poolhandles(); // 初始化连接池句柄
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
SetProcessingMode(NormalProcessing);
|
||||
exec_init_poolhandles();
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
/*
|
||||
* Ensure all barrier commond execuet on first coordinator
|
||||
*/
|
||||
do {
|
||||
if (IsFirstCn()) // 如果当前节点是第一个协调器节点
|
||||
if (IsFirstCn())
|
||||
break;
|
||||
// 输出调试信息,指示当前节点不是第一个协调器节点
|
||||
|
||||
ereport(DEBUG1, (errmsg("[BarrierCreator] Current node is not first node: %s",
|
||||
g_instance.attr.attr_common.PGXCNodeName)));
|
||||
// 如果收到重新加载连接池的信号,重新加载连接池并重置标志
|
||||
if (IsGotPoolReload()) {
|
||||
BarrierCreatorPoolerReload(); // 重新加载连接池
|
||||
ResetGotPoolReload(false); // 重置标志,表示已处理连接池重新加载
|
||||
BarrierCreatorPoolerReload();
|
||||
ResetGotPoolReload(false);
|
||||
}
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果是则处理中断
|
||||
pg_usleep(1000000L); // 休眠1秒钟
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
pg_usleep(1000000L);
|
||||
} while (1);
|
||||
#endif
|
||||
ereport(DEBUG1,
|
||||
(errmsg("[BarrierCreator] Init connections with CN/DN, dn count : %d, cn count : %d",
|
||||
u_sess->pgxc_cxt.NumDataNodes, u_sess->pgxc_cxt.NumCoords)));
|
||||
// 输出日志,指明当前节点是 barrier creator
|
||||
ereport(LOG, (errmsg("[BarrierCreator] %s is barrier creator", g_instance.attr.attr_common.PGXCNodeName)));
|
||||
// 设置停止标志为 false,表示不停止 barrier creator 线程
|
||||
g_instance.barrier_creator_cxt.stop = false;
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num != 0) { // 如果存在归档槽位
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num != 0) {
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names = GetAllArchiveSlotsName();
|
||||
if (t_thrd.barrier_creator_cxt.archive_slot_names == NIL ||
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names->length == 0) {
|
||||
return; // 没有获取到归档槽位名称,直接返回
|
||||
return;
|
||||
}
|
||||
// 获取归档槽位的 barrier 索引和last barrier 时间
|
||||
index = GetObsBarrierIndex(t_thrd.barrier_creator_cxt.archive_slot_names, &last_barrier_time);
|
||||
// 获取归档槽位的 barrier 索引和最后的 barrier 时间
|
||||
t_thrd.barrier_creator_cxt.first_cn_timeline =
|
||||
GetObsFirstCNBarrierTimeline(t_thrd.barrier_creator_cxt.archive_slot_names);
|
||||
/*
|
||||
|
|
@ -430,94 +397,83 @@ void barrier_creator_main(void)
|
|||
* wait for a while to prevent barrier time rollback.
|
||||
*/
|
||||
do {
|
||||
gettimeofday(&tv, NULL); // 获取当前时间
|
||||
gettimeofday(&tv, NULL);
|
||||
long current_time = TIME_GET_MILLISEC(tv);
|
||||
if (last_barrier_time < current_time) { // 如果最后一个barrier时间比当前时间小,则跳出循环
|
||||
if (last_barrier_time < current_time) {
|
||||
break;
|
||||
}
|
||||
// 计算时间差并打印日志信息
|
||||
long time_diff = last_barrier_time - current_time;
|
||||
ereport(LOG, (errmsg("[BarrierCreator] current time %ld is smaller than barrier time %ld, and sleep %ld ms",
|
||||
current_time, last_barrier_time, time_diff)));
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果有,则处理中断
|
||||
pg_usleep(time_diff * 1000L); // 休眠指定的时间差
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
pg_usleep(time_diff * 1000L);
|
||||
} while (1);
|
||||
// 如果是第一个barrier,记录全局barrier列表的开始时间
|
||||
if (t_thrd.barrier_creator_cxt.is_first_barrier) {
|
||||
gettimeofday(&tv, NULL);
|
||||
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv));
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 如果启用了多节点模式
|
||||
while (!START_AUTO_CSN_BARRIER) {
|
||||
// 在未收到 START_AUTO_CSN_BARRIER 信号之前,循环等待
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果有,则处理中断
|
||||
pg_usleep(1000000L); // 等待1秒
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
pg_usleep(1000000L);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 如果启用了多节点模式
|
||||
CleanupBarrierLock(); // 清理barrier锁
|
||||
CleanupBarrierLock();
|
||||
#endif
|
||||
|
||||
while (!g_instance.barrier_creator_cxt.stop) {
|
||||
if (t_thrd.barrier_creator_cxt.got_SIGHUP) {
|
||||
// 如果收到 SIGHUP 信号,执行配置文件处理
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
startCsnBarrier = g_instance.attr.attr_storage.auto_csn_barrier;
|
||||
}
|
||||
|
||||
/* in hadr switchover, barrier creator thread stop creating new barriers during service truncate.*/
|
||||
// 如果归档槽数量不为0且服务截断标志为true
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num != 0 &&
|
||||
g_instance.archive_obs_cxt.in_service_truncate == true) {
|
||||
continue; // 在服务截断期间继续循环下一次迭代
|
||||
continue;
|
||||
}
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num != 0) { // 如果存在归档槽
|
||||
if (g_instance.archive_obs_cxt.archive_slot_num != 0) {
|
||||
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info == NULL) {
|
||||
// 如果barrier更新的时间信息为空,则分配内存并初始化为0
|
||||
t_thrd.barrier_creator_cxt.barrier_update_last_time_info = (BarrierUpdateLastTimeInfo*)palloc0(
|
||||
sizeof(BarrierUpdateLastTimeInfo) * g_instance.attr.attr_storage.max_replication_slots);
|
||||
}
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL) { // 如果barrier LSN 信息为空
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL) {
|
||||
int nodeSize = *t_thrd.pgxc_cxt.shmemNumCoords + *t_thrd.pgxc_cxt.shmemNumDataNodes;
|
||||
AllocBarrierLsnInfo(nodeSize); // 分配barrier LSN 信息的内存
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局barrier锁
|
||||
g_instance.archive_obs_cxt.max_node_cnt = nodeSize; // 设置最大节点数为指定值
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放全局barrier锁
|
||||
AllocBarrierLsnInfo(nodeSize);
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
g_instance.archive_obs_cxt.max_node_cnt = nodeSize;
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock);
|
||||
}
|
||||
#endif
|
||||
archiveSlotNames = GetAllArchiveSlotsName(); // 获取所有归档槽的名称
|
||||
if (archiveSlotNames == NIL || archiveSlotNames->length == 0) { // 如果无法获取归档槽名称,发出警告并返回
|
||||
archiveSlotNames = GetAllArchiveSlotsName();
|
||||
if (archiveSlotNames == NIL || archiveSlotNames->length == 0) {
|
||||
ereport(WARNING, (errmsg("[BarrierCreator] could not get archive slot name when barrier start")));
|
||||
return;
|
||||
}
|
||||
if (t_thrd.barrier_creator_cxt.archive_slot_names == NULL) { // 如果归档槽名称尚未初始化
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames; // 获取第一个协调器节点的时间线信息
|
||||
if (t_thrd.barrier_creator_cxt.archive_slot_names == NULL) {
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
|
||||
t_thrd.barrier_creator_cxt.first_cn_timeline =
|
||||
GetObsFirstCNBarrierTimeline(t_thrd.barrier_creator_cxt.archive_slot_names);
|
||||
}
|
||||
if (archiveSlotNames->length > t_thrd.barrier_creator_cxt.archive_slot_names->length) { // 如果当前归档槽数量大于之前记录的数量
|
||||
if (archiveSlotNames->length > t_thrd.barrier_creator_cxt.archive_slot_names->length) {
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
|
||||
t_thrd.barrier_creator_cxt.is_first_barrier = true; // 将标志位设为true,表示是第一次创建屏障
|
||||
gettimeofday(&tv, NULL); // 获取当前时间
|
||||
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv)); // 记录全局屏障列表的开始时间
|
||||
t_thrd.barrier_creator_cxt.is_first_barrier = true;
|
||||
gettimeofday(&tv, NULL);
|
||||
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv));
|
||||
} else if (archiveSlotNames->length < t_thrd.barrier_creator_cxt.archive_slot_names->length) {
|
||||
// 如果当前归档槽数量小于之前记录的数量
|
||||
t_thrd.barrier_creator_cxt.archive_slot_names = archiveSlotNames;
|
||||
}
|
||||
}
|
||||
pg_usleep_retry(500000L, 0);
|
||||
pg_usleep_retry(500000L, 0);
|
||||
if (!startCsnBarrier && g_instance.archive_obs_cxt.archive_slot_num == 0) {
|
||||
// 如果不需要启动CSN barrier且归档槽数量为0
|
||||
g_instance.barrier_creator_cxt.stop = true;
|
||||
for (int i = 0; i < g_instance.attr.attr_storage.max_replication_slots; i++) {
|
||||
if (g_instance.archive_thread_info.obsBarrierArchPID[i] != 0) {
|
||||
// 向子进程发送退出信号
|
||||
signal_child(g_instance.archive_thread_info.obsBarrierArchPID[i], SIGUSR2, -1);
|
||||
}
|
||||
}
|
||||
|
|
@ -527,47 +483,41 @@ void barrier_creator_main(void)
|
|||
/* create barrier with increasing index */
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
if (IsGotPoolReload()) { // 如果收到重新加载的信号
|
||||
if (IsGotPoolReload()) {
|
||||
BarrierCreatorPoolerReload();
|
||||
ResetGotPoolReload(false);
|
||||
if (!IsFirstCn()) // 如果当前节点不是第一个协调器节点
|
||||
if (!IsFirstCn())
|
||||
break;
|
||||
}
|
||||
|
||||
ereport(DEBUG1, (errmsg("[BarrierCreator] auto_csn_barrier: %d", startCsnBarrier)));
|
||||
if (startCsnBarrier) { // 如果启用了自动CSN barrier
|
||||
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME); //构造CSN barrier名称
|
||||
if (startCsnBarrier) {
|
||||
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME);
|
||||
securec_check_ss_c(rc, "\0", "\0");
|
||||
|
||||
RequestBarrier(barrier_name, NULL); // 请求创建CSN barrier
|
||||
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name))); // 日志中记录barrier的创建
|
||||
RequestBarrier(barrier_name, NULL);
|
||||
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name)));
|
||||
}
|
||||
#else
|
||||
//构造CSN barrier名称
|
||||
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, "hadr_%020" PRIu64 "_%013ld", index,
|
||||
TIME_GET_MILLISEC(tv));
|
||||
securec_check_ss_c(rc, "\0", "\0");
|
||||
|
||||
DisasterRecoveryRequestBarrier(barrier_name); // 请求创建灾备 barrier
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name))); // 日志中记录barrier的创建
|
||||
DisasterRecoveryRequestBarrier(barrier_name);
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name)));
|
||||
#endif
|
||||
index++;
|
||||
}
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread exits."))); // 记录日志,指示Barrier Creator线程即将退出
|
||||
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info != 0) { // 检查是否分配了barrier_update_last_time_info结构的内存
|
||||
// 循环遍历barrier_update_last_time_info结构数组以释放资源
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread exits.")));
|
||||
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info != 0) {
|
||||
for (int i = 0; i < g_instance.attr.attr_storage.max_replication_slots; i++) {
|
||||
// 检查当前索引处的archiveSlotName是否不为NULL
|
||||
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info[i].archiveSlotName != NULL) {
|
||||
// 释放与archiveSlotName相关联的内存
|
||||
pfree_ext(t_thrd.barrier_creator_cxt.barrier_update_last_time_info[i].archiveSlotName);
|
||||
}
|
||||
}
|
||||
// 释放与barrier_update_last_time_info结构数组相关联的内存
|
||||
pfree_ext(t_thrd.barrier_creator_cxt.barrier_update_last_time_info);
|
||||
}
|
||||
// 执行清理操作
|
||||
destroy_handles(); // 销毁句柄
|
||||
FreeBarrierLsnInfo(); // 释放Barrier LSN信息
|
||||
destroy_handles();
|
||||
FreeBarrierLsnInfo();
|
||||
proc_exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,27 +40,17 @@
|
|||
#include "postmaster/barrier_preparse.h"
|
||||
|
||||
typedef struct XLogPageReadPrivate {
|
||||
const char *datadir; // 存储数据库的数据目录路径
|
||||
TimeLineID tli; // 存储 WAL 日志所在的时间线标识符
|
||||
const char *datadir;
|
||||
TimeLineID tli;
|
||||
} XLogPageReadPrivate;
|
||||
|
||||
/*
|
||||
* 宏,用于确定是否需要将条目插入哈希表。通过检查记录类型(xl_rmid)和某些条件来决定是否插入。
|
||||
* - 如果记录类型为RM_BARRIER_ID,并且info为XLOG_BARRIER_SWITCHOVER,
|
||||
* 或者节点是协调器且info为XLOG_BARRIER_COMMIT,
|
||||
* 或者节点是数据节点且info为XLOG_BARRIER_CREATE
|
||||
* 那么,需要将条目插入哈希表。
|
||||
*/
|
||||
#define NEED_INSERT_INTO_HASH \
|
||||
((record->xl_rmid == RM_BARRIER_ID) && ((info == XLOG_BARRIER_SWITCHOVER) || \
|
||||
(IS_PGXC_COORDINATOR && info == XLOG_BARRIER_COMMIT) || (IS_PGXC_DATANODE && info == XLOG_BARRIER_CREATE)))
|
||||
|
||||
//初始化与barrier相关的哈希表的函数
|
||||
static void InitBarrierHash()
|
||||
{
|
||||
// 检查是否已经创建了barrier上下文,如果没有则创建一个
|
||||
if (g_instance.csn_barrier_cxt.barrier_context == NULL) {
|
||||
// 创建一个新的内存上下文,命名为CsnBarrierContext,用于存储与barrier相关信息
|
||||
g_instance.csn_barrier_cxt.barrier_context = AllocSetContextCreate(g_instance.instance_context,
|
||||
"CsnBarrierContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -68,72 +58,66 @@ static void InitBarrierHash()
|
|||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
}
|
||||
// 定义HASHCTL结构,用于初始化哈希表的属性
|
||||
|
||||
HASHCTL ctl;
|
||||
errno_t rc = 0;
|
||||
|
||||
/* Init hash table */
|
||||
rc = memset_s(&ctl, sizeof(HASHCTL), 0, sizeof(HASHCTL)); // 将ctl结构清零,确保结构中的各字段正确初始化
|
||||
rc = memset_s(&ctl, sizeof(HASHCTL), 0, sizeof(HASHCTL));
|
||||
securec_check(rc, "", "");
|
||||
// 设置哈希表的键大小和每个条目的大小
|
||||
ctl.keysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
|
||||
ctl.entrysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
|
||||
ctl.hash = string_hash; // 设置哈希函数为string_hash,用于计算键的哈希值
|
||||
// 设置哈希表使用的上下文为之前创建的CsnBarrierContext上下文
|
||||
ctl.hash = string_hash;
|
||||
ctl.hcxt = g_instance.csn_barrier_cxt.barrier_context;
|
||||
// 创建屏障哈希表,指定上述哈希表的名称、初始大小和属性
|
||||
g_instance.csn_barrier_cxt.barrier_hash_table = hash_create("Barrier Id Storage Table", INIBARRIERCACHESIZE,
|
||||
&ctl, HASH_ELEM | HASH_FUNCTION | HASH_SHRCTX);
|
||||
// 为屏障哈希表分配轻量级锁,用于并发访问控制
|
||||
g_instance.csn_barrier_cxt.barrier_hashtbl_lock = LWLockAssign(LWTRANCHE_BARRIER_TBL);
|
||||
}
|
||||
// 设置barrier ID
|
||||
|
||||
static void SetBarrieID(const char *barrierId, XLogRecPtr lsn)
|
||||
{
|
||||
errno_t rc = EOK;
|
||||
const uint32 shiftSize = 32;
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||
|
||||
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,确保数据一致性
|
||||
// 使用strncpy_s函数将barrierId复制到lastReceivedBarrierId中,确保字符串安全性
|
||||
SpinLockAcquire(&walrcv->mutex);
|
||||
rc = strncpy_s((char *)walrcv->lastReceivedBarrierId, MAX_BARRIER_ID_LENGTH, barrierId, MAX_BARRIER_ID_LENGTH - 1);
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
walrcv->lastReceivedBarrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 在末尾添加字符串结束符
|
||||
walrcv->lastReceivedBarrierLSN = lsn; // 设置最后接收到的barrier LSN
|
||||
SpinLockRelease(&walrcv->mutex); // 释放互斥锁
|
||||
walrcv->lastReceivedBarrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0';
|
||||
walrcv->lastReceivedBarrierLSN = lsn;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
|
||||
// 输出日志,记录设置的barrier ID和barrier LSN
|
||||
ereport(LOG, (errmsg("SetBarrieID set the barrier ID is %s, the barrier LSN is %08X/%08X", barrierId,
|
||||
(uint32)(lsn >> shiftSize), (uint32)lsn)));
|
||||
}
|
||||
// 处理SIGHUP信号
|
||||
|
||||
static void BarrierPreParseSigHupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = true; // 设置标志位,表示收到SIGHUP信号
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = true;
|
||||
if (t_thrd.proc) {
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置进程的Latch,用于唤醒进程处理SIGHUP信号
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
}
|
||||
errno = save_errno;
|
||||
}
|
||||
// 处理关闭请求信号
|
||||
|
||||
static void BarrierPreParseShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.barrier_preparse_cxt.shutdown_requested = true; // 设置关闭请求标志为true,表示收到了关闭请求信号
|
||||
t_thrd.barrier_preparse_cxt.shutdown_requested = true;
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置线程的Latch,用于唤醒线程处理关闭请求信号
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = save_errno;
|
||||
}
|
||||
// 处理快速终止信号 用于在出现严重问题时强制终止进程
|
||||
|
||||
static void BarrierPreParseQuickDie(SIGNAL_ARGS)
|
||||
{
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除对信号的阻塞,以便进行快速终止
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
|
||||
/*
|
||||
* We DO NOT want to run proc_exit() callbacks -- we're here because
|
||||
|
|
@ -143,7 +127,7 @@ static void BarrierPreParseQuickDie(SIGNAL_ARGS)
|
|||
* things by calling exit() directly, we have to reset the callbacks
|
||||
* explicitly to make this work as intended.
|
||||
*/
|
||||
on_exit_reset(); // 重置退出回调函数
|
||||
on_exit_reset();
|
||||
|
||||
/*
|
||||
* Note we do exit(2) not exit(0). This is to force the postmaster into a
|
||||
|
|
@ -153,108 +137,105 @@ static void BarrierPreParseQuickDie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2); //以代码2 退出进程
|
||||
exit(2);
|
||||
}
|
||||
// 处理SIGUSR1信号
|
||||
|
||||
static void BarrierPreParseSigUsr1Handler(SIGNAL_ARGS)
|
||||
{
|
||||
int saveErrno = errno; // 保存当前错误码的值
|
||||
int saveErrno = errno;
|
||||
|
||||
latch_sigusr1_handler(); // 处理SIGUSR1信号的回调函数
|
||||
latch_sigusr1_handler();
|
||||
|
||||
errno = saveErrno; // 恢复之前保存的错误码
|
||||
errno = saveErrno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the BarrierPreParseMain is ending.
|
||||
*/
|
||||
// 在BarrierPreParseMain结束时调用
|
||||
static void ShutdownBarrierPreParse(int code, Datum arg)
|
||||
{
|
||||
// 将BarrierPreParseLatch设为NULL,表示关闭BarrierPreParse线程
|
||||
g_instance.proc_base->BarrierPreParseLatch = NULL;
|
||||
}
|
||||
// 设置BarrierPreParse线程的LSN
|
||||
|
||||
void SetBarrierPreParseLsn(XLogRecPtr startptr)
|
||||
{
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv; // 获取WalRcvData结构体的指针
|
||||
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,保证操作的原子性
|
||||
walrcv->lastReceivedBarrierLSN = startptr; // 设置lastReceivedBarrierLSN为指定的LSN
|
||||
SpinLockRelease(&walrcv->mutex); // 释放互斥锁
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||
SpinLockAcquire(&walrcv->mutex);
|
||||
walrcv->lastReceivedBarrierLSN = startptr;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
}
|
||||
|
||||
void BarrierPreParseMain(void)
|
||||
{
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv; // 获取WalRcvData结构体的指针
|
||||
MemoryContext preParseContext; // 内存上下文,用于分配内存
|
||||
XLogRecord *record = NULL; // XLog记录指针
|
||||
XLogReaderState *xlogreader = NULL; // XLog读取器的状态结构体指针
|
||||
char *errormsg = NULL; // 错误消息
|
||||
XLogPageReadPrivate readprivate; // XLog页读取的私有数据
|
||||
XLogRecPtr startLSN = InvalidXLogRecPtr; // 起始LSN
|
||||
XLogRecPtr preStartLSN = InvalidXLogRecPtr; // 前一个起始LSN
|
||||
XLogRecPtr lastReadLSN = InvalidXLogRecPtr; // 上一次读取的LSN
|
||||
bool found = false; // 是否找到待处理的记录
|
||||
XLogRecPtr barrierLSN = InvalidXLogRecPtr; // barrier记录的LSN
|
||||
char *xLogBarrierId = NULL; // XLog中的barrierID
|
||||
char barrierId[MAX_BARRIER_ID_LENGTH] = {0}; // barrier ID字符串
|
||||
const uint32 shiftSize = 32; // 位移大小
|
||||
int rc; // 函数返回值
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv;
|
||||
MemoryContext preParseContext;
|
||||
XLogRecord *record = NULL;
|
||||
XLogReaderState *xlogreader = NULL;
|
||||
char *errormsg = NULL;
|
||||
XLogPageReadPrivate readprivate;
|
||||
XLogRecPtr startLSN = InvalidXLogRecPtr;
|
||||
XLogRecPtr preStartLSN = InvalidXLogRecPtr;
|
||||
XLogRecPtr lastReadLSN = InvalidXLogRecPtr;
|
||||
bool found = false;
|
||||
XLogRecPtr barrierLSN = InvalidXLogRecPtr;
|
||||
char *xLogBarrierId = NULL;
|
||||
char barrierId[MAX_BARRIER_ID_LENGTH] = {0};
|
||||
const uint32 shiftSize = 32;
|
||||
int rc;
|
||||
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] barrier preparse thread started"))); // 记录日志,标记线程开始
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] barrier preparse thread started")));
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGHUP, BarrierPreParseSigHupHandler); // 处理SIGHUP信号的回调函数
|
||||
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号
|
||||
(void)gspqsignal(SIGTERM, BarrierPreParseShutdownHandler); // 处理SIGTERM信号的回调函数
|
||||
(void)gspqsignal(SIGQUIT, BarrierPreParseQuickDie); /* hard crash time */ // 处理SIGQUIT信号的回调函数,用于快速终止
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号
|
||||
(void)gspqsignal(SIGUSR1, BarrierPreParseSigUsr1Handler); // 处理SIGUSR1信号的回调函数
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
|
||||
(void)gspqsignal(SIGHUP, BarrierPreParseSigHupHandler);
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
(void)gspqsignal(SIGTERM, BarrierPreParseShutdownHandler);
|
||||
(void)gspqsignal(SIGQUIT, BarrierPreParseQuickDie); /* hard crash time */
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, BarrierPreParseSigUsr1Handler);
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN);
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 恢复SIGCHLD信号的默认处理
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL); // 恢复SIGTTIN信号的默认处理
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL); // 恢复SIGTTOU信号的默认处理
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL); // 恢复SIGCONT信号的默认处理
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL); // 恢复SIGWINCH信号的默认处理
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL);
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许在任何时间接收SIGQUIT 信号
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
|
||||
on_shmem_exit(ShutdownBarrierPreParse, 0); // 在进程退出时调用ShutdownBarrierPreParse函数
|
||||
on_shmem_exit(ShutdownBarrierPreParse, 0);
|
||||
|
||||
preParseContext = AllocSetContextCreate(t_thrd.top_mem_cxt, "Barrier PreParse", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); // 创建内存上下文
|
||||
(void)MemoryContextSwitchTo(preParseContext); // 切换到preParseContext上下文
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
(void)MemoryContextSwitchTo(preParseContext);
|
||||
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us)
|
||||
*/
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除对信号的阻塞
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除对SIGUSR2信号的阻塞
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch; // 设置BarrierPreParseLatch
|
||||
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch;
|
||||
|
||||
startLSN = walrcv->lastReceivedBarrierLSN; // 获取上次接收的barrier LSN
|
||||
startLSN = walrcv->lastReceivedBarrierLSN;
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread start at %08X/%08X", (uint32)(startLSN >> shiftSize),
|
||||
(uint32)startLSN))); // 记录日志,标记线程起始位置
|
||||
(uint32)startLSN)));
|
||||
|
||||
if (g_instance.csn_barrier_cxt.barrier_hash_table == NULL) {// 如果barrier哈希表为空,初始化
|
||||
if (g_instance.csn_barrier_cxt.barrier_hash_table == NULL) {
|
||||
InitBarrierHash();
|
||||
}
|
||||
|
||||
readprivate.datadir = t_thrd.proc_cxt.DataDir; // 设置读取私有数据的数据目录
|
||||
readprivate.tli = GetRecoveryTargetTLI(); // 获取恢复目标的时间线ID
|
||||
readprivate.datadir = t_thrd.proc_cxt.DataDir;
|
||||
readprivate.tli = GetRecoveryTargetTLI();
|
||||
|
||||
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate); // 分配XLog读取器
|
||||
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate);
|
||||
if (xlogreader == NULL)
|
||||
// 如果分配失败,报错
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("memory is temporarily unavailable while allocate xlog reader")));
|
||||
|
||||
|
|
@ -263,98 +244,91 @@ void BarrierPreParseMain(void)
|
|||
*/
|
||||
for (;;) {
|
||||
/* Clear any already-pending wakeups */
|
||||
ResetLatch(&t_thrd.proc->procLatch); //清除已挂起的唤醒
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
if (t_thrd.barrier_preparse_cxt.got_SIGHUP) { // 如果收到SIGHUP信号,重新读取配置文件
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = false; // 重置信号标志
|
||||
ProcessConfigFile(PGC_SIGHUP); // 处理SIGHUP信号,重新读取配置文件
|
||||
if (t_thrd.barrier_preparse_cxt.got_SIGHUP) {
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
}
|
||||
|
||||
if (t_thrd.barrier_preparse_cxt.shutdown_requested) { // 如果收到关闭请求,结束线程
|
||||
if (t_thrd.barrier_preparse_cxt.shutdown_requested) {
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down")));
|
||||
XLogReaderFree(xlogreader); // 释放XLog读取器的资源
|
||||
XLogReaderFree(xlogreader);
|
||||
proc_exit(0); /* done */
|
||||
}
|
||||
|
||||
found = false; // 初始化found标志为false
|
||||
preStartLSN = startLSN; // 保存上一次的起始LSN
|
||||
// 记录日志,标记预解析开始位置
|
||||
found = false;
|
||||
preStartLSN = startLSN;
|
||||
ereport(DEBUG1, (errmsg("[BarrierPreParse] start to preparse at: %08X/%08X",
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
|
||||
startLSN = XLogFindNextRecord(xlogreader, startLSN); // 查找下一个XLog记录的LSN
|
||||
if (XLogRecPtrIsInvalid(startLSN)) { // 如果找不到,回到上一个起始LSN
|
||||
startLSN = preStartLSN; // 使用之前记录的起始LSN
|
||||
startLSN = XLogFindNextRecord(xlogreader, startLSN);
|
||||
if (XLogRecPtrIsInvalid(startLSN)) {
|
||||
startLSN = preStartLSN;
|
||||
if (!XLByteEQ(walrcv->receiver_flush_location, startLSN) &&
|
||||
!XLByteEQ(walrcv->lastRecoveredBarrierLSN, startLSN)) {
|
||||
/* reset startLSN */
|
||||
startLSN = walrcv->lastRecoveredBarrierLSN; // 使用上次恢复的Barrier LSN
|
||||
startLSN = walrcv->lastRecoveredBarrierLSN;
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] reset startLSN with lastRecoveredBarrierLSN: %08X/%08X",
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN))); // 记录日志,标记重置startLSN
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
// 从XLog中读取记录,从startLSN开始
|
||||
record = XLogReadRecord(xlogreader, startLSN, &errormsg);
|
||||
if (record == NULL) { // 如果读取到了NULL记录,即无法继续读取,跳出循环
|
||||
if (record == NULL) {
|
||||
break;
|
||||
}
|
||||
lastReadLSN = xlogreader->EndRecPtr; // 记录最后读取的LSN
|
||||
// 获取XLog记录的信息位,去除XLR_INFO_MASK标志位
|
||||
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
|
||||
lastReadLSN = xlogreader->EndRecPtr;
|
||||
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
|
||||
if (NEED_INSERT_INTO_HASH) {
|
||||
// 如果需要将记录插入到哈希表中
|
||||
xLogBarrierId = XLogRecGetData(xlogreader); // 获取XLog记录的数据部分
|
||||
if (!IS_CSN_BARRIER(xLogBarrierId)) { // 如果不是用于备机集群的barrier记录
|
||||
xLogBarrierId = XLogRecGetData(xlogreader);
|
||||
if (!IS_CSN_BARRIER(xLogBarrierId)) {
|
||||
ereport(WARNING, (errmsg("[BarrierPreParse] %s is not for standby cluster", xLogBarrierId)));
|
||||
} else {
|
||||
// insert into hash table
|
||||
// 插入到哈希表中
|
||||
found = true; // 标记找到适用于备用集群的barrier记录
|
||||
barrierLSN = xlogreader->EndRecPtr; // 记录barrier的LSN
|
||||
found = true;
|
||||
barrierLSN = xlogreader->EndRecPtr;
|
||||
rc = strncpy_s((char *)barrierId, MAX_BARRIER_ID_LENGTH, xLogBarrierId, MAX_BARRIER_ID_LENGTH - 1);
|
||||
securec_check(rc, "\0", "\0");
|
||||
barrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 确保barrierId字符串有效性
|
||||
// 获取哈希表锁,插入barrierId
|
||||
barrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0';
|
||||
LWLockAcquire(g_instance.csn_barrier_cxt.barrier_hashtbl_lock, LW_EXCLUSIVE);
|
||||
BarrierCacheInsertBarrierId(barrierId); // 将barrierId插入哈希表
|
||||
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock); // 释放哈希表锁
|
||||
// 记录日志,说明插入了barrierId到哈希表中
|
||||
BarrierCacheInsertBarrierId(barrierId);
|
||||
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock);
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] insert barrierID %s to the hash table, rmid: %d, crc: %d.",
|
||||
barrierId, record->xl_rmid, record->xl_crc)));
|
||||
}
|
||||
}
|
||||
startLSN = InvalidXLogRecPtr; // 将startLSN重置为InvalidXLogRecPtr,以便下次循环处理下一个XLog记录
|
||||
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested); // 收到关闭请求退出循环,否则继续
|
||||
startLSN = InvalidXLogRecPtr;
|
||||
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested);
|
||||
|
||||
/* close xlogreadfd after circulation */
|
||||
CloseXlogFile(); // 关闭当前使用的XLOG文件
|
||||
CloseXlogFile();
|
||||
|
||||
if (found) { // 如果找到了需要插入到哈希表的barrier,将其插入
|
||||
if (found) {
|
||||
SetBarrieID(barrierId, barrierLSN);
|
||||
}
|
||||
|
||||
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN; // 更新起始LSN
|
||||
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN;
|
||||
|
||||
if (XLogRecPtrIsInvalid(xlogreader->ReadRecPtr) && errormsg) { // 如果在解析过程中出现错误,记录错误信息
|
||||
if (XLogRecPtrIsInvalid(xlogreader->ReadRecPtr) && errormsg) {
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread get an error info %s", errormsg)));
|
||||
}
|
||||
const long sleepTime = 1000; // 定义休眠时间为1000毫秒
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleepTime); // 等待信号量或超时
|
||||
const long sleepTime = 1000;
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleepTime);
|
||||
if (((unsigned int)rc) & WL_POSTMASTER_DEATH) {
|
||||
XLogReaderFree(xlogreader); // 释放XLogReader资源
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1"))); // 记录线程以代码1关闭的日志
|
||||
gs_thread_exit(1);// 以代码1退出线程
|
||||
XLogReaderFree(xlogreader);
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1")));
|
||||
gs_thread_exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 用于唤醒 BarrierPreParse 后台进程
|
||||
|
||||
void WakeUpBarrierPreParseBackend()
|
||||
{
|
||||
if (g_instance.pid_cxt.BarrierPreParsePID != 0) { // 如果存在BarrierPreParse进程
|
||||
if (g_instance.proc_base->BarrierPreParseLatch != NULL) { // 如果存在BarrierPreParse进程的Latch
|
||||
SetLatch(g_instance.proc_base->BarrierPreParseLatch); // 触发Latch,唤醒BarrierPreParse进程
|
||||
if (g_instance.pid_cxt.BarrierPreParsePID != 0) {
|
||||
if (g_instance.proc_base->BarrierPreParseLatch != NULL) {
|
||||
SetLatch(g_instance.proc_base->BarrierPreParseLatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,163 +25,119 @@
|
|||
#include "utils/snapmgr.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "pgstat.h"
|
||||
// 外部函数声明:
|
||||
// 函数声明:StreamSaveTxnContext,用于保存流复制事务上下文
|
||||
|
||||
extern void StreamSaveTxnContext(StreamTxnContext* stc);
|
||||
// 函数声明:StreamRestoreTxnContext,用于恢复流复制事务上下文
|
||||
extern void StreamRestoreTxnContext(StreamTxnContext* stc);
|
||||
// 函数声明:CopySnapshotByCurrentMcxt,通过当前内存上下文复制快照
|
||||
extern Snapshot CopySnapshotByCurrentMcxt(Snapshot snapshot);
|
||||
// 函数声明:SetGlobalSnapshotData,设置全局快照数据
|
||||
extern void SetGlobalSnapshotData(
|
||||
TransactionId xmin, TransactionId xmax, uint64 csn, GTM_Timeline timeline, bool ssNeedSyncWaitAll);
|
||||
|
||||
// 全局变量:最大后台工作进程数,初始值为 64
|
||||
int g_max_worker_processes = 64;
|
||||
/*
|
||||
* Return true if the thread is bgworker.
|
||||
*/
|
||||
// 判断当前进程是否为后台工作进程
|
||||
// 返回值:若为后台工作进程,返回 true;否则返回 false
|
||||
bool IsBgWorkerProcess(void)
|
||||
{
|
||||
return t_thrd.role == BGWORKER; // 返回当前线程是否为 BGWORKER(后台工作进程)
|
||||
return t_thrd.role == BGWORKER;
|
||||
}
|
||||
|
||||
// 内联函数定义:BgworkerPutBackToFreeList
|
||||
// 功能:将后台工作进程放回空闲列表
|
||||
// 参数:bgworker - 后台工作进程指针
|
||||
static inline void BgworkerPutBackToFreeList(BackgroundWorker* bgworker)
|
||||
{
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;// 获取后台工作进程头指针
|
||||
// 使用 memset_s 清空 bgworker 结构体的内容,确保隐私信息被清除
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
|
||||
errno_t rc = memset_s(bgworker, sizeof(BackgroundWorker), 0, sizeof(BackgroundWorker));
|
||||
securec_check(rc, "", ""); // 检查正确性
|
||||
// 将 bgworker 放回空闲后台工作进程链表
|
||||
securec_check(rc, "", "");
|
||||
bgworker->links.next = (SHM_QUEUE *)bgworker_base->free_bgws;
|
||||
bgworker_base->free_bgws = bgworker;
|
||||
}
|
||||
|
||||
// 内联函数定义:GetFreeBgworker
|
||||
// 功能:获取空闲后台工作进程
|
||||
// 返回值:获取的空闲后台工作进程指针,若没有可用的返回 NULL
|
||||
static inline BackgroundWorker* GetFreeBgworker()
|
||||
{
|
||||
// 获取后台工作进程头指针
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
|
||||
// 若空闲后台工作进程链表为空,则返回 NULL
|
||||
if (!bgworker_base->free_bgws) {
|
||||
return NULL;
|
||||
}
|
||||
// 从空闲后台工作进程链表中获取一个后台工作进程
|
||||
BackgroundWorker* bgworker = bgworker_base->free_bgws;
|
||||
bgworker_base->free_bgws = (BackgroundWorker *)bgworker->links.next;
|
||||
return bgworker;
|
||||
}
|
||||
|
||||
// 初始化后台工作进程全局数据
|
||||
void InitBgworkerGlobal(void)
|
||||
{
|
||||
BGW_HDR* bgworker_base = NULL; // 后台工作进程头指针
|
||||
BackgroundWorker* bgws = NULL; // 后台工作进程指针
|
||||
bool needPalloc = false; // 是否需要进行 palloc 内存分配
|
||||
// 切换内存上下文到 MEMORY_CONTEXT_CBB 组中
|
||||
BGW_HDR* bgworker_base = NULL;
|
||||
BackgroundWorker* bgws = NULL;
|
||||
bool needPalloc = false;
|
||||
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB));
|
||||
// 若 g_instance.bgw_base 为空,表示尚未创建后台工作进程共享结构
|
||||
if (g_instance.bgw_base == NULL) {
|
||||
/* Create the g_instance.proc_base shared structure */
|
||||
// 创建 g_instance.bgw_base 共享结构,为其分配内存,确保地址对齐
|
||||
bgworker_base = (BGW_HDR *)CACHELINEALIGN(palloc(sizeof(BGW_HDR) + PG_CACHE_LINE_SIZE));
|
||||
// 将分配的内存设置为后台工作进程共享结构
|
||||
g_instance.bgw_base = (void *)bgworker_base;
|
||||
needPalloc = true;// 标记需要进行内存分配
|
||||
needPalloc = true;
|
||||
} else {
|
||||
// 将 g_instance.bgw_base 转换为 BGW_HDR 指针,表示后台工作进程共享头部
|
||||
bgworker_base = (BGW_HDR *)g_instance.bgw_base;
|
||||
// 断言确保 bgworker_base 的 bgws 指针不为空,即后台工作进程数组已存在
|
||||
Assert(bgworker_base->bgws != NULL);
|
||||
}
|
||||
// 初始化 bgworker_base 的 bgw_id_seq 计数为 1,用于跟踪后台工作进程的唯一标识符
|
||||
pg_atomic_init_u64(&bgworker_base->bgw_id_seq, 1);
|
||||
// 如果需要进行内存分配
|
||||
|
||||
if (needPalloc) {
|
||||
// 分配足够大小的内存用于存储后台工作进程数组,确保地址对齐
|
||||
bgws = (BackgroundWorker*)CACHELINEALIGN(
|
||||
palloc0(g_max_worker_processes * sizeof(BackgroundWorker) + PG_CACHE_LINE_SIZE));
|
||||
bgworker_base->bgws = bgws; // 将分配的内存设置为后台工作进程数组
|
||||
} else { // 如果不需要进行内存分配
|
||||
bgws = bgworker_base->bgws; // 直接获取已存在的后台工作进程数组指针
|
||||
bgworker_base->bgws = bgws;
|
||||
} else {
|
||||
bgws = bgworker_base->bgws;
|
||||
}
|
||||
// 将所有后台工作进程放回空闲列表
|
||||
|
||||
for (int i = 0; i < g_max_worker_processes; i++) {
|
||||
BgworkerPutBackToFreeList(&bgws[i]);
|
||||
}
|
||||
// 初始化后台工作进程数据锁
|
||||
|
||||
pthread_mutex_init(&g_instance.bgw_base_lock, NULL);
|
||||
// 切换回原来的内存上下文
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
}
|
||||
|
||||
// 设置后台工作进程的事务环境
|
||||
void SetUpBgWorkerTxnEnvironment()
|
||||
{
|
||||
/* resotre transaction context. */
|
||||
// 获取后台工作进程上下文
|
||||
BgWorkerContext *bwc = (BgWorkerContext *)t_thrd.bgworker_cxt.bgwcontext;
|
||||
// 恢复事务上下文
|
||||
|
||||
StreamRestoreTxnContext(&bwc->transactionCxt);
|
||||
|
||||
/* transaction id. */
|
||||
// SetNextTransactionId 函数的第二个参数用于控制是否自动增加事务ID。
|
||||
// 当设为 false 时,函数不会自动增加事务ID,而是使用传递的 txnId 参数作为下一个事务ID
|
||||
// 确保后续的事务使用指定的事务ID,保持一致性
|
||||
SetNextTransactionId(bwc->transactionCxt.txnId, false);
|
||||
StreamTxnContextSetTransactionState(&bwc->transactionCxt); // 设置事务状态为当前状态
|
||||
StreamTxnContextSetTransactionState(&bwc->transactionCxt);
|
||||
|
||||
/* snapshot. */
|
||||
Snapshot snapshot = CopySnapshotByCurrentMcxt(bwc->transactionCxt.snapshot); // 复制当前内存上下文中的快照
|
||||
// SetGlobalSnapshotData 函数的最后一个参数用于控制是否需要等待所有事务同步完成。
|
||||
// 当设为 false 时,函数将不会等待所有事务同步完成,而是立即返回,以提高响应速度和效率
|
||||
SetGlobalSnapshotData(snapshot->xmin, snapshot->xmax, snapshot->snapshotcsn, snapshot->timeline, false); // 设置全局快照数据
|
||||
StreamTxnContextSetSnapShot(snapshot); // 将快照信息设置到流复制事务上下文中
|
||||
StreamTxnContextSetMyPgXactXmin(snapshot->xmin); // 设置 PGXACT 的 xmin
|
||||
Snapshot snapshot = CopySnapshotByCurrentMcxt(bwc->transactionCxt.snapshot);
|
||||
SetGlobalSnapshotData(snapshot->xmin, snapshot->xmax, snapshot->snapshotcsn, snapshot->timeline, false);
|
||||
StreamTxnContextSetSnapShot(snapshot);
|
||||
StreamTxnContextSetMyPgXactXmin(snapshot->xmin);
|
||||
|
||||
/* command id. */
|
||||
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId); // 保存接收到的命令ID
|
||||
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId);
|
||||
|
||||
/* timestamp. */
|
||||
SetCurrentGTMDeltaTimestamp(); // 设置当前的 GTM Delta 时间戳
|
||||
SetCurrentGTMDeltaTimestamp();
|
||||
}
|
||||
|
||||
// 保存后台工作进程的错误信息
|
||||
static void BgWorkerSaveError()
|
||||
{
|
||||
// 获取当前后台工作进程的指针
|
||||
BackgroundWorker *bgw = (BackgroundWorker *)t_thrd.bgworker_cxt.bgworker;
|
||||
// 获取当前错误信息的指针
|
||||
ErrorData *edata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth];
|
||||
errno_t rc = EOK;
|
||||
int len;
|
||||
// 设置错误信息和详情的默认字符串
|
||||
char *failmsg = "Worker failed during parallel build index.";
|
||||
char *nulldetail = "N/A";
|
||||
|
||||
// 将错误级别和 SQL 错误码保存到 bgw_edata 结构
|
||||
bgw->bgw_edata.elevel = edata->elevel;
|
||||
bgw->bgw_edata.sqlerrcode = edata->sqlerrcode;
|
||||
|
||||
// 获取错误消息,如果为空则使用默认消息
|
||||
char *message = (edata->message != NULL ? edata->message : failmsg);
|
||||
// 限制消息长度,并复制到 bgw_edata 的 message 字段
|
||||
len = Min(strlen(message), BGWORKER_MAX_ERROR_LEN - 1);
|
||||
rc = strncpy_s(bgw->bgw_edata.message, BGWORKER_MAX_ERROR_LEN, message, len);
|
||||
// 确保字符串的复制操作不会造成缓冲区溢出,增加代码的健壮性
|
||||
securec_check_c(rc, "", "");
|
||||
bgw->bgw_edata.message[len] = '\0'; // 确保字符串以 C 字符串的形式结束
|
||||
bgw->bgw_edata.message[len] = '\0';
|
||||
|
||||
// 获取错误详情,如果为空则使用默认详情
|
||||
char *detail = (edata->detail != NULL ? edata->detail : nulldetail);
|
||||
// 限制详情长度,并复制到 bgw_edata 的 detail 字段
|
||||
len = Min(strlen(detail), BGWORKER_MAX_ERROR_LEN - 1);
|
||||
rc = strncpy_s(bgw->bgw_edata.detail, BGWORKER_MAX_ERROR_LEN, detail, len);
|
||||
securec_check_c(rc, "", "");
|
||||
|
|
@ -191,55 +147,43 @@ static void BgWorkerSaveError()
|
|||
/*
|
||||
* Called when the Bgworker thread is ending.
|
||||
*/
|
||||
/*
|
||||
* BgworkerQuitAndClean
|
||||
* 功能:当后台工作进程线程即将结束时调用
|
||||
* 参数:
|
||||
* code: 退出代码
|
||||
* arg: 用户定义的参数
|
||||
*/
|
||||
static void BgworkerQuitAndClean(int code, Datum arg)
|
||||
{
|
||||
// 获取当前后台工作进程的指针
|
||||
BackgroundWorker *bgw = (BackgroundWorker *)t_thrd.bgworker_cxt.bgworker;
|
||||
|
||||
// 根据后台工作进程的状态设置相应状态
|
||||
if (bgw->bgw_status == BGW_STOPPED) {
|
||||
bgw->bgw_status = BGW_TERMINATED; // 将状态更新为 BGW_TERMINATED,表示进程已正常终止
|
||||
bgw->bgw_status = BGW_TERMINATED;
|
||||
} else {
|
||||
bgw->bgw_status = BGW_FAILED; // 将状态更新为 BGW_FAILED,表示进程因某种原因失败
|
||||
bgw->bgw_status = BGW_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// 后台工作进程的初始化函数
|
||||
static void BackgroundWorkerInit(void)
|
||||
{
|
||||
/* we are a postmaster subprocess now */
|
||||
IsUnderPostmaster = true; // 将 IsUnderPostmaster 标志设置为 true,表示当前进程是在后台运行
|
||||
t_thrd.role = BGWORKER; // 设置当前线程的角色为 BGWORKER,表示当前线程是一个后台工作进程
|
||||
IsUnderPostmaster = true;
|
||||
t_thrd.role = BGWORKER;
|
||||
|
||||
/* reset t_thrd.proc_cxt.MyProcPid */
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); // 重置当前线程的进程ID,将其设置为当前线程的实际线程ID
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
|
||||
t_thrd.proc_cxt.MyProgName = "BgWorker"; // 设置当前线程的进程名称为 "BgWorker"
|
||||
t_thrd.proc_cxt.MyProgName = "BgWorker";
|
||||
|
||||
/* record Start Time for logging */
|
||||
t_thrd.proc_cxt.MyStartTime = time(NULL); // 记录当前线程的开始时间,用于日志记录
|
||||
t_thrd.proc_cxt.MyStartTime = time(NULL);
|
||||
|
||||
init_ps_display("Bgworker process", "", "", ""); // 初始化显示进程状态
|
||||
init_ps_display("Bgworker process", "", "", "");
|
||||
|
||||
SetProcessingMode(InitProcessing); // 设置当前的处理模式为初始化阶段
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
on_proc_exit(BgworkerQuitAndClean, 0); // 在进程退出时调用 BgworkerQuitAndClean 函数,执行清理操作
|
||||
on_proc_exit(BgworkerQuitAndClean, 0);
|
||||
|
||||
/*
|
||||
* SIGINT is used to signal canceling the current action
|
||||
*/
|
||||
// 为 SIGINT、SIGTERM 和 SIGALRM 信号设置相应的处理函数
|
||||
(void)gspqsignal(SIGINT, StatementCancelHandler);
|
||||
(void)gspqsignal(SIGTERM, die);
|
||||
(void)gspqsignal(SIGALRM, handle_sig_alarm);
|
||||
// 对于 SIGQUIT、SIGPIPE、SIGUSR1 、SIGUSR2 和 SIGHUP 信号,设置忽略处理
|
||||
(void)gspqsignal(SIGQUIT, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, SIG_IGN);
|
||||
|
|
@ -247,7 +191,6 @@ static void BackgroundWorkerInit(void)
|
|||
(void)gspqsignal(SIGHUP, SIG_IGN);
|
||||
|
||||
/* Reset some signals that are accepted by postmaster but not here */
|
||||
// 重置一些在 postmaster 中接受但在这里不接受的信号的处理方式
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
|
|
@ -255,11 +198,11 @@ static void BackgroundWorkerInit(void)
|
|||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* Early initialization */
|
||||
BaseInit(); // 执行早期初始化操作
|
||||
BaseInit();
|
||||
|
||||
#ifndef EXEC_BACKEND
|
||||
InitProcess(); // 初始化进程数据结构和状态
|
||||
#endif
|
||||
InitProcess();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -268,7 +211,6 @@ static void BackgroundWorkerInit(void)
|
|||
* This is the main entry point for background worker, to be called from
|
||||
* postmaster.
|
||||
*/
|
||||
// 后台工作进程的主要入口函数,从 postmaster 被调用
|
||||
void BackgroundWorkerMain(void)
|
||||
{
|
||||
BgWorkerContext *bwc = (BgWorkerContext *)t_thrd.bgworker_cxt.bgwcontext;
|
||||
|
|
@ -280,67 +222,57 @@ void BackgroundWorkerMain(void)
|
|||
int *oldTryCounter = NULL;
|
||||
int curTryCounter;
|
||||
|
||||
// 获取后台工作进程上下文锁,用于确保安全访问 bgworker 数据结构
|
||||
pthread_mutex_lock(&g_instance.bgw_base_lock);
|
||||
// 如果 bgwId 与 bgw->bgw_id 不匹配,或者已经被禁用,则退出
|
||||
if (bgwId != bgw->bgw_id || pg_atomic_fetch_add_u32(&bgw->disable_count, 1) > 0) {
|
||||
/* The leader disallowed this worker to do index build due to startup time longer than 5s. */
|
||||
ereport(WARNING, (errmsg("BgWorker thread %lu was disabled for long startup time.",
|
||||
t_thrd.proc_cxt.MyProcPid))); // 报告警告信息
|
||||
t_thrd.proc_cxt.MyProcPid)));
|
||||
/* Note that we are in the state BGW_NOT_YET_STARTED. */
|
||||
// 解锁并跳转到 out 标签,退出函数
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
goto out;
|
||||
}
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
|
||||
BackgroundWorkerInit(); // 初始化后台工作进程
|
||||
// 创建一个内存上下文来管理后台工作进程的内存
|
||||
BackgroundWorkerInit();
|
||||
workerContext = AllocSetContextCreate(t_thrd.top_mem_cxt, "BgWorker", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
(void)MemoryContextSwitchTo(workerContext); // 切换到工作内存上下文
|
||||
(void)MemoryContextSwitchTo(workerContext);
|
||||
|
||||
/* Unblock signals (they were blocked when the postmaster forked us) */
|
||||
// 解除对信号的阻塞(在 postmaster fork 时会阻塞信号)
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/* If an exception is encountered, processing resumes here. */
|
||||
// 如果遇到异常,处理将会跳转到此处
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
gstrace_tryblock_exit(true, oldTryCounter); // 退出异常处理块,恢复之前的 try counter
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
|
||||
/* Since not using PG_TRY, must reset error stack by hand */
|
||||
// 手动重置错误堆栈和调用堆栈
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
t_thrd.log_cxt.call_stack = NULL;
|
||||
|
||||
/* Prevent interrupts while cleaning up */
|
||||
HOLD_INTERRUPTS(); // 在清理期间阻止中断
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/* save bgworker error data for leader reporting */
|
||||
BgWorkerSaveError(); // 保存 bgworker 错误信息
|
||||
BgWorkerSaveError();
|
||||
|
||||
/* Report the error to the parallel leader and the server log */
|
||||
EmitErrorReport(); // 报告错误给主进程和日志
|
||||
EmitErrorReport();
|
||||
|
||||
/* release resource held by lsc */
|
||||
AtEOXact_SysDBCache(false); // 释放系统缓存中的资源
|
||||
AtEOXact_SysDBCache(false);
|
||||
/*
|
||||
* These operations are really just a minimal subset of
|
||||
* AbortTransaction(). We don't have very many resources to worry
|
||||
* about in bgwriter, but we do have LWLocks, buffers, and temp files.
|
||||
*/
|
||||
LWLockReleaseAll(); // 释放所有的轻量级锁
|
||||
AbortBufferIO(); // 中止所有的缓冲区输入/输出操作
|
||||
UnlockBuffers(); // 解锁所有缓冲区
|
||||
LWLockReleaseAll();
|
||||
AbortBufferIO();
|
||||
UnlockBuffers();
|
||||
/* buffer pins are released here */
|
||||
// 如果当前资源拥有者存在,进行资源释放
|
||||
if (t_thrd.utils_cxt.CurrentResourceOwner != NULL) {
|
||||
// 释放资源拥有者的资源,并指定 RESOURCE_RELEASE_BEFORE_LOCKS 模式
|
||||
// 第三个参数为 false,表示不释放连接级别的资源
|
||||
// 第四个参数为 true,表示在释放资源后也执行锁的释放
|
||||
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +280,6 @@ void BackgroundWorkerMain(void)
|
|||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
// 切换回原始的内存上下文,刷新错误状态并清理内存
|
||||
(void)MemoryContextSwitchTo(workerContext);
|
||||
FlushErrorState();
|
||||
|
||||
|
|
@ -356,44 +287,35 @@ void BackgroundWorkerMain(void)
|
|||
MemoryContextResetAndDeleteChildren(workerContext);
|
||||
|
||||
/* and go away */
|
||||
proc_exit(1); // 退出进程
|
||||
proc_exit(1);
|
||||
}
|
||||
// 获取旧的 try 计数器的值,以便在异常恢复时进行恢复
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
|
||||
/* We can now handle ereport(ERROR) */
|
||||
// 设置异常处理机制的跳转点,以便在发生错误时进行处理
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
|
||||
// 设置会话的开始时间戳
|
||||
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
|
||||
bgw->bgw_status = BGW_STARTED; // 设置 bgworker 状态
|
||||
bgw->bgw_status = BGW_STARTED;
|
||||
|
||||
/* General initialization. */
|
||||
/* user_name and database_name in u_sess->proc_cxt.MyProcPort is under t_thrd.top_mem_cxt */
|
||||
// 切换到存储上下文,对 MyProcPort 的数据库名和用户名进行重置
|
||||
oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
// 如果当前 MyProcPort 的数据库名不为 NULL,则释放其内存
|
||||
if (u_sess->proc_cxt.MyProcPort->database_name != NULL) {
|
||||
pfree_ext(u_sess->proc_cxt.MyProcPort->database_name);
|
||||
}
|
||||
// 如果当前 MyProcPort 的用户名不为 NULL,则释放其内存
|
||||
if (u_sess->proc_cxt.MyProcPort->user_name != NULL) {
|
||||
pfree_ext(u_sess->proc_cxt.MyProcPort->user_name);
|
||||
}
|
||||
// 设置 MyProcPort 的数据库名和用户名
|
||||
u_sess->proc_cxt.MyProcPort->database_name = pstrdup(bwc->databaseName);
|
||||
u_sess->proc_cxt.MyProcPort->user_name = pstrdup(bwc->userName);
|
||||
// 切换回之前的上下文
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
// 设置数据库和用户,初始化后台工作
|
||||
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(bwc->databaseName, InvalidOid, bwc->userName);
|
||||
t_thrd.proc_cxt.PostInit->InitBgWorker();
|
||||
t_thrd.proc_cxt.PostInit->GetDatabaseName(u_sess->proc_cxt.MyProcPort->database_name);
|
||||
// 记录日志,表示 bgworker 的线程 ID
|
||||
ereport(LOG, (errmsg("bgworker threadId is %lu.", t_thrd.proc_cxt.MyProcPid)));
|
||||
|
||||
StartTransactionCommand(); // 开启事务
|
||||
SetUpBgWorkerTxnEnvironment(); // 设置后台工作的事务环境
|
||||
StartTransactionCommand();
|
||||
SetUpBgWorkerTxnEnvironment();
|
||||
|
||||
/*
|
||||
* Join locking group. We must do this before anything that could try to
|
||||
|
|
@ -404,28 +326,21 @@ void BackgroundWorkerMain(void)
|
|||
* deadlock. (If we can't join the lock group, the leader has gone away,
|
||||
* so just exit quietly.)
|
||||
*/
|
||||
/*
|
||||
加入锁定组的目的是避免死锁的发生。
|
||||
在并行处理中,如果一个进程获取了重量级锁,而另一个进程又试图获取与之冲突的锁,就可能引发死锁。
|
||||
通过加入锁定组,可以确保所有并行进程都在同一个锁定组内,从而避免死锁的发生。
|
||||
如果无法加入锁定组,那么可能是因为并行组的领导者已经退出,这时后台工作进程会安静地退出,而不做其他操作。
|
||||
*/
|
||||
BecomeLockGroupMember(bwc->leader); // 加入锁定组,避免死锁
|
||||
BecomeLockGroupMember(bwc->leader);
|
||||
|
||||
u_sess->attr.attr_sql.enable_cluster_resize = bwc->enable_cluster_resize; // 设置是否启用集群调整大小
|
||||
u_sess->attr.attr_sql.enable_cluster_resize = bwc->enable_cluster_resize;
|
||||
|
||||
/*
|
||||
* Now invoke the user-defined worker code
|
||||
*/
|
||||
bwc->main_entry(bwc); // 调用用户定义的后台工作函数
|
||||
// 结束并重置后台工作的事务
|
||||
bwc->main_entry(bwc);
|
||||
EndParallelWorkerTransaction();
|
||||
ResetTransactionInfo();
|
||||
/* ... and if it returns, we're done */
|
||||
bgw->bgw_status = BGW_STOPPED; // 设置 bgworker 状态为已停止
|
||||
bgw->bgw_status = BGW_STOPPED;
|
||||
|
||||
out:
|
||||
proc_exit(0); // 退出进程
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -434,46 +349,27 @@ out:
|
|||
* This can only be called in the _PG_init function of a module library
|
||||
* that's loaded by shared_preload_libraries; otherwise it has no effect.
|
||||
*/
|
||||
/*
|
||||
* 在处理 shared_preload_libraries 期间注册一个新的后台工作进程。
|
||||
*
|
||||
* 该函数用于在 _PG_init 函数中被调用,仅在被 shared_preload_libraries 加载的
|
||||
* 模块库中有效。它的作用是为后台工作进程注册提供了一个接口,用于准备并启动新的
|
||||
* 后台工作进程。
|
||||
*
|
||||
* 参数:
|
||||
* bwc - 后台工作上下文,包含了后台工作进程的相关信息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功注册了新的后台工作进程,返回 true;否则,返回 false。
|
||||
*/
|
||||
bool RegisterBackgroundWorker(BgWorkerContext *bwc)
|
||||
{
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base; // 获取指向后台工作进程池的指针
|
||||
// 声明后台工作进程结构和后台工作进程参数结构的指针
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
|
||||
BackgroundWorker *bgw = NULL;
|
||||
BackgroundWorkerArgs *bwa = NULL;
|
||||
|
||||
// 加锁,以便在操作后台工作进程池时保持同步
|
||||
pthread_mutex_lock(&g_instance.bgw_base_lock);
|
||||
bgw = GetFreeBgworker(); // 获取一个空闲的后台工作进程
|
||||
if (bgw == NULL) { // 如果没有空闲的后台工作进程可用
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁后台工作进程池
|
||||
// 输出警告信息,表示没有空闲的后台工作进程可用
|
||||
bgw = GetFreeBgworker();
|
||||
if (bgw == NULL) {
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
ereport(WARNING, (errmsg("There are no more free background workers available")));
|
||||
return false; // 返回 false,表示注册失败
|
||||
return false;
|
||||
}
|
||||
// 为该后台工作进程分配唯一的 ID
|
||||
bgw->bgw_id = pg_atomic_fetch_add_u64(&bgworker_base->bgw_id_seq, 1);
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 解锁后台工作进程池
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
|
||||
// 设置后台工作进程的状态为尚未启动,以及相关的状态持续时间和禁用计数
|
||||
bgw->bgw_status = BGW_NOT_YET_STARTED;
|
||||
bgw->bgw_status_dur = 0;
|
||||
bgw->disable_count = 0;
|
||||
|
||||
/* Construct bgworker thread args */
|
||||
// 构造后台工作线程参数
|
||||
bwa = (BackgroundWorkerArgs*)MemoryContextAllocZero(
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), sizeof(BackgroundWorkerArgs));
|
||||
bwa->bgwcontext = bwc;
|
||||
|
|
@ -481,221 +377,153 @@ bool RegisterBackgroundWorker(BgWorkerContext *bwc)
|
|||
bwa->bgworkerId = bgw->bgw_id;
|
||||
|
||||
/* Fork a new worker thread */
|
||||
// 创建一个新的工作线程
|
||||
bgw->bgw_notify_pid = initialize_util_thread(BGWORKER, bwa);
|
||||
/* failed to fork a new thread */
|
||||
// 如果创建线程失败
|
||||
if (bgw->bgw_notify_pid == 0) {
|
||||
pfree_ext(bwa); // 释放分配的参数内存
|
||||
return false; // 返回注册失败
|
||||
pfree_ext(bwa);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy the registration data into the registered workers list. */
|
||||
slist_push_head(&t_thrd.bgworker_cxt.bgwlist, &bgw->rw_lnode); // 将注册数据复制到已注册工作线程列表中
|
||||
return true; // 返回注册成功
|
||||
slist_push_head(&t_thrd.bgworker_cxt.bgwlist, &bgw->rw_lnode);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 功能:清理后台工作进程的共享上下文
|
||||
static void BgworkerCleanupSharedContext()
|
||||
{
|
||||
Assert(!IsBgWorkerProcess()); // 确保不是后台工作进程调用该函数
|
||||
Assert(!IsBgWorkerProcess());
|
||||
/* clean up backgroud shared context */
|
||||
if (t_thrd.bgworker_cxt.bgwcontext) { // 如果存在后台工作上下文
|
||||
if (t_thrd.bgworker_cxt.bgwcontext) {
|
||||
BgWorkerContext *bwc = (BgWorkerContext*)t_thrd.bgworker_cxt.bgwcontext;
|
||||
|
||||
if (bwc->exit_entry) { // 如果有退出函数,即程序退出时执行操作
|
||||
bwc->exit_entry(bwc); // 调用
|
||||
if (bwc->exit_entry) {
|
||||
bwc->exit_entry(bwc);
|
||||
}
|
||||
pfree_ext(bwc->bgshared); // 释放分配的后台工作共享内存
|
||||
pfree_ext(t_thrd.bgworker_cxt.bgwcontext); // 释放分配的后台工作上下文内存
|
||||
pfree_ext(bwc->bgshared);
|
||||
pfree_ext(t_thrd.bgworker_cxt.bgwcontext);
|
||||
}
|
||||
slist_init(&t_thrd.bgworker_cxt.bgwlist); // 初始化后台工作列表
|
||||
slist_init(&t_thrd.bgworker_cxt.bgwlist);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能: 同步遍历后台工作进程列表,执行终止或管理操作
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
void BgworkerListSyncQuit()
|
||||
{
|
||||
slist_mutable_iter iter;
|
||||
bool alldone = false;
|
||||
bool sigsent = false;
|
||||
// 如果后台工作进程列表为空,直接返回
|
||||
|
||||
if (slist_is_empty(&t_thrd.bgworker_cxt.bgwlist)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loop:
|
||||
alldone = true;
|
||||
// 遍历后台工作进程列表
|
||||
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.bgwlist) {
|
||||
// 获取当前遍历到的后台工作进程
|
||||
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
|
||||
// 如果后台工作进程状态为 BGW_FAILED 或 BGW_TERMINATED
|
||||
if (bgw->bgw_status == BGW_FAILED || bgw->bgw_status == BGW_TERMINATED) {
|
||||
slist_delete_current(&iter); // 从列表中删除后台工作进程
|
||||
// 获取全局后台工作进程列表互斥锁,防止多个线程同时访问列表
|
||||
slist_delete_current(&iter);
|
||||
pthread_mutex_lock(&g_instance.bgw_base_lock);
|
||||
// 将当前后台工作进程放回空闲列表
|
||||
BgworkerPutBackToFreeList(bgw);
|
||||
// 释放全局后台工作进程列表互斥锁,允许其他线程访问列表
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
} else if (bgw->bgw_status == BGW_NOT_YET_STARTED) { // 如果后台工作进程状态为 BGW_NOT_YET_STARTED
|
||||
} else if (bgw->bgw_status == BGW_NOT_YET_STARTED) {
|
||||
alldone = false;
|
||||
// 增加状态持续时间,如果超过限制且尚未禁用,则标记为失败
|
||||
if (++bgw->bgw_status_dur > BGWORKER_STATUS_DURLIMIT &&
|
||||
(pg_atomic_fetch_add_u32(&bgw->disable_count, 1) == 0)) {
|
||||
bgw->bgw_status = BGW_FAILED;
|
||||
}
|
||||
} else {
|
||||
// 如果后台工作进程状态为其他值
|
||||
// 如果之前没有发送过信号且成功地发送了 SIGINT 信号给指定的进程
|
||||
if (!sigsent && gs_signal_send(bgw->bgw_notify_pid, SIGINT) != 0) {
|
||||
ereport(WARNING, (errmsg("BgworkerListSyncQuit kill(pid %lu, stat %d) failed: %m",
|
||||
bgw->bgw_notify_pid, bgw->bgw_status))); // 警告消息记录到日志
|
||||
bgw->bgw_notify_pid, bgw->bgw_status)));
|
||||
}
|
||||
alldone = false; // 表示尚未完成所有后台工作进程的处理。
|
||||
alldone = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果未完成遍历,等待一段时间并继续
|
||||
if (!alldone) {
|
||||
usleep(BGWORKER_LOOP_SLEEP_TIME);
|
||||
sigsent = true;
|
||||
goto loop;
|
||||
}
|
||||
// 清理后台工作进程的共享上下文
|
||||
BgworkerCleanupSharedContext();
|
||||
}
|
||||
|
||||
/*
|
||||
* 清理未能成功启动的后台工作进程
|
||||
*
|
||||
* 该函数用于遍历后台工作进程列表,如果发现某个后台工作进程的状态为 BGW_NOT_YET_STARTED,
|
||||
* 即未能成功启动,就将其从列表中删除,并将其返回到空闲列表中。这样做是为了确保未能成功启动的
|
||||
* 后台工作进程不会影响后续处理。
|
||||
*
|
||||
* 参数: nunstarts 未能成功启动的后台工作进程的数量
|
||||
*/
|
||||
static inline void CleanupUnstartBgworkers(int nunstarts)
|
||||
{
|
||||
slist_mutable_iter iter;
|
||||
|
||||
// 如果存在未能成功启动的后台工作进程
|
||||
if (nunstarts > 0) {
|
||||
// 遍历后台工作进程列表
|
||||
slist_foreach_modify(iter, &t_thrd.bgworker_cxt.bgwlist) {
|
||||
// 获取当前迭代器指向的后台工作进程结构
|
||||
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
|
||||
// 如果当前后台工作进程的状态为 BGW_NOT_YET_STARTED,即未能成功启动
|
||||
if (bgw->bgw_status == BGW_NOT_YET_STARTED) {
|
||||
/* the bgworker thread is unable to start, remove it from the waiting list */
|
||||
slist_delete_current(&iter); // 从列表中删除当前迭代器指向的元素
|
||||
pthread_mutex_lock(&g_instance.bgw_base_lock); // 获取全局互斥锁,以便对全局数据进行修改
|
||||
BgworkerPutBackToFreeList(bgw); // 将未能成功启动的后台工作进程返回到空闲列表中
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock); // 释放全局互斥锁
|
||||
slist_delete_current(&iter);
|
||||
pthread_mutex_lock(&g_instance.bgw_base_lock);
|
||||
BgworkerPutBackToFreeList(bgw);
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BgworkerListWaitFinish
|
||||
*
|
||||
* 功能:用于等待后台工作者的完成状态,直到所有的后台工作者都完成了任务为止
|
||||
*
|
||||
* 参数:
|
||||
* nparticipants:表示参与任务的后台工作者的数量
|
||||
*/
|
||||
void BgworkerListWaitFinish(int *nparticipants)
|
||||
{
|
||||
slist_iter iter;
|
||||
bool alldone = false; // 表示是否所有后台工作者都已完成任务
|
||||
uint32 disable_count; // 用于存储禁用计数的变量
|
||||
int nfinished; // 记录已完成任务的后台工作者数量
|
||||
int nunstarts = 0; // 记录未能启动的后台工作者数量
|
||||
bool alldone = false;
|
||||
uint32 disable_count;
|
||||
int nfinished;
|
||||
int nunstarts = 0;
|
||||
|
||||
Assert(nparticipants != NULL); // 断言,确保传入的参数 nparticipants 不为空
|
||||
// 在等待状态报告中设置当前状态为等待同步的后台工作者
|
||||
Assert(nparticipants != NULL);
|
||||
WaitState oldStatus = pgstat_report_waitstatus(STATE_WAIT_SYNC_BGWORKERS);
|
||||
// 循环,直到所有后台工作者都完成了任务
|
||||
while (!alldone) {
|
||||
nfinished = 0;
|
||||
// 遍历后台工作者列表
|
||||
slist_foreach(iter, &t_thrd.bgworker_cxt.bgwlist) {
|
||||
// 获取当前迭代中的后台工作者
|
||||
BackgroundWorker *bgw = slist_container(BackgroundWorker, rw_lnode, iter.cur);
|
||||
// 如果后台工作者状态为 BGW_NOT_YET_STARTED,且超过了状态持续时间限制
|
||||
if (bgw->bgw_status == BGW_NOT_YET_STARTED && ++bgw->bgw_status_dur > BGWORKER_STATUS_DURLIMIT) {
|
||||
disable_count = pg_atomic_fetch_add_u32(&bgw->disable_count, 1); // 原子操作地增加后台工作者的禁用计数
|
||||
// 如果禁用计数为 0,表示该后台工作者需要被禁用
|
||||
disable_count = pg_atomic_fetch_add_u32(&bgw->disable_count, 1);
|
||||
if (disable_count == 0) {
|
||||
// 输出警告信息,表示该后台工作者在 5 秒内未能启动,被禁用
|
||||
ereport(WARNING, (errmsg("The bgworker thread %lu hasn't started in 5 seconds, disable it.",
|
||||
bgw->bgw_notify_pid)));
|
||||
(*nparticipants)--; // 减少参与任务的后台工作者数量
|
||||
nunstarts++; // 增加未启动的后台工作者数量
|
||||
}
|
||||
} else if (bgw->bgw_status == BGW_FAILED) { // 如果后台工作者状态为 BGW_FAILED
|
||||
// 检查后台工作者的错误级别是否大于或等于 ERROR
|
||||
(*nparticipants)--;
|
||||
nunstarts++;
|
||||
}
|
||||
} else if (bgw->bgw_status == BGW_FAILED) {
|
||||
if (bgw->bgw_edata.elevel >= ERROR) {
|
||||
// 输出错误信息,包括错误码、错误消息和错误详情
|
||||
ereport(bgw->bgw_edata.elevel, (errcode(bgw->bgw_edata.sqlerrcode), errmsg("%s",
|
||||
bgw->bgw_edata.message), errdetail("%s", bgw->bgw_edata.detail)));
|
||||
} else {
|
||||
// 输出错误信息,表示后台工作者在并行索引构建过程中失败了
|
||||
ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("Background worker failed during parallel index building.")));
|
||||
}
|
||||
} else if (bgw->bgw_status == BGW_TERMINATED) { // 如果后台工作者状态为 BGW_TERMINATED
|
||||
nfinished++; // 已完成任务的后台工作者数量+1
|
||||
} else if (bgw->bgw_status == BGW_TERMINATED) {
|
||||
nfinished++;
|
||||
}
|
||||
}
|
||||
|
||||
alldone = (nfinished >= *nparticipants); // 判断是否所有后台工作者都已完成任务
|
||||
alldone = (nfinished >= *nparticipants);
|
||||
|
||||
// 如果所有后台工作者都已完成任务,进行清理未启动的后台工作者
|
||||
if (alldone) {
|
||||
CleanupUnstartBgworkers(nunstarts);
|
||||
} else {
|
||||
// 在等待期间检查是否有中断请求,然后进行短暂的等待
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
usleep(BGWORKER_LOOP_SLEEP_TIME);
|
||||
}
|
||||
}
|
||||
pgstat_report_waitstatus(oldStatus); // 恢复之前的等待状态报告
|
||||
pgstat_report_waitstatus(oldStatus);
|
||||
}
|
||||
|
||||
/*
|
||||
* LaunchBackgroundWorkers
|
||||
*
|
||||
* 功能:启动指定数量的后台工作者进程,用于并行处理任务
|
||||
*
|
||||
* 参数:
|
||||
* nworkers: 要启动的后台工作者数量
|
||||
* bgshared: 共享给后台工作者的数据结构
|
||||
* bgmain: 后台工作者的主入口函数
|
||||
* bgexit: 后台工作者的退出入口函数
|
||||
*
|
||||
* 返回值:
|
||||
* 返回实际成功启动的后台工作者数量
|
||||
*/
|
||||
int LaunchBackgroundWorkers(int nworkers, void *bgshared, bgworker_main bgmain, bgworker_exit bgexit)
|
||||
{
|
||||
int actualWorkers = 0;
|
||||
MemoryContext oldcontext;
|
||||
BgWorkerContext *bwc;
|
||||
|
||||
Assert(nworkers > 0); // 确保要启动的后台工作者数量大于 0
|
||||
Assert(nworkers > 0);
|
||||
/* We need to be a lock group leader. */
|
||||
BecomeLockGroupLeader(); // 成为锁组的领导者
|
||||
BecomeLockGroupLeader();
|
||||
|
||||
/* We might be running in a short-lived memory context. */
|
||||
oldcontext = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt); // 保存旧的内存上下文
|
||||
oldcontext = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt);
|
||||
|
||||
/*
|
||||
* Start workers.
|
||||
|
|
@ -705,34 +533,30 @@ int LaunchBackgroundWorkers(int nworkers, void *bgshared, bgworker_main bgmain,
|
|||
* fails. It wouldn't help much anyway, because registering the worker in
|
||||
* no way guarantees that it will start up and initialize successfully.
|
||||
*/
|
||||
// 开始创建工作者
|
||||
// 分配一个后台工作者上下文内存,并将其初始化为零
|
||||
bwc = (BgWorkerContext*)MemoryContextAllocZero(
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), sizeof(BgWorkerContext));
|
||||
bwc->transactionCxt.txnId = GetCurrentTransactionIdIfAny(); // 设置事务上下文的事务ID
|
||||
bwc->transactionCxt.snapshot = GetActiveSnapshot(); // 设置事务上下文的快照
|
||||
bwc->bgshared = bgshared; // 将共享的数据结构指针存储在上下文中
|
||||
bwc->databaseName = get_database_name(u_sess->proc_cxt.MyDatabaseId); // 获取当前数据库的名称并存储在上下文中
|
||||
bwc->userName = u_sess->proc_cxt.MyProcPort->user_name; // 获取当前会话用户的用户名并存储在上下文中
|
||||
bwc->transactionCxt.txnId = GetCurrentTransactionIdIfAny();
|
||||
bwc->transactionCxt.snapshot = GetActiveSnapshot();
|
||||
bwc->bgshared = bgshared;
|
||||
bwc->databaseName = get_database_name(u_sess->proc_cxt.MyDatabaseId);
|
||||
bwc->userName = u_sess->proc_cxt.MyProcPort->user_name;
|
||||
/* pass enable_cluster_resize to bgwokers to optimize parallel index building performance during redistribution */
|
||||
bwc->enable_cluster_resize = u_sess->attr.attr_sql.enable_cluster_resize; // 将集群调整标志传递给后台工作者以优化并行索引构建性能
|
||||
bwc->leader = t_thrd.proc; // 设置后台工作者的领导者为当前线程
|
||||
bwc->main_entry = bgmain; // 设置后台工作者的主要入口点
|
||||
bwc->exit_entry = bgexit; // 设置后台工作者的退出入口点
|
||||
bwc->enable_cluster_resize = u_sess->attr.attr_sql.enable_cluster_resize;
|
||||
bwc->leader = t_thrd.proc;
|
||||
bwc->main_entry = bgmain;
|
||||
bwc->exit_entry = bgexit;
|
||||
|
||||
t_thrd.bgworker_cxt.bgwcontext = bwc; // 将刚刚初始化的后台工作者上下文设置到全局上下文中
|
||||
t_thrd.bgworker_cxt.bgwcontext = bwc;
|
||||
|
||||
StreamSaveTxnContext(&bwc->transactionCxt); // 将事务上下文保存到流复制上下文中,以便在后续流复制进程中使用
|
||||
StreamSaveTxnContext(&bwc->transactionCxt);
|
||||
|
||||
for (int i = 0; i < nworkers; ++i) { // 遍历注册指定数量的后台工作者
|
||||
// 调用 RegisterBackgroundWorker 函数注册后台工作者
|
||||
// 如果注册成功,则增加 actualWorkers 计数
|
||||
for (int i = 0; i < nworkers; ++i) {
|
||||
if (RegisterBackgroundWorker(bwc)) {
|
||||
actualWorkers++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore previous memory context. */
|
||||
MemoryContextSwitchTo(oldcontext); // 恢复之前的内存上下文
|
||||
return actualWorkers; // 返回实际注册的后台工作者数量
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
return actualWorkers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,50 +88,33 @@ const int MAX_THREAD_NAME_LEN = 128;
|
|||
static void drop_rel_all_forks_buffers();
|
||||
static void drop_rel_one_fork_buffers();
|
||||
|
||||
/*
|
||||
* 功能:用于为 bgwriter 进程设置信号处理函数或信号处理方式,以适应其需求。
|
||||
* 它忽略了某些信号,设置了一些信号的处理方式为默认值,并指定了一些信号的处理函数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void setup_bgwriter_signalhook(void)
|
||||
{
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGHUP, bgwriter_sighup_handler); // 当收到SIGHUP信号时,设置标志以重新读取配置文件
|
||||
(void)gspqsignal(SIGINT, SIG_IGN); // 忽略SIGINT信号(中断信号)
|
||||
(void)gspqsignal(SIGTERM, bgwriter_request_shutdown_handler); // 当收到SIGTERM信号时,请求关闭进程
|
||||
(void)gspqsignal(SIGQUIT, bgwriter_quickdie); // 当收到SIGQUIT信号时,执行快速崩溃
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略SIGALRM信号(定时器信号)
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // SIGPIPE信号的默认行为是终止进程,将SIGPIPE信号的处理方式设置为忽略,不会导致进程终止,而是允许程序继续执行
|
||||
(void)gspqsignal(SIGUSR1, bgwriter_sigusr1_handler); // 当收到SIGUSR1信号时,调用相应的处理函数
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); // 忽略SIGUSR2信号
|
||||
(void)gspqsignal(SIGHUP, bgwriter_sighup_handler); /* set flag to read config file */
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
(void)gspqsignal(SIGTERM, bgwriter_request_shutdown_handler); /* shutdown */
|
||||
(void)gspqsignal(SIGQUIT, bgwriter_quickdie); /* hard crash time */
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, bgwriter_sigusr1_handler);
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN);
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 设置SIGCHLD信号的默认处理方式
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL);
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从信号屏蔽集中移除 SIGQUIT 信号
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:用于在后台写入进程遇到异常情况时执行一系列清理操作,以尽量保证进程能够继续正常运行。
|
||||
*
|
||||
* 参数:
|
||||
* - wb_context: 写回上下文,用于控制后台写入进程的行为。
|
||||
* - bgwriter_cxt: 后台写入进程的内存上下文。
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
static void bgwriter_handle_exceptions(WritebackContext wb_context, MemoryContext bgwriter_cxt)
|
||||
{
|
||||
/*
|
||||
|
|
@ -204,102 +187,90 @@ static void bgwriter_handle_exceptions(WritebackContext wb_context, MemoryContex
|
|||
* This is invoked from AuxiliaryProcessMain, which has already created the
|
||||
* basic execution environment, but not enabled signals yet.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 功能:后台写入进程 (bgwriter) 的主要入口点。
|
||||
* 其从 AuxiliaryProcessMain 被调用的,AuxiliaryProcessMain 已经创建了基本的执行环境,
|
||||
* 但还没有启用信号。
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
void BackgroundWriterMain(void)
|
||||
{
|
||||
sigjmp_buf local_sigjmp_buf; // 声明用于保存跳转位置的缓冲区
|
||||
MemoryContext bgwriter_context; // 声明后台写入进程的内存上下文
|
||||
bool prev_hibernate = false; // 声明前一个休眠状态,用于控制休眠
|
||||
WritebackContext wb_context; // 写回上下文,用于控制后台写入进程的行为
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
MemoryContext bgwriter_context;
|
||||
bool prev_hibernate = false;
|
||||
WritebackContext wb_context;
|
||||
|
||||
t_thrd.role = BGWRITER; // 设置当前线程角色为后台写入进程
|
||||
t_thrd.role = BGWRITER;
|
||||
|
||||
ereport(LOG, (errmsg("bgwriter started"))); // 记录日志,标记 bgwriter 开始运行
|
||||
ereport(LOG, (errmsg("bgwriter started")));
|
||||
|
||||
setup_bgwriter_signalhook(); // 设置信号处理函数
|
||||
setup_bgwriter_signalhook();
|
||||
|
||||
/*
|
||||
* We just started, assume there has been either a shutdown or
|
||||
* end-of-recovery snapshot.
|
||||
*/
|
||||
last_snapshot_ts = GetCurrentTimestamp(); // 获取当前时间戳作为最后快照时间戳
|
||||
last_snapshot_ts = GetCurrentTimestamp();
|
||||
|
||||
/*
|
||||
* Create a resource owner to keep track of our resources (currently only
|
||||
* buffer pins).
|
||||
*/
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Background Writer",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE)); // 创建资源所有者并设置名称
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
/*
|
||||
* Create a memory context that we will do all our work in. We do this so
|
||||
* that we can reset the context during error recovery and thereby avoid
|
||||
* possible memory leaks. Formerly this code just ran in
|
||||
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
|
||||
*/
|
||||
// 创建内存上下文,在其中执行所有工作。便于在错误恢复期间能够重置上下文,避免可能的内存泄漏
|
||||
bgwriter_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"Background Writer",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE); // 创建后台写入进程的内存上下文
|
||||
MemoryContextSwitchTo(bgwriter_context); // 切换到新创建的内存上下文
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
MemoryContextSwitchTo(bgwriter_context);
|
||||
|
||||
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after); // 初始化写回上下文
|
||||
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after);
|
||||
|
||||
/*
|
||||
* If an exception is encountered, processing resumes here.
|
||||
*
|
||||
* See notes in postgres.c about the design of this coding.
|
||||
*/
|
||||
// 遇到异常,从此开始执行
|
||||
int curTryCounter; // 用于保存当前的错误尝试计数器
|
||||
int* oldTryCounter = NULL; // 用于保存旧的错误尝试计数器指针
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) { // 设置跳转点以处理异常
|
||||
gstrace_tryblock_exit(true, oldTryCounter); // 退出错误尝试块
|
||||
bgwriter_handle_exceptions(wb_context, bgwriter_context); // 处理异常
|
||||
int curTryCounter;
|
||||
int* oldTryCounter = NULL;
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
bgwriter_handle_exceptions(wb_context, bgwriter_context);
|
||||
|
||||
/* Report wait end here, when there is no further possibility of wait */
|
||||
pgstat_report_waitevent(WAIT_EVENT_END); // 报告等待事件结束
|
||||
pgstat_report_waitevent(WAIT_EVENT_END);
|
||||
}
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 进入错误尝试块,并获取旧的错误尝试计数器
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
|
||||
/* We can now handle ereport(ERROR) */
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; // 设置异常堆栈
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
|
||||
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us)
|
||||
*/
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除信号掩码
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除 SIGUSR2 信号的阻塞
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/*
|
||||
* Use the recovery target timeline ID during recovery
|
||||
*/
|
||||
if (RecoveryInProgress()) // 如果正在进行恢复操作
|
||||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI(); // 获取恢复目标时间线 ID
|
||||
if (RecoveryInProgress())
|
||||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
|
||||
|
||||
/*
|
||||
* Reset hibernation state after any error.
|
||||
*/
|
||||
prev_hibernate = false; // 设置初始化休眠状态
|
||||
prev_hibernate = false;
|
||||
|
||||
pgstat_report_appname("Background writer"); // 报告应用程序名称到统计信息
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲
|
||||
pgstat_report_appname("Background writer");
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
|
||||
/*
|
||||
* Loop forever
|
||||
*/
|
||||
for (;;) {
|
||||
bool can_hibernate = false; // 是否可以休眠的标志
|
||||
bool can_hibernate = false;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
|
|
@ -308,25 +279,25 @@ void BackgroundWriterMain(void)
|
|||
*/
|
||||
if (pg_atomic_read_u32(&g_instance.dw_batch_cxt.dw_version) < DW_SUPPORT_REABLE_DOUBLE_WRITE
|
||||
&& t_thrd.proc->workingVersionNum >= DW_SUPPORT_REABLE_DOUBLE_WRITE) {
|
||||
dw_upgrade_renable_double_write(); // 执行双写升级操作
|
||||
dw_upgrade_renable_double_write();
|
||||
}
|
||||
|
||||
/* Clear any already-pending wakeups */
|
||||
ResetLatch(&t_thrd.proc->procLatch); // 重置进程的 latch
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
pgstat_report_activity(STATE_RUNNING, NULL); // 报告活动状态为运行中
|
||||
pgstat_report_activity(STATE_RUNNING, NULL);
|
||||
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) { // 如果收到了 SIGHUP 信号
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false; // 清除 SIGHUP 信号标志
|
||||
ProcessConfigFile(PGC_SIGHUP); // 处理配置文件变更
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
}
|
||||
|
||||
if (t_thrd.bgwriter_cxt.shutdown_requested) { // 如果请求了关闭
|
||||
/*
|
||||
if (t_thrd.bgwriter_cxt.shutdown_requested) {
|
||||
/*
|
||||
* From here on, elog(ERROR) should end with exit(1), not send
|
||||
* control back to the sigsetjmp block above
|
||||
*/
|
||||
u_sess->attr.attr_common.ExitOnAnyError = true; // 设置在任何错误时退出
|
||||
u_sess->attr.attr_common.ExitOnAnyError = true;
|
||||
/* Normal exit from the bgwriter is here */
|
||||
proc_exit(0); /* done */
|
||||
}
|
||||
|
|
@ -339,24 +310,16 @@ void BackgroundWriterMain(void)
|
|||
/*
|
||||
* Send off activity statistics to the stats collector
|
||||
*/
|
||||
pgstat_send_bgwriter(); // 发送后台写入进程的统计信息
|
||||
pgstat_send_bgwriter();
|
||||
|
||||
if (FirstCallSinceLastCheckpoint()) { // 如果自上次检查点以来是首次调用
|
||||
if (FirstCallSinceLastCheckpoint()) {
|
||||
/*
|
||||
* After any checkpoint, close all smgr files. This is so we
|
||||
* won't hang onto smgr references to deleted files indefinitely.
|
||||
*/
|
||||
smgrcloseall(); // 关闭所有 smgr 文件
|
||||
smgrcloseall();
|
||||
}
|
||||
// 描述在后台写入进程(bgwriter)中定期记录 xl_running_xacts 的目的和原因
|
||||
/*
|
||||
*1.维护复制一致性: 通过记录 xl_running_xacts,帮助数据库复制进程更快地达到一致状态。这对于确保主数据库和备份数据库的数据一致性非常重要,尤其是在处理子溢出的快照时。
|
||||
*2.资源清理: 定期记录有助于更频繁地清理资源,如锁和 KnownXids* 结构。这有助于减少资源占用,提高数据库性能和稳定性。
|
||||
*3.低成本: 记录 xl_running_xacts 的成本相对较低,因此可以定期执行而不会对系统性能产生显著影响。
|
||||
*4.适当的执行频率: 记录操作每分钟执行 4 次,这个频率足以维护复制一致性,同时也不会对系统的超时处理造成不必要的复杂性。
|
||||
*5.选择后台写入进程执行: 由于后台写入进程是唯一定期执行并且会一直返回到其主循环的进程,因此在此处执行记录操作是合适的。
|
||||
其他进程,如检查点进程,通常不会频繁执行其主循环,不适合用于定期记录 xl_running_xacts。
|
||||
|
||||
* Log a new xl_running_xacts every now and then so replication can get
|
||||
* into a consistent state faster (think of suboverflowed snapshots)
|
||||
* and clean up resources (locks, KnownXids*) more frequently. The
|
||||
|
|
@ -377,21 +340,21 @@ void BackgroundWriterMain(void)
|
|||
* time. E.g. Checkpointer, when active, is barely ever in its
|
||||
* mainloop and thus makes it hard to log regularly.
|
||||
*/
|
||||
if (XLogStandbyInfoActive() && !RecoveryInProgress()) { // 如果正在运行热备,且不在恢复过程中
|
||||
if (XLogStandbyInfoActive() && !RecoveryInProgress()) {
|
||||
TimestampTz timeout = 0;
|
||||
TimestampTz now = GetCurrentTimestamp(); // 获取当前时间戳
|
||||
timeout = TimestampTzPlusMilliseconds(last_snapshot_ts, LOG_SNAPSHOT_INTERVAL_MS); // 计算下次记录时间
|
||||
TimestampTz now = GetCurrentTimestamp();
|
||||
timeout = TimestampTzPlusMilliseconds(last_snapshot_ts, LOG_SNAPSHOT_INTERVAL_MS);
|
||||
|
||||
/*
|
||||
* only log if enough time has passed and some xlog record has been
|
||||
* inserted.
|
||||
*/
|
||||
if (now >= timeout && !XLByteEQ(last_snapshot_lsn, GetXLogInsertRecPtr())) { // 检查是否应记录
|
||||
last_snapshot_lsn = LogStandbySnapshot(); // 记录热备快照
|
||||
last_snapshot_ts = now; // 更新最后快照时间
|
||||
if (now >= timeout && !XLByteEQ(last_snapshot_lsn, GetXLogInsertRecPtr())) {
|
||||
last_snapshot_lsn = LogStandbySnapshot();
|
||||
last_snapshot_ts = now;
|
||||
}
|
||||
if (now >= timeout) {
|
||||
LogCheckSlot(); // 记录检查槽信息
|
||||
LogCheckSlot();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -405,16 +368,12 @@ void BackgroundWriterMain(void)
|
|||
* down with latch events that are likely to happen frequently during
|
||||
* normal operation.
|
||||
*/
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch,
|
||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||
u_sess->attr.attr_storage.BgWriterDelay /* ms */); // 等待信号或超时
|
||||
u_sess->attr.attr_storage.BgWriterDelay /* ms */);
|
||||
|
||||
/*
|
||||
*1.休眠模式延长:当没有标志事件触发并且 BgBufferSync 表示系统当前没有活动时,会进入 "休眠" 模式,
|
||||
休眠时间比 bgwriter_delay 规定的时间更长,以节省能源。
|
||||
*2.潜在的竞争条件:存在潜在的竞争条件,即后台进程可能在 BgBufferSync 观察到无缓冲区分配计数后分配缓冲区。
|
||||
为减少错过唤醒的可能性,只有在连续两个周期没有活动时才会进入休眠,并且不会永久休眠。
|
||||
* If no latch event and BgBufferSync says nothing's happening, extend
|
||||
* the sleep in "hibernation" mode, where we sleep for much longer
|
||||
* than bgwriter_delay says. Fewer wakeups save electricity. When a
|
||||
|
|
@ -447,10 +406,10 @@ void BackgroundWriterMain(void)
|
|||
* Emergency bailout if postmaster has died. This is to avoid the
|
||||
* necessity for manual cleanup of all postmaster children.
|
||||
*/
|
||||
if (rc & WL_POSTMASTER_DEATH) // 如果 postmaster 已经死亡
|
||||
gs_thread_exit(1); // 紧急退出
|
||||
if (rc & WL_POSTMASTER_DEATH)
|
||||
gs_thread_exit(1);
|
||||
|
||||
prev_hibernate = can_hibernate; // 更新前一个休眠状态
|
||||
prev_hibernate = can_hibernate;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -464,23 +423,9 @@ void BackgroundWriterMain(void)
|
|||
* Some backend has bought the farm,
|
||||
* so we need to stop what we're doing and exit.
|
||||
*/
|
||||
|
||||
/*
|
||||
*功能:
|
||||
* 在收到 postmaster 发送的 SIGQUIT 信号时触发,用于处理信号。
|
||||
* 意味着某个后端进程发生了严重错误,需要立即停止当前操作并退出。函数会关闭所有可能的资源并退出进程。
|
||||
* 这里不执行 proc_exit() 回调函数,因为共享内存可能已经损坏,所以不尝试清理事务。
|
||||
* 为了确保 postmaster 进入系统复位周期,使用 exit(2) 以代码2退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理器的参数宏,用于接收信号信息。
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void bgwriter_quickdie(SIGNAL_ARGS)
|
||||
{
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除信号屏蔽
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
|
||||
/*
|
||||
* We DO NOT want to run proc_exit() callbacks -- we're here because
|
||||
|
|
@ -490,7 +435,7 @@ static void bgwriter_quickdie(SIGNAL_ARGS)
|
|||
* things by calling exit() directly, we have to reset the callbacks
|
||||
* explicitly to make this work as intended.
|
||||
*/
|
||||
on_exit_reset(); // 重置 exit() 回调函数
|
||||
on_exit_reset();
|
||||
|
||||
/*
|
||||
* Note we do exit(2) not exit(0). This is to force the postmaster into a
|
||||
|
|
@ -500,139 +445,88 @@ static void bgwriter_quickdie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2); // 使用 exit(2) 退出进程,强制 postmaster 进入系统复位周期
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
// 功能:当收到 SIGHUP 信号时,设置标志以在下一个合适的时机重新读取配置文件
|
||||
static void bgwriter_sighup_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的 errno 值
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = true; // 设置收到 SIGHUP 信号的标志
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = true;
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置进程的标志事件,以便重新读取配置文件
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = save_errno; // 恢复之前保存的 errno 值
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* SIGTERM: set flag to shutdown and exit */
|
||||
// 功能:当收到 SIGTERM 信号时,设置标志以请求关闭并退出
|
||||
static void bgwriter_request_shutdown_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的 errno 值
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.bgwriter_cxt.shutdown_requested = true; // 设置请求关闭的标志
|
||||
t_thrd.bgwriter_cxt.shutdown_requested = true;
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);// 设置进程的标志事件,以便请求关闭并退出
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = save_errno; // 恢复之前保存的 errno 值
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* SIGUSR1: used for latch wakeups */
|
||||
// 功能:用于标志事件唤醒
|
||||
static void bgwriter_sigusr1_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的 errno 值
|
||||
int save_errno = errno;
|
||||
|
||||
latch_sigusr1_handler(); // 处理 SIGUSR1 信号,通常用于唤醒标志事件
|
||||
latch_sigusr1_handler();
|
||||
|
||||
errno = save_errno; // 恢复之前保存的 errno 值
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
// 检查当前进程是否为后台写入进程
|
||||
// 参数: 无
|
||||
// 返回值:bool类型 [如果当前进程的角色是后台写入进程,则返回 true]
|
||||
bool IsBgwriterProcess(void)
|
||||
{
|
||||
return (t_thrd.role == BGWRITER); // 如果当前进程的角色是后台写入进程,则返回 true
|
||||
return (t_thrd.role == BGWRITER);
|
||||
}
|
||||
|
||||
/* bgwriter view function */
|
||||
/*
|
||||
* 功能:获取当前实例的节点名称。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:
|
||||
* 如果节点名称未定义,则返回 "not define" 的文本数据。
|
||||
* 否则返回节点名称的文本数据。
|
||||
*/
|
||||
Datum bgwriter_view_get_node_name()
|
||||
{
|
||||
if (g_instance.attr.attr_common.PGXCNodeName == NULL || g_instance.attr.attr_common.PGXCNodeName[0] == '\0') {
|
||||
return CStringGetTextDatum("not define");// 如果节点名称未定义,则返回 "not define" 的文本数据
|
||||
return CStringGetTextDatum("not define");
|
||||
} else {
|
||||
return CStringGetTextDatum(g_instance.attr.attr_common.PGXCNodeName); // 否则返回节点名称的文本数据
|
||||
return CStringGetTextDatum(g_instance.attr.attr_common.PGXCNodeName);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取后台写入进程执行的实际刷新次数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回整数 0,表示实际刷新次数。
|
||||
*/
|
||||
Datum bgwriter_view_get_actual_flush_num()
|
||||
{
|
||||
return Int64GetDatum(0); // 返回整数 0,表示实际刷新次数
|
||||
return Int64GetDatum(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取上一次后台写入进程执行的刷新次数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回整数 0,表示上次刷新次数。
|
||||
*/
|
||||
|
||||
Datum bgwriter_view_get_last_flush_num()
|
||||
{
|
||||
return Int32GetDatum(0); // 返回整数 0,表示上次刷新次数
|
||||
return Int32GetDatum(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取用于刷新的候选缓冲区总数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回候选缓冲区数量的整数。
|
||||
*/
|
||||
Datum bgwriter_view_get_candidate_nums()
|
||||
{
|
||||
int candidate_num = get_curr_candidate_nums(true) + get_curr_candidate_nums(false);
|
||||
return Int32GetDatum(candidate_num); // 返回候选缓冲区数量
|
||||
return Int32GetDatum(candidate_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取用于刷新的缓冲区候选列表的数量。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回缓冲区候选列表数量的整数。
|
||||
*/
|
||||
Datum bgwriter_view_get_num_candidate_list()
|
||||
{
|
||||
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_candidate_list); // 返回缓冲区候选列表数量
|
||||
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_candidate_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取后台写入进程执行的缓冲区时钟扫描迭代次数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回缓冲区时钟扫描次数的整数。
|
||||
*/
|
||||
Datum bgwriter_view_get_num_clock_sweep()
|
||||
{
|
||||
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_clock_sweep); // 返回缓冲区时钟扫描次数
|
||||
return Int64GetDatum(g_instance.ckpt_cxt_ctl->get_buf_num_clock_sweep);
|
||||
}
|
||||
|
||||
// 定义了后台写入进程视图的列信息
|
||||
const incre_ckpt_view_col g_bgwriter_view_col[INCRE_CKPT_BGWRITER_VIEW_COL_NUM] = {
|
||||
// 列名 数据类型 获取数据需要调用的函数
|
||||
{"node_name", TEXTOID, bgwriter_view_get_node_name},
|
||||
{"bgwr_actual_flush_total_num", INT8OID, bgwriter_view_get_actual_flush_num},
|
||||
{"bgwr_last_flush_num", INT4OID, bgwriter_view_get_last_flush_num},
|
||||
|
|
@ -641,24 +535,17 @@ const incre_ckpt_view_col g_bgwriter_view_col[INCRE_CKPT_BGWRITER_VIEW_COL_NUM]
|
|||
{"get_buf_clock_sweep", INT8OID, bgwriter_view_get_num_clock_sweep}};
|
||||
|
||||
|
||||
const uint THREAD_SLEEP_TIME = 10 * 60 * 1000; // 后台写入进程睡眠时间(以毫秒为单位)
|
||||
/*
|
||||
* 功能:后台无效缓冲区写入进程的主要执行函数,负责管理无效缓冲区的清理工作。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
const uint THREAD_SLEEP_TIME = 10 * 60 * 1000;
|
||||
void invalid_buffer_bgwriter_main()
|
||||
{
|
||||
sigjmp_buf localSigjmpBuf; // 用于处理异常的跳转标记
|
||||
MemoryContext bgwriter_context; // 内存上下文,用于执行工作并处理错误恢复
|
||||
char name[MAX_THREAD_NAME_LEN] = {0}; // 线程名称
|
||||
WritebackContext wb_context; // 写入上下文,用于配置写入行为
|
||||
t_thrd.role = SPBGWRITER; // 设置线程角色为 SPBGWRITER
|
||||
sigjmp_buf localSigjmpBuf;
|
||||
MemoryContext bgwriter_context;
|
||||
char name[MAX_THREAD_NAME_LEN] = {0};
|
||||
WritebackContext wb_context;
|
||||
t_thrd.role = SPBGWRITER;
|
||||
|
||||
setup_bgwriter_signalhook(); // 设置信号处理函数
|
||||
ereport(LOG, (errmsg("invalidate buffer bgwriter started"))); // 记录日志,表示后台无效缓冲区写入进程已启动
|
||||
setup_bgwriter_signalhook();
|
||||
ereport(LOG, (errmsg("invalidate buffer bgwriter started")));
|
||||
|
||||
errno_t err_rc = snprintf_s(name, MAX_THREAD_NAME_LEN, MAX_THREAD_NAME_LEN - 1, "%s", "spbgwriter");
|
||||
securec_check_ss(err_rc, "", "");
|
||||
|
|
@ -677,10 +564,10 @@ void invalid_buffer_bgwriter_main()
|
|||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
MemoryContextSwitchTo(bgwriter_context);
|
||||
|
||||
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after); // 初始化写入上下文
|
||||
WritebackContextInit(&wb_context, &u_sess->attr.attr_storage.bgwriter_flush_after);
|
||||
|
||||
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
|
||||
ereport(WARNING, (errmsg("invalidate buffer bgwriter exception occured."))); // 处理异常情况
|
||||
ereport(WARNING, (errmsg("invalidate buffer bgwriter exception occured.")));
|
||||
bgwriter_handle_exceptions(wb_context, bgwriter_context);
|
||||
}
|
||||
|
||||
|
|
@ -688,7 +575,7 @@ void invalid_buffer_bgwriter_main()
|
|||
t_thrd.log_cxt.PG_exception_stack = &localSigjmpBuf;
|
||||
|
||||
/* Unblock signals (they were blocked when the postmaster forked us) */
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/* Use the recovery target timeline ID during recovery */
|
||||
|
|
@ -696,230 +583,187 @@ void invalid_buffer_bgwriter_main()
|
|||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
|
||||
}
|
||||
|
||||
pgstat_report_appname("InvalidBufferBgWriter"); // 设置应用程序名称以进行统计
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告进程状态为空闲
|
||||
g_instance.bgwriter_cxt.invalid_buf_proc_latch = &t_thrd.proc->procLatch; // 设置 Latch 用于等待事件触发
|
||||
pgstat_report_appname("InvalidBufferBgWriter");
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
g_instance.bgwriter_cxt.invalid_buf_proc_latch = &t_thrd.proc->procLatch;
|
||||
/* Loop forever */
|
||||
for (;;) {
|
||||
int rc;
|
||||
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP); // 处理 SIGHUP 信号,重新加载配置文件
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
}
|
||||
|
||||
if (t_thrd.bgwriter_cxt.shutdown_requested) {
|
||||
ereport(LOG, (errmsg("invalidate buffer bgwriter thread shut down"))); // 记录日志,表示线程即将关闭
|
||||
u_sess->attr.attr_common.ExitOnAnyError = true;
|
||||
proc_exit(0); // 退出线程
|
||||
ereport(LOG, (errmsg("invalidate buffer bgwriter thread shut down")));
|
||||
u_sess->attr.attr_common.ExitOnAnyError = true;
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, THREAD_SLEEP_TIME); // 等待事件触发
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, THREAD_SLEEP_TIME);
|
||||
if (rc & WL_POSTMASTER_DEATH) {
|
||||
gs_thread_exit(1); // 如果 Postmaster 已经关闭,线程退出
|
||||
gs_thread_exit(1);
|
||||
}
|
||||
|
||||
/* Clear any already-pending wakeups */
|
||||
ResetLatch(&t_thrd.proc->procLatch); // 清除已经触发的事件
|
||||
drop_rel_all_forks_buffers(); // 执行释放所有关系的缓冲区的操作
|
||||
drop_rel_one_fork_buffers(); // 执行释放一个关系的缓冲区的操作
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
drop_rel_all_forks_buffers();
|
||||
drop_rel_one_fork_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
const int HASH_TABLE_ELEMENT_MIN_NUM = 512;
|
||||
|
||||
/*
|
||||
* 功能:创建关系文件节点哈希表,用于存储关于删除的关系文件的信息
|
||||
*
|
||||
* 参数:
|
||||
* name - 哈希表的名称
|
||||
* use_heap_mem - 是否使用堆内存
|
||||
*
|
||||
* 返回值:
|
||||
* 返回创建的哈希表
|
||||
*/
|
||||
HTAB *relfilenode_hashtbl_create(const char *name, bool use_heap_mem)
|
||||
{
|
||||
HASHCTL hashCtrl; // 哈希表控制信息结构体
|
||||
HTAB *hashtbl = NULL; // 哈希表指针,初始化为空
|
||||
errno_t rc; // 用于错误处理的返回码
|
||||
HASHCTL hashCtrl;
|
||||
HTAB *hashtbl = NULL;
|
||||
errno_t rc;
|
||||
|
||||
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl)); // 初始化 hashCtrl 结构体为 0
|
||||
securec_check(rc, "", ""); // 检查 memset_s 的返回值
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext; // 设置哈希表的内存上下文
|
||||
hashCtrl.hash = tag_hash; // 设置哈希函数
|
||||
hashCtrl.keysize = sizeof(RelFileNode); // 设置键的大小
|
||||
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
|
||||
securec_check(rc, "", "");
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
|
||||
hashCtrl.hash = tag_hash;
|
||||
hashCtrl.keysize = sizeof(RelFileNode);
|
||||
/* keep entrysize >= keysize, stupid limits */
|
||||
hashCtrl.entrysize = sizeof(DelFileTag); // 设置哈希表中每个条目的大小
|
||||
hashCtrl.entrysize = sizeof(DelFileTag);
|
||||
|
||||
if (use_heap_mem) { // 根据 use_heap_mem 参数判断是否使用堆内存
|
||||
if (use_heap_mem) {
|
||||
hashtbl = HeapMemInitHash(name, HASH_TABLE_ELEMENT_MIN_NUM,
|
||||
Max(g_instance.attr.attr_common.max_files_per_process, t_thrd.storage_cxt.max_userdatafiles), &hashCtrl,
|
||||
(HASH_FUNCTION | HASH_ELEM)); // 在堆内存上创建哈希表
|
||||
if (hashtbl == NULL) {
|
||||
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table"))); // 如果创建失败,报致命错误
|
||||
}
|
||||
} else {
|
||||
// 在当前内存上下文中创建哈希表
|
||||
hashtbl = hash_create(name, HASH_TABLE_ELEMENT_MIN_NUM, &hashCtrl, (HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM));
|
||||
}
|
||||
return hashtbl; // 返回创建的哈希表指针
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建关系文件节点分支哈希表,用于存储关于删除的关系文件分支的信息
|
||||
*
|
||||
* 参数:
|
||||
* name - 哈希表的名称
|
||||
* use_heap_mem - 是否使用堆内存
|
||||
*
|
||||
* 返回值:
|
||||
* 返回创建的哈希表
|
||||
*/
|
||||
HTAB *relfilenode_fork_hashtbl_create(const char* name, bool use_heap_mem)
|
||||
{
|
||||
HASHCTL hashCtrl; // 哈希表控制信息结构体
|
||||
HTAB *hashtbl = NULL; // 哈希表指针,初始化为空
|
||||
errno_t rc; // 用于错误处理的返回码
|
||||
|
||||
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl)); // 初始化 hashCtrl 结构体为 0
|
||||
securec_check(rc, "", ""); // 检查 memset_s 的返回值
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext; // 设置哈希表的内存上下文
|
||||
hashCtrl.hash = tag_hash; // 设置哈希函数
|
||||
hashCtrl.keysize = sizeof(ForkRelFileNode); // 设置键的大小
|
||||
/* keep entrysize >= keysize, stupid limits */
|
||||
hashCtrl.entrysize = sizeof(DelForkFileTag); // 设置哈希表中每个条目的大小
|
||||
|
||||
if (use_heap_mem) { // 根据 use_heap_mem 参数判断是否使用堆内存
|
||||
hashtbl = HeapMemInitHash(name, HASH_TABLE_ELEMENT_MIN_NUM,
|
||||
Max(g_instance.attr.attr_common.max_files_per_process, t_thrd.storage_cxt.max_userdatafiles),
|
||||
&hashCtrl, (HASH_FUNCTION | HASH_ELEM)); // 在堆内存上创建哈希表
|
||||
(HASH_FUNCTION | HASH_ELEM));
|
||||
if (hashtbl == NULL) {
|
||||
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table")));
|
||||
}
|
||||
} else {
|
||||
hashtbl = hash_create(name, HASH_TABLE_ELEMENT_MIN_NUM, &hashCtrl, (HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM));
|
||||
}
|
||||
return hashtbl; // 返回创建的哈希表指针
|
||||
return hashtbl;
|
||||
}
|
||||
|
||||
HTAB *relfilenode_fork_hashtbl_create(const char* name, bool use_heap_mem)
|
||||
{
|
||||
HASHCTL hashCtrl;
|
||||
HTAB *hashtbl = NULL;
|
||||
errno_t rc;
|
||||
|
||||
rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
|
||||
securec_check(rc, "", "");
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
|
||||
hashCtrl.hash = tag_hash;
|
||||
hashCtrl.keysize = sizeof(ForkRelFileNode);
|
||||
/* keep entrysize >= keysize, stupid limits */
|
||||
hashCtrl.entrysize = sizeof(DelForkFileTag);
|
||||
|
||||
if (use_heap_mem) {
|
||||
hashtbl = HeapMemInitHash(name, HASH_TABLE_ELEMENT_MIN_NUM,
|
||||
Max(g_instance.attr.attr_common.max_files_per_process, t_thrd.storage_cxt.max_userdatafiles),
|
||||
&hashCtrl, (HASH_FUNCTION | HASH_ELEM));
|
||||
if (hashtbl == NULL) {
|
||||
ereport(FATAL, (errmsg("could not initialize unlinik relation hash table")));
|
||||
}
|
||||
} else {
|
||||
hashtbl = hash_create(name, HASH_TABLE_ELEMENT_MIN_NUM, &hashCtrl, (HASH_CONTEXT | HASH_FUNCTION | HASH_ELEM));
|
||||
}
|
||||
return hashtbl;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:
|
||||
* 丢弃所有表的所有分支的缓冲区,并注册相关的忘记请求
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void drop_rel_all_forks_buffers()
|
||||
{
|
||||
HASH_SEQ_STATUS status; // 哈希表遍历状态
|
||||
DelFileTag *entry = NULL; // 哈希表中的条目指针
|
||||
DelFileTag *temp_entry = NULL; // 临时条目指针
|
||||
bool found = false; // 是否找到标志
|
||||
uint rel_num = 0; // 关系数量
|
||||
HTAB *unlink_rel_hashtbl = g_instance.bgwriter_cxt.unlink_rel_hashtbl; // 获取哈希表指针
|
||||
HTAB *rel_bak = relfilenode_hashtbl_create("unlink_rel_bak", false); // 创建临时哈希表
|
||||
HASH_SEQ_STATUS status;
|
||||
DelFileTag *entry = NULL;
|
||||
DelFileTag *temp_entry = NULL;
|
||||
bool found = false;
|
||||
uint rel_num = 0;
|
||||
HTAB *unlink_rel_hashtbl = g_instance.bgwriter_cxt.unlink_rel_hashtbl;
|
||||
HTAB *rel_bak = relfilenode_hashtbl_create("unlink_rel_bak", false);
|
||||
|
||||
/* Obtains the entry in hashtable. */
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_SHARED); // 获取共享锁
|
||||
hash_seq_init(&status, unlink_rel_hashtbl); // 初始化哈希表遍历状态
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_SHARED);
|
||||
hash_seq_init(&status, unlink_rel_hashtbl);
|
||||
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) {
|
||||
entry = (DelFileTag*)hash_search(rel_bak, (void *)&temp_entry->rnode, HASH_ENTER, &found); // 将哈希表中的数据复制到临时哈希表
|
||||
entry = (DelFileTag*)hash_search(rel_bak, (void *)&temp_entry->rnode, HASH_ENTER, &found);
|
||||
if (!found) {
|
||||
entry->rnode = temp_entry->rnode;
|
||||
entry->maxSegNo = temp_entry->maxSegNo;
|
||||
rel_num++;
|
||||
}
|
||||
}
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock); // 释放共享锁
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
|
||||
|
||||
if (rel_num > 0) {
|
||||
DropRelFileNodeAllBuffersUsingHash(rel_bak); // 释放哈希表中所有关系的缓冲区
|
||||
DropRelFileNodeAllBuffersUsingHash(rel_bak);
|
||||
|
||||
hash_seq_init(&status, rel_bak); // 初始化哈希表遍历状态
|
||||
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) { // 循环遍历哈希表中的每个条目
|
||||
if (temp_entry->maxSegNo == -1) { // 如果maxSegNo为-1,表示不处理该关系
|
||||
hash_seq_init(&status, rel_bak);
|
||||
while ((temp_entry = (DelFileTag *)hash_seq_search(&status)) != NULL) {
|
||||
if (temp_entry->maxSegNo == -1) {
|
||||
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
|
||||
errmsg("the max segno is -1, skip forget this rel %u/%u/%u, bucketNode is %d",
|
||||
temp_entry->rnode.spcNode, temp_entry->rnode.dbNode, temp_entry->rnode.relNode,
|
||||
temp_entry->rnode.bucketNode)));
|
||||
continue; // 跳过不处理的关系
|
||||
continue;
|
||||
}
|
||||
for (int32 i = 0; i < temp_entry->maxSegNo; i++) { // 循环处理关系的每个段
|
||||
for (int fork_num = 0; fork_num <= (int)MAX_FORKNUM; fork_num++) { // 循环处理关系的每个分支
|
||||
md_register_forget_request(temp_entry->rnode, fork_num, i); // 注册忘记请求
|
||||
for (int32 i = 0; i < temp_entry->maxSegNo; i++) {
|
||||
for (int fork_num = 0; fork_num <= (int)MAX_FORKNUM; fork_num++) {
|
||||
md_register_forget_request(temp_entry->rnode, fork_num, i);
|
||||
}
|
||||
}
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_EXCLUSIVE); // 获取锁以操作关系哈希表
|
||||
// 如果在哈希表中没有找到关系,报告数据损坏错误
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_hashtbl_lock, LW_EXCLUSIVE);
|
||||
if (hash_search(unlink_rel_hashtbl, (void *)&temp_entry->rnode, HASH_REMOVE, NULL) == NULL) {
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
|
||||
hash_destroy(rel_bak);
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("unlink rel hash table corrupted")));
|
||||
} else {
|
||||
// 关系处理完成,记录日志
|
||||
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
|
||||
errmsg("invalidate buffer has been finished for rel %u/%u/%u, bucketNode is %d",
|
||||
temp_entry->rnode.spcNode, temp_entry->rnode.dbNode, temp_entry->rnode.relNode,
|
||||
temp_entry->rnode.bucketNode)));
|
||||
}
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock); // 释放关系哈希表锁
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_hashtbl_lock);
|
||||
}
|
||||
}
|
||||
|
||||
hash_destroy(rel_bak); // 销毁临时哈希表
|
||||
hash_destroy(rel_bak);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:
|
||||
* 释放与一个特定关系的单个分叉文件相关的所有缓冲区,并将其从哈希表中移除
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void drop_rel_one_fork_buffers()
|
||||
{
|
||||
HASH_SEQ_STATUS status; // 哈希表遍历状态
|
||||
DelForkFileTag *entry = NULL; // 哈希表中的条目指针
|
||||
DelForkFileTag *temp_entry = NULL; // 临时条目指针
|
||||
bool found = false; // 是否找到标志
|
||||
uint rel_num = 0; // 关系数量
|
||||
HTAB *unlink_rel_fork_hashtbl = g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl; // 获取哈希表指针
|
||||
HTAB *rel_bak = relfilenode_fork_hashtbl_create("unlink_rel_one_fork_bak", false); // 创建临时哈希表
|
||||
HASH_SEQ_STATUS status;
|
||||
DelForkFileTag *entry = NULL;
|
||||
DelForkFileTag *temp_entry = NULL;
|
||||
bool found = false;
|
||||
uint rel_num = 0;
|
||||
HTAB *unlink_rel_fork_hashtbl = g_instance.bgwriter_cxt.unlink_rel_fork_hashtbl;
|
||||
HTAB *rel_bak = relfilenode_fork_hashtbl_create("unlink_rel_one_fork_bak", false);
|
||||
/* Obtains the entry in hashtable. */
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_SHARED); // 获取哈希表锁(共享模式)
|
||||
hash_seq_init(&status, unlink_rel_fork_hashtbl); // 初始化哈希表遍历状态
|
||||
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) { // 循环遍历哈希表中的每个条目
|
||||
entry = (DelForkFileTag*)hash_search(rel_bak, temp_entry, HASH_ENTER, &found); // 在临时哈希表中查找或插入条目
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_SHARED);
|
||||
hash_seq_init(&status, unlink_rel_fork_hashtbl);
|
||||
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) {
|
||||
entry = (DelForkFileTag*)hash_search(rel_bak, temp_entry, HASH_ENTER, &found);
|
||||
if (!found) {
|
||||
// 如果没有找到,初始化条目数据
|
||||
entry->forkrnode.rnode.spcNode = temp_entry->forkrnode.rnode.spcNode;
|
||||
entry->forkrnode.rnode.dbNode = temp_entry->forkrnode.rnode.dbNode;
|
||||
entry->forkrnode.rnode.relNode = temp_entry->forkrnode.rnode.relNode;
|
||||
entry->forkrnode.rnode.bucketNode = temp_entry->forkrnode.rnode.bucketNode;
|
||||
entry->forkrnode.forkNum = temp_entry->forkrnode.forkNum;
|
||||
entry->maxSegNo = temp_entry->maxSegNo;
|
||||
rel_num++; // 增加关系数量
|
||||
rel_num++;
|
||||
}
|
||||
}
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock); // 释放哈希表锁
|
||||
LWLockRelease(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock);
|
||||
|
||||
if (rel_num > 0) { // 如果有要处理的关系
|
||||
DropRelFileNodeOneForkAllBuffersUsingHash(rel_bak); // 释放临时哈希表中所有关系的缓冲区
|
||||
hash_seq_init(&status, rel_bak); // 重新初始化哈希表遍历状态
|
||||
if (rel_num > 0) {
|
||||
DropRelFileNodeOneForkAllBuffersUsingHash(rel_bak);
|
||||
hash_seq_init(&status, rel_bak);
|
||||
while ((temp_entry = (DelForkFileTag *)hash_seq_search(&status)) != NULL) {
|
||||
// 再次遍历临时哈希表中的每个条目
|
||||
if (temp_entry->maxSegNo == -1) {
|
||||
// 如果maxSegNo为-1,表示不处理该关系
|
||||
ereport(DEBUG1, (errmodule(MOD_INCRE_BG),
|
||||
errmsg("the max segno is -1, skip forget this rel %u/%u/%u, bucketNode is %d",
|
||||
temp_entry->forkrnode.rnode.spcNode, temp_entry->forkrnode.rnode.dbNode,
|
||||
temp_entry->forkrnode.rnode.relNode, temp_entry->forkrnode.rnode.bucketNode)));
|
||||
continue; // 跳过不处理的关系
|
||||
continue;
|
||||
}
|
||||
for (int32 i = 0; i < temp_entry->maxSegNo; i++) { // 循环处理关系的每个段
|
||||
for (int32 i = 0; i < temp_entry->maxSegNo; i++) {
|
||||
md_register_forget_request(temp_entry->forkrnode.rnode, temp_entry->forkrnode.forkNum, i);
|
||||
}
|
||||
LWLockAcquire(g_instance.bgwriter_cxt.rel_one_fork_hashtbl_lock, LW_EXCLUSIVE);
|
||||
|
|
|
|||
|
|
@ -56,23 +56,12 @@ static void CBMwriter_sigusr1_handler(SIGNAL_ARGS);
|
|||
* This is invoked from AuxiliaryProcessMain, which has already created the
|
||||
* basic execution environment, but not enabled signals yet.
|
||||
*/
|
||||
/*
|
||||
* 功能:
|
||||
* cbmwriter 进程的主要入口点。负责管理提交位图(CBM)的持久化写入,以及处理配置文件、信号和异常。
|
||||
*
|
||||
* 参数:
|
||||
* 无
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
void CBMWriterMain(void)
|
||||
{
|
||||
sigjmp_buf local_sigjmp_buf; // 用于错误处理的跳转点
|
||||
ResourceOwner cbmwriter_resourceOwner; // 用于跟踪 CBM Writer 进程的资源
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
ResourceOwner cbmwriter_resourceOwner;
|
||||
|
||||
ereport(LOG, (errmsg("cbm writer started")));
|
||||
// 根据配置决定检查点超时时间
|
||||
u_sess->attr.attr_storage.CheckPointTimeout = ENABLE_INCRE_CKPT
|
||||
? u_sess->attr.attr_storage.incrCheckPointTimeout
|
||||
: u_sess->attr.attr_storage.fullCheckPointTimeout;
|
||||
|
|
@ -86,33 +75,31 @@ void CBMWriterMain(void)
|
|||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGHUP, CBMSigHupHandler); /* set flag to read config file */ // 收到 SIGHUP 信号时重新读取配置文件
|
||||
(void)gspqsignal(SIGINT, CBMShutdownHandler); /* request shutdown */ // 收到 SIGINT 信号时请求正常关闭
|
||||
(void)gspqsignal(SIGTERM, CBMShutdownHandler); /* request shutdown */ // 收到 SIGTERM 信号时请求正常关闭
|
||||
(void)gspqsignal(SIGQUIT, CBM_quickdie); /* hard crash time */ // 收到 SIGQUIT 信号时执行硬崩溃操作
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 忽略 SIGALRM 信号
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略 SIGPIPE 信号
|
||||
(void)gspqsignal(SIGUSR1, CBMwriter_sigusr1_handler); // 执行自定义操作以响应 SIGUSR1 信号
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */ // 忽略 SIGUSR2 信号
|
||||
(void)gspqsignal(SIGHUP, CBMSigHupHandler); /* set flag to read config file */
|
||||
(void)gspqsignal(SIGINT, CBMShutdownHandler); /* request shutdown */
|
||||
(void)gspqsignal(SIGTERM, CBMShutdownHandler); /* request shutdown */
|
||||
(void)gspqsignal(SIGQUIT, CBM_quickdie); /* hard crash time */
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, CBMwriter_sigusr1_handler);
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
// 恢复各信号处理程序为系统默认行为
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // SIGCHLD 信号在子进程终止或停止时发出
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL); // SIGTTIN 信号在后台进程尝试从终端读取时发出
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL); // SIGTTOU 信号在后台进程尝试向终端写入时发出
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL); // SIGCONT 信号用于继续已停止的进程
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL); // SIGWINCH 信号在终端窗口大小变化时发出
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
(void)gspqsignal(SIGCONT, SIG_DFL);
|
||||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许 SIGQUIT信号在任何时候都可以触发
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
|
||||
/*
|
||||
* Create a resource owner to keep track of our resources (not clear that
|
||||
* we need this, but may as well have one).
|
||||
*/
|
||||
// 创建 ResourceOwner 用于跟踪管理资源
|
||||
cbmwriter_resourceOwner = ResourceOwnerCreate(NULL, "CBM Writer",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = cbmwriter_resourceOwner;
|
||||
|
|
@ -122,7 +109,6 @@ void CBMWriterMain(void)
|
|||
* that we can reset the context during error recovery and thereby avoid
|
||||
* possible memory leaks.
|
||||
*/
|
||||
// 创建内存上下文,以便在错误恢复期间重置上下文,避免内存泄漏
|
||||
t_thrd.cbm_cxt.cbmwriter_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"CBM Writer",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -138,26 +124,24 @@ void CBMWriterMain(void)
|
|||
/*
|
||||
* If an exception is encountered, processing resumes here.
|
||||
*/
|
||||
// 发生异常跳转到此处
|
||||
int curTryCounter;
|
||||
int* oldTryCounter = NULL;
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
|
||||
/* Since not using PG_TRY, must reset error stack by hand */
|
||||
// 手动重置错误栈
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
t_thrd.log_cxt.call_stack = NULL;
|
||||
|
||||
/* Prevent interrupts while cleaning up */
|
||||
HOLD_INTERRUPTS(); // 防止在清理期间发生中断
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/* Report the error to the server log */
|
||||
EmitErrorReport(); // 报告错误到服务器日志
|
||||
EmitErrorReport();
|
||||
|
||||
/* release resource held by lsc */
|
||||
AtEOXact_SysDBCache(false); // 释放由 lsc 持有的资源
|
||||
AtEOXact_SysDBCache(false);
|
||||
/*
|
||||
* These operations are really just a minimal subset of
|
||||
* AbortTransaction(). We don't have very many resources to worry
|
||||
|
|
@ -216,7 +200,7 @@ void CBMWriterMain(void)
|
|||
int rc;
|
||||
|
||||
/* Clear any already-pending wakeups */
|
||||
ResetLatch(&t_thrd.proc->procLatch); // 清除挂起的唤醒请求
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
pgstat_report_activity(STATE_RUNNING, NULL);
|
||||
|
||||
|
|
@ -225,12 +209,12 @@ void CBMWriterMain(void)
|
|||
*/
|
||||
if (t_thrd.cbm_cxt.got_SIGHUP) {
|
||||
t_thrd.cbm_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP); // 配置文件
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
u_sess->attr.attr_storage.CheckPointTimeout = ENABLE_INCRE_CKPT
|
||||
? u_sess->attr.attr_storage.incrCheckPointTimeout
|
||||
: u_sess->attr.attr_storage.fullCheckPointTimeout;
|
||||
}
|
||||
// 如果收到关闭请求,则退出循环
|
||||
|
||||
if (t_thrd.cbm_cxt.shutdown_requested) {
|
||||
g_instance.proc_base->cbmwriterLatch = NULL;
|
||||
/* Normal exit from the walwriter is here */
|
||||
|
|
@ -239,8 +223,7 @@ void CBMWriterMain(void)
|
|||
|
||||
CBMFollowXlog();
|
||||
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态
|
||||
// 等待事件的发生,包括信号量设置、超时、postmaster 死亡
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch,
|
||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||
(long)u_sess->attr.attr_storage.CheckPointTimeout * 1000);
|
||||
|
|
@ -248,7 +231,6 @@ void CBMWriterMain(void)
|
|||
/* Emergency bailout if postmaster has died. This is to avoid the
|
||||
* necessity for manual cleanup of all postmaster children.
|
||||
*/
|
||||
// postmaster 退出时应急退出以避免手动清理所有 postmaster 子进程
|
||||
if (rc & WL_POSTMASTER_DEATH) {
|
||||
g_instance.proc_base->cbmwriterLatch = NULL;
|
||||
gs_thread_exit(1);
|
||||
|
|
@ -266,21 +248,11 @@ void CBMWriterMain(void)
|
|||
* Some backend has bought the farm,
|
||||
* so we need to stop what we're doing and exit.
|
||||
*/
|
||||
/*
|
||||
* 功能:处理CBM Writer进程的紧急退出。
|
||||
*
|
||||
* 参数: SIGNAL_ARGS 信号处理函数的参数
|
||||
* 返回值: 无
|
||||
*/
|
||||
static void CBM_quickdie(SIGNAL_ARGS)
|
||||
{
|
||||
g_instance.proc_base->cbmwriterLatch = NULL; // 设置 cbmwriterLatch 为 NULL,防止后续的唤醒
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除信号的阻塞状态
|
||||
g_instance.proc_base->cbmwriterLatch = NULL;
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
|
||||
/*
|
||||
当共享内存可能已经受到损坏或出现其他严重问题时,cbmwriter进程会立即终止,而不会执行正常的清理操作或调用`proc_exit()`注册的回调函数。
|
||||
确保迅速终止进程,以防止进一步的数据损坏或其他潜在的风险。
|
||||
*/
|
||||
/*
|
||||
* We DO NOT want to run proc_exit() callbacks -- we're here because
|
||||
* shared memory may be corrupted, so we don't want to try to clean up our
|
||||
|
|
@ -291,10 +263,6 @@ static void CBM_quickdie(SIGNAL_ARGS)
|
|||
*/
|
||||
on_exit_reset();
|
||||
|
||||
/*
|
||||
以exit(2)退出,强制 postmaster 进入系统复位循环
|
||||
确保如果有人不小心终止了cbmwriter进程,系统会认为发生了严重问题,采取额外措施确保系统稳定性。
|
||||
*/
|
||||
/*
|
||||
* Note we do exit(2) not exit(0). This is to force the postmaster into a
|
||||
* system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
|
||||
|
|
@ -307,64 +275,37 @@ static void CBM_quickdie(SIGNAL_ARGS)
|
|||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
/*
|
||||
* 功能:SIGHUP 信号处理程序,在下一个方便的时间重新读取配置文件
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void CBMSigHupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.cbm_cxt.got_SIGHUP = true; // 设置标志以指示需要在下一个方便的时间重新读取配置文件
|
||||
t_thrd.cbm_cxt.got_SIGHUP = true;
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* SIGTERM: set flag to exit normally */
|
||||
/*
|
||||
* 功能:SIGTERM 信号处理程序,请求正常退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void CBMShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.cbm_cxt.shutdown_requested = true; // 设置标志以指示需要正常退出
|
||||
t_thrd.cbm_cxt.shutdown_requested = true;
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/* SIGUSR1: used for latch wakeups */
|
||||
/*
|
||||
* 功能:SIGUSR1 信号处理程序,用于处理Latch唤醒
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void CBMwriter_sigusr1_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
int save_errno = errno;
|
||||
|
||||
latch_sigusr1_handler(); // 处理SIGUSR1信号,唤醒进程
|
||||
latch_sigusr1_handler();
|
||||
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
errno = save_errno;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -43,7 +43,6 @@ pid_t fork_process(void)
|
|||
* Presently stdout and stderr are the only stdio output channels used by
|
||||
* the postmaster, so fflush'ing them should be sufficient.
|
||||
*/
|
||||
// 刷新标准输出和标准错误流,避免输出问题
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
|
|
@ -58,16 +57,15 @@ pid_t fork_process(void)
|
|||
getitimer(ITIMER_PROF, &prof_itimer);
|
||||
#endif
|
||||
|
||||
result = fork(); // 创建子进程
|
||||
result = fork();
|
||||
|
||||
if (result == 0) { // fork成功
|
||||
if (result == 0) {
|
||||
/* fork succeeded, in child */
|
||||
#ifdef LINUX_PROFILE
|
||||
setitimer(ITIMER_PROF, &prof_itimer, NULL); // 设置性能分析计时器,以保证子进程也能进行性能分析
|
||||
setitimer(ITIMER_PROF, &prof_itimer, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 强调了在Linux系统中保护postmaster进程免受OOM杀死的问题,并提供了一些解决方法和建议
|
||||
* By default, Linux tends to kill the postmaster in out-of-memory
|
||||
* situations, because it blames the postmaster for the sum of child
|
||||
* process sizes *including shared memory*. (This is unbelievably
|
||||
|
|
@ -86,19 +84,19 @@ pid_t fork_process(void)
|
|||
* Use open() not stdio, to ensure we control the open flags. Some
|
||||
* Linux security environments reject anything but O_WRONLY.
|
||||
*/
|
||||
int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0); // 打开OOM分数调整文件
|
||||
int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0);
|
||||
|
||||
/* We ignore all errors */
|
||||
if (fd >= 0) {
|
||||
char buf[16]; // 用于存储要写入文件的字符串
|
||||
int rc; // 用于存储write()函数的返回值
|
||||
char buf[16];
|
||||
int rc;
|
||||
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ); // 格式化OOM分数调整值为字符串
|
||||
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ);
|
||||
securec_check_intval(rcs, );
|
||||
|
||||
rc = write(fd, buf, strlen(buf)); // 将OOM分数调整值写入文件
|
||||
(void)rc; // 防止编译器警告
|
||||
close(fd); // 关闭文件
|
||||
rc = write(fd, buf, strlen(buf));
|
||||
(void)rc;
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#endif /* LINUX_OOM_SCORE_ADJ */
|
||||
|
|
@ -115,25 +113,25 @@ pid_t fork_process(void)
|
|||
* Use open() not stdio, to ensure we control the open flags. Some
|
||||
* Linux security environments reject anything but O_WRONLY.
|
||||
*/
|
||||
int fd = open("/proc/self/oom_adj", O_WRONLY, 0); // 打开OOM调整文件
|
||||
int fd = open("/proc/self/oom_adj", O_WRONLY, 0);
|
||||
|
||||
/* We ignore all errors */
|
||||
if (fd >= 0) {
|
||||
char buf[16];// 用于存储要写入文件的字符串
|
||||
int rc; // 用于存储write()函数的返回值
|
||||
char buf[16];
|
||||
int rc;
|
||||
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_ADJ);
|
||||
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
|
||||
securec_check_intval(rcs, );
|
||||
|
||||
rc = write(fd, buf, strlen(buf)); // 将OOM调整值写入文件
|
||||
(void)rc; // 防止编译器警告
|
||||
close(fd); // 关闭文件
|
||||
rc = write(fd, buf, strlen(buf));
|
||||
(void)rc;
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
#endif /* LINUX_OOM_ADJ */
|
||||
|
||||
/* Binding static TLS variables for current thread */
|
||||
EarlyBindingTLSVariables(); // 绑定当前线程的静态TLS变量
|
||||
EarlyBindingTLSVariables();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -60,94 +60,52 @@ static void GlobalstatsSigusr2Handler(SIGNAL_ARGS);
|
|||
static void GlobalstatsSigtermHandler(SIGNAL_ARGS);
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
/*
|
||||
* 功能:SIGHUP 信号处理程序,在下一个方便的时间重新读取配置文件
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void GlobalstatsSighupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int saveErrno = errno; // 保存当前的错误码
|
||||
int saveErrno = errno;
|
||||
|
||||
t_thrd.gstat_cxt.got_SIGHUP = true; // 设置标志以指示需要在下一个方便的时间重新读取配置文件
|
||||
t_thrd.gstat_cxt.got_SIGHUP = true;
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);// 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = saveErrno;// 恢复之前保存的错误码
|
||||
errno = saveErrno;
|
||||
}
|
||||
|
||||
/* SIGUSR2: a worker is up and running, or just finished, or failed to fork */
|
||||
/*
|
||||
* 功能:SIGUSR2 信号处理程序,表示一个工作进程已启动、运行完成或无法 fork。
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void GlobalstatsSigusr2Handler(SIGNAL_ARGS)
|
||||
{
|
||||
int saveErrno = errno; // 保存当前的错误码
|
||||
int saveErrno = errno;
|
||||
|
||||
t_thrd.gstat_cxt.got_SIGUSR2 = true; // 设置标志以指示接收到SIGUSR2信号
|
||||
t_thrd.gstat_cxt.got_SIGUSR2 = true;
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = saveErrno; // 恢复之前保存的错误码
|
||||
errno = saveErrno;
|
||||
}
|
||||
|
||||
/* SIGTERM: time to die */
|
||||
/*
|
||||
* 功能:SIGTERM 信号处理程序,请求正常退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void GlobalstatsSigtermHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int saveErrno = errno;// 保存当前的错误码
|
||||
int saveErrno = errno;
|
||||
|
||||
t_thrd.gstat_cxt.got_SIGTERM = true; // 设置标志以指示需要正常退出
|
||||
t_thrd.gstat_cxt.got_SIGTERM = true;
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
errno = saveErrno;// 恢复之前保存的错误码
|
||||
errno = saveErrno;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:准备切换全局统计信息哈希表的状态
|
||||
*
|
||||
* 在切换全局统计信息哈希表之前,需要确保没有其他线程正在读取哈希表。
|
||||
* 该函数会先设置一个 quiesce 标志,然后等待所有读取哈希表的线程完成。
|
||||
* 只有当 quiesce 标志为 0 时,才能安全地进行切换。
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
static void PrepareStatsHashForSwitch()
|
||||
{
|
||||
// 断言 quiesce 标志为 0,确保没有其他线程正在切换
|
||||
Assert(pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 0);
|
||||
// 将 quiesce 标志设置为 1,表示正在切换
|
||||
pg_atomic_exchange_u64(&g_instance.stat_cxt.tableStat->quiesce, 1);
|
||||
// 获取当前读取哈希表的线程数
|
||||
|
||||
uint64 readers = pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->readers);
|
||||
// 断言读取线程数小于总线程数
|
||||
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
|
||||
|
||||
/* Wait until all readers are done */
|
||||
while (readers > 0) { // 等待所有读取线程完成
|
||||
pg_usleep(10000L); // 等待一段时间,以免过于频繁地检查
|
||||
// 重新获取读取线程数
|
||||
while (readers > 0) {
|
||||
pg_usleep(10000L);
|
||||
readers = pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->readers);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,31 +118,16 @@ static void CompleteStatsHashSwitch()
|
|||
pg_atomic_exchange_u64(&g_instance.stat_cxt.tableStat->quiesce, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:匹配全局统计信息哈希表的键
|
||||
*
|
||||
* 用于比较两个哈希表键是否匹配。在全局统计信息哈希表中,键的类型为 PgStat_StartBlockTableKey。
|
||||
* 如果两个键的 relid、parentid 和 dbid 都相等,那么认为它们匹配。
|
||||
*
|
||||
* 参数:
|
||||
* - left: 左边的键
|
||||
* - right: 右边的键
|
||||
* - keysize: 键的大小
|
||||
*
|
||||
* 返回值: 0 表示匹配,非 0 表示不匹配
|
||||
*/
|
||||
static int MatchDictItem(const void* left, const void* right, Size keysize)
|
||||
{
|
||||
// 将左右两个键转换为 PgStat_StartBlockTableKey 类型
|
||||
const PgStat_StartBlockTableKey* leftItem = (PgStat_StartBlockTableKey*)left;
|
||||
const PgStat_StartBlockTableKey* rightItem = (PgStat_StartBlockTableKey*)right;
|
||||
Assert(leftItem != NULL && rightItem != NULL); // 断言左右键都不为空
|
||||
Assert(leftItem != NULL && rightItem != NULL);
|
||||
|
||||
/* we just care whether the result is 0 or not. */
|
||||
// 如果 relid、parentid 和 dbid 都相等,则返回 0,表示匹配
|
||||
if (leftItem->relid != rightItem->relid || leftItem->parentid != rightItem->parentid ||
|
||||
leftItem->dbid != rightItem->dbid) {
|
||||
return 1; // 不匹配,返回非 0 值
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -195,39 +138,26 @@ static uint32 HashDictItem(const void* key, Size keysize)
|
|||
return DatumGetUInt32(hash_any((const unsigned char*)key, sizeof(PgStat_StartBlockTableKey)));
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化全局统计信息追踪器
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
void GlobalStatsTrackerInit()
|
||||
{
|
||||
/* Setup a shared memory context that other backends can access.
|
||||
* This will hold the Global Statistics Hash
|
||||
*/
|
||||
// 设置一个其他后端进程可以访问的共享内存上下文
|
||||
g_instance.stat_cxt.tableStat->global_stats_cxt =
|
||||
AllocSetContextCreate((MemoryContext)g_instance.instance_context, "GlobalStatisticsContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, SHARED_CONTEXT);
|
||||
// 初始化哈希表控制结构
|
||||
|
||||
HASHCTL hashCtrl;
|
||||
errno_t rc = memset_s(&hashCtrl, sizeof(hashCtrl), 0, sizeof(hashCtrl));
|
||||
securec_check(rc, "", "");
|
||||
// 设置哈希表的哈希函数和比较函数
|
||||
hashCtrl.hash = (HashValueFunc)HashDictItem;
|
||||
hashCtrl.match = (HashCompareFunc)MatchDictItem;
|
||||
// 设置哈希表键的大小和条目的大小
|
||||
hashCtrl.keysize = (Size)sizeof(PgStat_StartBlockTableKey);
|
||||
hashCtrl.entrysize = (Size)sizeof(PgStat_StartBlockTableEntry);
|
||||
// 设置哈希表的上下文为全局统计信息上下文
|
||||
hashCtrl.hcxt = g_instance.stat_cxt.tableStat->global_stats_cxt;
|
||||
// 设置哈希表的分区数
|
||||
hashCtrl.num_partitions = NUM_STARTBLOCK_PARTITIONS;
|
||||
|
||||
int flags = (HASH_FUNCTION | HASH_COMPARE | HASH_ELEM | HASH_SHRCTX | HASH_PARTITION);
|
||||
// 创建全局统计信息哈希表
|
||||
g_instance.stat_cxt.tableStat->blocks_map =
|
||||
hash_create("Candidate Blocks for Pruning Hash", NUM_STARTBLOCK_PARTITIONS, &hashCtrl, flags);
|
||||
}
|
||||
|
|
@ -237,17 +167,9 @@ bool IsGlobalStatsTrackerProcess()
|
|||
return t_thrd.role == GLOBALSTATS_THREAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:全局统计信息追踪器的主要入口点,进行一系列的初始化操作,
|
||||
* 设置信号处理函数,处理异常情况,以及周期性地收集全局统计信息。
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
NON_EXEC_STATIC void GlobalStatsTrackerMain()
|
||||
{
|
||||
sigjmp_buf localSigjmpBuf; // 用于保存异常跳转的上下文信息
|
||||
sigjmp_buf localSigjmpBuf;
|
||||
|
||||
/* we are a postmaster subprocess now */
|
||||
IsUnderPostmaster = true;
|
||||
|
|
@ -259,31 +181,29 @@ NON_EXEC_STATIC void GlobalStatsTrackerMain()
|
|||
|
||||
t_thrd.proc_cxt.MyProgName = "StatsTracker";
|
||||
|
||||
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid); // 断言当前进程的 PID 与 t_thrd 中记录的一致
|
||||
init_ps_display("global stats process", "", "", ""); // 初始化进程状态的显示
|
||||
// 输出日志,表示全局统计信息收集器已启动
|
||||
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid);
|
||||
init_ps_display("global stats process", "", "", "");
|
||||
ereport(LOG, (errmsg("global stats collector started")));
|
||||
|
||||
SetProcessingMode(InitProcessing); // 设置当前进程的处理模式为初始化模式,用于执行一些初始化操作
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
/*
|
||||
* Set up signal handlers. We operate on databases much like a regular
|
||||
* backend, so we use the same signal handling. See equivalent code in
|
||||
* tcop/postgres.c.
|
||||
*/
|
||||
// 设置信号处理程序
|
||||
gspqsignal(SIGHUP, GlobalstatsSighupHandler); // 用于重新读取配置文件
|
||||
gspqsignal(SIGINT, StatementCancelHandler); // 用于取消正在执行的语句
|
||||
gspqsignal(SIGTERM, GlobalstatsSigtermHandler); // 用于终止进程
|
||||
gspqsignal(SIGHUP, GlobalstatsSighupHandler);
|
||||
gspqsignal(SIGINT, StatementCancelHandler);
|
||||
gspqsignal(SIGTERM, GlobalstatsSigtermHandler);
|
||||
|
||||
gspqsignal(SIGQUIT, quickdie); // 用于快速终止进程
|
||||
gspqsignal(SIGALRM, handle_sig_alarm); // 用于处理定时器信号
|
||||
gspqsignal(SIGQUIT, quickdie);
|
||||
gspqsignal(SIGALRM, handle_sig_alarm);
|
||||
|
||||
gspqsignal(SIGPIPE, SIG_IGN); // 用于避免进程在写入已关闭的管道时终止
|
||||
gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 用于用户自定义的信号处理
|
||||
gspqsignal(SIGUSR2, GlobalstatsSigusr2Handler); // 用于指示一个工作进程正在运行、刚刚完成或者在 fork 失败
|
||||
gspqsignal(SIGFPE, FloatExceptionHandler); // 用于处理浮点异常
|
||||
gspqsignal(SIGCHLD, SIG_DFL); // 用于处理子进程的状态变化
|
||||
gspqsignal(SIGPIPE, SIG_IGN);
|
||||
gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
||||
gspqsignal(SIGUSR2, GlobalstatsSigusr2Handler);
|
||||
gspqsignal(SIGFPE, FloatExceptionHandler);
|
||||
gspqsignal(SIGCHLD, SIG_DFL);
|
||||
|
||||
/* Early initialization */
|
||||
BaseInit();
|
||||
|
|
@ -295,75 +215,74 @@ NON_EXEC_STATIC void GlobalStatsTrackerMain()
|
|||
* had to do some stuff with LWLocks).
|
||||
*/
|
||||
#ifndef EXEC_BACKEND
|
||||
InitProcess(); // 初始化进程上下文
|
||||
InitProcess();
|
||||
#endif
|
||||
|
||||
SetProcessingMode(NormalProcessing); // 设置当前进程的处理模式为正常处理模式,用于正常的数据库操作
|
||||
SetProcessingMode(NormalProcessing);
|
||||
|
||||
/* Unblock signals (they were blocked when the postmaster forked us) */
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 取消了屏蔽以允许信号被处理
|
||||
(void)gs_signal_unblock_sigusr2(); // 取消对 SIGUSR2 信号[用于后台进程的状态更新]的屏蔽
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/*
|
||||
* If an exception is encountered, processing resumes here.
|
||||
*
|
||||
* This code is a stripped down version of PostgresMain error recovery.
|
||||
*/
|
||||
// 错误恢复处理
|
||||
if (sigsetjmp(localSigjmpBuf, 1) != 0) { // 如果在执行过程中出现错误,将会跳转到此处
|
||||
if (sigsetjmp(localSigjmpBuf, 1) != 0) {
|
||||
/* since not using PG_TRY, must reset error stack by hand */
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
t_thrd.log_cxt.call_stack = NULL;
|
||||
|
||||
/* Prevents interrupts while cleaning up */
|
||||
HOLD_INTERRUPTS(); // 禁止中断,以确保在清理操作期间不会被中断
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/* Report the error to the server log */
|
||||
EmitErrorReport(); // 将错误信息记录到服务器日志中
|
||||
EmitErrorReport();
|
||||
|
||||
/* release resource held by lsc */
|
||||
AtEOXact_SysDBCache(false); // 释放系统数据库缓存,以确保清理操作
|
||||
AtEOXact_SysDBCache(false);
|
||||
|
||||
FlushErrorState(); // 刷新错误状态,以清除错误信息
|
||||
FlushErrorState();
|
||||
|
||||
/* Now we can allow interrupts again */
|
||||
RESUME_INTERRUPTS(); // 允许中断,以便继续正常处理
|
||||
RESUME_INTERRUPTS();
|
||||
|
||||
/* if in shutdown mode, no need for anything further; just go away */
|
||||
if (t_thrd.gstat_cxt.got_SIGTERM) // 如果收到了 SIGTERM 信号,就跳转到关闭操作
|
||||
if (t_thrd.gstat_cxt.got_SIGTERM)
|
||||
goto shutdown;
|
||||
|
||||
/*
|
||||
* Sleep at least 1 second after any error. We don't want to be
|
||||
* filling the error logs as fast as we can.
|
||||
*/
|
||||
pg_usleep(1000000L); // 如果出现错误,等待至少1秒后再继续,以避免日志填充过快
|
||||
pg_usleep(1000000L);
|
||||
}
|
||||
|
||||
while (!t_thrd.gstat_cxt.got_SIGTERM) { // 循环,直到收到 SIGTERM 信号
|
||||
while (!t_thrd.gstat_cxt.got_SIGTERM) {
|
||||
/* backup the old one so we can delete it when nobody needs it anymore */
|
||||
MemoryContext oldStatLocalContext = u_sess->stat_cxt.pgStatLocalContext; // 保存旧的统计信息内存上下文,以便稍后进行清理操作
|
||||
// 创建一个新的内存上下文用于存储全局统计信息的快照数据
|
||||
MemoryContext oldStatLocalContext = u_sess->stat_cxt.pgStatLocalContext;
|
||||
|
||||
u_sess->stat_cxt.pgStatLocalContext =
|
||||
AllocSetContextCreate(u_sess->top_mem_cxt, "Global Statistics snapshot",
|
||||
ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE);
|
||||
|
||||
pgstat_fetch_global(); // 从全局统计信息中抓取数据并存储在新的内存上下文中
|
||||
pgstat_fetch_global();
|
||||
|
||||
/* switch global_stats_map to point to the newly loaded statistics */
|
||||
PrepareStatsHashForSwitch(); // 执行准备切换操作,确保全局统计信息的安全切换
|
||||
g_instance.stat_cxt.tableStat->global_stats_map = u_sess->stat_cxt.pgStatDBHash; // 将全局统计信息的映射指针切换到新的数据
|
||||
CompleteStatsHashSwitch(); // 完成切换操作,确保新的数据结构已生效
|
||||
PrepareStatsHashForSwitch();
|
||||
g_instance.stat_cxt.tableStat->global_stats_map = u_sess->stat_cxt.pgStatDBHash;
|
||||
CompleteStatsHashSwitch();
|
||||
|
||||
/* Now destroy the old one */
|
||||
if (oldStatLocalContext) { // 检查旧的统计信息内存上下文是否存在
|
||||
MemoryContextDelete(oldStatLocalContext); // 存在则释放
|
||||
if (oldStatLocalContext) {
|
||||
MemoryContextDelete(oldStatLocalContext);
|
||||
}
|
||||
|
||||
u_sess->stat_cxt.pgStatDBHash = NULL; // 设为NULL,确保不再引用旧的数据
|
||||
u_sess->stat_cxt.pgStatDBHash = NULL;
|
||||
|
||||
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L); // 使进程休眠,等待下一次获取全局统计信息的时间
|
||||
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L);
|
||||
}
|
||||
|
||||
shutdown:
|
||||
|
|
@ -371,11 +290,11 @@ shutdown:
|
|||
* Before the thread exits, set global_stats_map to NULL to prevent core dump when the
|
||||
* backend thread accesses the released memory during the prune operation.
|
||||
*/
|
||||
PrepareStatsHashForSwitch(); // 准备切换操作,确保在关闭之前对全局统计信息进行最后的处理
|
||||
g_instance.stat_cxt.tableStat->global_stats_map = NULL; // 确保在关闭时不再访问释放的内存
|
||||
CompleteStatsHashSwitch(); // 完成切换操作,确保新的状态已生效
|
||||
ereport(LOG, (errmsg("global stats shutting down"))); // 生成日志,表示全局统计信息追踪器正在关闭
|
||||
proc_exit(0); // 正常退出当前进程
|
||||
PrepareStatsHashForSwitch();
|
||||
g_instance.stat_cxt.tableStat->global_stats_map = NULL;
|
||||
CompleteStatsHashSwitch();
|
||||
ereport(LOG, (errmsg("global stats shutting down")));
|
||||
proc_exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -384,41 +303,24 @@ shutdown:
|
|||
* relid is the partition id for a Partitioned table, otherwise it's the Relation id.
|
||||
* parentid is the actual Relation id for a Partitioned table, otherwise it's InvalidOid.
|
||||
*/
|
||||
/*
|
||||
*功能:获取表的全局统计信息
|
||||
*
|
||||
* 参数:
|
||||
* Oid dbid:数据库的唯一标识符
|
||||
* Oid relid:表的唯一标识符
|
||||
* Oid parentid:表的父表的唯一标识符
|
||||
* PgStat_StatTabEntry *tableentry:于存储获取到的统计信息
|
||||
*
|
||||
* 返回值:
|
||||
* bool类型 如果成功获取了表的全局统计信息,则返回 true;
|
||||
* 如果无法获取,则返回 false
|
||||
*
|
||||
*/
|
||||
bool GetTableGstats(Oid dbid, Oid relid, Oid parentid, PgStat_StatTabEntry *tableentry)
|
||||
{
|
||||
/* return if stats is not ready */
|
||||
// 检查全局统计信息的映射是否为 NULL,以及是否处于 "quiesce" 状态。如果是,说明统计信息尚未准备好,返回 false
|
||||
if (g_instance.stat_cxt.tableStat->global_stats_map == NULL ||
|
||||
pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 readers PG_USED_FOR_ASSERTS_ONLY = pg_atomic_add_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1); // 增加全局统计信息读取者的计数
|
||||
Assert(readers < (uint64) GLOBAL_ALL_PROCS); // 断言检查 readers 是否小于 GLOBAL_ALL_PROCS,用于限制并发访问全局统计信息的进程数量
|
||||
uint64 readers PG_USED_FOR_ASSERTS_ONLY = pg_atomic_add_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
|
||||
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
|
||||
|
||||
/* recheck in case quiesce is updated between first check and increment readers */
|
||||
// 再次检查 "quiesce" 状态,以确保在增加读取者计数期间未更改状态。如果状态已更改,则减少读取者计数并返回 false
|
||||
if (pg_atomic_read_u64(&g_instance.stat_cxt.tableStat->quiesce) == 1) {
|
||||
pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert(g_instance.stat_cxt.tableStat->global_stats_map != NULL); // 确保全局统计信息的映射不为 NULL
|
||||
// 定义指向数据库和表统计信息条目的指针
|
||||
Assert(g_instance.stat_cxt.tableStat->global_stats_map != NULL);
|
||||
PgStat_StatDBEntry *dbentry = NULL;
|
||||
PgStat_StatTabEntry *tabentry = NULL;
|
||||
errno_t rc = 0;
|
||||
|
|
@ -427,129 +329,87 @@ bool GetTableGstats(Oid dbid, Oid relid, Oid parentid, PgStat_StatTabEntry *tabl
|
|||
PgStat_StatTabKey tabkey;
|
||||
tabkey.statFlag = parentid;
|
||||
tabkey.tableid = relid;
|
||||
// 通过数据库标识从全局统计信息映射中查找数据库统计信息条目。如果没有找到,dbentry 将为 NULL
|
||||
|
||||
dbentry = (PgStat_StatDBEntry*)hash_search(g_instance.stat_cxt.tableStat->global_stats_map,
|
||||
(void*)&dbid, HASH_FIND, NULL);
|
||||
if (dbentry == NULL) {
|
||||
goto done;
|
||||
}
|
||||
// 通过构建的键从数据库统计信息中查找表的统计信息条目。如果没有找到,tabentry 将为 NULL
|
||||
|
||||
tabentry = (PgStat_StatTabEntry*)hash_search(dbentry->tables, (void*)(&tabkey), HASH_FIND, NULL);
|
||||
if (tabentry == NULL) {
|
||||
goto done;
|
||||
}
|
||||
// 如果找到了统计信息条目,函数将其复制到 tableentry 中,以便返回给调用者
|
||||
|
||||
rc = memcpy_s(tableentry, sizeof(PgStat_StatTabEntry),
|
||||
tabentry, sizeof(PgStat_StatTabEntry));
|
||||
securec_check(rc, "", "");
|
||||
result = true; // 表示成功获取了统计信息
|
||||
result = true;
|
||||
|
||||
done:
|
||||
readers = pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1); // 减少读取者计数
|
||||
readers = pg_atomic_sub_fetch_u64(&g_instance.stat_cxt.tableStat->readers, 1);
|
||||
Assert(readers < (uint64) GLOBAL_ALL_PROCS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*功能:获取用于访问全局统计信息中 "StartBlock" 哈希表特定分区的互斥锁
|
||||
*
|
||||
* 参数:
|
||||
* PgStat_StartBlockTableKey *tabkey:唯一标识要访问的哈希表分区
|
||||
* LWLockMode mode:用于指定锁的模式,可以是共享锁(SHARED)或独占锁(EXCLUSIVE)
|
||||
*
|
||||
* 返回值:
|
||||
* LWLock * 类型,,用于表示获取的锁
|
||||
*
|
||||
*/
|
||||
static LWLock *LockStartBlockHashTablePartition(PgStat_StartBlockTableKey *tabkey, LWLockMode mode)
|
||||
{
|
||||
uint32 hashValue = get_hash_value(g_instance.stat_cxt.tableStat->blocks_map, tabkey); // 计算哈希值
|
||||
uint32 partition = hashValue % (NUM_STARTBLOCK_PARTITIONS); // 计算分区号,用于确定哈希表中的哪个分区将用于锁定
|
||||
uint32 lockid = (uint32)(FirstStartBlockMappingLock + partition); // 计算锁标识,用于确定要锁定的锁
|
||||
LWLock* lock = &t_thrd.shemem_ptr_cxt.mainLWLockArray[lockid].lock; // 使用的是哈希分区的锁来控制对哈希表分区的访问
|
||||
uint32 hashValue = get_hash_value(g_instance.stat_cxt.tableStat->blocks_map, tabkey);
|
||||
uint32 partition = hashValue % (NUM_STARTBLOCK_PARTITIONS);
|
||||
uint32 lockid = (uint32)(FirstStartBlockMappingLock + partition);
|
||||
LWLock* lock = &t_thrd.shemem_ptr_cxt.mainLWLockArray[lockid].lock;
|
||||
|
||||
LWLockAcquire(lock, mode); // 获取锁
|
||||
LWLockAcquire(lock, mode);
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
/*
|
||||
*功能:用于在全局统计信息的 "StartBlock" 哈希表中查找给定的 PgStat_StartBlockTableKey,并返回相应的 PgStat_StartBlockTableEntry 条目
|
||||
*
|
||||
* 参数:
|
||||
* PgStat_StartBlockTableKey *tabkey:用于唯一标识要查找的哈希表条目
|
||||
*
|
||||
* 返回值:
|
||||
* PgStat_StartBlockTableEntry * 类型,表示查找到的哈希表条目。如果没有找到匹配的条目,返回值为 NULL
|
||||
*/
|
||||
PgStat_StartBlockTableEntry *
|
||||
StartBlockHashTableLookup(PgStat_StartBlockTableKey *tabkey)
|
||||
{
|
||||
PgStat_StartBlockTableEntry *result = NULL; // 用于存储查找结果
|
||||
bool found = false; // 用于表示是否找到了匹配的条目
|
||||
PgStat_StartBlockTableEntry *result = NULL;
|
||||
bool found = false;
|
||||
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_SHARED); // 获取共享锁
|
||||
// 查找匹配的条目,如果找到了,就将它的地址赋给 result ,并将 found 设置为 true;否则,result 保持为 NULL,found 保持为 false。
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_SHARED);
|
||||
result = (PgStat_StartBlockTableEntry *) hash_search(g_instance.stat_cxt.tableStat->blocks_map,
|
||||
tabkey, HASH_FIND, &found);
|
||||
LWLockRelease(lock); // 释放之前获取的共享锁
|
||||
LWLockRelease(lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*功能:用于向全局统计信息的 "StartBlock" 哈希表中添加或更新条目,以记录开始块的统计信息
|
||||
*
|
||||
* 参数:
|
||||
* PgStat_StartBlockTableKey *tabkey:用于唯一标识要添加或更新的哈希表条目
|
||||
*
|
||||
* 返回值:
|
||||
* PgStat_StartBlockTableEntry * 类型,表示已添加或更新的哈希表条目
|
||||
*
|
||||
*/
|
||||
PgStat_StartBlockTableEntry *
|
||||
StartBlockHashTableAdd(PgStat_StartBlockTableKey *tabkey)
|
||||
{
|
||||
bool found = true; // 用于指示在添加条目时是否找到了已存在的匹配条目
|
||||
PgStat_StartBlockTableEntry *result = NULL; // 用于存储添加的条目或者已存在的匹配条目
|
||||
bool found = true;
|
||||
PgStat_StartBlockTableEntry *result = NULL;
|
||||
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_EXCLUSIVE);
|
||||
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_EXCLUSIVE); // 获取独占锁
|
||||
// 查找匹配的条目,如果找到了,就将它的地址赋给 result ,并将 found 设置为 true;否则,result 保持为 NULL,found 保持为 false。
|
||||
result = (PgStat_StartBlockTableEntry *)hash_search(g_instance.stat_cxt.tableStat->blocks_map,
|
||||
tabkey, HASH_ENTER, &found);
|
||||
|
||||
if (!found) { // 如果有找到匹配的条目
|
||||
// 用于初始化 result 条目中的 starting_blocks 数组。它将数组中的元素设置为从 0 到 START_BLOCK_ARRAY_SIZE - 1 的连续整数值
|
||||
if (!found) {
|
||||
for (int i = 0; i < START_BLOCK_ARRAY_SIZE; i++) {
|
||||
result->starting_blocks[i] = i;
|
||||
}
|
||||
}
|
||||
LWLockRelease(lock); // 释放获取的独占锁
|
||||
LWLockRelease(lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*功能:用于获取 "StartBlock" 哈希表中的条目。如果哈希表中不存在匹配的条目,则会添加一个新的条目并返回
|
||||
*
|
||||
* 参数:
|
||||
* PgStat_StartBlockTableKey *tabkey:用于唯一标识要获取或添加的哈希表条目
|
||||
*
|
||||
* 返回值:
|
||||
* PgStat_StartBlockTableEntry * 类型,表示已获取或添加的哈希表条目
|
||||
*
|
||||
*/
|
||||
PgStat_StartBlockTableEntry *
|
||||
GetStartBlockHashEntry(PgStat_StartBlockTableKey *tabkey)
|
||||
{
|
||||
PgStat_StartBlockTableEntry *result = NULL; // 用于存储获取的条目
|
||||
result = StartBlockHashTableLookup(tabkey); // 用于查找哈希表中是否存在与给定键匹配的条目
|
||||
PgStat_StartBlockTableEntry *result = NULL;
|
||||
result = StartBlockHashTableLookup(tabkey);
|
||||
/* not found, add it */
|
||||
if (result == NULL) { // 如果没有找到匹配的条目
|
||||
result = StartBlockHashTableAdd(tabkey); // 添加新的条目
|
||||
if (result == NULL) {
|
||||
result = StartBlockHashTableAdd(tabkey);
|
||||
}
|
||||
|
||||
Assert(result); // 确保 result 变量不为 NULL
|
||||
Assert(result);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue