opengauss项目代码注释 #26
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
|
|
@ -0,0 +1 @@
|
|||
DIR *.* /B >LIST.TXT
|
||||
|
|
@ -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,47 +84,31 @@ 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);
|
||||
}
|
||||
|
|
@ -137,13 +121,8 @@ 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);
|
||||
}
|
||||
|
|
@ -156,8 +135,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);
|
||||
}
|
||||
|
|
@ -167,29 +146,28 @@ 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");
|
||||
|
|
@ -217,8 +195,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)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -231,9 +209,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 */
|
||||
|
|
@ -242,16 +220,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)
|
||||
|
|
@ -259,8 +237,7 @@ 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
|
||||
|
|
@ -280,9 +257,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)
|
||||
|
|
@ -321,12 +298,16 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +419,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();
|
||||
|
||||
|
|
@ -484,7 +465,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) {
|
||||
|
|
@ -493,8 +474,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -521,9 +502,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;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +595,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;
|
||||
|
|
@ -693,7 +674,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;
|
||||
|
|
@ -727,7 +708,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();
|
||||
|
|
@ -737,9 +718,8 @@ 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;
|
||||
|
||||
|
|
@ -800,7 +780,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;
|
||||
|
|
@ -813,8 +793,7 @@ 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 */
|
||||
|
|
@ -824,10 +803,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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -850,7 +829,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;
|
||||
}
|
||||
|
|
@ -863,23 +842,20 @@ 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);
|
||||
|
|
@ -925,9 +901,8 @@ 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.
|
||||
|
|
@ -943,7 +918,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);
|
||||
}
|
||||
|
|
@ -951,8 +926,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);
|
||||
}
|
||||
|
|
@ -968,14 +943,15 @@ 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) {
|
||||
|
|
@ -1011,8 +987,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);
|
||||
|
||||
|
|
@ -1077,10 +1053,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));
|
||||
|
||||
|
|
@ -1093,10 +1069,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));
|
||||
|
||||
|
|
@ -1109,7 +1085,6 @@ char* GetUserNameById(Oid roleid)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Interlock-file support
|
||||
*
|
||||
|
|
@ -1130,7 +1105,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) {
|
||||
|
|
@ -1154,7 +1129,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};
|
||||
|
|
@ -1162,7 +1137,8 @@ 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) {
|
||||
|
|
@ -1180,7 +1156,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];
|
||||
|
|
@ -1189,7 +1165,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) {
|
||||
|
|
@ -1290,18 +1266,15 @@ 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)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1338,24 +1311,20 @@ 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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1372,7 +1341,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;
|
||||
|
||||
|
|
@ -1385,17 +1354,15 @@ 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1406,12 +1373,10 @@ 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();
|
||||
|
|
@ -1422,8 +1387,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);
|
||||
}
|
||||
|
|
@ -1438,18 +1403,13 @@ 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
|
||||
);
|
||||
|
||||
|
|
@ -1467,13 +1427,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);
|
||||
|
|
@ -1501,7 +1461,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));
|
||||
}
|
||||
|
|
@ -1522,7 +1482,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];
|
||||
|
||||
|
|
@ -1532,8 +1492,7 @@ 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");
|
||||
}
|
||||
|
||||
|
|
@ -1545,7 +1504,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') {
|
||||
|
|
@ -1589,12 +1548,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);
|
||||
|
|
@ -1623,11 +1582,8 @@ 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;
|
||||
}
|
||||
|
|
@ -1683,15 +1639,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);
|
||||
|
|
@ -1707,9 +1663,8 @@ 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)));
|
||||
}
|
||||
|
|
@ -1717,23 +1672,17 @@ 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)));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
|
|
@ -1746,12 +1695,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 */
|
||||
|
|
@ -1783,21 +1732,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);
|
||||
|
|
@ -1818,12 +1767,11 @@ 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;
|
||||
|
|
@ -1848,7 +1796,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
|
||||
|
||||
|
|
@ -1875,7 +1823,8 @@ 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;
|
||||
}
|
||||
|
|
@ -1890,7 +1839,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")));
|
||||
}
|
||||
}
|
||||
|
|
@ -1898,8 +1847,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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,47 +43,47 @@
|
|||
#include "catalog/pg_proc_fn.h"
|
||||
|
||||
/*
|
||||
* 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).
|
||||
DefineAggregate
|
||||
"oldstyle" 表示旧式聚合函数定义风格,
|
||||
其中聚合函数的输入类型由参数中的 BASETYPE 元素指定。
|
||||
否则,"args" 定义了输入类型
|
||||
*/
|
||||
void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
||||
{
|
||||
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;
|
||||
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;//用于存储聚合函数的初始值
|
||||
#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 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
|
||||
如果 isalter 为真,则将对象的所有者更改为命名空间的所有者,
|
||||
但前提是命名空间的所有者与命名空间本身具有相同的名称
|
||||
*/
|
||||
bool isalter = false;
|
||||
proowner = GetUserIdFromNspId(aggNamespace);
|
||||
|
|
@ -102,10 +102,8 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
foreach (pl, parameters) {
|
||||
DefElem* defel = (DefElem*)lfirst(pl);
|
||||
|
||||
/*
|
||||
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
|
||||
* for sfunc, stype, initcond.
|
||||
*/
|
||||
//sfunc1、stype1 和 initcond1 被认为 是sfunc、stype 和 initcond 的过时拼写方式
|
||||
|
||||
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
|
||||
transfuncName = defGetQualifiedName(defel);
|
||||
else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
|
||||
|
|
@ -124,6 +122,10 @@ 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);
|
||||
|
|
@ -135,29 +137,23 @@ 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: 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.
|
||||
Old style支持零个或一个输入的聚合函数,其中输入类型 ANY 表示零个输入
|
||||
以往我们允许命令看起来像 basetype = 'ANY',因此我们必须对名称 ANY 进行不区分大小写的比较
|
||||
*/
|
||||
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;
|
||||
|
|
@ -167,9 +163,6 @@ 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;
|
||||
|
||||
|
|
@ -178,32 +171,31 @@ void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
|||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("basetype is redundant with aggregate input type specification")));
|
||||
|
||||
/* 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. */
|
||||
/* 在使用没有直接参数的有序集合聚合函数时,aggr_args 变量会在 gram.y 文件中进行修改。
|
||||
因此,解析 aggr_args 的过程应该进行相应的更改。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
下面查找聚合函数的 transtype
|
||||
transtype 不能是伪类型,因为我们需要能够存储 transtype 的值。
|
||||
然而,在某些情况下,我们可以允许多态的 transtype(AggregateCreate 函数将会检查这一点)。
|
||||
此外,我们允许使用 "internal",用于那些希望传递指向私有数据结构的指针的函数
|
||||
*/
|
||||
transTypeId = typenameTypeId(NULL, transType);
|
||||
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId)
|
||||
|
|
@ -212,45 +204,44 @@ 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))));
|
||||
}
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
//大部分的参数检查都在 AggregateCreate 函数内部完成
|
||||
AggregateCreate(aggName, /* 聚合函数的名称 */
|
||||
aggNamespace, /* 命名空间 */
|
||||
aggKind, /* 聚合种类 */
|
||||
aggArgTypes, /* 输入类型种类 */
|
||||
numArgs, /* 输入参数数量 */
|
||||
transfuncName, /* 过渡函数名称 */
|
||||
#ifdef PGXC
|
||||
collectfuncName, /* collect function name */
|
||||
collectfuncName, /* 收集函数名称 */
|
||||
#endif
|
||||
finalfuncName, /* final function name */
|
||||
sortoperatorName, /* sort operator name */
|
||||
transTypeId, /* transition data type */
|
||||
finalfuncName, /* 最终函数名称 */
|
||||
sortoperatorName, /* 排序操作符名称 */
|
||||
transTypeId, /* 过渡数据类型 */
|
||||
#ifdef PGXC
|
||||
initval, /* initial condition */
|
||||
initcollect); /* initial condition for collection function */
|
||||
initval, /* 初始条件 */
|
||||
initcollect); /* 收集函数的初始条件 */
|
||||
#else
|
||||
initval); /* initial condition */
|
||||
initval); /* 初始条件 */
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenameAggregate(List* name, List* args, const char* newname)
|
||||
{
|
||||
Oid procOid;
|
||||
Oid namespaceOid;
|
||||
HeapTuple tup;
|
||||
Form_pg_proc procForm;
|
||||
Relation rel;
|
||||
AclResult aclresult;
|
||||
bool isNull = false;
|
||||
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
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 表 */
|
||||
|
||||
/* Look up function and make sure it's an aggregate */
|
||||
/* 查询函数并确保他是聚合的*/
|
||||
procOid = LookupAggNameTypeNames(name, args, false);
|
||||
|
||||
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
if (!HeapTupleIsValid(tup)) /* 如果运行正常这是不会出现的 */
|
||||
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid)));
|
||||
procForm = (Form_pg_proc)GETSTRUCT(tup);
|
||||
|
||||
|
|
@ -265,13 +256,14 @@ 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\"",
|
||||
|
|
@ -288,16 +280,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);
|
||||
|
|
@ -307,15 +299,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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -38,63 +38,102 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs)
|
||||
/*
|
||||
* 功能:根据本地失效消息判断是否需要丢弃计划缓存项
|
||||
*
|
||||
* 参数列表:
|
||||
* plansource:指向 CachedPlanSource 结构的指针,表示计划源对象
|
||||
* tot:失效消息的总数
|
||||
* idx:失效消息的索引数组
|
||||
* msgs:指向 SharedInvalidationMessage 结构的指针,表示失效消息数组
|
||||
*
|
||||
* 返回值:
|
||||
* 如果需要丢弃计划缓存项,返回 true;否则返回 false
|
||||
*/
|
||||
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) {
|
||||
if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid)
|
||||
{
|
||||
// 如果消息的 id 是 SHAREDINVALRELCACHE_ID
|
||||
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];
|
||||
|
||||
|
|
@ -103,20 +142,21 @@ 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);
|
||||
|
|
@ -129,7 +169,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 &&
|
||||
|
|
@ -138,7 +178,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -147,5 +187,6 @@ 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,7 +141,6 @@ static bool IsLegalIntervalStr(const char* str, bool numeric_only)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief get_calendar_clause
|
||||
* Get calendar clause and return its value;
|
||||
|
|
@ -154,7 +153,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -163,10 +162,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;
|
||||
}
|
||||
|
|
@ -205,10 +204,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;
|
||||
}
|
||||
|
|
@ -223,7 +222,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;
|
||||
|
|
@ -232,10 +231,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;
|
||||
}
|
||||
|
|
@ -306,10 +305,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 */
|
||||
|
|
@ -352,10 +351,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.")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -373,10 +372,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.")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,16 +392,26 @@ 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 */
|
||||
|
|
@ -421,10 +430,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.
|
||||
|
|
@ -449,10 +458,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);
|
||||
|
||||
|
|
@ -505,14 +514,13 @@ 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);
|
||||
|
|
@ -568,10 +576,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);
|
||||
|
||||
|
|
@ -590,9 +598,19 @@ 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 */
|
||||
|
|
@ -621,6 +639,7 @@ static char *get_calendar_byminute_val(Calendar calendar, char **tokens)
|
|||
calendar->minute_len *= -1;
|
||||
calendar->byminute[0] = mod;
|
||||
}
|
||||
// 没有指定 "byminute" 子句,返回 NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -667,15 +686,26 @@ 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) {
|
||||
|
|
@ -693,6 +723,7 @@ static char *get_calendar_bysecond_val(Calendar calendar, char **tokens)
|
|||
calendar->second_len *= -1;
|
||||
calendar->bysecond[0] = mod;
|
||||
}
|
||||
// 没有指定 "bysecond" 子句,返回 NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -740,7 +771,6 @@ static void get_calendar_bysecond(Calendar calendar, char **tokens)
|
|||
calendar->byfields |= INTERVAL_BYSECOND;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief tokenize_str
|
||||
* Tokenize string with given delimiters.
|
||||
|
|
@ -766,13 +796,21 @@ 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++) {
|
||||
|
|
@ -791,7 +829,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;
|
||||
|
|
@ -815,19 +853,18 @@ 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 */
|
||||
|
|
@ -1028,7 +1065,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) {
|
||||
|
|
@ -1222,8 +1259,7 @@ 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);
|
||||
|
||||
|
|
@ -1237,8 +1273,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);
|
||||
|
|
@ -1397,7 +1433,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),
|
||||
|
|
@ -1431,7 +1467,6 @@ static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_
|
|||
return base_date;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief evaluate_calendar_interval
|
||||
* Calculate next date base on start date.
|
||||
|
|
@ -1516,9 +1551,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,60 +61,63 @@ 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.
|
||||
*/
|
||||
int main(int argc, char* argv[])
|
||||
/*
|
||||
* 功能:GaussDB 主函数
|
||||
*
|
||||
* 参数列表:
|
||||
* argc:命令行参数的数量
|
||||
* argv:命令行参数的字符串数组
|
||||
*
|
||||
* 注意:
|
||||
* 该函数负责 GaussDB 的启动和主要控制流程。它会根据命令行参数分发到不同的子程序,如
|
||||
* initdb、Postmaster、GucInfoMain等。 在启动过程中,会进行一系列初始化操作,包括内存管理、信号处理、地区设置等。
|
||||
* 根据命令行参数,可能执行初始化数据库、显示配置信息或启动主 Postmaster 进程。
|
||||
*/
|
||||
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.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.account_context =
|
||||
AllocSetContextCreate(g_instance.instance_context, "StandbyAccontContext", 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.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);
|
||||
/*
|
||||
* Fire up essential subsystems: error and memory management
|
||||
*
|
||||
|
|
@ -126,8 +129,9 @@ 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;
|
||||
|
||||
|
|
@ -137,19 +141,21 @@ 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
|
||||
|
|
@ -191,7 +197,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);
|
||||
|
|
@ -258,8 +264,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);
|
||||
|
|
@ -310,7 +316,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;
|
||||
|
|
@ -363,7 +369,8 @@ 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);
|
||||
|
|
@ -462,7 +469,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) {
|
||||
|
|
@ -496,29 +503,49 @@ static void check_root(const char* progname)
|
|||
}
|
||||
#endif /* WIN32 */
|
||||
}
|
||||
|
||||
static char* get_current_username(const char* progname)
|
||||
/*
|
||||
* 功能:获取当前操作系统用户的用户名
|
||||
*
|
||||
* 参数列表:
|
||||
* progname:程序的名称,用于错误消息
|
||||
*
|
||||
* 返回值:
|
||||
* 当前用户的用户名,以字符串形式返回
|
||||
*
|
||||
* 注意:
|
||||
* 该函数在不同的操作系统下使用不同的方法获取用户名。
|
||||
* 在 Unix-like 系统下,使用 getpwuid 函数获取用户名。
|
||||
* 在 Windows 系统下,使用 GetUserName 函数获取用户名。
|
||||
* 函数内部包含线程安全措施,以确保在多线程环境中的正确性。
|
||||
*/
|
||||
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);
|
||||
|
|
@ -527,12 +554,29 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"utility": "cpp"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
// 该文件实现了openGauss的报警检查线程功能,主要用于检查数据库运行过程中的异常情况并进行相应的报警处理
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
|
|
@ -48,64 +49,77 @@
|
|||
#include "replication/walsender.h"
|
||||
|
||||
// declare the global variable of alarm module
|
||||
int g_alarmReportInterval;
|
||||
char g_alarmComponentPath[MAXPGPATH];
|
||||
int g_alarmReportMaxCount;
|
||||
// 声明用于控制报警模块行为的全局变量
|
||||
int g_alarmReportInterval; // 报警上报的时间间隔(单位:秒)
|
||||
char g_alarmComponentPath[MAXPGPATH]; // 报警组件路径,存储报警信息的组件的路径(长度为MAXPGPATH)
|
||||
int g_alarmReportMaxCount; // 最大报警上报次数
|
||||
/* seconds, interval of alarm check loop. */
|
||||
static const int AlarmCheckInterval = 1;
|
||||
static const int AlarmCheckInterval = 1; // 报警检查循环的时间间隔,初始设置为1秒
|
||||
|
||||
bool enable_alarm = false;
|
||||
bool enable_alarm = false; // 表示是否启用报警功能。初始值为false,通过设置为true来启用报警功能
|
||||
|
||||
static Alarm* DataInstAlarmList = NULL;
|
||||
static Alarm* DataInstAlarmList = NULL; // 指向 Alarm 结构体的指针,表示报警项的列表。报警项是用于检测不同类型报警的配置信息和处理函数的集合。
|
||||
|
||||
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 acSighupHandler(SIGNAL_ARGS);
|
||||
static void acSigquitHandler(SIGNAL_ARGS);
|
||||
static void DataInstAlarmItemInitialize(void); // 报警项初始化函数,用于初始化数据实例的报警项列表。
|
||||
|
||||
extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam);
|
||||
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 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()
|
||||
{
|
||||
|
||||
|
|
@ -113,21 +127,23 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
IsUnderPostmaster = true;
|
||||
|
||||
/* reset t_thrd.proc_cxt.MyProcPid */
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); //将当前线程的系统级线程ID分配给MyProcPid,以标识当前线程
|
||||
|
||||
/* 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", "", "", "");
|
||||
init_ps_display("AlarmChecker", "", "", ""); // 设置线程在进程状态(ps)显示中的标识为 "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);
|
||||
|
||||
|
|
@ -139,10 +155,15 @@ 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);
|
||||
|
|
@ -151,46 +172,57 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(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);
|
||||
// 重置一些信号的默认处理方式,以确保报警检查线程不会干扰其他信号的处理
|
||||
// 将信号的处理设置为默认处理方式 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(PGC_SIGHUP); // 调用ProcessConfigFile函数重新加载配置文件
|
||||
}
|
||||
|
||||
// 调用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);
|
||||
}
|
||||
|
||||
|
|
@ -203,15 +235,16 @@ NON_EXEC_STATIC void AlarmCheckerMain()
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
// 信号处理函数,用于处理 SIGHUP 信号,并设置相应的标志
|
||||
static void acSighupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前 errno 的值,以便后续恢复
|
||||
|
||||
t_thrd.alarm_cxt.gotSighup = true;
|
||||
t_thrd.alarm_cxt.gotSighup = true; // 将线程上下文中的 gotSighup 标志设置为 true,表示收到了 SIGHUP 信号
|
||||
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); // 使用 SetLatch 函数触发线程的私有 Latch,以便唤醒线程并处理信号
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 errno 的操作
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -220,84 +253,96 @@ static void acSighupHandler(SIGNAL_ARGS)
|
|||
* Description :
|
||||
* Notes :
|
||||
*/
|
||||
// 信号处理函数,用于处理 SIGTERM 或 SIGINT 信号,并设置相应的标志
|
||||
static void acSigquitHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno;// 保存当前 errno 的值,以便后续恢复
|
||||
|
||||
t_thrd.alarm_cxt.gotSigdie = true;
|
||||
t_thrd.alarm_cxt.gotSigdie = true;// 将线程上下文中的 gotSigdie 标志设置为 true,表示收到了 SIGTERM 或 SIGINT 信号
|
||||
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);
|
||||
SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch);// 使用 SetLatch 函数触发线程的私有 Latch,以便唤醒线程并处理信号
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno;// 恢复之前保存的 errno 值,确保不影响其他代码对 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,
|
||||
g_instance.attr.attr_common.PGXCNodeName,
|
||||
// 填写报警信息
|
||||
WriteAlarmAdditionalInfo(additionalParam, //additionalParam:报警的附加参数,用于存储报警信息的详细内容
|
||||
g_instance.attr.attr_common.PGXCNodeName, // 数据库实例的名称,用于标识报警发生的实例
|
||||
"",
|
||||
"",
|
||||
alarm,
|
||||
ALM_AT_Resume,
|
||||
g_instance.attr.attr_common.PGXCNodeName);
|
||||
return ALM_ACR_Normal;
|
||||
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_Fault, // 设置报警动作为“故障”
|
||||
g_instance.attr.attr_common.PGXCNodeName);
|
||||
return ALM_ACR_Abnormal;
|
||||
return ALM_ACR_Abnormal;// 返回报警检查结果为“异常”
|
||||
}
|
||||
}
|
||||
|
||||
/* implementation of alarm module. */
|
||||
// 用于释放动态分配的内存,避免内存泄漏
|
||||
void AlarmFree(void* pointer)
|
||||
{
|
||||
if (pointer != NULL)
|
||||
pfree(pointer);
|
||||
if (pointer != NULL)// 检查指针是否非空
|
||||
pfree(pointer); // 使用 pfree 函数释放内存
|
||||
}
|
||||
|
||||
// 用于分配指定大小的内存块
|
||||
void* AlarmAlloc(size_t size)
|
||||
{
|
||||
return palloc(size);
|
||||
return palloc(size);// 调用 palloc 函数分配指定大小的内存块,并返回分配的内存块指针
|
||||
}
|
||||
|
||||
// 日志输出函数,用于在不同级别输出报警信息
|
||||
// 输入参数分别为报警级别、前缀和报警文本,根据需要输出不同级别的报警信息以进行监控和调试。
|
||||
void AlarmLogImplementation(int level, const char* prefix, const char* logtext)
|
||||
{
|
||||
// 使用 switch 语句根据不同的级别选择不同的日志输出函数并输出信息
|
||||
switch (level) {
|
||||
case ALM_DEBUG:
|
||||
case ALM_DEBUG:// 在 DEBUG3 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
|
||||
ereport(DEBUG3, (errmsg("%s%s", prefix, logtext)));
|
||||
break;
|
||||
case ALM_LOG:
|
||||
case ALM_LOG:// 在 LOG 级别输出报警信息,使用 errmsg 函数输出带有前缀和文本的日志
|
||||
ereport(LOG, (errmsg("%s%s", prefix, logtext)));
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -52,60 +52,72 @@
|
|||
|
||||
#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;
|
||||
char pathPrefix[MAXPGPATH] = {0};
|
||||
errno_t rc = 0; // 用于存储函数调用的返回值,以便检查错误
|
||||
ArchiveConfig obsConfig; // 存储OBS配置的临时变量
|
||||
char pathPrefix[MAXPGPATH] = {0}; // 用于存储OBS路径前缀的临时变量
|
||||
|
||||
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name)));
|
||||
ereport(LOG, (errmsg("Write barrierId <%s> to obs start", barrier_name))); // 输出日志,表示开始写入BarrierID到OBS
|
||||
|
||||
/* copy OBS configs to temporary variable for customising file path */
|
||||
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig));
|
||||
rc = memcpy_s(&obsConfig, sizeof(ArchiveConfig), archive_obs, sizeof(ArchiveConfig)); // 将OBS配置复制到临时变量,以便自定义文件路径
|
||||
securec_check(rc, "", "");
|
||||
|
||||
if (!IS_PGXC_COORDINATOR) {
|
||||
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix);
|
||||
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
|
||||
rc = strcpy_s(pathPrefix, MAXPGPATH, obsConfig.archive_prefix); // 将OBS路径前缀复制到pathPrefix中
|
||||
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);
|
||||
ArchiveTaskStatus *archive_task_status = NULL; // 用于存储归档任务状态的指针
|
||||
archive_task_status = find_archive_task_status(slotName); // 根据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);
|
||||
pg_usleep(100000L); // 等待100000微秒(0.1秒)
|
||||
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"
|
||||
|
|
@ -113,23 +125,27 @@ 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);
|
||||
} while (1); // 无限循环,直到达到要求的LSN
|
||||
|
||||
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)));
|
||||
|
||||
|
|
@ -137,84 +153,99 @@ void ProcessBarrierQueryArchive(char* id)
|
|||
char *slotName;
|
||||
char *lsn;
|
||||
|
||||
lsn = strtok_r(id, ":", &slotName);
|
||||
lsn = strtok_r(id, ":", &slotName); // 使用":"分割id,获取LSN和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) {
|
||||
WaitBarrierArch(barrierTargetLsn, slotName);
|
||||
if (!IS_PGXC_COORDINATOR) { // 如果不是PGXC协调器
|
||||
WaitBarrierArch(barrierTargetLsn, slotName); // 等待达到指定的LSN
|
||||
}
|
||||
|
||||
pq_beginmessage(&buf, 'b');
|
||||
pq_sendstring(&buf, id);
|
||||
pq_endmessage(&buf);
|
||||
pq_flush();
|
||||
pq_beginmessage(&buf, 'b'); // 启动一个'b'类型的消息
|
||||
pq_sendstring(&buf, id); // 向消息中添加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;
|
||||
errno = save_errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
t_thrd.barrier_arch.got_SIGHUP = true; // 设置标志,表示收到了SIGHUP信号
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* Reset some signals that are accepted by postmaster but not here */
|
||||
// 设置不同信号的处理方式,确保程序在收到不同的信号时能够正确地处理
|
||||
static void BarrierArchSetupSignalHook(void)
|
||||
{
|
||||
(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(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(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); // 将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的处理函数设置为默认值
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从阻塞信号集中移除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;
|
||||
|
|
@ -223,23 +254,26 @@ 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",
|
||||
|
|
@ -249,13 +283,15 @@ 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);
|
||||
|
|
@ -263,123 +299,147 @@ 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);
|
||||
SpinLockAcquire(&g_instance.archive_obs_cxt.barrier_lock); // 获取全局锁
|
||||
// 检查当前的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到局部变量
|
||||
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;
|
||||
|
|
@ -388,25 +448,30 @@ 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));
|
||||
|
||||
|
|
@ -427,119 +492,139 @@ 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);
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrier_lsn_info not alloc.")));
|
||||
SpinLockRelease(&g_instance.archive_obs_cxt.barrier_lock); // 释放自旋锁
|
||||
// 输出警告消息,提示 barrier_lsn_info 未分配
|
||||
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();
|
||||
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
|
||||
// 如果 barrierName 为空或长度为 0
|
||||
if (g_instance.archive_obs_cxt.barrierName == NULL || strlen(g_instance.archive_obs_cxt.barrierName) == 0) {
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null.")));
|
||||
ereport(WARNING, (errmsg("[BarrierArch] barrierName is null."))); // 输出警告消息,提示 barrierName 为空
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
// 开启了多节点编译选项时执行以下代码块
|
||||
// 如果收到了连接池重载标志
|
||||
if (IsGotPoolReload()) {
|
||||
processPoolerReload();
|
||||
ResetGotPoolReload(false);
|
||||
if (!IsFirstCn())
|
||||
processPoolerReload(); // 执行连接池重载操作
|
||||
ResetGotPoolReload(false); // 重置连接池重载标志为 false
|
||||
if (!IsFirstCn()) // 如果当前节点不是第一个协调器节点,跳出循环
|
||||
break;
|
||||
}
|
||||
|
||||
PGXCNodeAllHandles* handles = GetAllNodesHandles();
|
||||
|
||||
// 获取所有节点的连接句柄
|
||||
PGXCNodeAllHandles* handles = GetAllNodesHandles();
|
||||
// 调用 QueryBarrierArch 函数处理Barrier归档
|
||||
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,100 +52,116 @@ 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) {
|
||||
if (isSwitchoverBarrier) { // 如果要切换Barrier
|
||||
// 构造用于切换Barrier的名称,将CSN插入到特定的格式字符串中
|
||||
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");
|
||||
securec_check_ss_c(rc, "\0", "\0"); // 检查格式化操作的返回值,确保操作成功
|
||||
// 记录调试日志,输出生成的Barrier名称和对应的CSN
|
||||
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;
|
||||
return 0; // 解析失败时返回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;
|
||||
return 0; // 解析失败时返回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;
|
||||
return true; // 如果符合要求,则认为是切换Barrier
|
||||
}
|
||||
|
||||
// 判断当前节点是否为第一个执行的协调器节点
|
||||
bool IsFirstCn()
|
||||
{
|
||||
char *firstExecNode = find_first_exec_cn();
|
||||
char *firstExecNode = find_first_exec_cn(); // 查找第一个执行的协调器节点
|
||||
// 将当前节点的名称与firstExecNode进行比较,如果两者相同,则返回true,表示当前节点是第一个执行的协调器节点;否则返回false
|
||||
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;
|
||||
t_thrd.barrier_creator_cxt.got_SIGHUP = true;
|
||||
errno = save_errno;
|
||||
}
|
||||
int save_errno = errno; // 保存当前errno
|
||||
t_thrd.barrier_creator_cxt.got_SIGHUP = true; // 设置标志,表示收到SIGHUP信号
|
||||
errno = save_errno; // 恢复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);
|
||||
(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(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(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); // 设置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信号处理函数为默认
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许随时处理SIGQUIT(quickdie)信号
|
||||
}
|
||||
|
||||
// 从OBS中读取Barrier标识
|
||||
static uint64_t read_barrier_id_from_obs(const char *slotName, long *currBarrierTime)
|
||||
{
|
||||
char barrier_name[BARRIER_NAME_LEN];
|
||||
|
|
@ -153,146 +169,153 @@ 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_name[BARRIER_NAME_LEN - 1] = '\0'; // 将读取的数据末尾设置为字符串结束符
|
||||
// 输出日志,表示从归档存储中读取了barrier ID
|
||||
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;
|
||||
return 0; // 解析失败,返回0
|
||||
}
|
||||
|
||||
// 获取归档槽中的最大barrier索引和最新的barrier时间
|
||||
uint64_t GetObsBarrierIndex(const List *archiveSlotNames, long *last_barrier_time)
|
||||
{
|
||||
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) {
|
||||
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) { // 如果归档槽名称为空,跳过本次循环
|
||||
continue;
|
||||
}
|
||||
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime);
|
||||
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex;
|
||||
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime;
|
||||
uint64_t readIndex = read_barrier_id_from_obs(slotName, &currBarrierTime); // 从归档存储中读取barrier索引和时间
|
||||
maxIndex = (readIndex > maxIndex) ? readIndex : maxIndex; // 更新最大barrier索引
|
||||
maxBarrierTime = (currBarrierTime > maxBarrierTime) ? currBarrierTime : maxBarrierTime; // 更新最大barrier时间
|
||||
}
|
||||
*last_barrier_time = maxBarrierTime;
|
||||
*last_barrier_time = maxBarrierTime; // 将最大barrier时间赋值给指针参数
|
||||
|
||||
return maxIndex;
|
||||
return maxIndex; // 返回最大barrier索引
|
||||
}
|
||||
|
||||
// 获取归档槽中第一个协调器节点的barrier时间线
|
||||
uint64 GetObsFirstCNBarrierTimeline(const List *archiveSlotNames)
|
||||
{
|
||||
uint64 timeline = 0;
|
||||
uint64 timeline = 0; // 时间线初始值为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);
|
||||
break;
|
||||
timeline = ReadBarrierTimelineRecordFromObs(slotName); // 从归档存储中读取协调器节点的barrier时间线
|
||||
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);
|
||||
securec_check(rc, "", "");
|
||||
sizeof(ArchiveBarrierLsnInfo) * nodeSize); // 将分配的内存内容初始化为0
|
||||
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);
|
||||
pfree_ext(g_instance.archive_obs_cxt.barrier_lsn_info); // 释放旧的BarrierLsnInfo数组内存
|
||||
}
|
||||
AllocBarrierLsnInfo(nodeSize);
|
||||
AllocBarrierLsnInfo(nodeSize); // 重新分配和初始化BarrierLsnInfo数组
|
||||
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;
|
||||
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; // 将最大节点数设置为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;
|
||||
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;
|
||||
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的标志
|
||||
// use InnerMaintenanceTools mode to avoid deadlock with thread pool
|
||||
u_sess->proc_cxt.IsInnerMaintenanceTools = true;
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator started")));
|
||||
g_instance.archive_obs_cxt.max_node_cnt = 0;
|
||||
SetProcessingMode(InitProcessing);
|
||||
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
|
||||
|
||||
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;
|
||||
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线程不停止
|
||||
|
||||
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
|
||||
on_shmem_exit(PGXCNodeCleanAndRelease, 0); // 注册一个在进程退出时执行的回调函数,用于清理PGXC节点资源
|
||||
|
||||
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));
|
||||
|
||||
|
|
@ -302,6 +325,7 @@ 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,
|
||||
|
|
@ -313,83 +337,92 @@ 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);
|
||||
AtEOXact_SysDBCache(false); // 释放lsc持有的资源
|
||||
|
||||
/* release resource */
|
||||
LWLockReleaseAll();
|
||||
// 释放资源
|
||||
LWLockReleaseAll(); // 释放所有的轻量级锁
|
||||
|
||||
FreeBarrierLsnInfo();
|
||||
FreeBarrierLsnInfo(); // 释放Barrier信息结构体的内存
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
MemoryContextSwitchTo(barrier_creator_context);
|
||||
FlushErrorState();
|
||||
MemoryContextResetAndDeleteChildren(barrier_creator_context);
|
||||
MemoryContextSwitchTo(barrier_creator_context); // 切换到 barrier_creator_context 内存上下文
|
||||
FlushErrorState(); // 清除错误状态信息
|
||||
MemoryContextResetAndDeleteChildren(barrier_creator_context); // 重置并删除 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();
|
||||
SetProcessingMode(NormalProcessing);
|
||||
exec_init_poolhandles();
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除先前阻塞的信号
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除对 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);
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果是则处理中断
|
||||
pg_usleep(1000000L); // 休眠1秒钟
|
||||
} 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);
|
||||
/*
|
||||
|
|
@ -397,83 +430,94 @@ 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) {
|
||||
if (last_barrier_time < current_time) { // 如果最后一个barrier时间比当前时间小,则跳出循环
|
||||
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) {
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
pg_usleep(1000000L);
|
||||
// 在未收到 START_AUTO_CSN_BARRIER 信号之前,循环等待
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否收到中断信号,如果有,则处理中断
|
||||
pg_usleep(1000000L); // 等待1秒
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
CleanupBarrierLock();
|
||||
// 如果启用了多节点模式
|
||||
CleanupBarrierLock(); // 清理barrier锁
|
||||
#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) {
|
||||
if (g_instance.archive_obs_cxt.barrier_lsn_info == NULL) { // 如果barrier LSN 信息为空
|
||||
int nodeSize = *t_thrd.pgxc_cxt.shmemNumCoords + *t_thrd.pgxc_cxt.shmemNumDataNodes;
|
||||
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);
|
||||
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锁
|
||||
}
|
||||
#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;
|
||||
gettimeofday(&tv, NULL);
|
||||
WriteGlobalBarrierListStartTimeOnMedia(TIME_GET_MILLISEC(tv));
|
||||
t_thrd.barrier_creator_cxt.is_first_barrier = true; // 将标志位设为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);
|
||||
}
|
||||
}
|
||||
|
|
@ -483,41 +527,47 @@ 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) {
|
||||
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME);
|
||||
if (startCsnBarrier) { // 如果启用了自动CSN barrier
|
||||
rc = snprintf_s(barrier_name, BARRIER_NAME_LEN, BARRIER_NAME_LEN - 1, CSN_BARRIER_NAME); //构造CSN barrier名称
|
||||
securec_check_ss_c(rc, "\0", "\0");
|
||||
|
||||
RequestBarrier(barrier_name, NULL);
|
||||
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name)));
|
||||
RequestBarrier(barrier_name, NULL); // 请求创建CSN barrier
|
||||
ereport(LOG, (errmsg("[BarrierCreator]barrier %s created", barrier_name))); // 日志中记录barrier的创建
|
||||
}
|
||||
#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);
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name)));
|
||||
DisasterRecoveryRequestBarrier(barrier_name); // 请求创建灾备 barrier
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier %s created", barrier_name))); // 日志中记录barrier的创建
|
||||
#endif
|
||||
index++;
|
||||
}
|
||||
ereport(LOG, (errmsg("[BarrierCreator] barrier creator thread exits.")));
|
||||
if (t_thrd.barrier_creator_cxt.barrier_update_last_time_info != 0) {
|
||||
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结构数组以释放资源
|
||||
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();
|
||||
// 执行清理操作
|
||||
destroy_handles(); // 销毁句柄
|
||||
FreeBarrierLsnInfo(); // 释放Barrier LSN信息
|
||||
proc_exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,17 +40,27 @@
|
|||
#include "postmaster/barrier_preparse.h"
|
||||
|
||||
typedef struct XLogPageReadPrivate {
|
||||
const char *datadir;
|
||||
TimeLineID tli;
|
||||
const char *datadir; // 存储数据库的数据目录路径
|
||||
TimeLineID tli; // 存储 WAL 日志所在的时间线标识符
|
||||
} 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,
|
||||
|
|
@ -58,66 +68,72 @@ 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));
|
||||
rc = memset_s(&ctl, sizeof(HASHCTL), 0, sizeof(HASHCTL)); // 将ctl结构清零,确保结构中的各字段正确初始化
|
||||
securec_check(rc, "", "");
|
||||
// 设置哈希表的键大小和每个条目的大小
|
||||
ctl.keysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
|
||||
ctl.entrysize = MAX_BARRIER_ID_LENGTH * sizeof(char);
|
||||
ctl.hash = string_hash;
|
||||
ctl.hash = string_hash; // 设置哈希函数为string_hash,用于计算键的哈希值
|
||||
// 设置哈希表使用的上下文为之前创建的CsnBarrierContext上下文
|
||||
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);
|
||||
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,确保数据一致性
|
||||
// 使用strncpy_s函数将barrierId复制到lastReceivedBarrierId中,确保字符串安全性
|
||||
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;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
walrcv->lastReceivedBarrierId[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 在末尾添加字符串结束符
|
||||
walrcv->lastReceivedBarrierLSN = lsn; // 设置最后接收到的barrier 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;
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = true; // 设置标志位,表示收到SIGHUP信号
|
||||
if (t_thrd.proc) {
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置进程的Latch,用于唤醒进程处理SIGHUP信号
|
||||
}
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
// 处理关闭请求信号
|
||||
static void BarrierPreParseShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
t_thrd.barrier_preparse_cxt.shutdown_requested = true;
|
||||
t_thrd.barrier_preparse_cxt.shutdown_requested = true; // 设置关闭请求标志为true,表示收到了关闭请求信号
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置线程的Latch,用于唤醒线程处理关闭请求信号
|
||||
|
||||
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
|
||||
|
|
@ -127,7 +143,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
|
||||
|
|
@ -137,105 +153,108 @@ static void BarrierPreParseQuickDie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2);
|
||||
exit(2); //以代码2 退出进程
|
||||
}
|
||||
|
||||
// 处理SIGUSR1信号
|
||||
static void BarrierPreParseSigUsr1Handler(SIGNAL_ARGS)
|
||||
{
|
||||
int saveErrno = errno;
|
||||
int saveErrno = errno; // 保存当前错误码的值
|
||||
|
||||
latch_sigusr1_handler();
|
||||
latch_sigusr1_handler(); // 处理SIGUSR1信号的回调函数
|
||||
|
||||
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;
|
||||
SpinLockAcquire(&walrcv->mutex);
|
||||
walrcv->lastReceivedBarrierLSN = startptr;
|
||||
SpinLockRelease(&walrcv->mutex);
|
||||
volatile WalRcvData *walrcv = t_thrd.walreceiverfuncs_cxt.WalRcv; // 获取WalRcvData结构体的指针
|
||||
SpinLockAcquire(&walrcv->mutex); // 获取互斥锁,保证操作的原子性
|
||||
walrcv->lastReceivedBarrierLSN = startptr; // 设置lastReceivedBarrierLSN为指定的LSN
|
||||
SpinLockRelease(&walrcv->mutex); // 释放互斥锁
|
||||
}
|
||||
|
||||
void BarrierPreParseMain(void)
|
||||
{
|
||||
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;
|
||||
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; // 函数返回值
|
||||
|
||||
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);
|
||||
(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);
|
||||
(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信号
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(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); // 恢复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信号的默认处理
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许在任何时间接收SIGQUIT 信号
|
||||
|
||||
on_shmem_exit(ShutdownBarrierPreParse, 0);
|
||||
on_shmem_exit(ShutdownBarrierPreParse, 0); // 在进程退出时调用ShutdownBarrierPreParse函数
|
||||
|
||||
preParseContext = AllocSetContextCreate(t_thrd.top_mem_cxt, "Barrier PreParse", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
(void)MemoryContextSwitchTo(preParseContext);
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); // 创建内存上下文
|
||||
(void)MemoryContextSwitchTo(preParseContext); // 切换到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();
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除对信号的阻塞
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除对SIGUSR2信号的阻塞
|
||||
|
||||
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch;
|
||||
g_instance.proc_base->BarrierPreParseLatch = &t_thrd.proc->procLatch; // 设置BarrierPreParseLatch
|
||||
|
||||
startLSN = walrcv->lastReceivedBarrierLSN;
|
||||
startLSN = walrcv->lastReceivedBarrierLSN; // 获取上次接收的barrier LSN
|
||||
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) {
|
||||
if (g_instance.csn_barrier_cxt.barrier_hash_table == NULL) {// 如果barrier哈希表为空,初始化
|
||||
InitBarrierHash();
|
||||
}
|
||||
|
||||
readprivate.datadir = t_thrd.proc_cxt.DataDir;
|
||||
readprivate.tli = GetRecoveryTargetTLI();
|
||||
readprivate.datadir = t_thrd.proc_cxt.DataDir; // 设置读取私有数据的数据目录
|
||||
readprivate.tli = GetRecoveryTargetTLI(); // 获取恢复目标的时间线ID
|
||||
|
||||
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate);
|
||||
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &readprivate); // 分配XLog读取器
|
||||
if (xlogreader == NULL)
|
||||
// 如果分配失败,报错
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("memory is temporarily unavailable while allocate xlog reader")));
|
||||
|
||||
|
|
@ -244,91 +263,98 @@ 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) {
|
||||
t_thrd.barrier_preparse_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
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.shutdown_requested) {
|
||||
if (t_thrd.barrier_preparse_cxt.shutdown_requested) { // 如果收到关闭请求,结束线程
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down")));
|
||||
XLogReaderFree(xlogreader);
|
||||
XLogReaderFree(xlogreader); // 释放XLog读取器的资源
|
||||
proc_exit(0); /* done */
|
||||
}
|
||||
|
||||
found = false;
|
||||
preStartLSN = startLSN;
|
||||
found = false; // 初始化found标志为false
|
||||
preStartLSN = startLSN; // 保存上一次的起始LSN
|
||||
// 记录日志,标记预解析开始位置
|
||||
ereport(DEBUG1, (errmsg("[BarrierPreParse] start to preparse at: %08X/%08X",
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
|
||||
startLSN = XLogFindNextRecord(xlogreader, startLSN);
|
||||
if (XLogRecPtrIsInvalid(startLSN)) {
|
||||
startLSN = preStartLSN;
|
||||
startLSN = XLogFindNextRecord(xlogreader, startLSN); // 查找下一个XLog记录的LSN
|
||||
if (XLogRecPtrIsInvalid(startLSN)) { // 如果找不到,回到上一个起始LSN
|
||||
startLSN = preStartLSN; // 使用之前记录的起始LSN
|
||||
if (!XLByteEQ(walrcv->receiver_flush_location, startLSN) &&
|
||||
!XLByteEQ(walrcv->lastRecoveredBarrierLSN, startLSN)) {
|
||||
/* reset startLSN */
|
||||
startLSN = walrcv->lastRecoveredBarrierLSN;
|
||||
startLSN = walrcv->lastRecoveredBarrierLSN; // 使用上次恢复的Barrier LSN
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] reset startLSN with lastRecoveredBarrierLSN: %08X/%08X",
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN)));
|
||||
(uint32)(startLSN >> shiftSize), (uint32)startLSN))); // 记录日志,标记重置startLSN
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
// 从XLog中读取记录,从startLSN开始
|
||||
record = XLogReadRecord(xlogreader, startLSN, &errormsg);
|
||||
if (record == NULL) {
|
||||
if (record == NULL) { // 如果读取到了NULL记录,即无法继续读取,跳出循环
|
||||
break;
|
||||
}
|
||||
lastReadLSN = xlogreader->EndRecPtr;
|
||||
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
|
||||
lastReadLSN = xlogreader->EndRecPtr; // 记录最后读取的LSN
|
||||
// 获取XLog记录的信息位,去除XLR_INFO_MASK标志位
|
||||
uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
|
||||
if (NEED_INSERT_INTO_HASH) {
|
||||
xLogBarrierId = XLogRecGetData(xlogreader);
|
||||
if (!IS_CSN_BARRIER(xLogBarrierId)) {
|
||||
// 如果需要将记录插入到哈希表中
|
||||
xLogBarrierId = XLogRecGetData(xlogreader); // 获取XLog记录的数据部分
|
||||
if (!IS_CSN_BARRIER(xLogBarrierId)) { // 如果不是用于备机集群的barrier记录
|
||||
ereport(WARNING, (errmsg("[BarrierPreParse] %s is not for standby cluster", xLogBarrierId)));
|
||||
} else {
|
||||
// insert into hash table
|
||||
found = true;
|
||||
barrierLSN = xlogreader->EndRecPtr;
|
||||
// 插入到哈希表中
|
||||
found = true; // 标记找到适用于备用集群的barrier记录
|
||||
barrierLSN = xlogreader->EndRecPtr; // 记录barrier的LSN
|
||||
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[MAX_BARRIER_ID_LENGTH - 1] = '\0'; // 确保barrierId字符串有效性
|
||||
// 获取哈希表锁,插入barrierId
|
||||
LWLockAcquire(g_instance.csn_barrier_cxt.barrier_hashtbl_lock, LW_EXCLUSIVE);
|
||||
BarrierCacheInsertBarrierId(barrierId);
|
||||
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock);
|
||||
BarrierCacheInsertBarrierId(barrierId); // 将barrierId插入哈希表
|
||||
LWLockRelease(g_instance.csn_barrier_cxt.barrier_hashtbl_lock); // 释放哈希表锁
|
||||
// 记录日志,说明插入了barrierId到哈希表中
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] insert barrierID %s to the hash table, rmid: %d, crc: %d.",
|
||||
barrierId, record->xl_rmid, record->xl_crc)));
|
||||
}
|
||||
}
|
||||
startLSN = InvalidXLogRecPtr;
|
||||
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested);
|
||||
startLSN = InvalidXLogRecPtr; // 将startLSN重置为InvalidXLogRecPtr,以便下次循环处理下一个XLog记录
|
||||
} while (!t_thrd.barrier_preparse_cxt.shutdown_requested); // 收到关闭请求退出循环,否则继续
|
||||
|
||||
/* close xlogreadfd after circulation */
|
||||
CloseXlogFile();
|
||||
CloseXlogFile(); // 关闭当前使用的XLOG文件
|
||||
|
||||
if (found) {
|
||||
if (found) { // 如果找到了需要插入到哈希表的barrier,将其插入
|
||||
SetBarrieID(barrierId, barrierLSN);
|
||||
}
|
||||
|
||||
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN;
|
||||
startLSN = XLogRecPtrIsInvalid(lastReadLSN) ? preStartLSN : lastReadLSN; // 更新起始LSN
|
||||
|
||||
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;
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, sleepTime);
|
||||
const long sleepTime = 1000; // 定义休眠时间为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);
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1")));
|
||||
gs_thread_exit(1);
|
||||
XLogReaderFree(xlogreader); // 释放XLogReader资源
|
||||
ereport(LOG, (errmsg("[BarrierPreParse] preparse thread shut down with code 1"))); // 记录线程以代码1关闭的日志
|
||||
gs_thread_exit(1);// 以代码1退出线程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用于唤醒 BarrierPreParse 后台进程
|
||||
void WakeUpBarrierPreParseBackend()
|
||||
{
|
||||
if (g_instance.pid_cxt.BarrierPreParsePID != 0) {
|
||||
if (g_instance.proc_base->BarrierPreParseLatch != NULL) {
|
||||
SetLatch(g_instance.proc_base->BarrierPreParseLatch);
|
||||
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进程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,119 +25,163 @@
|
|||
#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;
|
||||
return t_thrd.role == BGWORKER; // 返回当前线程是否为 BGWORKER(后台工作进程)
|
||||
}
|
||||
|
||||
// 内联函数定义:BgworkerPutBackToFreeList
|
||||
// 功能:将后台工作进程放回空闲列表
|
||||
// 参数:bgworker - 后台工作进程指针
|
||||
static inline void BgworkerPutBackToFreeList(BackgroundWorker* bgworker)
|
||||
{
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;
|
||||
BGW_HDR* bgworker_base = (BGW_HDR *)g_instance.bgw_base;// 获取后台工作进程头指针
|
||||
// 使用 memset_s 清空 bgworker 结构体的内容,确保隐私信息被清除
|
||||
errno_t rc = memset_s(bgworker, sizeof(BackgroundWorker), 0, sizeof(BackgroundWorker));
|
||||
securec_check(rc, "", "");
|
||||
securec_check(rc, "", ""); // 检查正确性
|
||||
// 将 bgworker 放回空闲后台工作进程链表
|
||||
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;
|
||||
|
||||
BGW_HDR* bgworker_base = NULL; // 后台工作进程头指针
|
||||
BackgroundWorker* bgws = NULL; // 后台工作进程指针
|
||||
bool needPalloc = false; // 是否需要进行 palloc 内存分配
|
||||
// 切换内存上下文到 MEMORY_CONTEXT_CBB 组中
|
||||
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(snapshot->xmin, snapshot->xmax, snapshot->snapshotcsn, snapshot->timeline, false);
|
||||
StreamTxnContextSetSnapShot(snapshot);
|
||||
StreamTxnContextSetMyPgXactXmin(snapshot->xmin);
|
||||
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
|
||||
|
||||
/* command id. */
|
||||
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId);
|
||||
SaveReceivedCommandId(bwc->transactionCxt.currentCommandId); // 保存接收到的命令ID
|
||||
|
||||
/* timestamp. */
|
||||
SetCurrentGTMDeltaTimestamp();
|
||||
SetCurrentGTMDeltaTimestamp(); // 设置当前的 GTM Delta 时间戳
|
||||
}
|
||||
|
||||
// 保存后台工作进程的错误信息
|
||||
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';
|
||||
bgw->bgw_edata.message[len] = '\0'; // 确保字符串以 C 字符串的形式结束
|
||||
|
||||
// 获取错误详情,如果为空则使用默认详情
|
||||
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, "", "");
|
||||
|
|
@ -147,43 +191,55 @@ 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->bgw_status = BGW_TERMINATED; // 将状态更新为 BGW_TERMINATED,表示进程已正常终止
|
||||
} else {
|
||||
bgw->bgw_status = BGW_FAILED;
|
||||
bgw->bgw_status = BGW_FAILED; // 将状态更新为 BGW_FAILED,表示进程因某种原因失败
|
||||
}
|
||||
}
|
||||
|
||||
// 后台工作进程的初始化函数
|
||||
static void BackgroundWorkerInit(void)
|
||||
{
|
||||
/* we are a postmaster subprocess now */
|
||||
IsUnderPostmaster = true;
|
||||
t_thrd.role = BGWORKER;
|
||||
IsUnderPostmaster = true; // 将 IsUnderPostmaster 标志设置为 true,表示当前进程是在后台运行
|
||||
t_thrd.role = BGWORKER; // 设置当前线程的角色为 BGWORKER,表示当前线程是一个后台工作进程
|
||||
|
||||
/* reset t_thrd.proc_cxt.MyProcPid */
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); // 重置当前线程的进程ID,将其设置为当前线程的实际线程ID
|
||||
|
||||
t_thrd.proc_cxt.MyProgName = "BgWorker";
|
||||
t_thrd.proc_cxt.MyProgName = "BgWorker"; // 设置当前线程的进程名称为 "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);
|
||||
on_proc_exit(BgworkerQuitAndClean, 0); // 在进程退出时调用 BgworkerQuitAndClean 函数,执行清理操作
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
|
@ -191,6 +247,7 @@ 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);
|
||||
|
|
@ -198,11 +255,11 @@ static void BackgroundWorkerInit(void)
|
|||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* Early initialization */
|
||||
BaseInit();
|
||||
BaseInit(); // 执行早期初始化操作
|
||||
|
||||
#ifndef EXEC_BACKEND
|
||||
InitProcess();
|
||||
#endif
|
||||
InitProcess(); // 初始化进程数据结构和状态
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -211,6 +268,7 @@ 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;
|
||||
|
|
@ -222,57 +280,67 @@ 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. */
|
||||
pthread_mutex_unlock(&g_instance.bgw_base_lock);
|
||||
// 解锁并跳转到 out 标签,退出函数
|
||||
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);
|
||||
gstrace_tryblock_exit(true, oldTryCounter); // 退出异常处理块,恢复之前的 try counter
|
||||
|
||||
/* 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();
|
||||
BgWorkerSaveError(); // 保存 bgworker 错误信息
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
|
@ -280,6 +348,7 @@ void BackgroundWorkerMain(void)
|
|||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
// 切换回原始的内存上下文,刷新错误状态并清理内存
|
||||
(void)MemoryContextSwitchTo(workerContext);
|
||||
FlushErrorState();
|
||||
|
||||
|
|
@ -287,35 +356,44 @@ 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;
|
||||
bgw->bgw_status = BGW_STARTED; // 设置 bgworker 状态
|
||||
|
||||
/* 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
|
||||
|
|
@ -326,21 +404,28 @@ 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;
|
||||
bgw->bgw_status = BGW_STOPPED; // 设置 bgworker 状态为已停止
|
||||
|
||||
out:
|
||||
proc_exit(0);
|
||||
proc_exit(0); // 退出进程
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -349,27 +434,46 @@ 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;
|
||||
return false; // 返回 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;
|
||||
|
|
@ -377,153 +481,221 @@ 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) {
|
||||
} else if (bgw->bgw_status == BGW_NOT_YET_STARTED) { // 如果后台工作进程状态为 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);
|
||||
Assert(nparticipants != NULL); // 断言,确保传入的参数 nparticipants 不为空
|
||||
// 在等待状态报告中设置当前状态为等待同步的后台工作者
|
||||
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);
|
||||
disable_count = pg_atomic_fetch_add_u32(&bgw->disable_count, 1); // 原子操作地增加后台工作者的禁用计数
|
||||
// 如果禁用计数为 0,表示该后台工作者需要被禁用
|
||||
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) {
|
||||
(*nparticipants)--; // 减少参与任务的后台工作者数量
|
||||
nunstarts++; // 增加未启动的后台工作者数量
|
||||
}
|
||||
} else if (bgw->bgw_status == BGW_FAILED) { // 如果后台工作者状态为 BGW_FAILED
|
||||
// 检查后台工作者的错误级别是否大于或等于 ERROR
|
||||
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) {
|
||||
nfinished++;
|
||||
} else if (bgw->bgw_status == BGW_TERMINATED) { // 如果后台工作者状态为 BGW_TERMINATED
|
||||
nfinished++; // 已完成任务的后台工作者数量+1
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Assert(nworkers > 0); // 确保要启动的后台工作者数量大于 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.
|
||||
|
|
@ -533,30 +705,34 @@ 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();
|
||||
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(); // 设置事务上下文的事务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; // 获取当前会话用户的用户名并存储在上下文中
|
||||
/* 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) {
|
||||
for (int i = 0; i < nworkers; ++i) { // 遍历注册指定数量的后台工作者
|
||||
// 调用 RegisterBackgroundWorker 函数注册后台工作者
|
||||
// 如果注册成功,则增加 actualWorkers 计数
|
||||
if (RegisterBackgroundWorker(bwc)) {
|
||||
actualWorkers++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore previous memory context. */
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
return actualWorkers;
|
||||
MemoryContextSwitchTo(oldcontext); // 恢复之前的内存上下文
|
||||
return actualWorkers; // 返回实际注册的后台工作者数量
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,33 +88,50 @@ 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); /* 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);
|
||||
(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信号
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL); // 设置SIGCHLD信号的默认处理方式
|
||||
(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);
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 从信号屏蔽集中移除 SIGQUIT 信号
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:用于在后台写入进程遇到异常情况时执行一系列清理操作,以尽量保证进程能够继续正常运行。
|
||||
*
|
||||
* 参数:
|
||||
* - wb_context: 写回上下文,用于控制后台写入进程的行为。
|
||||
* - bgwriter_cxt: 后台写入进程的内存上下文。
|
||||
*
|
||||
* 返回值: 无
|
||||
*/
|
||||
static void bgwriter_handle_exceptions(WritebackContext wb_context, MemoryContext bgwriter_cxt)
|
||||
{
|
||||
/*
|
||||
|
|
@ -187,90 +204,102 @@ 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")));
|
||||
ereport(LOG, (errmsg("bgwriter started"))); // 记录日志,标记 bgwriter 开始运行
|
||||
|
||||
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();
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除信号掩码
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除 SIGUSR2 信号的阻塞
|
||||
|
||||
/*
|
||||
* Use the recovery target timeline ID during recovery
|
||||
*/
|
||||
if (RecoveryInProgress())
|
||||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
|
||||
if (RecoveryInProgress()) // 如果正在进行恢复操作
|
||||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI(); // 获取恢复目标时间线 ID
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
|
|
@ -279,25 +308,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);
|
||||
ResetLatch(&t_thrd.proc->procLatch); // 重置进程的 latch
|
||||
|
||||
pgstat_report_activity(STATE_RUNNING, NULL);
|
||||
pgstat_report_activity(STATE_RUNNING, NULL); // 报告活动状态为运行中
|
||||
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) { // 如果收到了 SIGHUP 信号
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false; // 清除 SIGHUP 信号标志
|
||||
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 */
|
||||
}
|
||||
|
|
@ -310,16 +339,24 @@ 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();
|
||||
smgrcloseall(); // 关闭所有 smgr 文件
|
||||
}
|
||||
// 描述在后台写入进程(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
|
||||
|
|
@ -340,21 +377,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(); // 记录检查槽信息
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,12 +405,16 @@ 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
|
||||
|
|
@ -406,10 +447,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)
|
||||
gs_thread_exit(1);
|
||||
if (rc & WL_POSTMASTER_DEATH) // 如果 postmaster 已经死亡
|
||||
gs_thread_exit(1); // 紧急退出
|
||||
|
||||
prev_hibernate = can_hibernate;
|
||||
prev_hibernate = can_hibernate; // 更新前一个休眠状态
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,9 +464,23 @@ 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
|
||||
|
|
@ -435,7 +490,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();
|
||||
on_exit_reset(); // 重置 exit() 回调函数
|
||||
|
||||
/*
|
||||
* Note we do exit(2) not exit(0). This is to force the postmaster into a
|
||||
|
|
@ -445,88 +500,139 @@ 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); // 使用 exit(2) 退出进程,强制 postmaster 进入系统复位周期
|
||||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
// 功能:当收到 SIGHUP 信号时,设置标志以在下一个合适的时机重新读取配置文件
|
||||
static void bgwriter_sighup_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的 errno 值
|
||||
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = true;
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = true; // 设置收到 SIGHUP 信号的标志
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置进程的标志事件,以便重新读取配置文件
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的 errno 值
|
||||
}
|
||||
|
||||
/* SIGTERM: set flag to shutdown and exit */
|
||||
// 功能:当收到 SIGTERM 信号时,设置标志以请求关闭并退出
|
||||
static void bgwriter_request_shutdown_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = 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 = save_errno; // 恢复之前保存的 errno 值
|
||||
}
|
||||
|
||||
/* SIGUSR1: used for latch wakeups */
|
||||
// 功能:用于标志事件唤醒
|
||||
static void bgwriter_sigusr1_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的 errno 值
|
||||
|
||||
latch_sigusr1_handler();
|
||||
latch_sigusr1_handler(); // 处理 SIGUSR1 信号,通常用于唤醒标志事件
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的 errno 值
|
||||
}
|
||||
|
||||
// 检查当前进程是否为后台写入进程
|
||||
// 参数: 无
|
||||
// 返回值:bool类型 [如果当前进程的角色是后台写入进程,则返回 true]
|
||||
bool IsBgwriterProcess(void)
|
||||
{
|
||||
return (t_thrd.role == BGWRITER);
|
||||
return (t_thrd.role == BGWRITER); // 如果当前进程的角色是后台写入进程,则返回 true
|
||||
}
|
||||
|
||||
/* 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");
|
||||
return CStringGetTextDatum("not define");// 如果节点名称未定义,则返回 "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);
|
||||
return Int64GetDatum(0); // 返回整数 0,表示实际刷新次数
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:获取上一次后台写入进程执行的刷新次数。
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值: 返回整数 0,表示上次刷新次数。
|
||||
*/
|
||||
Datum bgwriter_view_get_last_flush_num()
|
||||
{
|
||||
return Int32GetDatum(0);
|
||||
return Int32GetDatum(0); // 返回整数 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},
|
||||
|
|
@ -535,17 +641,24 @@ 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;
|
||||
sigjmp_buf localSigjmpBuf; // 用于处理异常的跳转标记
|
||||
MemoryContext bgwriter_context; // 内存上下文,用于执行工作并处理错误恢复
|
||||
char name[MAX_THREAD_NAME_LEN] = {0}; // 线程名称
|
||||
WritebackContext wb_context; // 写入上下文,用于配置写入行为
|
||||
t_thrd.role = SPBGWRITER; // 设置线程角色为 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, "", "");
|
||||
|
|
@ -564,10 +677,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);
|
||||
}
|
||||
|
||||
|
|
@ -575,7 +688,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 */
|
||||
|
|
@ -583,187 +696,230 @@ 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;
|
||||
pgstat_report_appname("InvalidBufferBgWriter"); // 设置应用程序名称以进行统计
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告进程状态为空闲
|
||||
g_instance.bgwriter_cxt.invalid_buf_proc_latch = &t_thrd.proc->procLatch; // 设置 Latch 用于等待事件触发
|
||||
/* Loop forever */
|
||||
for (;;) {
|
||||
int rc;
|
||||
|
||||
if (t_thrd.bgwriter_cxt.got_SIGHUP) {
|
||||
t_thrd.bgwriter_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
ProcessConfigFile(PGC_SIGHUP); // 处理 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);
|
||||
gs_thread_exit(1); // 如果 Postmaster 已经关闭,线程退出
|
||||
}
|
||||
|
||||
/* 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));
|
||||
securec_check(rc, "", "");
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
|
||||
hashCtrl.hash = tag_hash;
|
||||
hashCtrl.keysize = sizeof(RelFileNode);
|
||||
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); // 设置键的大小
|
||||
/* keep entrysize >= keysize, stupid limits */
|
||||
hashCtrl.entrysize = sizeof(DelFileTag);
|
||||
hashCtrl.entrysize = sizeof(DelFileTag); // 设置哈希表中每个条目的大小
|
||||
|
||||
if (use_heap_mem) {
|
||||
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")));
|
||||
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; // 返回创建的哈希表指针
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建关系文件节点分支哈希表,用于存储关于删除的关系文件分支的信息
|
||||
*
|
||||
* 参数:
|
||||
* name - 哈希表的名称
|
||||
* use_heap_mem - 是否使用堆内存
|
||||
*
|
||||
* 返回值:
|
||||
* 返回创建的哈希表
|
||||
*/
|
||||
HTAB *relfilenode_fork_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));
|
||||
securec_check(rc, "", "");
|
||||
hashCtrl.hcxt = (MemoryContext)CurrentMemoryContext;
|
||||
hashCtrl.hash = tag_hash;
|
||||
hashCtrl.keysize = sizeof(ForkRelFileNode);
|
||||
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);
|
||||
hashCtrl.entrysize = sizeof(DelForkFileTag); // 设置哈希表中每个条目的大小
|
||||
|
||||
if (use_heap_mem) {
|
||||
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));
|
||||
&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;
|
||||
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) {
|
||||
hash_seq_init(&status, rel_bak); // 初始化哈希表遍历状态
|
||||
while ((temp_entry = (DelFileTag *)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->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,12 +56,23 @@ 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;
|
||||
sigjmp_buf local_sigjmp_buf; // 用于错误处理的跳转点
|
||||
ResourceOwner cbmwriter_resourceOwner; // 用于跟踪 CBM Writer 进程的资源
|
||||
|
||||
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;
|
||||
|
|
@ -75,31 +86,33 @@ void CBMWriterMain(void)
|
|||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(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 */
|
||||
(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 信号
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(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); // 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 信号在终端窗口大小变化时发出
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); // 允许 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;
|
||||
|
|
@ -109,6 +122,7 @@ 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,
|
||||
|
|
@ -124,24 +138,26 @@ 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);
|
||||
AtEOXact_SysDBCache(false); // 释放由 lsc 持有的资源
|
||||
/*
|
||||
* These operations are really just a minimal subset of
|
||||
* AbortTransaction(). We don't have very many resources to worry
|
||||
|
|
@ -200,7 +216,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);
|
||||
|
||||
|
|
@ -209,12 +225,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 */
|
||||
|
|
@ -223,7 +239,8 @@ void CBMWriterMain(void)
|
|||
|
||||
CBMFollowXlog();
|
||||
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态
|
||||
// 等待事件的发生,包括信号量设置、超时、postmaster 死亡
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch,
|
||||
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
|
||||
(long)u_sess->attr.attr_storage.CheckPointTimeout * 1000);
|
||||
|
|
@ -231,6 +248,7 @@ 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);
|
||||
|
|
@ -248,11 +266,21 @@ 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;
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
g_instance.proc_base->cbmwriterLatch = NULL; // 设置 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
|
||||
|
|
@ -263,6 +291,10 @@ 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
|
||||
|
|
@ -275,37 +307,64 @@ 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);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
|
||||
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);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
|
||||
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();
|
||||
latch_sigusr1_handler(); // 处理SIGUSR1信号,唤醒进程
|
||||
|
||||
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,6 +43,7 @@ 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);
|
||||
|
||||
|
|
@ -57,15 +58,16 @@ pid_t fork_process(void)
|
|||
getitimer(ITIMER_PROF, &prof_itimer);
|
||||
#endif
|
||||
|
||||
result = fork();
|
||||
result = fork(); // 创建子进程
|
||||
|
||||
if (result == 0) {
|
||||
if (result == 0) { // fork成功
|
||||
/* 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
|
||||
|
|
@ -84,19 +86,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);
|
||||
int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0); // 打开OOM分数调整文件
|
||||
|
||||
/* We ignore all errors */
|
||||
if (fd >= 0) {
|
||||
char buf[16];
|
||||
int rc;
|
||||
char buf[16]; // 用于存储要写入文件的字符串
|
||||
int rc; // 用于存储write()函数的返回值
|
||||
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ);
|
||||
securec_check_intval(rcs, );
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_SCORE_ADJ); // 格式化OOM分数调整值为字符串
|
||||
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
|
||||
|
||||
rc = write(fd, buf, strlen(buf));
|
||||
(void)rc;
|
||||
close(fd);
|
||||
rc = write(fd, buf, strlen(buf)); // 将OOM分数调整值写入文件
|
||||
(void)rc; // 防止编译器警告
|
||||
close(fd); // 关闭文件
|
||||
}
|
||||
}
|
||||
#endif /* LINUX_OOM_SCORE_ADJ */
|
||||
|
|
@ -113,25 +115,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);
|
||||
int fd = open("/proc/self/oom_adj", O_WRONLY, 0); // 打开OOM调整文件
|
||||
|
||||
/* We ignore all errors */
|
||||
if (fd >= 0) {
|
||||
char buf[16];
|
||||
int rc;
|
||||
char buf[16];// 用于存储要写入文件的字符串
|
||||
int rc; // 用于存储write()函数的返回值
|
||||
|
||||
errno_t rcs = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%d\n", LINUX_OOM_ADJ);
|
||||
securec_check_intval(rcs, );
|
||||
securec_check_intval(rcs, ); // 检查snprintf_s函数的返回值
|
||||
|
||||
rc = write(fd, buf, strlen(buf));
|
||||
(void)rc;
|
||||
close(fd);
|
||||
rc = write(fd, buf, strlen(buf)); // 将OOM调整值写入文件
|
||||
(void)rc; // 防止编译器警告
|
||||
close(fd); // 关闭文件
|
||||
}
|
||||
}
|
||||
#endif /* LINUX_OOM_ADJ */
|
||||
|
||||
/* Binding static TLS variables for current thread */
|
||||
EarlyBindingTLSVariables();
|
||||
EarlyBindingTLSVariables(); // 绑定当前线程的静态TLS变量
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -60,52 +60,94 @@ 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);
|
||||
SetLatch(&t_thrd.proc->procLatch);// 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
|
||||
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;
|
||||
t_thrd.gstat_cxt.got_SIGUSR2 = true; // 设置标志以指示接收到SIGUSR2信号
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
|
||||
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);
|
||||
SetLatch(&t_thrd.proc->procLatch); // 如果进程存在,则设置进程的Latch以唤醒进程
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -118,16 +160,31 @@ 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;
|
||||
return 1; // 不匹配,返回非 0 值
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -138,26 +195,39 @@ 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);
|
||||
}
|
||||
|
|
@ -167,9 +237,17 @@ 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;
|
||||
|
|
@ -181,29 +259,31 @@ NON_EXEC_STATIC void GlobalStatsTrackerMain()
|
|||
|
||||
t_thrd.proc_cxt.MyProgName = "StatsTracker";
|
||||
|
||||
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid);
|
||||
init_ps_display("global stats process", "", "", "");
|
||||
Assert(t_thrd.proc->pid == t_thrd.proc_cxt.MyProcPid); // 断言当前进程的 PID 与 t_thrd 中记录的一致
|
||||
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);
|
||||
gspqsignal(SIGFPE, FloatExceptionHandler);
|
||||
gspqsignal(SIGCHLD, SIG_DFL);
|
||||
gspqsignal(SIGPIPE, SIG_IGN); // 用于避免进程在写入已关闭的管道时终止
|
||||
gspqsignal(SIGUSR1, procsignal_sigusr1_handler); // 用于用户自定义的信号处理
|
||||
gspqsignal(SIGUSR2, GlobalstatsSigusr2Handler); // 用于指示一个工作进程正在运行、刚刚完成或者在 fork 失败
|
||||
gspqsignal(SIGFPE, FloatExceptionHandler); // 用于处理浮点异常
|
||||
gspqsignal(SIGCHLD, SIG_DFL); // 用于处理子进程的状态变化
|
||||
|
||||
/* Early initialization */
|
||||
BaseInit();
|
||||
|
|
@ -215,74 +295,75 @@ 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();
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 取消了屏蔽以允许信号被处理
|
||||
(void)gs_signal_unblock_sigusr2(); // 取消对 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)
|
||||
if (t_thrd.gstat_cxt.got_SIGTERM) // 如果收到了 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);
|
||||
pg_usleep(1000000L); // 如果出现错误,等待至少1秒后再继续,以避免日志填充过快
|
||||
}
|
||||
|
||||
while (!t_thrd.gstat_cxt.got_SIGTERM) {
|
||||
while (!t_thrd.gstat_cxt.got_SIGTERM) { // 循环,直到收到 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;
|
||||
u_sess->stat_cxt.pgStatDBHash = NULL; // 设为NULL,确保不再引用旧的数据
|
||||
|
||||
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L);
|
||||
pg_usleep(u_sess->attr.attr_storage.ustats_tracker_naptime * 1000000L); // 使进程休眠,等待下一次获取全局统计信息的时间
|
||||
}
|
||||
|
||||
shutdown:
|
||||
|
|
@ -290,11 +371,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); // 正常退出当前进程
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -303,24 +384,41 @@ 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);
|
||||
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,用于限制并发访问全局统计信息的进程数量
|
||||
|
||||
/* 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);
|
||||
Assert(g_instance.stat_cxt.tableStat->global_stats_map != NULL); // 确保全局统计信息的映射不为 NULL
|
||||
// 定义指向数据库和表统计信息条目的指针
|
||||
PgStat_StatDBEntry *dbentry = NULL;
|
||||
PgStat_StatTabEntry *tabentry = NULL;
|
||||
errno_t rc = 0;
|
||||
|
|
@ -329,87 +427,129 @@ 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);
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_SHARED); // 获取共享锁
|
||||
// 查找匹配的条目,如果找到了,就将它的地址赋给 result ,并将 found 设置为 true;否则,result 保持为 NULL,found 保持为 false。
|
||||
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;
|
||||
|
||||
LWLock* lock = LockStartBlockHashTablePartition(tabkey, LW_EXCLUSIVE);
|
||||
bool found = true; // 用于指示在添加条目时是否找到了已存在的匹配条目
|
||||
PgStat_StartBlockTableEntry *result = NULL; // 用于存储添加的条目或者已存在的匹配条目
|
||||
|
||||
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) {
|
||||
if (!found) { // 如果有找到匹配的条目
|
||||
// 用于初始化 result 条目中的 starting_blocks 数组。它将数组中的元素设置为从 0 到 START_BLOCK_ARRAY_SIZE - 1 的连续整数值
|
||||
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);
|
||||
Assert(result); // 确保 result 变量不为 NULL
|
||||
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
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,5 @@
|
|||
// startup启动进程负责初始化服务器并执行恢复操作。一旦初始化完成,启动进程就会结束
|
||||
|
||||
/*
|
||||
*
|
||||
* startup.cpp
|
||||
|
|
@ -60,9 +62,19 @@ static void SetStaticConnNum(void);
|
|||
* Some backend has bought the farm,
|
||||
* so we need to stop what we're doing and exit.
|
||||
*/
|
||||
/*
|
||||
*功能:处理快速终止信号(当主进程发出SIGQUIT信号)
|
||||
* 紧急退出操作,不会触发正常的清理和关闭步骤,因为共享内存可能已损坏
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void startupproc_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
|
||||
|
|
@ -72,7 +84,7 @@ static void startupproc_quickdie(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
|
||||
|
|
@ -82,40 +94,62 @@ static void startupproc_quickdie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2);
|
||||
exit(2); // 以状态码 2 退出进程,表示进程异常退出
|
||||
}
|
||||
|
||||
/* SIGUSR1: let latch facility handle the signal */
|
||||
/*
|
||||
*功能: 处理 SIGUSR1 信号,用于唤醒进程中的等待操作
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void StartupProcSigUsr1Handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
latch_sigusr1_handler();
|
||||
latch_sigusr1_handler(); // 处理 SIGUSR1 信号
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
#ifndef ENABLE_MULTIPLE_NODES // 只在未启用多节点功能时才会编译和执行以下代码块
|
||||
/*
|
||||
* 功能:等待 DCF (分布式事务一致性框架)日志的应用
|
||||
* 如果成功应用所有 DCF 日志,将取消当前线程的 DCF 领导者标记,并生成相应的日志消息
|
||||
* 如果应用失败,将生成一个致命错误消息
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void WaitApplyAllDCFLog(void)
|
||||
{
|
||||
unsigned int all_applied = 0;
|
||||
// Check if walreceiver has written all DCF log into xlog
|
||||
// 检查是否启用了 DCF,以及当前线程是否被标记为 DCF 领导者
|
||||
if (g_instance.attr.attr_storage.dcf_attr.enable_dcf &&
|
||||
t_thrd.dcf_cxt.dcfCtxInfo->dcf_to_be_leader) {
|
||||
ereport(LOG, (errmsg("Begin to wait read xlog from DCF!")));
|
||||
ereport(LOG, (errmsg("Begin to wait read xlog from DCF!"))); // 生成日志消息(LOG级别) 表示开始等待从 DCF 读取 xlog
|
||||
/* Check if all the dcf log has been applied to xlog every 10 milliseconds. */
|
||||
int ret = 0;
|
||||
int ret = 0; // 用于存储后续函数调用的返回值
|
||||
// 检查是否所有 DCF 日志都已应用到 xlog
|
||||
while ((ret = dcf_check_if_all_logs_applied(1, &all_applied)) == 0) {
|
||||
// 如果已应用的日志数量不等于0,表示所有 DCF 日志都已经被应用
|
||||
if (all_applied != 0) {
|
||||
t_thrd.dcf_cxt.dcfCtxInfo->dcf_to_be_leader = false;
|
||||
ereport(LOG, (errmsg("All DCF log has been applied!")));
|
||||
return;
|
||||
t_thrd.dcf_cxt.dcfCtxInfo->dcf_to_be_leader = false; // 当前线程不再被标记为 DCF 领导者
|
||||
ereport(LOG, (errmsg("All DCF log has been applied!"))); // 生成日志消息(LOG级别),表示所有 DCF 日志都已经被应用
|
||||
return; // 函数返回,结束循环
|
||||
}
|
||||
pg_usleep(10000L); /* 10 milliseconds */
|
||||
pg_usleep(10000L); /* 10 milliseconds */ // 如果未应用的日志数量仍然为0,函数会休眠10毫秒继续循环
|
||||
}
|
||||
// 如果ret不为0,表示应用 DCF 日志失败
|
||||
if (ret) {
|
||||
t_thrd.dcf_cxt.dcfCtxInfo->dcf_to_be_leader = false;
|
||||
ereport(FATAL, (errmsg("Apply all DCF log failed for dcf return false!")));
|
||||
t_thrd.dcf_cxt.dcfCtxInfo->dcf_to_be_leader = false; // 当前线程不再被标记为 DCF 领导者
|
||||
ereport(FATAL, (errmsg("Apply all DCF log failed for dcf return false!"))); // 生成一个致命错误日志消息(FATAL级别),表示 DCF 日志的应用失败
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,158 +162,243 @@ static void WaitApplyAllDCFLog(void)
|
|||
* When the SIGUSR2 is receiverd, then check the reason of the SIGUSR2
|
||||
* and do the corresponding operations
|
||||
*/
|
||||
/*
|
||||
* 功能:处理 SIGUSR2 信号,根据不同的通知信号采取相应的操作
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void StartupProcSigusr2Handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
// 检查变量 dummyStandbyMode 是否为真,即检查是否处于虚拟备用模式
|
||||
if (dummyStandbyMode)
|
||||
return;
|
||||
return; // 如果是,返回
|
||||
|
||||
if (CheckNotifySignal(NOTIFY_PRIMARY)) {
|
||||
t_thrd.startup_cxt.primary_triggered = true;
|
||||
} else if (CheckNotifySignal(NOTIFY_STANDBY)) {
|
||||
t_thrd.startup_cxt.standby_triggered = true;
|
||||
if (CheckNotifySignal(NOTIFY_PRIMARY)) { // 检查是否收到 NOTIFY_PRIMARY 通知信号
|
||||
t_thrd.startup_cxt.primary_triggered = true; // 表示主服务器触发了某个事件
|
||||
} else if (CheckNotifySignal(NOTIFY_STANDBY)) { // 检查是否收到 NOTIFY_STANDBY 通知信号
|
||||
t_thrd.startup_cxt.standby_triggered = true; // 表示备用服务器触发了某个事件
|
||||
// 检查故障切换是否已经触发以及当前服务器是否是高可用性和灾难恢复(HADR)系统的主备模式中的主服务器
|
||||
if (t_thrd.startup_cxt.failover_triggered && t_thrd.postmaster_cxt.HaShmData->is_hadr_main_standby) {
|
||||
t_thrd.startup_cxt.failover_triggered = false;
|
||||
t_thrd.startup_cxt.failover_triggered = false; // 重置故障切换触发标志,避免多次触发故障切换逻辑,确保只有一个服务器被选为主服务器
|
||||
}
|
||||
} else if (CheckNotifySignal(NOTIFY_CASCADE_STANDBY)) {
|
||||
t_thrd.startup_cxt.standby_triggered = true;
|
||||
} else if (CheckNotifySignal(NOTIFY_FAILOVER)) {
|
||||
t_thrd.startup_cxt.failover_triggered = true;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
WaitApplyAllDCFLog();
|
||||
} else if (CheckNotifySignal(NOTIFY_CASCADE_STANDBY)) { // 检查是否收到 NOTIFY_CASCADE_STANDBY 通知信号
|
||||
t_thrd.startup_cxt.standby_triggered = true; // 表示级联备用服务器触发了某个事件
|
||||
} else if (CheckNotifySignal(NOTIFY_FAILOVER)) { // 检查是否收到 NOTIFY_FAILOVER 通知信号
|
||||
t_thrd.startup_cxt.failover_triggered = true; // 表示发生了故障切换
|
||||
#ifndef ENABLE_MULTIPLE_NODES // 如果未启用多节点功能
|
||||
WaitApplyAllDCFLog(); // 等待所有 DCF 日志的应用
|
||||
#endif
|
||||
WakeupRecovery();
|
||||
} else if (CheckNotifySignal(NOTIFY_SWITCHOVER)) {
|
||||
t_thrd.startup_cxt.switchover_triggered = true;
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
WaitApplyAllDCFLog();
|
||||
WakeupRecovery(); // 唤醒恢复进程
|
||||
} else if (CheckNotifySignal(NOTIFY_SWITCHOVER)) { // 检查是否收到 NOTIFY_SWITCHOVER 通知信号
|
||||
t_thrd.startup_cxt.switchover_triggered = true; // 表示发生了切换
|
||||
#ifndef ENABLE_MULTIPLE_NODES // 如果未启用多节点功能
|
||||
WaitApplyAllDCFLog(); // 等待所有 DCF 日志的应用
|
||||
#endif
|
||||
WakeupRecovery();
|
||||
WakeupRecovery(); // 唤醒恢复进程
|
||||
}
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
/*
|
||||
*功能:处理 SIGHUP 信号,用于重新读取配置文件
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void StartupProcSigHupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.startup_cxt.got_SIGHUP = true;
|
||||
WakeupRecovery();
|
||||
t_thrd.startup_cxt.got_SIGHUP = true; // 表示接收到 SIGHUP 信号
|
||||
WakeupRecovery(); // 唤醒恢复进程
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* SIGINT: set flag to check repair page */
|
||||
/*
|
||||
*功能:处理 SIGINT 信号 请求进程终止
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void StartupProcSigIntHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.startup_cxt.check_repair = true;
|
||||
t_thrd.startup_cxt.check_repair = true; // 表示接收到 SIGINT 信号后需要进行检查修复操作
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
|
||||
/* SIGTERM: set flag to abort redo and exit */
|
||||
/*
|
||||
*功能:处理 SIGTERM 信号,用于正常退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void StartupProcShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
if (t_thrd.startup_cxt.in_restore_command)
|
||||
proc_exit(1);
|
||||
if (t_thrd.startup_cxt.in_restore_command) // 检查当前是否正在执行恢复命令
|
||||
proc_exit(1); // 启动进程退出
|
||||
else
|
||||
t_thrd.startup_cxt.shutdown_requested = true;
|
||||
t_thrd.startup_cxt.shutdown_requested = true; // 表示已经收到了关闭请求
|
||||
|
||||
WakeupRecovery();
|
||||
WakeupRecovery(); // 唤醒恢复进程
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:处理启动过程中的页面修复
|
||||
*
|
||||
* 参数:
|
||||
* key:一个表示修复块的关键信息的结构或标识
|
||||
* pblk:一个表示物理块的标识或信息
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void HandleStartupPageRepair(RepairBlockKey key, XLogPhyBlock pblk)
|
||||
{
|
||||
XLogReaderState *record = g_instance.startup_cxt.current_record;
|
||||
XLogReaderState *record = g_instance.startup_cxt.current_record; // 用于解析和处理 WAL记录,获取当前正在处理的 WAL 记录
|
||||
// 记录坏块信息并将其推送到远程位置
|
||||
// 参数:record-当前的 WAL 记录;key-修复块的关键信息;CRC_CHECK_FAIL-表示 CRC 校验失败
|
||||
// InvalidXLogRecPtr-表示无效的 XLog 记录位置;pblk-表示物理块的标识或信息
|
||||
parallel_recovery::RecordBadBlockAndPushToRemote(record, key, CRC_CHECK_FAIL,
|
||||
InvalidXLogRecPtr, pblk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle SIGHUP and SIGTERM signals of startup process */
|
||||
/*
|
||||
* 功能:处理启动进程接收到的SIGHUP 和 SIGTERM 信号
|
||||
* -处理 SIGHUP 信号,重新读取配置文件
|
||||
* -执行块检查和修复操作
|
||||
* -处理关闭请求,但避免在智能关闭状态下退出
|
||||
* -紧急退出启动进程(当不是 Postmaster 进程或 Postmaster 进程已经终止)
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void HandleStartupProcInterrupts(void)
|
||||
{
|
||||
/*
|
||||
* Check if we were requested to re-read config file.
|
||||
*/
|
||||
if (t_thrd.startup_cxt.got_SIGHUP) {
|
||||
if (t_thrd.startup_cxt.got_SIGHUP) { // 检查是否收到 SIGHUP 信号
|
||||
t_thrd.startup_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
ProcessConfigFile(PGC_SIGHUP); // 重新读取配置文件
|
||||
}
|
||||
|
||||
if (t_thrd.startup_cxt.check_repair) {
|
||||
if (t_thrd.startup_cxt.check_repair) { // 检查是否需要执行块检查和修复操作
|
||||
// 如果当前不处于极端重做且不是并行重做
|
||||
if (!IsExtremeRedo() && !IsParallelRedo()) {
|
||||
parallel_recovery::SeqCheckRemoteReadAndRepairPage();
|
||||
parallel_recovery::SeqCheckRemoteReadAndRepairPage(); // 执行块检查和修复操作
|
||||
}
|
||||
t_thrd.startup_cxt.check_repair = false;
|
||||
t_thrd.startup_cxt.check_repair = false; // 表示检查和修复操作已完成
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we were requested to exit without finishing recovery.
|
||||
*/
|
||||
// 检查是否收到了关闭请求且非智能关闭(避免在智能关闭过程中强制退出)
|
||||
if (t_thrd.startup_cxt.shutdown_requested && SmartShutdown != g_instance.status) {
|
||||
proc_exit(1);
|
||||
proc_exit(1); // 紧急退出启动进程
|
||||
}
|
||||
|
||||
/*
|
||||
* Emergency bailout if postmaster has died. This is to avoid the
|
||||
* necessity for manual cleanup of all postmaster children.
|
||||
*/
|
||||
// 检查是否处于 Postmaster 进程下,以及Postmaster 是否还存活
|
||||
if (IsUnderPostmaster && !PostmasterIsAlive())
|
||||
gs_thread_exit(1);
|
||||
gs_thread_exit(1); // 强制退出
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:在启动过程中释放所有锁
|
||||
* 销毁哈希表(如果存在)。
|
||||
* 根据当前的热备模式状态,释放标准锁和用户级别的锁。
|
||||
* 在需要时清理与锁相关的错误状态。
|
||||
*
|
||||
* 参数:
|
||||
* code、arg:未被使用,可能是函数的历史原因或未来扩展性考虑而存在的
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void StartupReleaseAllLocks(int code, Datum arg)
|
||||
{
|
||||
Assert(t_thrd.proc != NULL);
|
||||
Assert(t_thrd.proc != NULL); // 确保当前线程的 proc 结构不为空
|
||||
|
||||
if (g_instance.startup_cxt.badPageHashTbl != NULL) {
|
||||
hash_destroy(g_instance.startup_cxt.badPageHashTbl);
|
||||
g_instance.startup_cxt.badPageHashTbl = NULL;
|
||||
if (g_instance.startup_cxt.badPageHashTbl != NULL) { // 检查哈希表是否不为空
|
||||
hash_destroy(g_instance.startup_cxt.badPageHashTbl); // 销毁哈希表,释放哈希表中的内存
|
||||
g_instance.startup_cxt.badPageHashTbl = NULL; // 表示哈希表已被销毁
|
||||
}
|
||||
|
||||
/* Do nothing if we're not in hot standby mode */
|
||||
// 检查当前线程的 standbyState 是否等于 STANDBY_DISABLED,即检查当前是否处于热备模式
|
||||
if (t_thrd.xlog_cxt.standbyState == STANDBY_DISABLED)
|
||||
return;
|
||||
return; // 不处于则返回
|
||||
|
||||
/* If waiting, get off wait queue (should only be needed after error) */
|
||||
LockErrorCleanup();
|
||||
LockErrorCleanup(); // 清理与锁相关的错误状态
|
||||
/* Release standard locks, including session-level if aborting */
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, true);
|
||||
LockReleaseAll(DEFAULT_LOCKMETHOD, true); // 释放所有标准锁(默认锁定方法)并确保它们在事务终止时被释放,防止锁定资源泄漏或死锁
|
||||
|
||||
/*
|
||||
* User locks are not released by transaction end, so be sure to release
|
||||
* them explicitly.
|
||||
*/
|
||||
LockReleaseAll(USER_LOCKMETHOD, true);
|
||||
LockReleaseAll(USER_LOCKMETHOD, true); // 释放所有用户级别的锁 并确保它们在事务结束时被释放
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:在集群备用模式下删除一个文件如果文件不存在或删除失败,会生成相应的警告消息。
|
||||
*
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void DeleteDisConnFileInClusterStandby()
|
||||
{
|
||||
if (!IS_SHARED_STORAGE_MODE) {
|
||||
if (!IS_SHARED_STORAGE_MODE) { // 检查是否处于共享存储模式
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(disable_conn_file, &st) < 0) {
|
||||
struct stat st; // 用于存储文件状态信息
|
||||
if (stat(disable_conn_file, &st) < 0) { // 如果获取文件状态信息失败
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = unlink(disable_conn_file);
|
||||
if (ret < 0) {
|
||||
int ret = unlink(disable_conn_file); // 尝试删除指定的文件,如果删除成功,则返回值为0,否则为-1
|
||||
if (ret < 0) { // 如果删除文件失败
|
||||
// 输出一条警告消息,表示无法删除文件,并包含错误消息
|
||||
ereport(WARNING, (errcode_for_file_access(), errmsg("cluster standby mode, could not remove file \"%s\": %m",
|
||||
disable_conn_file)));
|
||||
} else {
|
||||
} else { // 如果删除文件成功
|
||||
// 输出一条日志消息,表示文件删除成功
|
||||
ereport(LOG, (errcode_for_file_access(), errmsg("removed file \"%s\" success.", disable_conn_file)));
|
||||
}
|
||||
}
|
||||
|
|
@ -289,6 +408,13 @@ void DeleteDisConnFileInClusterStandby()
|
|||
* Startup Process main entry point
|
||||
* ----------------------------------
|
||||
*/
|
||||
/*
|
||||
* 功能:启动进程的主要入口函数 StartupProcessMain,负责启动进程的初始化和执行
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void StartupProcessMain(void)
|
||||
{
|
||||
/*
|
||||
|
|
@ -301,77 +427,90 @@ void StartupProcessMain(void)
|
|||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
(void)gspqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */
|
||||
(void)gspqsignal(SIGINT, StartupProcSigIntHandler); /* check repair page and file */
|
||||
(void)gspqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */
|
||||
(void)gspqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */ // 重新加载配置文件
|
||||
(void)gspqsignal(SIGINT, StartupProcSigIntHandler); /* check repair page and file */ // 检查修复页和文件
|
||||
(void)gspqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ // 请求关闭
|
||||
(void)gspqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */
|
||||
|
||||
if (g_instance.attr.attr_storage.EnableHotStandby)
|
||||
(void)gspqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless
|
||||
if (g_instance.attr.attr_storage.EnableHotStandby) // 检查是否启用了热备模式
|
||||
(void)gspqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless // 如果启用了热备模式,设置SIGALRM的信号处理函数
|
||||
|
||||
* InHotStandby */
|
||||
else
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN); // 否则忽略该信号
|
||||
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
(void)gspqsignal(SIGUSR1, StartupProcSigUsr1Handler);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE
|
||||
(void)gspqsignal(SIGUSR1, StartupProcSigUsr1Handler); // 设置信号处理函数
|
||||
(void)gspqsignal(SIGUSR2, StartupProcSigusr2Handler);
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here
|
||||
*/
|
||||
// 设置信号处理函数为默认行为
|
||||
(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)RegisterRedoInterruptCallBack(HandleStartupProcInterrupts);
|
||||
(void)RegisterRedoPageRepairCallBack(HandleStartupPageRepair);
|
||||
/*
|
||||
* Unblock signals (they were blocked when the postmaster forked us)
|
||||
*/
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); // 解除信号的阻塞状态,允许信号传递
|
||||
(void)gs_signal_unblock_sigusr2(); // 解除对 SIGUSR2 信号的阻塞
|
||||
|
||||
// 创建了资源拥有者,用于跟踪和管理资源的分配和释放,确保在启动进程执行期间对资源的正确管理
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "StartupXLOG",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
|
||||
SetStaticConnNum();
|
||||
pgstat_report_appname("Startup");
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
SetStaticConnNum(); // 设置静态连接数,用于配置启动进程的连接池参数
|
||||
pgstat_report_appname("Startup"); // 设置进程的应用程序名称
|
||||
pgstat_report_activity(STATE_IDLE, NULL); // 设置进程的活动状态为 "STATE_IDLE",表示进程当前处于空闲状态
|
||||
|
||||
if (dummyStandbyMode) {
|
||||
StartupDummyStandby();
|
||||
if (dummyStandbyMode) { // 检查是否处于虚拟备用模式
|
||||
StartupDummyStandby(); // 如果处于虚拟备用模式,执行虚拟备用模式下的操作
|
||||
} else {
|
||||
on_shmem_exit(StartupReleaseAllLocks, 0);
|
||||
on_shmem_exit(StartupReleaseAllLocks, 0); // 注册一个在共享内存退出时执行的回调函数,用于释放所有锁
|
||||
|
||||
#ifdef ENABLE_MOT
|
||||
#ifdef ENABLE_MOT // 检查是否启用了 MOT 存储引擎
|
||||
// 如果启用了 执行以下代码块
|
||||
/*
|
||||
* Init MOT first
|
||||
*/
|
||||
InitMOT();
|
||||
InitMOT(); // 进行 MOT 存储引擎的初始化
|
||||
|
||||
/*
|
||||
* MOT recovery is part of StartupXlog
|
||||
*/
|
||||
#endif
|
||||
DeleteDisConnFileInClusterStandby();
|
||||
if (!dummyStandbyMode) {
|
||||
DeleteDisConnFileInClusterStandby(); // 删除集群备用模式下的连接文件(如果存在)
|
||||
if (!dummyStandbyMode) { // 检查是否处于虚拟备用模式
|
||||
// 如果不是 NULL,表示该表已经存在,发生错误。确保在创建之前没有遗留的数据结构
|
||||
Assert(g_instance.startup_cxt.badPageHashTbl == NULL);
|
||||
// 创建一个坏块哈希表,用于记录坏块的信息,在恢复过程中跟踪坏块的状态
|
||||
g_instance.startup_cxt.badPageHashTbl = parallel_recovery::BadBlockHashTblCreate();
|
||||
}
|
||||
|
||||
StartupXLOG();
|
||||
StartupXLOG(); // 处理事务日志和执行数据库恢复
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit normally. Exit code 0 tells postmaster that we completed recovery
|
||||
* successfully.
|
||||
*/
|
||||
proc_exit(0);
|
||||
proc_exit(0); // 正常退出进程。退出码为0表示成功完成恢复过程,通知主进程恢复成功
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:在执行恢复命令之前进行一些预处理操作
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void PreRestoreCommand(void)
|
||||
{
|
||||
/*
|
||||
|
|
@ -380,37 +519,53 @@ void PreRestoreCommand(void)
|
|||
* Check if we had already received the signal, so that we don't miss a
|
||||
* shutdown request received just before this.
|
||||
*/
|
||||
t_thrd.startup_cxt.in_restore_command = true;
|
||||
t_thrd.startup_cxt.in_restore_command = true; // 表示启动进程正在执行恢复命令
|
||||
|
||||
if (t_thrd.startup_cxt.shutdown_requested) {
|
||||
proc_exit(1);
|
||||
if (t_thrd.startup_cxt.shutdown_requested) { // 检查是否收到关闭请求, 以免错过在执行恢复命令前立即退出的机会
|
||||
proc_exit(1); // 如果收到关闭请求,退出
|
||||
}
|
||||
}
|
||||
|
||||
void PostRestoreCommand(void)
|
||||
{
|
||||
t_thrd.startup_cxt.in_restore_command = false;
|
||||
t_thrd.startup_cxt.in_restore_command = false; // 退出恢复命令状态
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否触发了故障切换信号
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:bool类型 ,如果触发了则返回 true,否则返回 false。
|
||||
*/
|
||||
bool IsFailoverTriggered(void)
|
||||
{
|
||||
if (AmStartupProcess()) {
|
||||
return t_thrd.startup_cxt.failover_triggered;
|
||||
if (AmStartupProcess()) { // 检查当前是否处于启动进程
|
||||
return t_thrd.startup_cxt.failover_triggered; // 返回,表示故障切换信号已触发
|
||||
} else {
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState));
|
||||
if (tgigger == (uint32)extreme_rto::TRIGGER_FAILOVER) {
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState)); // 通过原子操作读取全局变量,以确保线程安全
|
||||
// 检查读取的值是否等于 extreme_rto::TRIGGER_FAILOVER,如果相等,表示故障切换信号已触发
|
||||
if (tgigger == (uint32)extreme_rto::TRIGGER_FAILOVER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否触发了切换信号
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:bool类型,如果触发了则返回 true,否则返回 false。
|
||||
*/
|
||||
bool IsSwitchoverTriggered(void)
|
||||
{
|
||||
if (AmStartupProcess()) {
|
||||
return t_thrd.startup_cxt.switchover_triggered;
|
||||
if (AmStartupProcess()) { // 检查当前是否处于启动进程
|
||||
return t_thrd.startup_cxt.switchover_triggered; // 返回,表示切换信号已触发
|
||||
} else {
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState));
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState)); // 通过原子操作读取全局变量,以确保线程安全
|
||||
// 检查读取的值是否等于 extreme_rto::TRIGGER_SWITCHOVER,如果相等,表示切换信号已触发
|
||||
if (tgigger == (uint32)extreme_rto::TRIGGER_SWITCHOVER) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -418,12 +573,20 @@ bool IsSwitchoverTriggered(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否触发了主节点信号
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:bool类型,如果触发了则返回 true,否则返回 false。
|
||||
*/
|
||||
bool IsPrimaryTriggered(void)
|
||||
{
|
||||
if (AmStartupProcess()) {
|
||||
return t_thrd.startup_cxt.primary_triggered;
|
||||
if (AmStartupProcess()) { // 检查当前是否处于启动进程
|
||||
return t_thrd.startup_cxt.primary_triggered; // 返回,表示主节点信号已触发
|
||||
} else {
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState));
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState)); // 通过原子操作读取全局变量,以确保线程安全
|
||||
// 检查读取的值是否等于 extreme_rto::TRIGGER_PRIMARY,如果相等,表示主节点信号已触发
|
||||
if (tgigger == (uint32)extreme_rto::TRIGGER_PRIMARY) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -431,12 +594,20 @@ bool IsPrimaryTriggered(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否触发了备用节点信号
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:bool类型,如果触发了则返回 true,否则返回 false。
|
||||
*/
|
||||
bool IsStandbyTriggered(void)
|
||||
{
|
||||
if (AmStartupProcess()) {
|
||||
return t_thrd.startup_cxt.standby_triggered;
|
||||
if (AmStartupProcess()) { // 检查当前是否处于启动进程
|
||||
return t_thrd.startup_cxt.standby_triggered; // 返回,表示备用节点信号已触发
|
||||
} else {
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState));
|
||||
uint32 tgigger = pg_atomic_read_u32(&(extreme_rto::g_startupTriggerState)); // 通过原子操作读取全局变量,以确保线程安全
|
||||
// 检查读取的值是否等于 extreme_rto::TRIGGER_STADNBY,如果相等,表示备用节点信号已触发
|
||||
if (tgigger == (uint32)extreme_rto::TRIGGER_STADNBY) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -444,44 +615,92 @@ bool IsStandbyTriggered(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
// 重置触发信号的状态变量
|
||||
|
||||
/*
|
||||
* 功能:重置故障切换信号触发状态
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void ResetFailoverTriggered(void)
|
||||
{
|
||||
t_thrd.startup_cxt.failover_triggered = false;
|
||||
t_thrd.startup_cxt.failover_triggered = false; // 表示故障切换信号未触发
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置切换信号触发状态
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void ResetSwitchoverTriggered(void)
|
||||
{
|
||||
t_thrd.startup_cxt.switchover_triggered = false;
|
||||
t_thrd.startup_cxt.switchover_triggered = false; // 表示切换信号未触发
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置主节点信号触发状态
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void ResetPrimaryTriggered(void)
|
||||
{
|
||||
t_thrd.startup_cxt.primary_triggered = false;
|
||||
t_thrd.startup_cxt.primary_triggered = false; // 表示主节点信号未触发
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置备用节点信号触发状态
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void ResetStandbyTriggered(void)
|
||||
{
|
||||
t_thrd.startup_cxt.standby_triggered = false;
|
||||
t_thrd.startup_cxt.standby_triggered = false; // 表示备用节点信号未触发
|
||||
}
|
||||
|
||||
// 管理共享内存中的通知信号数据
|
||||
|
||||
/*
|
||||
* 功能:计算通知信号数据结构的大小
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:Size类型,返回通知信号数据结构的大小
|
||||
*/
|
||||
Size NotifySignalShmemSize(void)
|
||||
{
|
||||
Size size = 0;
|
||||
|
||||
size = add_size(size, sizeof(NotifySignalData));
|
||||
size = add_size(size, sizeof(NotifySignalData)); // 获取通知信号数据结构 NotifySignalData 的大小,并添加到 size 变量中
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化通知信号数据结构的共享内存
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void NotifySignalShmemInit(void)
|
||||
{
|
||||
bool found = false;
|
||||
errno_t rc = 0;
|
||||
bool found = false; // 用于表示在共享内存中是否已经找到通知信号数据结构
|
||||
errno_t rc = 0; // 用于处理错误码
|
||||
|
||||
// 获取通知信号数据结构的共享内存指针,获取成功 found->true
|
||||
t_thrd.startup_cxt.NotifySigState =
|
||||
(NotifySignalData*)ShmemInitStruct("NotifySignalState", NotifySignalShmemSize(), &found);
|
||||
|
||||
if (!found) {
|
||||
if (!found) { // 如果在共享内存中没有找到通知信号数据结构
|
||||
// 将通知信号数据结构的内存区域初始化为0,以确保数据的初始状态是清零的
|
||||
rc = memset_s(t_thrd.startup_cxt.NotifySigState, NotifySignalShmemSize(), 0, NotifySignalShmemSize());
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
|
@ -490,11 +709,23 @@ void NotifySignalShmemInit(void)
|
|||
/*
|
||||
* Set the reason in notify signal share memory, and send the SIGUSR2 to the process
|
||||
*/
|
||||
|
||||
/*
|
||||
* 功能:发送通知信号并触发相应操作
|
||||
*
|
||||
* 参数:
|
||||
* ProcPid-进程的线程标识
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void SendNotifySignal(NotifyReason reason, ThreadId ProcPid)
|
||||
{
|
||||
// 根据指定的通知原因 reason 将数组中相应的标志设置为 true,表示某个特定事件或条件已经发生
|
||||
t_thrd.startup_cxt.NotifySigState->NotifySignalFlags[reason] = true;
|
||||
|
||||
// 使用 gs_signal_send 函数向指定的进程(由 ProcPid 参数指定)发送 SIGUSR2 信号
|
||||
if (0 != gs_signal_send(ProcPid, SIGUSR2)) {
|
||||
// 如果发送信号失败,生成一个警告消息,指示信号发送失败
|
||||
ereport(WARNING, (errmsg("Send signal failed")));
|
||||
}
|
||||
}
|
||||
|
|
@ -502,10 +733,22 @@ void SendNotifySignal(NotifyReason reason, ThreadId ProcPid)
|
|||
/*
|
||||
* Check the notify sinal share memory, and find the reason of the signal.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 功能:检查通知信号
|
||||
*
|
||||
* 参数:
|
||||
* reason-要检查的通知原
|
||||
*
|
||||
* 返回值:
|
||||
* bool类型,如果通知信号已触发,返回true;否则,返回false
|
||||
*/
|
||||
bool CheckNotifySignal(NotifyReason reason)
|
||||
{
|
||||
/* Careful here --- don't clear flag if we haven't seen it set */
|
||||
// 根据指定的通知原因 reason 检查数组中相应的标志是否为 true,即某个特定事件或条件是否已经发生
|
||||
if (t_thrd.startup_cxt.NotifySigState->NotifySignalFlags[reason]) {
|
||||
// 重新设置为false,表示通知信号已被处理
|
||||
t_thrd.startup_cxt.NotifySigState->NotifySignalFlags[reason] = false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -515,19 +758,29 @@ bool CheckNotifySignal(NotifyReason reason)
|
|||
/*
|
||||
* Updata the static connection numbers in HaSHmData
|
||||
*/
|
||||
|
||||
/*
|
||||
* 功能:更新哈希共享内存数据结构中的静态连接数
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void SetStaticConnNum(void)
|
||||
{
|
||||
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData;
|
||||
volatile HaShmemData* hashmdata = t_thrd.postmaster_cxt.HaShmData; // 用于存储与高可用相关的共享信息
|
||||
int i = 0;
|
||||
int repl_list_num = 0;
|
||||
int repl_list_num = 0; // 用于统计连接数
|
||||
|
||||
for (i = 1; i < MAX_REPLNODE_NUM; i++) {
|
||||
for (i = 1; i < MAX_REPLNODE_NUM; i++) { // 循环检查连接数组中的每个元素
|
||||
// 检查 ReplConnArray 数组中的元素是否为空指针,如果不为空则增加 repl_list_num 计数器的值
|
||||
repl_list_num = (t_thrd.postmaster_cxt.ReplConnArray[i] != NULL) ? (repl_list_num + 1) : repl_list_num;
|
||||
// 检查 CrossClusterReplConnArray 数组中的元素是否为空指针,如果不为空则增加 repl_list_num 计数器的值
|
||||
repl_list_num =
|
||||
(t_thrd.postmaster_cxt.CrossClusterReplConnArray[i] != NULL) ? (repl_list_num + 1) : repl_list_num;
|
||||
SpinLockAcquire(&hashmdata->mutex);
|
||||
hashmdata->repl_list_num = repl_list_num;
|
||||
SpinLockRelease(&hashmdata->mutex);
|
||||
SpinLockAcquire(&hashmdata->mutex); // 获取哈希共享内存数据结构中的互斥锁
|
||||
hashmdata->repl_list_num = repl_list_num; // 更新计算得到的连接数值
|
||||
SpinLockRelease(&hashmdata->mutex); // 释放互斥锁
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -62,16 +62,18 @@
|
|||
#include "utils/resowner.h"
|
||||
|
||||
#include "gssignal/gs_signal.h"
|
||||
|
||||
// 用于将毫秒和秒转换为纳秒的常量值
|
||||
#define NANOSECONDS_PER_MILLISECOND 1000000L
|
||||
#define NANOSECONDS_PER_SECOND 1000000000L
|
||||
|
||||
/* Signal handlers */
|
||||
// 声明信号处理函数
|
||||
static void wal_quickdie(SIGNAL_ARGS);
|
||||
static void WalSigHupHandler(SIGNAL_ARGS);
|
||||
static void WalShutdownHandler(SIGNAL_ARGS);
|
||||
static void walwriter_sigusr1_handler(SIGNAL_ARGS);
|
||||
|
||||
// 表示WAL writer的睡眠超时时间
|
||||
THR_LOCAL const int g_sleep_timeout_ms = 300; /* WAL writer sleep timeout in millisecond. */
|
||||
|
||||
/*
|
||||
|
|
@ -80,20 +82,32 @@ THR_LOCAL const int g_sleep_timeout_ms = 300; /* WAL writer sleep timeout in mil
|
|||
* This is invoked from AuxiliaryProcessMain, which has already created the
|
||||
* basic execution environment, but not enabled signals yet.
|
||||
*/
|
||||
/*
|
||||
* 功能: 主要入口函数,它负责WAL写入和相关任务的处理
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void WalWriterMain(void)
|
||||
{
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
MemoryContext walwriter_context;
|
||||
sigset_t old_sig_mask;
|
||||
bool wrote_something = true;
|
||||
long times_wrote_nothing = 0;
|
||||
struct timespec time_to_wait;
|
||||
int sleep_times_counter = 0;
|
||||
int time_out_counter = 0;
|
||||
sigjmp_buf local_sigjmp_buf; // 用于实现异常跳转点
|
||||
MemoryContext walwriter_context; // 用于创建WAL writer进程的工作内存上下文
|
||||
sigset_t old_sig_mask; // 用于保存函数中的信号屏蔽状态
|
||||
bool wrote_something = true; // 用于跟踪WAL writer是否在当前循环中写入了任何内容
|
||||
long times_wrote_nothing = 0; // 用于计算WAL writer在连续循环中没有写入任何WAL记录的次数
|
||||
struct timespec time_to_wait; // 用于表示时间间隔
|
||||
int sleep_times_counter = 0; // 用于计算WAL writer执行休眠操作的次数
|
||||
int time_out_counter = 0; // 用于计算WAL writer在等待某个条件时超时的次数
|
||||
|
||||
load_server_mode();
|
||||
load_server_mode(); // 加载WAL writer进程的服务器模式
|
||||
// 检查当前数据库服务器的运行模式
|
||||
// 如果服务器模式是主服务器模式或正常模式
|
||||
if (t_thrd.xlog_cxt.server_mode == PRIMARY_MODE ||
|
||||
t_thrd.xlog_cxt.server_mode == NORMAL_MODE) {
|
||||
// 解释在何种情况会将 isWalWriterUp 标志设置为 true
|
||||
// isWalWriterUp 用于表示WAL writer线程不仅被创建以及将WAL日志从WAL缓冲区写入到磁盘中
|
||||
// 用于告诉其他线程WAL writer正在运行并且正在刷新WAL缓冲区
|
||||
/*
|
||||
* Different from WalWriterPID, isWalWriterUp is used to signal that
|
||||
* the WAL writer thread is not only created, it is also created to
|
||||
|
|
@ -107,16 +121,22 @@ void WalWriterMain(void)
|
|||
*/
|
||||
g_instance.wal_cxt.isWalWriterUp = true;
|
||||
}
|
||||
|
||||
// 生成日志消息,表明WAL writer已经启动
|
||||
ereport(LOG, (errmsg("walwriter started")));
|
||||
|
||||
// 检查配置参数 walwriter_cpu_bind 是否大于等于零
|
||||
// 说明要为WAL writer指定CPU核心
|
||||
if (g_instance.attr.attr_storage.walwriter_cpu_bind >= 0) {
|
||||
cpu_set_t walWriterSet;
|
||||
CPU_ZERO(&walWriterSet);
|
||||
cpu_set_t walWriterSet; // 用于表示CPU亲和性掩码
|
||||
CPU_ZERO(&walWriterSet); // 初始化为一个空的CPU亲和性掩码
|
||||
// 将指定的CPU核心添加到 walWriterSet 中
|
||||
// 指定了WAL writer进程应该绑定到的CPU核心
|
||||
// 将WAL writer限制在这个特定的核心上运行
|
||||
CPU_SET(g_instance.attr.attr_storage.walwriter_cpu_bind, &walWriterSet);
|
||||
|
||||
// 将WAL writer进程绑定到CPU核心
|
||||
int rc = sched_setaffinity(0, sizeof(cpu_set_t), &walWriterSet);
|
||||
// 如果绑定失败
|
||||
if (rc == -1) {
|
||||
// 错误报告
|
||||
ereport(FATAL, (errcode(ERRCODE_OPERATE_INVALID_PARAM), errmsg("Invalid attribute for thread pool."),
|
||||
errdetail("Current thread num %d is out of range.", g_instance.attr.attr_storage.walwriter_cpu_bind)));
|
||||
}
|
||||
|
|
@ -129,18 +149,26 @@ void WalWriterMain(void)
|
|||
*
|
||||
* Reset some signals that are accepted by postmaster but not here.
|
||||
*/
|
||||
// 设置信号处理程序
|
||||
// SIGHUP 重新加载配置文件
|
||||
(void)gspqsignal(SIGHUP, WalSigHupHandler); /* set flag to read config file */
|
||||
// SIGINT SIGTERM 请求进程正常终止或关闭
|
||||
(void)gspqsignal(SIGINT, WalShutdownHandler); /* request shutdown */
|
||||
(void)gspqsignal(SIGTERM, WalShutdownHandler); /* request shutdown */
|
||||
// SIGQUIT 请求进程立即终止
|
||||
(void)gspqsignal(SIGQUIT, wal_quickdie); /* hard crash time */
|
||||
// SIGALRM SIGPIPE 忽略
|
||||
(void)gspqsignal(SIGALRM, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
// SIGUSR1 用于自定义用途
|
||||
(void)gspqsignal(SIGUSR1, walwriter_sigusr1_handler);
|
||||
// SIGUSR2 忽略
|
||||
(void)gspqsignal(SIGUSR2, SIG_IGN); /* not used */
|
||||
|
||||
/*
|
||||
* Reset some signals that are accepted by postmaster but not here.
|
||||
*/
|
||||
// 重置默认值,以下信号在WAL writer进程中不需要特殊处理
|
||||
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
||||
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
||||
|
|
@ -148,12 +176,14 @@ void WalWriterMain(void)
|
|||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
// 保证在需要的情况下能够立即终止进程
|
||||
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).
|
||||
*/
|
||||
// 创建资源管理器
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Wal Writer",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
|
||||
|
|
@ -163,11 +193,13 @@ void WalWriterMain(void)
|
|||
* possible memory leaks. Formerly this code just ran in
|
||||
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
|
||||
*/
|
||||
// 创建内存上下文,用于执行WAL writer进程的所有工作
|
||||
walwriter_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"Wal Writer",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
// 切换到新的内存上下文
|
||||
(void)MemoryContextSwitchTo(walwriter_context);
|
||||
|
||||
/*
|
||||
|
|
@ -177,61 +209,75 @@ void WalWriterMain(void)
|
|||
*/
|
||||
int curTryCounter;
|
||||
int* oldTryCounter = NULL;
|
||||
// 设置异常跳转点
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
/*
|
||||
* Close all open files after any error. This is helpful on Windows,
|
||||
* where holding deleted files open causes various strange errors.
|
||||
* It's not clear we need it elsewhere, but shouldn't hurt.
|
||||
*/
|
||||
// 关闭所有打开的文件
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
|
||||
/* We need restore the signal mask of current thread. */
|
||||
// 恢复之前保存的信号掩码,以确保信号处理恢复到之前的状态
|
||||
pthread_sigmask(SIG_SETMASK, &old_sig_mask, NULL);
|
||||
|
||||
/* 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;
|
||||
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(); // 报告错误到服务器日志
|
||||
|
||||
/* abort async io, must before LWlock release */
|
||||
AbortAsyncListIO();
|
||||
AbortAsyncListIO(); // 中止异步I/O操作
|
||||
|
||||
/* 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 walwriter, but we do have LWLocks, and perhaps buffers?
|
||||
*/
|
||||
LWLockReleaseAll();
|
||||
pgstat_report_waitevent(WAIT_EVENT_END);
|
||||
AbortBufferIO();
|
||||
UnlockBuffers();
|
||||
LWLockReleaseAll(); // 释放锁
|
||||
pgstat_report_waitevent(WAIT_EVENT_END); // 报告等待事件结束,用于性能统计和诊断
|
||||
AbortBufferIO(); // 中止缓冲区I/O操作的函数调用
|
||||
UnlockBuffers(); // 解锁缓冲区,确保在异常情况下不会保留未解锁的缓冲区
|
||||
/* buffer pins are released here: */
|
||||
// 释放资源,包括资源所有者持有的资源
|
||||
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
|
||||
/* we needn't bother with the other ResourceOwnerRelease phases */
|
||||
// 处理事务结束的函数调用 与缓冲区相关的清理工作
|
||||
AtEOXact_Buffers(false);
|
||||
// 处理事务结束的函数调用 与存储管理器相关的清理工作
|
||||
AtEOXact_SMgr();
|
||||
// 处理事务结束的函数调用 用于关闭与文件操作相关的资源
|
||||
AtEOXact_Files();
|
||||
// 处理事务结束的函数调用 用于清理哈希表和相关数据结构
|
||||
AtEOXact_HashTables(false);
|
||||
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
// 切换程序的内存上下文
|
||||
(void)MemoryContextSwitchTo(walwriter_context);
|
||||
// 清除错误状态,将之前可能记录的错误信息和状态重置为空
|
||||
FlushErrorState();
|
||||
|
||||
/* Flush any leaked data in the top-level context */
|
||||
// 重置 walwriter_context 上下文并删除其所有子级上下文
|
||||
// 确保在异常处理完成后释放已分配的内存,以避免内存泄漏
|
||||
MemoryContextResetAndDeleteChildren(walwriter_context);
|
||||
|
||||
/* Now we can allow interrupts again */
|
||||
// 允许中断信号再次被捕获
|
||||
RESUME_INTERRUPTS();
|
||||
|
||||
/*
|
||||
|
|
@ -239,78 +285,97 @@ void WalWriterMain(void)
|
|||
* to be repeated, and we don't want to be filling the error logs as
|
||||
* fast as we can.
|
||||
*/
|
||||
pg_usleep(1000000L);
|
||||
pg_usleep(1000000L); // 休眠
|
||||
}
|
||||
// 记录当前的错误处理堆栈计数器
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
|
||||
/* We can now handle ereport(ERROR) */
|
||||
// 将错误处理的跳转缓冲区设置为当前线程的错误处理堆栈
|
||||
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);
|
||||
// 解除对 SIGUSR2 信号的屏蔽,允许接收 SIGUSR2 信号
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/*
|
||||
* Reset hibernation state after any error.
|
||||
*/
|
||||
// 设置 walwriter 进程的休眠状态为不休眠
|
||||
SetWalWriterSleeping(false);
|
||||
|
||||
/*
|
||||
* Advertise our latch that backends can use to wake us up while we're
|
||||
* sleeping.
|
||||
*/
|
||||
// 将 walwriter 进程的标识设置为当前线程的标识
|
||||
// 允许其他后台进程使用标识唤醒 walwriter 进程
|
||||
g_instance.proc_base->walwriterLatch = &t_thrd.proc->procLatch;
|
||||
|
||||
// 报告 walwriter 进程的应用程序名称,用于性能统计和监控
|
||||
pgstat_report_appname("Wal Writer");
|
||||
// 报告 walwriter 进程的活动状态为 "STATE_IDLE",表示当前进程处于空闲状态
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
|
||||
/*
|
||||
* Loop forever
|
||||
*/
|
||||
for (;;) {
|
||||
// 报告当前活动状态为 "STATE_RUNNING",表示进程正在执行
|
||||
pgstat_report_activity(STATE_RUNNING, NULL);
|
||||
|
||||
/*
|
||||
* Process any requests or signals received recently.
|
||||
*/
|
||||
// 如果收到了 SIGHUP 信号
|
||||
if (t_thrd.walwriter_cxt.got_SIGHUP) {
|
||||
t_thrd.walwriter_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
ProcessConfigFile(PGC_SIGHUP); // 重新读取配置文件
|
||||
}
|
||||
|
||||
// 如果收到了关闭请求
|
||||
if (t_thrd.walwriter_cxt.shutdown_requested) {
|
||||
/* Normal exit from the walwriter is here */
|
||||
proc_exit(0); /* done */
|
||||
proc_exit(0); /* done */ // 正常退出
|
||||
}
|
||||
|
||||
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
|
||||
wrote_something = XLogBackgroundFlush();
|
||||
LWLockRelease(WALWriteLock);
|
||||
|
||||
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE); // 获取 WAL 写锁
|
||||
wrote_something = XLogBackgroundFlush(); // 执行后台的 WAL 刷新操作
|
||||
LWLockRelease(WALWriteLock); // 释放 WAL 写锁,允许其他进程访问 WAL 日志
|
||||
// 检查是否已经连续一段时间没有写入新的 WAL 记录,并且超过了配置的阈值
|
||||
if (!wrote_something && ++times_wrote_nothing > g_instance.attr.attr_storage.walwriter_sleep_threshold) {
|
||||
/*
|
||||
* Wait for the first entry after last flushed entry to be updated
|
||||
*/
|
||||
// 获取上次刷新的 WAL 记录位置
|
||||
int lastFlushedEntry = g_instance.wal_cxt.lastWalStatusEntryFlushed;
|
||||
// 计算下一个要检查的 WAL 记录的位置,以查看是否已经被复制
|
||||
int nextStatusEntry =
|
||||
GET_NEXT_STATUS_ENTRY(g_instance.attr.attr_storage.wal_insert_status_entries_power, lastFlushedEntry);
|
||||
volatile WalInsertStatusEntry *pCriticalEntry =
|
||||
&g_instance.wal_cxt.walInsertStatusTable[nextStatusEntry];
|
||||
// 如果 walwriter 进程处于活动状态,并且下一个 WAL 记录的状态为未复制
|
||||
if (g_instance.wal_cxt.isWalWriterUp && pCriticalEntry->status == WAL_NOT_COPIED) {
|
||||
sleep_times_counter++;
|
||||
sleep_times_counter++; // 增加计数器
|
||||
// 获取临界区的互斥锁
|
||||
(void)pthread_mutex_lock(&g_instance.wal_cxt.criticalEntryMutex);
|
||||
g_instance.wal_cxt.isWalWriterSleeping = true;
|
||||
g_instance.wal_cxt.isWalWriterSleeping = true; // 表示 walwriter 进程正在休眠
|
||||
// 循环等待下一个 WAL 记录被复制,或者等待关闭请求
|
||||
while (pCriticalEntry->status == WAL_NOT_COPIED && !t_thrd.walwriter_cxt.shutdown_requested) {
|
||||
(void)clock_gettime(CLOCK_MONOTONIC, &time_to_wait);
|
||||
(void)clock_gettime(CLOCK_MONOTONIC, &time_to_wait); // 获取当前的时间
|
||||
// 计算等待时间
|
||||
time_to_wait.tv_nsec += g_sleep_timeout_ms * NANOSECONDS_PER_MILLISECOND;
|
||||
// 检查等待时间是否超过了一秒
|
||||
if (time_to_wait.tv_nsec >= NANOSECONDS_PER_SECOND) {
|
||||
time_to_wait.tv_nsec -= NANOSECONDS_PER_SECOND;
|
||||
time_to_wait.tv_nsec -= NANOSECONDS_PER_SECOND; // 减去一秒的纳秒部分
|
||||
time_to_wait.tv_sec += 1;
|
||||
}
|
||||
// 用条件变量进行等待,如果在规定的时间内没有被唤醒,将返回 res 值
|
||||
int res = pthread_cond_timedwait(&g_instance.wal_cxt.criticalEntryCV,
|
||||
&g_instance.wal_cxt.criticalEntryMutex, &time_to_wait);
|
||||
// 如果等待成功
|
||||
if (res == 0) {
|
||||
/*
|
||||
* We should not break out here because we may be notified by an
|
||||
|
|
@ -318,24 +383,28 @@ void WalWriterMain(void)
|
|||
* entry status is WAL_NOT_COPIED.
|
||||
*/
|
||||
continue;
|
||||
} else if (res == ETIMEDOUT) {
|
||||
time_out_counter++;
|
||||
} else {
|
||||
} else if (res == ETIMEDOUT) { // 如果等待超时
|
||||
time_out_counter++; // 增加超时计数器
|
||||
} else { // 如果等待出现错误
|
||||
// 报告错误信息
|
||||
ereport(WARNING, (errmsg("WAL writer pthread_cond_timedwait returned error code = %d.",
|
||||
errno)));
|
||||
}
|
||||
/* wakeup other producer if possible to avoid hang */
|
||||
// 唤醒其他可能正在等待的线程,以防止出现死锁
|
||||
WakeupWalSemaphore(&g_instance.wal_cxt.walFlushWaitLock->l.sem);
|
||||
// 唤醒其他可能正在等待的线程,以防止出现死锁
|
||||
WakeupWalSemaphore(&g_instance.wal_cxt.walBufferInitWaitLock->l.sem);
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
CHECK_FOR_INTERRUPTS(); // 检查是否有中断请求
|
||||
}
|
||||
g_instance.wal_cxt.isWalWriterSleeping = false;
|
||||
// 释放临界区的互斥锁
|
||||
(void)pthread_mutex_unlock(&g_instance.wal_cxt.criticalEntryMutex);
|
||||
time_out_counter = 0;
|
||||
time_out_counter = 0; // 重置超时计数器
|
||||
}
|
||||
times_wrote_nothing = 0;
|
||||
times_wrote_nothing = 0; // 重置 跟踪连续写入WAL记录的次数
|
||||
}
|
||||
|
||||
// 报告活动状态 表示 walwriter 进程目前处于空闲状态
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -350,13 +419,24 @@ void WalWriterMain(void)
|
|||
* Some backend has bought the farm,
|
||||
* so we need to stop what we're doing and exit.
|
||||
*/
|
||||
/*
|
||||
*功能:处理快速终止信号(当主进程发出SIGQUIT信号)
|
||||
* 紧急退出操作,不会触发正常的清理和关闭步骤,因为共享内存可能已损坏
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void wal_quickdie(SIGNAL_ARGS)
|
||||
{
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); // 解除对信号的阻塞
|
||||
|
||||
g_instance.wal_cxt.isWalWriterUp = false;
|
||||
pg_memory_barrier();
|
||||
g_instance.wal_cxt.isWalWriterUp = false; // 表示 WAL Writer 进程不再处于运行状态
|
||||
pg_memory_barrier(); // 执行内存屏障操作以确保对共享内存的修改得到正确同步
|
||||
/* Stop WalWriterAuxiliary from waiting. */
|
||||
// 唤醒等待在g_instance.wal_cxt.walInitSegLock->l.sem 信号量上的任何进程
|
||||
WakeupWalSemaphore(&g_instance.wal_cxt.walInitSegLock->l.sem);
|
||||
|
||||
/*
|
||||
|
|
@ -367,7 +447,7 @@ static void wal_quickdie(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
|
||||
|
|
@ -377,47 +457,76 @@ static void wal_quickdie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2);
|
||||
exit(2); // 以状态码 2 退出进程,表示进程异常退出
|
||||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
/*
|
||||
*功能:处理 SIGHUP 信号,用于重新读取配置文件
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void WalSigHupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.walwriter_cxt.got_SIGHUP = true;
|
||||
t_thrd.walwriter_cxt.got_SIGHUP = true; // 表示接收到 SIGHUP 信号
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
if (t_thrd.proc) // 如果存在进程
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置该进程的进程latch
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* SIGTERM: set flag to exit normally */
|
||||
/*
|
||||
*功能:处理 SIGTERM 信号,用于正常退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void WalShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.walwriter_cxt.shutdown_requested = true;
|
||||
t_thrd.walwriter_cxt.shutdown_requested = true; // 表示请求正常退出
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
if (t_thrd.proc) // 如果存在进程
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置该进程的进程latch
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
|
||||
g_instance.wal_cxt.isWalWriterUp = false;
|
||||
pg_memory_barrier();
|
||||
g_instance.wal_cxt.isWalWriterUp = false; // 表示 WAL Writer 进程不再处于运行状态
|
||||
pg_memory_barrier(); // 执行内存屏障操作以确保对共享内存的修改得到正确同步
|
||||
/* Stop WalWriterAuxiliary from waiting. */
|
||||
// 唤醒等待在 g_instance.wal_cxt.walInitSegLock->l.sem 信号量上的任何进程
|
||||
WakeupWalSemaphore(&g_instance.wal_cxt.walInitSegLock->l.sem);
|
||||
|
||||
}
|
||||
|
||||
/* SIGUSR1: used for latch wakeups */
|
||||
/*
|
||||
*功能: 处理 SIGUSR1 信号,用于唤醒进程中的等待操作
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void walwriter_sigusr1_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
latch_sigusr1_handler();
|
||||
|
||||
errno = save_errno;
|
||||
latch_sigusr1_handler(); // 处理 SIGUSR1 信号
|
||||
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,13 +54,21 @@ static void walwriterauxiliary_sigusr1_handler(SIGNAL_ARGS);
|
|||
* This is invoked from AuxiliaryProcessMain, which has already created the
|
||||
* basic execution environment, but not enabled signals yet.
|
||||
*/
|
||||
/*
|
||||
* 功能:
|
||||
*
|
||||
* 参数:无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void WalWriterAuxiliaryMain(void)
|
||||
{
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
MemoryContext walwriterauxiliary_context;
|
||||
sigset_t old_sig_mask;
|
||||
sigjmp_buf local_sigjmp_buf; // 用于异常跳转点
|
||||
MemoryContext walwriterauxiliary_context; // 声明内存上下文
|
||||
sigset_t old_sig_mask; // 用于保存旧信号掩码的变量
|
||||
|
||||
t_thrd.role = WALWRITERAUXILIARY;
|
||||
t_thrd.role = WALWRITERAUXILIARY; // 将当前线程的角色设置为 WAL Writer 辅助进程
|
||||
// 表示 WAL Writer 辅助进程已启动
|
||||
ereport(LOG, (errmsg("walwriterauxiliary started")));
|
||||
|
||||
/*
|
||||
|
|
@ -71,6 +79,7 @@ void WalWriterAuxiliaryMain(void)
|
|||
*
|
||||
* Reset some signals that are accepted by postmaster but not here.
|
||||
*/
|
||||
// 设置不同的信号处理函数
|
||||
(void)gspqsignal(SIGHUP, WalwriterauxiliarySigHupHandler); /* set flag to read config file */
|
||||
(void)gspqsignal(SIGINT, WalwriterauxiliaryShutdownHandler); /* request shutdown */
|
||||
(void)gspqsignal(SIGTERM, WalwriterauxiliaryShutdownHandler); /* request shutdown */
|
||||
|
|
@ -86,12 +95,14 @@ void WalWriterAuxiliaryMain(void)
|
|||
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
||||
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
// 移除 SIGQUIT 信号,允许在任何时候接收 SIGQUIT 信号
|
||||
(void)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).
|
||||
*/
|
||||
// 创建一个资源所有者,用于跟踪本进程的资源
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Wal Writer Auxiliary",
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
||||
|
||||
|
|
@ -101,8 +112,10 @@ void WalWriterAuxiliaryMain(void)
|
|||
* possible memory leaks. Formerly this code just ran in
|
||||
* t_thrd.top_mem_cxt, but resetting that would be a really bad idea.
|
||||
*/
|
||||
// 为 WAL Writer 辅助进程创建一个内存上下文
|
||||
walwriterauxiliary_context = AllocSetContextCreate(t_thrd.top_mem_cxt, "Wal Writer Auxiliary",
|
||||
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
// 切换上下文
|
||||
(void)MemoryContextSwitchTo(walwriterauxiliary_context);
|
||||
|
||||
/*
|
||||
|
|
@ -110,55 +123,68 @@ void WalWriterAuxiliaryMain(void)
|
|||
*
|
||||
* This code is heavily based on bgwriter.c, q.v.
|
||||
*/
|
||||
// 异常跳转点
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
/* We need restore the signal mask of current thread */
|
||||
// 恢复当前线程的信号掩码为之前保存的旧信号掩码
|
||||
(void)pthread_sigmask(SIG_SETMASK, &old_sig_mask, NULL);
|
||||
|
||||
/* 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;
|
||||
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(); // 报告错误到服务器日志
|
||||
|
||||
/* abort async io, must before LWlock release */
|
||||
AbortAsyncListIO();
|
||||
AbortAsyncListIO(); // 中止异步I/O操作
|
||||
|
||||
/* 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 walwriterauxiliary, but we do have LWLocks, and perhaps buffers?
|
||||
*/
|
||||
LWLockReleaseAll();
|
||||
pgstat_report_waitevent(WAIT_EVENT_END);
|
||||
AbortBufferIO();
|
||||
UnlockBuffers();
|
||||
LWLockReleaseAll(); // 释放锁
|
||||
pgstat_report_waitevent(WAIT_EVENT_END); // 报告等待事件结束,用于性能统计和诊断
|
||||
AbortBufferIO(); // 中止缓冲区I/O操作的函数调用
|
||||
UnlockBuffers(); // 解锁缓冲区,确保在异常情况下不会保留未解锁的缓冲区
|
||||
/* buffer pins are released here: */
|
||||
// 释放资源,包括资源所有者持有的资源
|
||||
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true);
|
||||
/* we needn't bother with the other ResourceOwnerRelease phases */
|
||||
// 处理事务结束的函数调用 与缓冲区相关的清理工作
|
||||
AtEOXact_Buffers(false);
|
||||
// 处理事务结束的函数调用 与存储管理器相关的清理工作
|
||||
AtEOXact_SMgr();
|
||||
// 处理事务结束的函数调用 用于关闭与文件操作相关的资源
|
||||
AtEOXact_Files();
|
||||
// 处理事务结束的函数调用 用于清理哈希表和相关数据结构
|
||||
AtEOXact_HashTables(false);
|
||||
|
||||
/*
|
||||
* Now return to normal top-level context and clear ErrorContext for
|
||||
* next time.
|
||||
*/
|
||||
// 切换程序的内存上下文
|
||||
(void)MemoryContextSwitchTo(walwriterauxiliary_context);
|
||||
// 清除错误状态,将之前可能记录的错误信息和状态重置为空
|
||||
FlushErrorState();
|
||||
|
||||
/* Flush any leaked data in the top-level context */
|
||||
// 重置 walwriter_context 上下文并删除其所有子级上下文
|
||||
// 确保在异常处理完成后释放已分配的内存,以避免内存泄漏
|
||||
MemoryContextResetAndDeleteChildren(walwriterauxiliary_context);
|
||||
|
||||
/* Now we can allow interrupts again */
|
||||
// 允许中断信号再次被捕获
|
||||
RESUME_INTERRUPTS();
|
||||
|
||||
/*
|
||||
|
|
@ -166,29 +192,34 @@ void WalWriterAuxiliaryMain(void)
|
|||
* to be repeated, and we don't want to be filling the error logs as
|
||||
* fast as we can.
|
||||
*/
|
||||
pg_usleep(1000000L);
|
||||
pg_usleep(1000000L); // 休眠
|
||||
|
||||
/*
|
||||
* Close all open files after any error. This is helpful on Windows,
|
||||
* where holding deleted files open causes various strange errors.
|
||||
* It's not clear we need it elsewhere, but shouldn't hurt.
|
||||
*/
|
||||
smgrcloseall();
|
||||
smgrcloseall(); // 关闭所有打开的文件
|
||||
}
|
||||
|
||||
/* We can now handle ereport(ERROR) */
|
||||
// 设置异常处理栈
|
||||
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);
|
||||
// 解除对 SIGUSR2 信号的阻塞,允许接收此信号
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
/*
|
||||
* Use the recovery target timeline ID during recovery
|
||||
*/
|
||||
// v如果正在进行恢复操作
|
||||
if (RecoveryInProgress()) {
|
||||
// 设置当前线程的时间线ID为恢复目标的时间线ID
|
||||
t_thrd.xlog_cxt.ThisTimeLineID = GetRecoveryTargetTLI();
|
||||
}
|
||||
|
||||
|
|
@ -196,50 +227,62 @@ void WalWriterAuxiliaryMain(void)
|
|||
* Advertise our latch that backends can use to wake us up while we're
|
||||
* sleeping.
|
||||
*/
|
||||
// 将当前线程的进程Latch与 walwriterauxiliaryLatch 相关联
|
||||
// 以便其他后台进程可以使用此Latch唤醒 WalWriterAuxiliary 进程
|
||||
g_instance.proc_base->walwriterauxiliaryLatch = &t_thrd.proc->procLatch;
|
||||
|
||||
// 报告进程的应用程序名称为 "Wal Writer Auxiliary",用于性能统计
|
||||
pgstat_report_appname("Wal Writer Auxiliary");
|
||||
// 报告进程的活动状态为 "STATE_IDLE",用于性能统计
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
|
||||
/*
|
||||
* Loop forever
|
||||
*/
|
||||
for (;;) {
|
||||
// 获取等待超时的时间
|
||||
long curTimeout = u_sess->attr.attr_storage.WalWriterDelay;
|
||||
int rc = 0;
|
||||
int rc = 0; // 用于存储等待结果
|
||||
|
||||
/* Clear any already-pending wakeups */
|
||||
// 清除任何已经挂起的Latch,以准备接收新的Latch通知
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
|
||||
/*
|
||||
* Process any requests or signals received recently.
|
||||
*/
|
||||
// 处理任何最近接收到的请求或信号
|
||||
if (t_thrd.walwriterauxiliary_cxt.got_SIGHUP) {
|
||||
t_thrd.walwriterauxiliary_cxt.got_SIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
ProcessConfigFile(PGC_SIGHUP); // 重新读取配置文件
|
||||
}
|
||||
|
||||
// 如果接收到了 shutdown_requested 标志
|
||||
if (t_thrd.walwriterauxiliary_cxt.shutdown_requested) {
|
||||
/* Normal exit from the walwriterauxiliary is here. */
|
||||
proc_exit(0); /* done */
|
||||
proc_exit(0); /* done */ // 正常退出
|
||||
}
|
||||
|
||||
// 检查 Wal Writer 进程是否正在运行
|
||||
if (g_instance.wal_cxt.isWalWriterUp) {
|
||||
// 等待主节点初始化Xlog文件的信号
|
||||
PGSemaphoreLock(&g_instance.wal_cxt.walInitSegLock->l.sem, true);
|
||||
// 执行与主节点相关的初始化操作
|
||||
PreInitXlogFileForPrimary(g_instance.attr.attr_storage.wal_file_init_num);
|
||||
} else {
|
||||
// 如果初始化的Xlog文件数量大于0且当前的运行模式为 STANDBY_MODE
|
||||
// 且当前的 Postmaster 状态为 PM_RECOVERY 或 PM_HOT_STANDBY
|
||||
// 且函数检查已完成恢复操作
|
||||
if (g_instance.attr.attr_storage.advance_xlog_file_num > 0 &&
|
||||
t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE &&
|
||||
(pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY) &&
|
||||
IsRecoveryDone()) {
|
||||
// 读取本地最大LSN,表示当前备节点已经恢复的进度
|
||||
XLogRecPtr curMaxLsn = pg_atomic_read_u64(&g_instance.comm_cxt.predo_cxt.redoPf.local_max_lsn);
|
||||
PreInitXlogFileForStandby(curMaxLsn);
|
||||
PreInitXlogFileForStandby(curMaxLsn); // 执行与备节点相关的初始化操作
|
||||
}
|
||||
}
|
||||
|
||||
// 等待新的Latch通知或超时
|
||||
rc = WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, curTimeout);
|
||||
if (rc & WL_POSTMASTER_DEATH) {
|
||||
gs_thread_exit(1);
|
||||
if (rc & WL_POSTMASTER_DEATH) { // 接收到 Postmaster 终止信号
|
||||
gs_thread_exit(1); // 退出线程
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -255,9 +298,19 @@ void WalWriterAuxiliaryMain(void)
|
|||
* Some backend has bought the farm,
|
||||
* so we need to stop what we're doing and exit.
|
||||
*/
|
||||
/*
|
||||
*功能:处理快速终止信号(当主进程发出SIGQUIT信号)
|
||||
* 紧急退出操作,不会触发正常的清理和关闭步骤,因为共享内存可能已损坏
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void walwriterauxiliary_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
|
||||
|
|
@ -267,7 +320,7 @@ static void walwriterauxiliary_quickdie(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
|
||||
|
|
@ -277,41 +330,68 @@ static void walwriterauxiliary_quickdie(SIGNAL_ARGS)
|
|||
* should ensure the postmaster sees this as a crash, too, but no harm in
|
||||
* being doubly sure.)
|
||||
*/
|
||||
exit(2);
|
||||
exit(2); // 以状态码 2 退出进程,表示进程异常退出
|
||||
}
|
||||
|
||||
/* SIGHUP: set flag to re-read config file at next convenient time */
|
||||
/*
|
||||
*功能:处理 SIGHUP 信号,用于重新读取配置文件
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void WalwriterauxiliarySigHupHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.walwriterauxiliary_cxt.got_SIGHUP = true;
|
||||
t_thrd.walwriterauxiliary_cxt.got_SIGHUP = true; // 表示接收到 SIGHUP 信号
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
if (t_thrd.proc) // 如果存在进程
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置该进程的进程latch
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* SIGTERM: set flag to exit normally */
|
||||
/*
|
||||
*功能:处理 SIGTERM 信号,用于正常退出
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void WalwriterauxiliaryShutdownHandler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
t_thrd.walwriterauxiliary_cxt.shutdown_requested = true;
|
||||
t_thrd.walwriterauxiliary_cxt.shutdown_requested = true; // 表示请求正常退出
|
||||
|
||||
if (t_thrd.proc)
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
if (t_thrd.proc) // 如果存在进程
|
||||
SetLatch(&t_thrd.proc->procLatch); // 设置该进程的进程latch
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
||||
/* SIGUSR1: used for latch wakeups */
|
||||
/*
|
||||
*功能: 处理 SIGUSR1 信号,用于唤醒进程中的等待操作
|
||||
*
|
||||
* 参数:
|
||||
* SIGNAL_ARGS: 信号处理程序的参数列表,提供关于触发信号的上下文信息
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*/
|
||||
static void walwriterauxiliary_sigusr1_handler(SIGNAL_ARGS)
|
||||
{
|
||||
int save_errno = errno;
|
||||
int save_errno = errno; // 保存当前的错误码
|
||||
|
||||
latch_sigusr1_handler();
|
||||
latch_sigusr1_handler(); // 处理 SIGUSR1 信号
|
||||
|
||||
errno = save_errno;
|
||||
errno = save_errno; // 恢复之前保存的错误码
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
DIR *.* /B >LIST.TXT
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,7 +14,7 @@
|
|||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* streamConsumer.cpp
|
||||
* Support methods for class StreamConsumer.
|
||||
* 支持StreamConsumer的方法
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/gausskernel/process/stream/streamConsumer.cpp
|
||||
|
|
@ -36,20 +36,22 @@
|
|||
|
||||
extern GlobalNodeDefinition* global_node_definition;
|
||||
|
||||
// 构造函数,初始化StreamConsumer对象
|
||||
StreamConsumer::StreamConsumer(MemoryContext context) : StreamObj(context, STREAM_CONSUMER)
|
||||
{
|
||||
m_sharedContext = NULL;
|
||||
m_originProducerNodeList = NULL;
|
||||
m_ready = false;
|
||||
m_expectProducer = NULL;
|
||||
m_currentProducerNum = 0;
|
||||
m_sharedContext = NULL; // 初始化共享内存上下文为NULL
|
||||
m_originProducerNodeList = NULL; // 初始化原始生产者节点列表为NULL
|
||||
m_ready = false; // 初始化就绪状态为false
|
||||
m_expectProducer = NULL; // 初始化预期生产者为NULL
|
||||
m_currentProducerNum = 0; // 初始化当前生产者数量为0
|
||||
}
|
||||
|
||||
// 析构函数,释放StreamConsumer对象的资源
|
||||
StreamConsumer::~StreamConsumer()
|
||||
{
|
||||
m_originProducerNodeList = NULL;
|
||||
m_expectProducer = NULL;
|
||||
m_sharedContext = NULL;
|
||||
m_originProducerNodeList = NULL; // 将原始生产者节点列表设为NULL
|
||||
m_expectProducer = NULL; // 将预期生产者设为NULL
|
||||
m_sharedContext = NULL; // 将共享内存上下文设为NULL
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -61,12 +63,16 @@ StreamConsumer::~StreamConsumer()
|
|||
* @param[IN] transType: transport type
|
||||
* @return: void
|
||||
*/
|
||||
/*
|
||||
函数通过参数设置了StreamConsumer对象的各个成员变量,包括传输类型、共享上下文、传输对象等,还根据传入的执行生产者节点信息,
|
||||
初始化了预期的生产者信息,并将其加入哈希表中
|
||||
*/
|
||||
void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc desc, StreamTransType transType,
|
||||
StreamSharedContext* sharedContext)
|
||||
{
|
||||
int i = 0;
|
||||
bool found = false;
|
||||
|
||||
|
||||
int producerNum = 0;
|
||||
AutoMutexLock streamLock(&m_streamInfoLock);
|
||||
AutoMutexLock copyLock(&nodeDefCopyLock);
|
||||
|
|
@ -79,18 +85,19 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
|
||||
Assert(producerNum > 0);
|
||||
|
||||
m_parallel_desc = desc;
|
||||
m_currentProducerNum = 0;
|
||||
m_connNum = producerNum;
|
||||
m_key = key;
|
||||
m_ready = false;
|
||||
m_transtype = transType;
|
||||
m_sharedContext = sharedContext;
|
||||
m_transport = (StreamTransport**)MemoryContextAllocZero(m_memoryCxt, producerNum * sizeof(StreamTransport*));
|
||||
m_expectProducer = (StreamConnInfo*)MemoryContextAllocZero(m_memoryCxt, producerNum * sizeof(StreamConnInfo));
|
||||
m_parallel_desc = desc; // 设置并行描述符
|
||||
m_currentProducerNum = 0; // 当前生产者数量置零
|
||||
m_connNum = producerNum; // 连接数量等于生产者数量
|
||||
m_key = key; // 设置StreamKey
|
||||
m_ready = false; // 初始状态为未就绪
|
||||
m_transtype = transType; // 设置传输类型
|
||||
m_sharedContext = sharedContext; // 共享上下文
|
||||
m_transport = (StreamTransport**)MemoryContextAllocZero(m_memoryCxt, producerNum * sizeof(StreamTransport*)); // 分配传输对象数组内存
|
||||
m_expectProducer = (StreamConnInfo*)MemoryContextAllocZero(m_memoryCxt, producerNum * sizeof(StreamConnInfo)); // 分配预期生产者信息内存
|
||||
|
||||
/* Initialize the origin nodelist */
|
||||
/* 初始化原始生产者节点列表 */
|
||||
m_originProducerNodeList = NIL;
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
copyLock.lock();
|
||||
ListCell* nodelistCell = NULL;
|
||||
|
|
@ -112,6 +119,8 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
}
|
||||
copyLock.unLock();
|
||||
#endif
|
||||
|
||||
// 初始化传输对象数组
|
||||
for (i = 0; i < producerNum; i++) {
|
||||
int nodeNameLen = 0;
|
||||
libcommaddrinfo* libcommaddr = NULL;
|
||||
|
|
@ -131,7 +140,7 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
scomm->m_addr->streamKey.consumerSmpId = m_key.smpIdentifier;
|
||||
}
|
||||
|
||||
HOLD_INTERRUPTS(); /* Add this macro for double safety. */
|
||||
HOLD_INTERRUPTS(); // 加入此宏以确保安全性
|
||||
|
||||
streamLock.lock();
|
||||
AutoContextSwitch streamInfoCxtGuard(StreamInfoContext);
|
||||
|
|
@ -139,8 +148,7 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
StreamElement* element = (StreamElement*)hash_search(m_streamInfoTbl, &m_key, HASH_ENTER, &found);
|
||||
if (element == NULL) {
|
||||
streamLock.unLock();
|
||||
ereport(
|
||||
ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to create stream element due to out of memory")));
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to create stream element due to out of memory")));
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
|
|
@ -154,16 +162,15 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
if (NULL == element->value) {
|
||||
hash_search(m_streamInfoTbl, &key, HASH_REMOVE, NULL);
|
||||
streamLock.unLock();
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate stream element due to out of memory")));
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate stream element due to out of memory")));
|
||||
}
|
||||
|
||||
element->value->connNum = 0;
|
||||
element->value->connInfoSize = 0;
|
||||
/* No need to allocate the array size. */
|
||||
/* 不需要分配数组大小。 */
|
||||
element->value->connInfo = NULL;
|
||||
} else {
|
||||
/* WTF? find a duplicate element in the hash table. */
|
||||
/* 在哈希表中找到重复的元素。 */
|
||||
if (element->value == NULL || element->value->consumer) {
|
||||
streamLock.unLock();
|
||||
ereport(ERROR,
|
||||
|
|
@ -173,7 +180,7 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
}
|
||||
}
|
||||
|
||||
/* Found true means some information has not been updated, protected by streamLock. */
|
||||
/* 找到为真表示某些信息尚未更新,由streamLock保护。 */
|
||||
if (found == true) {
|
||||
Assert(element->value->connInfo != NULL);
|
||||
updateTransportInfo(element->value);
|
||||
|
|
@ -215,65 +222,64 @@ void StreamConsumer::init(StreamKey key, List* execProducerNodes, ParallelDesc d
|
|||
*/
|
||||
void StreamConsumer::deInit()
|
||||
{
|
||||
AutoMutexLock streamHashLock(&m_streamInfoLock);
|
||||
AutoMutexLock streamHashLock(&m_streamInfoLock); // 加锁,保护哈希表操作
|
||||
StreamElement* delinfo = NULL;
|
||||
|
||||
HOLD_INTERRUPTS();
|
||||
streamHashLock.lock();
|
||||
HOLD_INTERRUPTS(); // 持有中断,禁止中断发生
|
||||
streamHashLock.lock(); // 加锁,保护哈希表操作
|
||||
|
||||
/* Do not need de init. */
|
||||
if (m_init == false) {
|
||||
if (m_threadSyncObjInit == true) {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
m_threadSyncObjInit = false;
|
||||
/* 不需要去初始化。 */
|
||||
if (m_init == false) { // 如果未初始化
|
||||
if (m_threadSyncObjInit == true) { // 如果线程同步对象已经初始化
|
||||
pthread_mutex_destroy(&m_mutex); // 销毁互斥锁
|
||||
pthread_cond_destroy(&m_cond); // 销毁条件变量
|
||||
m_threadSyncObjInit = false; // 标记线程同步对象未初始化
|
||||
}
|
||||
|
||||
streamHashLock.unLock();
|
||||
RESUME_INTERRUPTS();
|
||||
streamHashLock.unLock(); // 解锁,释放锁
|
||||
RESUME_INTERRUPTS(); // 恢复中断
|
||||
return;
|
||||
}
|
||||
|
||||
releaseCommStream();
|
||||
releaseCommStream(); // 释放通信流
|
||||
|
||||
/* Close local stream connection. */
|
||||
/* 关闭本地流连接。 */
|
||||
if (NULL != m_sharedContext) {
|
||||
gs_memory_close_conn(m_sharedContext, m_connNum, u_sess->stream_cxt.smp_id);
|
||||
gs_memory_close_conn(m_sharedContext, m_connNum, u_sess->stream_cxt.smp_id); // 关闭共享内存中的连接
|
||||
}
|
||||
|
||||
if (m_ready || u_sess->stream_cxt.dummy_thread == true) {
|
||||
delinfo = (StreamElement*)hash_search(m_streamInfoTbl, &m_key, HASH_REMOVE, NULL);
|
||||
if (delinfo != NULL) {
|
||||
if (m_ready || u_sess->stream_cxt.dummy_thread == true) { // 如果已经就绪或者是虚拟线程
|
||||
delinfo = (StreamElement*)hash_search(m_streamInfoTbl, &m_key, HASH_REMOVE, NULL); // 从哈希表中移除该消费者信息
|
||||
if (delinfo != NULL) { // 如果找到了消费者信息
|
||||
if (delinfo->value->connInfo != NULL) {
|
||||
pfree_ext(delinfo->value->connInfo);
|
||||
pfree_ext(delinfo->value->connInfo); // 释放连接信息内存
|
||||
delinfo->value->connInfo = NULL;
|
||||
}
|
||||
|
||||
pfree_ext(delinfo->value);
|
||||
pfree_ext(delinfo->value); // 释放消费者信息内存
|
||||
delinfo->value = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* set flag to release net port in case some producer not ready and element
|
||||
* in stream info hash table can not get removed in wakeUpConsumer,
|
||||
* or consumer get canceled when waiting producer ready.
|
||||
* 设置标志以释放网络端口,在某些生产者未就绪的情况下,
|
||||
* 或者在等待生产者就绪时,如果消费者被取消。
|
||||
*/
|
||||
u_sess->stream_cxt.global_obj->setNeedClean(true);
|
||||
u_sess->stream_cxt.global_obj->setNeedClean(true); // 设置需要清理标志
|
||||
}
|
||||
|
||||
if (m_threadSyncObjInit == true) {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
m_threadSyncObjInit = false;
|
||||
if (m_threadSyncObjInit == true) { // 如果线程同步对象已经初始化
|
||||
pthread_mutex_destroy(&m_mutex); // 销毁互斥锁
|
||||
pthread_cond_destroy(&m_cond); // 销毁条件变量
|
||||
m_threadSyncObjInit = false; // 标记线程同步对象未初始化
|
||||
}
|
||||
|
||||
m_init = false;
|
||||
streamHashLock.unLock();
|
||||
m_init = false; // 标记消费者未初始化
|
||||
streamHashLock.unLock(); // 解锁,释放锁
|
||||
|
||||
/* Cleanup the original producer list */
|
||||
m_originProducerNodeList = NIL;
|
||||
/* 清理原始生产者列表 */
|
||||
m_originProducerNodeList = NIL; // 清空原始生产者节点列表
|
||||
|
||||
RESUME_INTERRUPTS();
|
||||
RESUME_INTERRUPTS(); // 恢复中断
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -283,12 +289,13 @@ void StreamConsumer::deInit()
|
|||
*/
|
||||
void StreamConsumer::releaseCommStream()
|
||||
{
|
||||
if (m_transport != NULL) {
|
||||
for (int i = 0; i < m_connNum; i++) {
|
||||
Assert(t_thrd.int_cxt.ImmediateInterruptOK == false);
|
||||
StreamCOMM* scomm = (StreamCOMM*)m_transport[i];
|
||||
if (scomm != NULL)
|
||||
scomm->release();
|
||||
if (m_transport != NULL) { // 如果传输对象数组不为空
|
||||
for (int i = 0; i < m_connNum; i++) { // 遍历所有连接
|
||||
Assert(t_thrd.int_cxt.ImmediateInterruptOK == false); // 断言当前不可中断
|
||||
|
||||
StreamCOMM* scomm = (StreamCOMM*)m_transport[i]; // 获取当前连接的通信对象
|
||||
if (scomm != NULL) // 如果通信对象不为空
|
||||
scomm->release(); // 释放通信资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -301,11 +308,11 @@ void StreamConsumer::releaseCommStream()
|
|||
*/
|
||||
int StreamConsumer::getNodeIdx(const char* nodename)
|
||||
{
|
||||
for (int i = 0; i < m_connNum; i++) {
|
||||
if (pg_strncasecmp(m_expectProducer[i].nodeName, nodename, strlen(nodename)) == 0)
|
||||
return m_expectProducer[i].nodeIdx;
|
||||
for (int i = 0; i < m_connNum; i++) { // 遍历所有生产者节点
|
||||
if (pg_strncasecmp(m_expectProducer[i].nodeName, nodename, strlen(nodename)) == 0) // 如果节点名匹配
|
||||
return m_expectProducer[i].nodeIdx; // 返回节点索引
|
||||
}
|
||||
return -1;
|
||||
return -1; // 如果未找到匹配节点,返回-1
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -318,17 +325,17 @@ void StreamConsumer::findUnconnectProducer(StringInfo str)
|
|||
{
|
||||
bool found = false;
|
||||
|
||||
for (int j = 0; j < m_connNum; j++) {
|
||||
for (int j = 0; j < m_connNum; j++) { // 遍历所有预期的生产者节点
|
||||
found = false;
|
||||
for (int i = 0; i < m_currentProducerNum; i++) {
|
||||
if (strcmp(m_expectProducer[j].nodeName, m_transport[i]->m_nodeName) == 0) {
|
||||
found = true;
|
||||
for (int i = 0; i < m_currentProducerNum; i++) { // 遍历当前已连接的生产者节点
|
||||
if (strcmp(m_expectProducer[j].nodeName, m_transport[i]->m_nodeName) == 0) { // 如果找到匹配节点
|
||||
found = true; // 标记为已连接
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
appendStringInfo(str, " %s", m_expectProducer[j].nodeName);
|
||||
if (!found) // 如果未找到匹配节点(即未连接)
|
||||
appendStringInfo(str, " %s", m_expectProducer[j].nodeName); // 将节点名追加到字符串中
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -342,21 +349,21 @@ int StreamConsumer::getFirstUnconnectedProducerNodeIdx()
|
|||
bool found = false;
|
||||
int nodeIdx = -1;
|
||||
|
||||
for (int j = 0; j < m_connNum; j++) {
|
||||
for (int j = 0; j < m_connNum; j++) { // 遍历所有预期的生产者节点
|
||||
found = false;
|
||||
for (int i = 0; i < m_currentProducerNum; i++) {
|
||||
if (strcmp(m_expectProducer[j].nodeName, m_transport[i]->m_nodeName) == 0) {
|
||||
found = true;
|
||||
for (int i = 0; i < m_currentProducerNum; i++) { // 遍历当前已连接的生产者节点
|
||||
if (strcmp(m_expectProducer[j].nodeName, m_transport[i]->m_nodeName) == 0) { // 如果找到匹配节点
|
||||
found = true; // 标记为已连接
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
nodeIdx = m_expectProducer[j].nodeIdx;
|
||||
if (!found) { // 如果未找到匹配节点(即未连接)
|
||||
nodeIdx = m_expectProducer[j].nodeIdx; // 设置未连接节点的索引
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nodeIdx;
|
||||
return nodeIdx; // 返回第一个未连接节点的索引
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -366,7 +373,7 @@ int StreamConsumer::getFirstUnconnectedProducerNodeIdx()
|
|||
*/
|
||||
void StreamConsumer::waitProducerReady()
|
||||
{
|
||||
/* For local stream, producer will never connect to consumer. Consumer is ready, just return. */
|
||||
/* 对于本地流,生产者永远不会连接到消费者。消费者准备好了,直接返回。 */
|
||||
if (STREAM_IS_LOCAL_NODE(m_parallel_desc.distriType)) {
|
||||
m_ready = true;
|
||||
return;
|
||||
|
|
@ -384,7 +391,7 @@ void StreamConsumer::waitProducerReady()
|
|||
Assert(t_thrd.int_cxt.ImmediateInterruptOK == false);
|
||||
streamLock.lock();
|
||||
|
||||
/* 900s timeout. */
|
||||
/* 900秒的超时时间。 */
|
||||
struct timespec timer;
|
||||
int ret;
|
||||
int ntimes = 1;
|
||||
|
|
@ -419,11 +426,11 @@ void StreamConsumer::waitProducerReady()
|
|||
ereport(ERROR,
|
||||
(errmodule(MOD_STREAM),
|
||||
errcode(ERRCODE_CONNECTION_TIMED_OUT),
|
||||
errmsg("Distribute query initializing network connection timeout. un-connected nodes: %s",
|
||||
errmsg("Distribute query initializing network connection timeout. un-connected nodes:%s",
|
||||
str.data)));
|
||||
}
|
||||
|
||||
/* Check for interrupts.(cancel signal?). */
|
||||
/* 检查中断(取消信号?)。 */
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
streamLock.lock();
|
||||
}
|
||||
|
|
@ -442,6 +449,7 @@ void StreamConsumer::waitProducerReady()
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @Description: Wake up consumer and let it work
|
||||
*
|
||||
|
|
@ -465,7 +473,7 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
|
||||
streamLock.lock();
|
||||
|
||||
/* Check if can accept connection now */
|
||||
/* 检查是否可以接受连接。 */
|
||||
if (StreamNodeGroup::checkStreamConnectPermission(key.queryId) == false) {
|
||||
streamLock.unLock();
|
||||
return false;
|
||||
|
|
@ -474,9 +482,9 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
AutoContextSwitch streamInfoCxtGuard(StreamInfoContext);
|
||||
|
||||
/*
|
||||
* Register me in the global consumer table if the consumer thread has not been started.
|
||||
* Libcomm r_flow_ctrl thread call this, must not use palloc and elog.
|
||||
* so use isLibcommThread flag, return false where malloc failed.
|
||||
* 如果消费者线程尚未启动,则在全局消费者表中注册我。
|
||||
* Libcomm r_flow_ctrl线程调用这个函数,不能使用palloc和elog。
|
||||
* 所以使用isLibcommThread标志,在malloc失败时返回false。
|
||||
*/
|
||||
StreamElement* element = (StreamElement*)hash_search(m_streamInfoTbl, &key, HASH_ENTER, &found);
|
||||
if (element == NULL) {
|
||||
|
|
@ -485,7 +493,7 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
return false;
|
||||
}
|
||||
|
||||
/* If StreamElement not register by the consumer thread, we save the information. */
|
||||
/* 如果StreamElement尚未由消费者线程注册,则保存信息。 */
|
||||
if (found == false) {
|
||||
element->value = (StreamValue*)palloc0_noexcept(sizeof(StreamValue));
|
||||
if (NULL == element->value) {
|
||||
|
|
@ -516,7 +524,7 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
element->value->consumer = NULL;
|
||||
} else {
|
||||
if (element->value) {
|
||||
/* Consumer thread has not been registered yet. */
|
||||
/* 消费者线程尚未注册。 */
|
||||
if (element->value->consumer == NULL) {
|
||||
element->value->connInfo[element->value->connNum].port.libcomm_layer.gsock =
|
||||
connInfo.port.libcomm_layer.gsock;
|
||||
|
|
@ -528,7 +536,7 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
element->value->connInfo[element->value->connNum].producerSmpId = connInfo.producerSmpId;
|
||||
element->value->connNum++;
|
||||
|
||||
/* Check if need realloc to remember more connection info. */
|
||||
/* 检查是否需要重新分配内存以记住更多的连接信息。 */
|
||||
if (element->value->connNum == element->value->connInfoSize) {
|
||||
StreamConnInfo* new_connInfo = NULL;
|
||||
int new_connInfoSize = 2 * element->value->connInfoSize;
|
||||
|
|
@ -557,10 +565,10 @@ bool StreamConsumer::wakeUpConsumerCallBack(CommStreamKey commKey, StreamConnInf
|
|||
element->value->connInfoSize = new_connInfoSize;
|
||||
}
|
||||
} else {
|
||||
/* Have registered, so we update the stream info. */
|
||||
/* 已经注册,所以我们更新流信息。 */
|
||||
res = element->value->consumer->updateStreamCommInfo(&connInfo);
|
||||
|
||||
/* We can free the memory, no longer need it. */
|
||||
/* 我们可以释放内存了,不再需要它。 */
|
||||
if (element->value->connInfo != NULL) {
|
||||
pfree_ext(element->value->connInfo);
|
||||
element->value->connInfo = NULL;
|
||||
|
|
@ -588,7 +596,7 @@ bool StreamConsumer::updateStreamCommInfo(StreamConnInfo* connInfo)
|
|||
|
||||
streamLock.lock();
|
||||
|
||||
/* WTF, duplicate plan id encounter or plan error. */
|
||||
/* 出现重复的计划ID或计划错误。 */
|
||||
if (m_currentProducerNum >= m_connNum) {
|
||||
streamLock.unLock();
|
||||
return false;
|
||||
|
|
@ -632,4 +640,4 @@ void StreamConsumer::updateTransportInfo(StreamValue* val)
|
|||
if (m_currentProducerNum == m_connNum)
|
||||
m_ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -50,17 +50,29 @@
|
|||
#include "utils/timestamp.h"
|
||||
#include "instruments/instr_handle_mgr.h"
|
||||
|
||||
// 声明CodeGenThreadInitialize函数,用于初始化代码生成线程
|
||||
extern void CodeGenThreadInitialize();
|
||||
// 声明InitRecursiveCTEGlobalVariables函数,用于初始化递归CTE的全局变量
|
||||
extern void InitRecursiveCTEGlobalVariables(const PlannedStmt* planstmt);
|
||||
|
||||
// 下面的函数都是当前文件内的静态函数,用于初始化流执行计划的各个组件和处理信号
|
||||
// 初始化流执行计划的线程
|
||||
static void InitStreamThread();
|
||||
// 初始化流执行计划的路径
|
||||
static void InitStreamPath();
|
||||
// 初始化流执行计划的信号处理
|
||||
static void InitStreamSignal();
|
||||
// 初始化流执行计划的资源
|
||||
static void InitStreamResource();
|
||||
// 处理流执行计划的信号跳转
|
||||
static void HandleStreamSigjmp();
|
||||
// 执行流执行计划的主函数,用于生成和发送流数据
|
||||
static void execute_stream_plan(StreamProducer* producer);
|
||||
// 流执行计划结束时的处理函数
|
||||
static void execute_stream_end(StreamProducer* producer);
|
||||
// 退出并清理流执行计划的函数
|
||||
static void StreamQuitAndClean(int code, Datum arg);
|
||||
// 重置流执行计划的工作线程信息
|
||||
static void ResetStreamWorkerInfo();
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
|
@ -68,32 +80,38 @@ static void ResetStreamWorkerInfo();
|
|||
* stream thread main entrance
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
// StreamMain函数是流执行计划的主函数,用于处理流数据生成和发送
|
||||
|
||||
int StreamMain()
|
||||
{
|
||||
sigjmp_buf local_sigjmp_buf;
|
||||
|
||||
// 初始化流执行计划的线程
|
||||
InitStreamThread();
|
||||
|
||||
// 设置处理模式为NormalProcessing
|
||||
SetProcessingMode(NormalProcessing);
|
||||
|
||||
// 在进程退出时调用StreamQuitAndClean函数进行清理工作
|
||||
on_proc_exit(StreamQuitAndClean, 0);
|
||||
|
||||
/*
|
||||
* process any libraries that should be preloaded at backend start (this
|
||||
* likewise can't be done until GUC settings are complete)
|
||||
* 处理在后端启动时预加载的任何库
|
||||
* (这同样不能在GUC设置完成之前执行)
|
||||
*/
|
||||
process_local_preload_libraries();
|
||||
|
||||
int curTryCounter;
|
||||
int* oldTryCounter = NULL;
|
||||
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
|
||||
/* reset signal block flag for threadpool worker */
|
||||
// 重置信号块标志,以便线程池工作线程可以继续处理信号
|
||||
ResetInterruptCxt();
|
||||
if (g_threadPoolControler) {
|
||||
g_threadPoolControler->GetSessionCtrl()->releaseLockIfNecessary();
|
||||
}
|
||||
|
||||
gstrace_tryblock_exit(true, oldTryCounter);
|
||||
// 处理信号跳转
|
||||
HandleStreamSigjmp();
|
||||
if (IS_THREAD_POOL_STREAM) {
|
||||
t_thrd.threadpool_cxt.stream->CleanUp();
|
||||
|
|
@ -103,23 +121,27 @@ int StreamMain()
|
|||
}
|
||||
|
||||
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
|
||||
|
||||
|
||||
// 切换内存上下文为执行器的内存上下文
|
||||
MemoryContext oldMemory = MemoryContextSwitchTo(
|
||||
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR));
|
||||
#ifdef ENABLE_LLVM_COMPILE
|
||||
// 初始化LLVM代码生成线程
|
||||
CodeGenThreadInitialize();
|
||||
#endif
|
||||
(void)MemoryContextSwitchTo(oldMemory);
|
||||
|
||||
/* We can now handle ereport(ERROR) */
|
||||
|
||||
/* 现在我们可以处理ereport(ERROR) */
|
||||
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf;
|
||||
|
||||
if (IS_THREAD_POOL_STREAM) {
|
||||
// 重置流工作线程的信息
|
||||
ResetStreamWorkerInfo();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (IS_THREAD_POOL_STREAM) {
|
||||
// 汇报当前状态为STATE_IDLE,并等待任务的到来
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
pgstat_report_waitstatus(STATE_WAIT_COMM);
|
||||
t_thrd.threadpool_cxt.stream->WaitMission();
|
||||
|
|
@ -127,25 +149,28 @@ int StreamMain()
|
|||
pgstat_report_waitstatus(STATE_WAIT_UNDEFINED);
|
||||
}
|
||||
|
||||
// 汇报调试查询ID等信息
|
||||
pgstat_report_queryid(u_sess->debug_query_id);
|
||||
pgstat_report_unique_sql_id(false);
|
||||
pgstat_report_global_session_id(u_sess->globalSessionId);
|
||||
pgstat_report_smpid(u_sess->stream_cxt.smp_id);
|
||||
timeInfoRecordStart();
|
||||
|
||||
/* Wait thread ID ready */
|
||||
/* 等待线程ID准备就绪 */
|
||||
u_sess->stream_cxt.producer_obj->waitThreadIdReady();
|
||||
|
||||
// 执行流计划
|
||||
execute_stream_plan(u_sess->stream_cxt.producer_obj);
|
||||
// 流执行计划结束后的处理
|
||||
execute_stream_end(u_sess->stream_cxt.producer_obj);
|
||||
WLMReleaseNodeFromHash();
|
||||
WLMReleaseIoInfoFromHash();
|
||||
/* Reset here so that we can get debug_query_string when Stream thread is in Sync point */
|
||||
/* 在这里重置,以便在流线程处于同步点时可以获取到debug_query_string */
|
||||
t_thrd.postgres_cxt.debug_query_string = NULL;
|
||||
|
||||
/*
|
||||
* Note that parent thread will do commit or abort transaction.
|
||||
* Stream thread should not change clog file
|
||||
* 请注意,父线程将进行提交或回滚事务。
|
||||
* 流线程不应该改变clog文件
|
||||
*/
|
||||
ResetTransactionInfo();
|
||||
|
||||
|
|
@ -156,50 +181,64 @@ int StreamMain()
|
|||
}
|
||||
}
|
||||
|
||||
// 关闭GTM连接
|
||||
CloseGTM();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// InitStreamThread函数用于初始化流执行计划线程
|
||||
|
||||
static void InitStreamThread()
|
||||
{
|
||||
// 初始化随机数生成器
|
||||
initRandomState(0, GetCurrentTimestamp());
|
||||
|
||||
// 设置流执行计划线程的进程ID和运行标志
|
||||
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
||||
u_sess->exec_cxt.under_stream_runtime = true;
|
||||
t_thrd.codegen_cxt.g_runningInFmgr = false;
|
||||
// 设置前端协议版本和远程连接类型
|
||||
FrontendProtocol = PG_PROTOCOL_LATEST;
|
||||
u_sess->attr.attr_common.remoteConnType = REMOTE_CONN_DATANODE;
|
||||
|
||||
// 初始化流执行计划的路径
|
||||
InitStreamPath();
|
||||
|
||||
// 初始化流执行计划的信号
|
||||
InitStreamSignal();
|
||||
|
||||
/* Early initialization */
|
||||
/* 早期初始化 */
|
||||
BaseInit();
|
||||
|
||||
/* We need to allow SIGINT, etc during the initial transaction */
|
||||
/* 在初始事务期间,我们需要允许SIGINT等信号 */
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
|
||||
/* Initialize the memory tracking information */
|
||||
/* 初始化内存跟踪信息 */
|
||||
MemoryTrackingInit();
|
||||
|
||||
if (!IS_THREAD_POOL_STREAM) {
|
||||
// 提取生产者信息
|
||||
ExtractProduerInfo();
|
||||
// 设置数据库和用户信息
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(
|
||||
u_sess->stream_cxt.producer_obj->getDbName(), InvalidOid, u_sess->stream_cxt.producer_obj->getUserName());
|
||||
// 修复GUC变量
|
||||
repair_guc_variables();
|
||||
}
|
||||
// 初始化流执行计划的资源
|
||||
t_thrd.proc_cxt.PostInit->InitStreamWorker();
|
||||
|
||||
// 初始化向量函数映射表
|
||||
InitVecFuncMap();
|
||||
|
||||
// 初始化流执行计划的资源
|
||||
InitStreamResource();
|
||||
}
|
||||
|
||||
static void InitStreamPath()
|
||||
{
|
||||
/* Compute paths, if we didn't inherit them from postmaster */
|
||||
/* 计算路径,如果我们没有从postmaster继承它们的话 */
|
||||
if (my_exec_path[0] == '\0') {
|
||||
if (find_my_exec("postgres", my_exec_path) < 0)
|
||||
ereport(FATAL, (errmsg("openGauss: could not locate my own executable path")));
|
||||
|
|
@ -213,23 +252,23 @@ static void InitStreamSignal()
|
|||
{
|
||||
(void)gspqsignal(SIGINT, StatementCancelHandler);
|
||||
(void)gspqsignal(SIGTERM, die);
|
||||
(void)gspqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */
|
||||
(void)gspqsignal(SIGALRM, handle_sig_alarm); /* 超时条件 */
|
||||
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
||||
(void)gs_signal_unblock_sigusr2();
|
||||
|
||||
if (IsUnderPostmaster) {
|
||||
/* We allow SIGQUIT (quickdie) at all times */
|
||||
/* 我们在任何时候都允许SIGQUIT (quickdie) */
|
||||
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
|
||||
}
|
||||
}
|
||||
|
||||
// InitStreamResource函数用于初始化流执行计划线程的资源
|
||||
|
||||
static void InitStreamResource()
|
||||
{
|
||||
/*
|
||||
* Create the memory context we will use in the main loop.
|
||||
*
|
||||
* t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon
|
||||
* completion of processing of each command message from the client.
|
||||
* 创建主循环中将要使用的内存上下文。
|
||||
* t_thrd.mem_cxt.msg_mem_cxt在主循环的每次迭代中重置,即在完成处理客户端的每个命令消息后。
|
||||
*/
|
||||
t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"MessageContext",
|
||||
|
|
@ -253,11 +292,13 @@ static void InitStreamResource()
|
|||
}
|
||||
}
|
||||
|
||||
// ExtractProduerInfo函数用于提取生产者的信息
|
||||
void ExtractProduerInfo()
|
||||
{
|
||||
if (u_sess->stream_cxt.producer_obj == NULL) {
|
||||
return;
|
||||
}
|
||||
// 提取WLM参数、查询ID、跟踪标志等信息
|
||||
u_sess->wlm_cxt->wlm_params = u_sess->stream_cxt.producer_obj->getWlmParams();
|
||||
u_sess->instr_cxt.gs_query_id->procId = u_sess->stream_cxt.producer_obj->getExplainThreadid();
|
||||
u_sess->exec_cxt.need_track_resource = u_sess->stream_cxt.producer_obj->getExplainTrack();
|
||||
|
|
@ -266,16 +307,17 @@ void ExtractProduerInfo()
|
|||
u_sess->stream_cxt.producer_obj->getGlobalSessionId(&u_sess->globalSessionId);
|
||||
|
||||
WLMGeneralParam *g_wlm_params = &u_sess->wlm_cxt->wlm_params;
|
||||
// 设置控制组信息
|
||||
errno_t ret = sprintf_s(u_sess->wlm_cxt->control_group,
|
||||
sizeof(u_sess->wlm_cxt->control_group), "%s", g_wlm_params->cgroup);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
|
||||
/* Get the node group information */
|
||||
/* 获取节点组信息 */
|
||||
t_thrd.wlm_cxt.thread_node_group = WLMGetNodeGroupFromHTAB(g_wlm_params->ngroup);
|
||||
t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr;
|
||||
t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr;
|
||||
|
||||
/* Set the right pgxcnodeid */
|
||||
/* 设置正确的pgxcnodeid */
|
||||
u_sess->pgxc_cxt.PGXCNodeId = u_sess->stream_cxt.producer_obj->getPgxcNodeId();
|
||||
u_sess->instr_cxt.global_instr = u_sess->stream_cxt.producer_obj->getStreamInstrumentation();
|
||||
u_sess->proc_cxt.MyProcPort->database_name = u_sess->stream_cxt.producer_obj->getDbName();
|
||||
|
|
@ -302,14 +344,14 @@ void ExtractProduerInfo()
|
|||
u_sess->stream_cxt.dummy_thread = false;
|
||||
}
|
||||
|
||||
/* stream workers share the same session memory entry as their parents */
|
||||
/* 流式工作者共享与其父进程相同的会话内存条目 */
|
||||
t_thrd.shemem_ptr_cxt.mySessionMemoryEntry = u_sess->stream_cxt.producer_obj->getSessionMemory();
|
||||
if (t_thrd.proc != NULL) {
|
||||
t_thrd.proc->sessMemorySessionid = u_sess->stream_cxt.producer_obj->getParentSessionid();
|
||||
Assert(t_thrd.proc->sessMemorySessionid == t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->sessionid);
|
||||
}
|
||||
|
||||
/* Initialize the global variables for recursive */
|
||||
/* 初始化递归的全局变量 */
|
||||
InitRecursiveCTEGlobalVariables(u_sess->stream_cxt.producer_obj->getPlan());
|
||||
|
||||
STREAM_LOG(DEBUG2, "enter StreamMain, StreamKey(%lu, %u, %u)",
|
||||
|
|
@ -318,34 +360,36 @@ void ExtractProduerInfo()
|
|||
u_sess->stream_cxt.producer_obj->getKey().smpIdentifier);
|
||||
}
|
||||
|
||||
// HandleStreamSigjmp函数用于处理流执行计划线程的信号跳转
|
||||
|
||||
static void HandleStreamSigjmp()
|
||||
{
|
||||
// 报告等待状态为STATE_WAIT_UNDEFINED
|
||||
pgstat_report_waitstatus(STATE_WAIT_UNDEFINED);
|
||||
t_thrd.pgxc_cxt.GlobalNetInstr = NULL;
|
||||
|
||||
/* output the memory tracking information when error happened */
|
||||
/* 当发生错误时,输出内存追踪信息 */
|
||||
MemoryTrackingOutputFile();
|
||||
|
||||
/* Since not using PG_TRY, must reset error stack by hand */
|
||||
/* 由于没有使用PG_TRY,必须手动重置错误堆栈 */
|
||||
t_thrd.log_cxt.error_context_stack = NULL;
|
||||
|
||||
t_thrd.log_cxt.call_stack = NULL;
|
||||
|
||||
/* reset buffer strategy flag */
|
||||
/* 重置缓冲策略标志 */
|
||||
t_thrd.storage_cxt.is_btree_split = false;
|
||||
|
||||
/* Prevent interrupts while cleaning up */
|
||||
/* 在清理时阻止中断 */
|
||||
HOLD_INTERRUPTS();
|
||||
|
||||
/*
|
||||
* Turn off these interrupts too. This is only needed here and not in
|
||||
* other exception-catching places since these interrupts are only
|
||||
* enabled while we wait for client input.
|
||||
* 也关闭这些中断。这仅在此处需要,而不是在其他捕获异常的地方,
|
||||
* 因为这些中断只有在等待客户端输入时才启用。
|
||||
*/
|
||||
t_thrd.postgres_cxt.DoingCommandRead = false;
|
||||
|
||||
/*
|
||||
* Abort the current transaction in order to recover.
|
||||
* 为了恢复,中止当前事务。
|
||||
*/
|
||||
ereport(DEBUG1,
|
||||
(errmsg("stream thread %lu end transaction " XID_FMT " abnormally",
|
||||
|
|
@ -353,20 +397,20 @@ static void HandleStreamSigjmp()
|
|||
GetCurrentTransactionIdIfAny())));
|
||||
|
||||
/*
|
||||
* when clearing the BCM encounter ERROR, we should ResetBCMArray, or it
|
||||
* will enter ClearBCMArray infinite loop, then coredump.
|
||||
* 在清除BCM时遇到ERROR时,我们应该ResetBCMArray,
|
||||
* 否则它将进入ClearBCMArray无限循环,然后core dump。
|
||||
*/
|
||||
ResetBCMArray();
|
||||
|
||||
/* release operator-level hash table in memory */
|
||||
/* 在内存中释放操作符级别的哈希表 */
|
||||
releaseExplainTable();
|
||||
|
||||
/* Mark recursive vfd is invalid before aborting transaction. */
|
||||
/* 在中止事务之前标记递归vfd为无效 */
|
||||
StreamNodeGroup::MarkRecursiveVfdInvalid();
|
||||
|
||||
AbortCurrentTransaction();
|
||||
|
||||
/* release resource held by lsc */
|
||||
/* 释放lsc持有的资源 */
|
||||
AtEOXact_SysDBCache(false);
|
||||
|
||||
LWLockReleaseAll();
|
||||
|
|
@ -384,66 +428,62 @@ static void HandleStreamSigjmp()
|
|||
RESUME_INTERRUPTS();
|
||||
|
||||
timeInfoRecordEnd();
|
||||
// 调用StreamNodeGroup的syncQuit函数,传入STREAM_ERROR参数,表示异常退出
|
||||
StreamNodeGroup::syncQuit(STREAM_ERROR);
|
||||
}
|
||||
|
||||
// execute_stream_plan函数用于执行流执行计划
|
||||
|
||||
static void execute_stream_plan(StreamProducer* producer)
|
||||
{
|
||||
/*
|
||||
* Start up a transaction command. All queries generated by the
|
||||
* query_string will be in this same command block, *unless* we find a
|
||||
* BEGIN/COMMIT/ABORT statement; we have to force a new xact command after
|
||||
* one of those, else bad things will happen in xact.c. (Note that this
|
||||
* will normally change current memory context.)
|
||||
* 启动一个事务命令。由query_string生成的所有查询都将在相同的命令块中,*除非*我们找到了
|
||||
* 一个BEGIN/COMMIT/ABORT语句;在这种情况下,我们必须在其后强制新的xact命令,否则在xact.c中会出现问题。
|
||||
* (请注意,这通常会改变当前内存上下文。)
|
||||
*/
|
||||
start_xact_command();
|
||||
|
||||
producer->setUpStreamTxnEnvironment();
|
||||
producer->setUpStreamTxnEnvironment(); // 设置流事务环境
|
||||
|
||||
PlannedStmt* planstmt = producer->getPlan();
|
||||
CommandDest dest = producer->getDest();
|
||||
bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats;
|
||||
PlannedStmt* planstmt = producer->getPlan(); // 获取计划语句
|
||||
CommandDest dest = producer->getDest(); // 获取命令目标
|
||||
bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; // 保存log_statement_stats设置
|
||||
bool isTopLevel = false;
|
||||
const char* commandTag = NULL;
|
||||
const char* commandTag = NULL; // 命令标签
|
||||
char completionTag[COMPLETION_TAG_BUFSIZE];
|
||||
Portal portal = NULL;
|
||||
DestReceiver* receiver = NULL;
|
||||
int16 format;
|
||||
char msec_str[PRINTF_DST_MAX];
|
||||
|
||||
t_thrd.postgres_cxt.debug_query_string = planstmt->query_string;
|
||||
pgstat_report_activity(STATE_RUNNING, t_thrd.postgres_cxt.debug_query_string);
|
||||
/* Use planNodeId as thread_level, same as the key which SCTP use for send/receive */
|
||||
t_thrd.postgres_cxt.debug_query_string = planstmt->query_string; // 设置调试查询字符串
|
||||
pgstat_report_activity(STATE_RUNNING, t_thrd.postgres_cxt.debug_query_string); // 报告活动状态
|
||||
/* 使用planNodeId作为thread_level,与SCTP用于发送/接收的键相同 */
|
||||
pgstat_report_parent_sessionid(producer->getParentSessionid(), producer->getKey().planNodeId);
|
||||
|
||||
if (u_sess->instr_cxt.global_instr &&
|
||||
u_sess->instr_cxt.perf_monitor_enable) // Don't use perf util you set has_use_perf = true
|
||||
CPUMon::Initialize(CMON_GENERAL);
|
||||
u_sess->instr_cxt.perf_monitor_enable) // 仅当has_use_perf = true时,使用perf监控,避免使用perf util
|
||||
CPUMon::Initialize(CMON_GENERAL); // 初始化CPU监控
|
||||
|
||||
/*
|
||||
* We use save_log_statement_stats so ShowUsage doesn't report incorrect
|
||||
* results because ResetUsage wasn't called.
|
||||
* 我们使用save_log_statement_stats以便ShowUsage不会因为ResetUsage未被调用而报告不正确的结果。
|
||||
*/
|
||||
if (save_log_statement_stats)
|
||||
ResetUsage();
|
||||
|
||||
isTopLevel = true;
|
||||
|
||||
// For now plan shipping is used only for SELECTs, in future
|
||||
// we should remove this hard coding and get the tag automatically
|
||||
// 目前,计划运送仅用于SELECT语句,将来我们应该去掉这个硬编码,自动获取标签
|
||||
commandTag = "SELECT";
|
||||
|
||||
set_ps_display(commandTag, false);
|
||||
|
||||
BeginCommand(commandTag, dest);
|
||||
BeginCommand(commandTag, dest); // 开始命令
|
||||
|
||||
/*
|
||||
* If we are in an aborted transaction, reject all commands except
|
||||
* COMMIT/ABORT. It is important that this test occur before we try
|
||||
* to do parse analysis, rewrite, or planning, since all those phases
|
||||
* try to do database accesses, which may fail in abort state. (It
|
||||
* might be safe to allow some additional utility commands in this
|
||||
* state, but not many...)
|
||||
* 如果我们处于一个中止的事务块状态,拒绝除了COMMIT/ABORT之外的所有命令。
|
||||
* 这个测试在我们尝试进行解析分析、重写或计划之前发生,因为所有这些阶段都尝试进行数据库访问,
|
||||
* 而在中止状态下可能会失败。(这可能安全地允许在这种状态下执行一些其他实用程序命令,但不是太多...)
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState()) // &&
|
||||
ereport(ERROR,
|
||||
|
|
@ -453,51 +493,50 @@ static void execute_stream_plan(StreamProducer* producer)
|
|||
u_sess->proc_cxt.firstChar),
|
||||
errdetail_abort()));
|
||||
|
||||
/* Make sure we are in a transaction command */
|
||||
/* 确保我们在一个事务命令中 */
|
||||
start_xact_command();
|
||||
|
||||
/* If we got a cancel signal in parsing or prior command, quit */
|
||||
/* 如果在解析或之前的命令中收到取消信号,则退出 */
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
/*
|
||||
* Create unnamed portal to run the query or queries in. If there
|
||||
* already is one, silently drop it.
|
||||
* 创建一个无名portal来运行查询或查询。如果已经有一个,就默默地放弃它。
|
||||
*/
|
||||
portal = CreatePortal("", true, true);
|
||||
/* Don't display the portal in pg_cursors */
|
||||
/* 在pg_cursors中不显示portal */
|
||||
portal->visible = false;
|
||||
|
||||
u_sess->instr_cxt.global_instr = producer->getStreamInstrumentation();
|
||||
u_sess->instr_cxt.obs_instr = producer->getOBSInstrumentation();
|
||||
u_sess->instr_cxt.global_instr = producer->getStreamInstrumentation(); // 获取流仪器信息
|
||||
u_sess->instr_cxt.obs_instr = producer->getOBSInstrumentation(); // 获取OBS仪器信息
|
||||
if (u_sess->instr_cxt.obs_instr)
|
||||
u_sess->instr_cxt.p_OBS_instr_valid = u_sess->instr_cxt.obs_instr->m_p_globalOBSInstrument_valid;
|
||||
|
||||
PortalDefineQuery(portal, NULL, "DUMMY", commandTag, lappend(NULL, planstmt), NULL);
|
||||
|
||||
/*
|
||||
* Start the portal. No parameters here.
|
||||
* 启动portal。这里没有参数。
|
||||
*/
|
||||
PortalStart(portal, producer->getParams(), 0, producer->getSnapShot());
|
||||
format = 0;
|
||||
PortalSetResultFormat(portal, 1, &format);
|
||||
|
||||
receiver = CreateDestReceiver(dest);
|
||||
if (dest >= DestTupleBroadCast)
|
||||
receiver = CreateDestReceiver(dest); // 创建目标接收器
|
||||
if (dest >= DestTupleBroadCast) // 如果目标是广播,设置流接收器参数
|
||||
SetStreamReceiverParams(receiver, producer, portal);
|
||||
|
||||
/*
|
||||
* Run the portal to completion, and then drop it (and the receiver).
|
||||
* 运行portal到完成,并且然后丢弃它(以及接收器)。
|
||||
*/
|
||||
(void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag);
|
||||
|
||||
(*receiver->rDestroy)(receiver);
|
||||
(*receiver->rDestroy)(receiver); // 销毁接收器
|
||||
|
||||
PortalDrop(portal, false);
|
||||
PortalDrop(portal, false); // 丢弃portal
|
||||
|
||||
finish_xact_command();
|
||||
finish_xact_command(); // 完成事务命令
|
||||
|
||||
/*
|
||||
* Emit duration logging if appropriate.
|
||||
* 如果合适,记录持续时间日志。
|
||||
*/
|
||||
switch (check_log_duration(msec_str, false)) {
|
||||
case 1:
|
||||
|
|
@ -518,17 +557,19 @@ static void execute_stream_plan(StreamProducer* producer)
|
|||
ShowUsage("QUERY STATISTICS");
|
||||
}
|
||||
|
||||
// execute_stream_end函数用于结束流执行计划
|
||||
|
||||
static void execute_stream_end(StreamProducer* producer)
|
||||
{
|
||||
int consumer_number;
|
||||
int i, res;
|
||||
|
||||
consumer_number = producer->getConnNum();
|
||||
StreamTransport** transport = producer->getTransport();
|
||||
consumer_number = producer->getConnNum(); // 获取连接数
|
||||
StreamTransport** transport = producer->getTransport(); // 获取传输对象
|
||||
|
||||
// prepare an end message to all the consumer backend thread.
|
||||
// 准备一个结束消息发送到所有的消费者后端线程。
|
||||
//
|
||||
// if is dummy, do not bother send
|
||||
// 如果是虚拟的,就不需要发送了
|
||||
if (producer->isDummy() == false) {
|
||||
for (i = 0; i < consumer_number; i++) {
|
||||
if (producer->netSwitchDest(i)) {
|
||||
|
|
@ -540,7 +581,7 @@ static void execute_stream_end(StreamProducer* producer)
|
|||
pq_endmessage(&buf);
|
||||
} else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
|
||||
pq_putemptymessage('Z');
|
||||
/* Flush output at end of cycle in any case. */
|
||||
/* 在任何情况下,在周期结束时刷新输出。 */
|
||||
res = pq_flush();
|
||||
if (res == EOF) {
|
||||
transport[i]->release();
|
||||
|
|
@ -549,10 +590,10 @@ static void execute_stream_end(StreamProducer* producer)
|
|||
}
|
||||
}
|
||||
}
|
||||
producer->finalizeLocalStream();
|
||||
timeInfoRecordEnd();
|
||||
StreamNodeGroup::syncQuit(STREAM_COMPLETE);
|
||||
ForgetRegisterStreamSnapshots();
|
||||
producer->finalizeLocalStream(); // 完成本地流处理
|
||||
timeInfoRecordEnd(); // 记录时间信息结束
|
||||
StreamNodeGroup::syncQuit(STREAM_COMPLETE); // 同步退出
|
||||
ForgetRegisterStreamSnapshots(); // 忘记注册流快照
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -560,66 +601,67 @@ static void execute_stream_end(StreamProducer* producer)
|
|||
*/
|
||||
static void StreamQuitAndClean(int code, Datum arg)
|
||||
{
|
||||
/* Close connection with GTM, if active */
|
||||
/* 关闭与GTM的连接,如果激活 */
|
||||
CloseGTM();
|
||||
|
||||
/* Free remote xact state */
|
||||
/* 免费远程精确状态 */
|
||||
free_RemoteXactState();
|
||||
}
|
||||
|
||||
// reset some flag related to stream
|
||||
// 重置一些与流相关的标志
|
||||
void ResetStreamEnv()
|
||||
{
|
||||
t_thrd.subrole = NO_SUBROLE;
|
||||
u_sess->stream_cxt.dummy_thread = false;
|
||||
u_sess->exec_cxt.executorStopFlag = false;
|
||||
u_sess->stream_cxt.global_obj = NULL;
|
||||
u_sess->stream_cxt.producer_obj = NULL;
|
||||
u_sess->instr_cxt.global_instr = NULL;
|
||||
u_sess->instr_cxt.thread_instr = NULL;
|
||||
u_sess->exec_cxt.under_stream_runtime = false;
|
||||
u_sess->stream_cxt.in_waiting_quit = false;
|
||||
u_sess->stream_cxt.enter_sync_point = false;
|
||||
t_thrd.pgxc_cxt.GlobalNetInstr = NULL;
|
||||
t_thrd.subrole = NO_SUBROLE; // 重置子角色
|
||||
u_sess->stream_cxt.dummy_thread = false; // 虚拟线程标志重置为false
|
||||
u_sess->exec_cxt.executorStopFlag = false; // 执行器停止标志重置为false
|
||||
u_sess->stream_cxt.global_obj = NULL; // 全局对象重置为NULL
|
||||
u_sess->stream_cxt.producer_obj = NULL; // 生产者对象重置为NULL
|
||||
u_sess->instr_cxt.global_instr = NULL; // 全局指令重置为NULL
|
||||
u_sess->instr_cxt.thread_instr = NULL; // 线程指令重置为NULL
|
||||
u_sess->exec_cxt.under_stream_runtime = false; // 在流运行时标志重置为false
|
||||
u_sess->stream_cxt.in_waiting_quit = false; // 在等待退出标志重置为false
|
||||
u_sess->stream_cxt.enter_sync_point = false; // 进入同步点标志重置为false
|
||||
t_thrd.pgxc_cxt.GlobalNetInstr = NULL; // 全局网络指令重置为NULL
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
u_sess->opt_cxt.query_dop = u_sess->attr.attr_sql.query_dop_tmp;
|
||||
u_sess->opt_cxt.query_dop = u_sess->attr.attr_sql.query_dop_tmp; // 查询DOP重置为临时DOP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When gaussdb backend running in Query or Operator level, we are going to use global
|
||||
* variable notplanshipping to mark current query is not plan shipping, so we do string
|
||||
* initialization here
|
||||
* 当GaussDB后端在查询或操作级别运行时,我们将使用全局变量notplanshipping来标记当前查询不是计划发货,
|
||||
* 所以我们在这里进行字符串初始化。
|
||||
*/
|
||||
u_sess->opt_cxt.not_shipping_info->need_log = true;
|
||||
errno_t errorno = memset_s(
|
||||
u_sess->opt_cxt.not_shipping_info->not_shipping_reason, NOTPLANSHIPPING_LENGTH, '\0', NOTPLANSHIPPING_LENGTH);
|
||||
u_sess->opt_cxt.not_shipping_info->need_log = true; // 需要日志标志设置为true
|
||||
errno_t errorno = memset_s(u_sess->opt_cxt.not_shipping_info->not_shipping_reason,
|
||||
NOTPLANSHIPPING_LENGTH, '\0', NOTPLANSHIPPING_LENGTH);
|
||||
securec_check_c(errorno, "\0", "\0");
|
||||
|
||||
t_thrd.postgres_cxt.table_created_in_CTAS = false;
|
||||
t_thrd.postgres_cxt.table_created_in_CTAS = false; // CTAS标志重置为false
|
||||
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
u_sess->exec_cxt.need_track_resource = false;
|
||||
u_sess->exec_cxt.need_track_resource = false; // 需要跟踪资源标志重置为false
|
||||
}
|
||||
|
||||
u_sess->instr_cxt.gs_query_id->queryId = 0;
|
||||
u_sess->instr_cxt.gs_query_id->queryId = 0; // 查询ID重置为0
|
||||
|
||||
u_sess->wlm_cxt->local_foreign_respool = NULL;
|
||||
t_thrd.postmaster_cxt.forceNoSeparate = false;
|
||||
u_sess->wlm_cxt->local_foreign_respool = NULL; // 本地外部资源池重置为NULL
|
||||
t_thrd.postmaster_cxt.forceNoSeparate = false; // 强制不分离标志重置为false
|
||||
|
||||
u_sess->pcache_cxt.gpc_remote_msg = false;
|
||||
u_sess->pcache_cxt.gpc_remote_msg = false; // 远程消息标志重置为false
|
||||
|
||||
t_thrd.postgres_cxt.gpc_fisrt_send_clean = true;
|
||||
t_thrd.postgres_cxt.gpc_fisrt_send_clean = true; // 第一次发送清除标志重置为true
|
||||
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
WLMStatusTag status = t_thrd.wlm_cxt.collect_info->status;
|
||||
if (!(status == WLM_STATUS_FINISHED || status == WLM_STATUS_ABORT))
|
||||
if (!(status == WLM_STATUS_FINISHED || status == WLM_STATUS_ABORT)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_PGXC_DATANODE) {
|
||||
WLMStatusTag status = t_thrd.wlm_cxt.dn_cpu_detail->status;
|
||||
if (!(status == WLM_STATUS_FINISHED || status == WLM_STATUS_ABORT))
|
||||
if (!(status == WLM_STATUS_FINISHED || status == WLM_STATUS_ABORT)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->initMemInChunks = t_thrd.utils_cxt.trackedMemChunks;
|
||||
|
|
@ -654,28 +696,29 @@ void SetStreamWorkerInfo(StreamProducer* proObj)
|
|||
return;
|
||||
}
|
||||
|
||||
u_sess->stream_cxt.producer_obj = proObj;
|
||||
u_sess->stream_cxt.smp_id = proObj->getKey().smpIdentifier;
|
||||
u_sess->stream_cxt.producer_dop = proObj->getParallelDesc().producerDop;
|
||||
u_sess->debug_query_id = proObj->getKey().queryId;
|
||||
u_sess->utils_cxt.sync_guc_variables = u_sess->stream_cxt.producer_obj->get_sync_guc_variables();
|
||||
// set the stopFlag;
|
||||
u_sess->stream_cxt.global_obj = u_sess->stream_cxt.producer_obj->getNodeGroup();
|
||||
u_sess->stream_cxt.producer_obj = proObj; // 设置生产者对象
|
||||
u_sess->stream_cxt.smp_id = proObj->getKey().smpIdentifier; // 设置SMP标识符
|
||||
u_sess->stream_cxt.producer_dop = proObj->getParallelDesc().producerDop; // 设置生产者DOP
|
||||
u_sess->debug_query_id = proObj->getKey().queryId; // 设置调试查询ID
|
||||
u_sess->utils_cxt.sync_guc_variables = u_sess->stream_cxt.producer_obj->get_sync_guc_variables(); // 同步GUC变量
|
||||
// 设置停止标志
|
||||
u_sess->stream_cxt.global_obj = u_sess->stream_cxt.producer_obj->getNodeGroup(); // 设置全局对象
|
||||
u_sess->stream_cxt.global_obj->setStopFlagPoint(
|
||||
u_sess->stream_cxt.producer_obj->getNodeGroupIdx(), &u_sess->exec_cxt.executorStopFlag);
|
||||
u_sess->stream_cxt.producer_obj->getNodeGroupIdx(), &u_sess->exec_cxt.executorStopFlag); // 设置停止标志点
|
||||
}
|
||||
|
||||
static void ResetStreamWorkerInfo()
|
||||
{
|
||||
u_sess->stream_cxt.producer_obj = NULL;
|
||||
u_sess->stream_cxt.global_obj = NULL;
|
||||
u_sess->stream_cxt.producer_obj = NULL; // 重置生产者对象为NULL
|
||||
u_sess->stream_cxt.global_obj = NULL; // 重置全局对象为NULL
|
||||
}
|
||||
|
||||
static void StoreStreamSyncParam(StreamSyncParam *syncParam)
|
||||
{
|
||||
syncParam->TempNamespace = u_sess->catalog_cxt.myTempNamespace;
|
||||
syncParam->TempToastNamespace = u_sess->catalog_cxt.myTempToastNamespace;
|
||||
syncParam->IsBinaryUpgrade = u_sess->proc_cxt.IsBinaryUpgrade;
|
||||
syncParam->TempNamespace = u_sess->catalog_cxt.myTempNamespace; // 存储临时命名空间
|
||||
syncParam->TempToastNamespace = u_sess->catalog_cxt.myTempToastNamespace; // 存储临时TOAST命名空间
|
||||
syncParam->IsBinaryUpgrade = u_sess->proc_cxt.IsBinaryUpgrade; // 存储二进制升级标志
|
||||
// 如果通信IPC模块的日志开启,并且当前进程的工作版本号大于等于92060,则设置CommIpcLog为true,否则为false。
|
||||
if (module_logging_is_on(MOD_COMM_IPC) && (t_thrd.proc && t_thrd.proc->workingVersionNum >= 92060)) {
|
||||
syncParam->CommIpcLog = true;
|
||||
} else {
|
||||
|
|
@ -695,39 +738,39 @@ void RestoreStreamSyncParam(StreamSyncParam *syncParam)
|
|||
|
||||
ThreadId ApplyStreamThread(StreamProducer *producer)
|
||||
{
|
||||
ThreadId tid = InvalidTid;
|
||||
ThreadId tid = InvalidTid; // 初始化线程ID为无效值
|
||||
|
||||
producer->setParentSessionid(t_thrd.proc->sessMemorySessionid);
|
||||
StoreStreamSyncParam(&producer->m_syncParam);
|
||||
producer->setParentSessionid(t_thrd.proc->sessMemorySessionid); // 设置生产者的父会话ID为当前会话的内存会话ID
|
||||
StoreStreamSyncParam(&producer->m_syncParam); // 存储流同步参数
|
||||
|
||||
if (t_thrd.threadpool_cxt.group != NULL) {
|
||||
tid = t_thrd.threadpool_cxt.group->GetStreamFromPool(producer);
|
||||
if (t_thrd.threadpool_cxt.group != NULL) { // 如果线程池组不为空
|
||||
tid = t_thrd.threadpool_cxt.group->GetStreamFromPool(producer); // 从线程池组中获取流线程
|
||||
STREAM_LOG(DEBUG2, "[StreamPool] Apply thread %lu query_id %lu, tlevel %u, smpid %u",
|
||||
tid,
|
||||
producer->getKey().queryId,
|
||||
producer->getKey().planNodeId,
|
||||
producer->getKey().smpIdentifier);
|
||||
producer->getKey().smpIdentifier); // 记录日志
|
||||
} else {
|
||||
producer->setChildSlot(AssignPostmasterChildSlot());
|
||||
if (producer->getChildSlot() == -1) {
|
||||
producer->setChildSlot(AssignPostmasterChildSlot()); // 分配子进程槽位
|
||||
if (producer->getChildSlot() == -1) { // 如果槽位分配失败,返回无效线程ID
|
||||
return InvalidTid;
|
||||
}
|
||||
tid = initialize_util_thread(STREAM_WORKER, producer);
|
||||
tid = initialize_util_thread(STREAM_WORKER, producer); // 初始化流工作线程
|
||||
}
|
||||
|
||||
return tid;
|
||||
return tid; // 返回线程ID
|
||||
}
|
||||
|
||||
void RestoreStream()
|
||||
{
|
||||
/*
|
||||
* We should restoreStreamEnter after release the memory context.
|
||||
* Make sure top consumer thread exit after stream thread.
|
||||
* 在释放内存上下文后,我们应该在restoreStreamEnter之后进行恢复。
|
||||
* 确保顶级消费者线程在流线程之后退出。
|
||||
*/
|
||||
if (StreamThreadAmI() && u_sess->stream_cxt.global_obj) {
|
||||
/*
|
||||
* Set CurrentResourceOwner to NULL or will core dumped in ResourceOwnerEnlargePthreadMutex
|
||||
* case t_thrd.top_mem_cxt has been set NULL.
|
||||
* 设置CurrentResourceOwner为NULL,否则在ResourceOwnerEnlargePthreadMutex中将发生核心转储,
|
||||
* 因为t_thrd.top_mem_cxt已经设置为NULL。
|
||||
*/
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = NULL;
|
||||
u_sess->stream_cxt.global_obj->restoreStreamEnter();
|
||||
|
|
@ -741,7 +784,7 @@ void StreamExit()
|
|||
return;
|
||||
}
|
||||
|
||||
/* Reset to Local vfd if we have attach it to global vfdcache */
|
||||
/* 如果我们已经将其附加到全局vfdcache,则重置为本地vfd */
|
||||
ResetToLocalVfdCache();
|
||||
|
||||
CleanupDfsHandlers(true);
|
||||
|
|
@ -750,7 +793,7 @@ void StreamExit()
|
|||
|
||||
AtProcExit_Buffers(0, 0);
|
||||
ShutdownPostgres(0, 0);
|
||||
if(!EnableLocalSysCache()) {
|
||||
if (!EnableLocalSysCache()) {
|
||||
AtProcExit_Files(0, 0);
|
||||
}
|
||||
StreamQuitAndClean(0, 0);
|
||||
|
|
@ -758,21 +801,21 @@ void StreamExit()
|
|||
RestoreStream();
|
||||
|
||||
if (!EnableLocalSysCache()) {
|
||||
/* release memory context and reset flags. */
|
||||
/* 释放内存上下文并重置标志。 */
|
||||
MemoryContextReset(u_sess->syscache_cxt.SysCacheMemCxt);
|
||||
errno_t rc = EOK;
|
||||
rc = memset_s(u_sess->syscache_cxt.SysCache, sizeof(CatCache*) * SysCacheSize,
|
||||
0, sizeof(CatCache*) * SysCacheSize);
|
||||
0, sizeof(CatCache*) * SysCacheSize);
|
||||
securec_check(rc, "\0", "\0");
|
||||
rc = memset_s(u_sess->syscache_cxt.SysCacheRelationOid, sizeof(Oid) * SysCacheSize,
|
||||
0, sizeof(Oid) * SysCacheSize);
|
||||
0, sizeof(Oid) * SysCacheSize);
|
||||
securec_check(rc, "\0", "\0");
|
||||
}
|
||||
|
||||
/* release statement_cxt */
|
||||
/* 释放statement_cxt */
|
||||
if (t_thrd.proc_cxt.MyBackendId != InvalidBackendId) {
|
||||
release_statement_context(t_thrd.shemem_ptr_cxt.MyBEEntry, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
free_session_context(u_sess);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,11 +32,17 @@
|
|||
|
||||
StreamCOMM::StreamCOMM(libcommaddrinfo* addr, bool flag) : m_addr(addr)
|
||||
{
|
||||
/* 初始化节点名称为空字符串 */
|
||||
m_nodeName[0] = '\0';
|
||||
/* 初始化节点OID为无效OID */
|
||||
m_nodeoid = InvalidOid;
|
||||
/* 设置通信类型为STREAM_COMM */
|
||||
m_type = STREAM_COMM;
|
||||
/* 设置发送方标志 */
|
||||
m_sendSide = flag;
|
||||
/* 初始化端口为NULL */
|
||||
m_port = NULL;
|
||||
/* 初始化缓冲区为NULL */
|
||||
m_buffer = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -99,13 +105,20 @@ void StreamCOMM::init(char* dbname, char* usrname)
|
|||
*/
|
||||
void StreamCOMM::allocNetBuffer()
|
||||
{
|
||||
/* 分配端口结构内存并初始化为零 */
|
||||
m_port = (Port*)palloc0(sizeof(Port));
|
||||
|
||||
/* 如果是发送方 */
|
||||
if (m_sendSide) {
|
||||
/* 分配StreamBuffer结构内存并初始化为零 */
|
||||
m_buffer = (StreamBuffer*)palloc0(sizeof(StreamBuffer));
|
||||
/* 设置发送缓冲区大小为STREAM_BUFFER_SIZE */
|
||||
m_buffer->PqSendBufferSize = STREAM_BUFFER_SIZE;
|
||||
/* 设置发送指针为0 */
|
||||
m_buffer->PqSendPointer = 0;
|
||||
/* 设置发送开始位置为0 */
|
||||
m_buffer->PqSendStart = 0;
|
||||
/* 设置通信忙标志为false */
|
||||
m_buffer->PqCommBusy = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -118,17 +131,17 @@ void StreamCOMM::allocNetBuffer()
|
|||
bool StreamCOMM::setActive()
|
||||
{
|
||||
/*
|
||||
* if we use parallel send mode,
|
||||
* and the head of address info list is already close,
|
||||
* we must continue to send,
|
||||
* and gs_broadcast can send to other node in address info list.
|
||||
* 如果使用并行发送模式,
|
||||
* 并且地址信息列表的头部已经关闭,
|
||||
* 我们必须继续发送,
|
||||
* 并且gs_broadcast可以发送到地址信息列表中的其他节点。
|
||||
*/
|
||||
if (m_addr->parallel_send_mode == true) {
|
||||
/*
|
||||
* if we use parallel send mode,
|
||||
* we only send to head node of address info list,
|
||||
* and do not care other node in address info list,
|
||||
* gs_broadcast can parallel send to other node.
|
||||
* 如果使用并行发送模式,
|
||||
* 我们只发送到地址信息列表的头节点,
|
||||
* 不关心地址信息列表中的其他节点,
|
||||
* gs_broadcast可以并行发送到其他节点。
|
||||
*/
|
||||
if (m_addr->addr_list_size == 0)
|
||||
return false;
|
||||
|
|
@ -136,8 +149,10 @@ bool StreamCOMM::setActive()
|
|||
return false;
|
||||
}
|
||||
|
||||
/* 设置当前线程的ProcPort */
|
||||
u_sess->proc_cxt.MyProcPort = m_port;
|
||||
|
||||
/* 设置发送缓冲区指针、大小和起始位置以及通信忙标志 */
|
||||
t_thrd.libpq_cxt.PqSendBuffer = &m_buffer->PqSendBuffer[0];
|
||||
t_thrd.libpq_cxt.PqSendPointer = m_buffer->PqSendPointer;
|
||||
t_thrd.libpq_cxt.PqSendBufferSize = m_buffer->PqSendBufferSize;
|
||||
|
|
@ -177,11 +192,14 @@ void StreamCOMM::setInActive()
|
|||
*/
|
||||
void StreamCOMM::updateInfo(StreamConnInfo* connInfo)
|
||||
{
|
||||
/* 获取节点名称的长度 */
|
||||
int nodeNameLen = strlen(connInfo->nodeName);
|
||||
errno_t rc = EOK;
|
||||
|
||||
/* 将连接信息中的套接字信息复制到StreamCOMM对象的套接字信息中 */
|
||||
m_addr->gs_sock = connInfo->port.libcomm_layer.gsock;
|
||||
rc = strncpy_s(m_nodeName, NAMEDATALEN, connInfo->nodeName, nodeNameLen + 1);
|
||||
/* 将连接信息中的节点名称复制到StreamCOMM对象的节点名称中 */
|
||||
errno_t rc = strncpy_s(m_nodeName, NAMEDATALEN, connInfo->nodeName, nodeNameLen + 1);
|
||||
/* 检查字符串复制是否成功 */
|
||||
securec_check(rc, "\0", "\0");
|
||||
/* 将连接信息中的生产者SMP ID复制到StreamCOMM对象的streamKey中 */
|
||||
m_addr->streamKey.producerSmpId = connInfo->producerSmpId;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,245 +1,248 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* stream_cost.cpp
|
||||
* functions used to calculate stream plan costs.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/gaussdbkernel/porcess/stream/stream_cost.cpp
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include "access/hash.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/dataskew.h"
|
||||
#include "optimizer/planner.h"
|
||||
|
||||
void parallel_stream_info_print(ParallelDesc* smpDesc, StreamType type)
|
||||
{
|
||||
char* distri_type = NULL;
|
||||
|
||||
if (NULL == smpDesc)
|
||||
return;
|
||||
|
||||
/* Set stream type tag. */
|
||||
switch (smpDesc->distriType) {
|
||||
case REMOTE_DISTRIBUTE:
|
||||
distri_type = "REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case REMOTE_SPLIT_DISTRIBUTE:
|
||||
distri_type = "SPLIT REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case REMOTE_BROADCAST:
|
||||
distri_type = "BROADCAST";
|
||||
break;
|
||||
|
||||
case REMOTE_SPLIT_BROADCAST:
|
||||
distri_type = "SPLIT BROADCAST";
|
||||
break;
|
||||
|
||||
case LOCAL_DISTRIBUTE:
|
||||
distri_type = "LOCAL REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case LOCAL_BROADCAST:
|
||||
distri_type = "LOCAL BROADCAST";
|
||||
break;
|
||||
|
||||
case LOCAL_ROUNDROBIN:
|
||||
distri_type = "LOCAL ROUNDROBIN";
|
||||
break;
|
||||
|
||||
default:
|
||||
if (type == STREAM_BROADCAST)
|
||||
distri_type = "BROADCAST";
|
||||
else
|
||||
distri_type = "REDISTRIBUTE";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Print log. */
|
||||
elog(DEBUG1,
|
||||
"Stream cost: SMP INFO: sendDop: %d, receiveDop: %d, distribute type: %s",
|
||||
SET_DOP(smpDesc->producerDop),
|
||||
SET_DOP(smpDesc->consumerDop),
|
||||
distri_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_stream
|
||||
* computer cost of stream a RepOptInfo object
|
||||
*/
|
||||
void cost_stream(StreamPath* stream, int width, bool isJoin)
|
||||
{
|
||||
const double startup_cost_broadcast = 0.0;
|
||||
|
||||
AssertEreport(stream != NULL && stream->subpath != NULL, MOD_OPT, "The stream or subplan is invalid");
|
||||
|
||||
stream->path.startup_cost = startup_cost_broadcast;
|
||||
stream->path.startup_cost += stream->subpath->startup_cost;
|
||||
stream->path.total_cost = stream->subpath->total_cost;
|
||||
stream->path.stream_cost = stream->subpath->startup_cost;
|
||||
|
||||
unsigned int producer_num_datanodes = bms_num_members(stream->path.distribution.bms_data_nodeids);
|
||||
unsigned int consumer_num_datanodes = bms_num_members(stream->consumer_distribution.bms_data_nodeids);
|
||||
|
||||
compute_stream_cost(stream->type,
|
||||
stream->subpath->locator_type,
|
||||
PATH_LOCAL_ROWS(stream->subpath),
|
||||
stream->subpath->rows,
|
||||
stream->path.multiple,
|
||||
width,
|
||||
isJoin,
|
||||
stream->path.distribute_keys,
|
||||
&stream->path.total_cost,
|
||||
&stream->path.rows,
|
||||
producer_num_datanodes,
|
||||
consumer_num_datanodes,
|
||||
stream->smpDesc,
|
||||
stream->skew_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
List* get_max_cost_distkey_for_hasdistkey(PlannerInfo* root, List* subPlans, int subPlanNum,
|
||||
List** subPlanKeyArray, Cost* subPlanCostArray, Bitmapset** redistributePlanSetCopy)
|
||||
{
|
||||
Cost* keyCostArray = NULL;
|
||||
int counter = 0;
|
||||
int maxCostIndex = 0;
|
||||
int subPlanIndex = 0;
|
||||
List* redistributeKeyIndex = NULL;
|
||||
|
||||
/*
|
||||
* There are more than one distribute key of subplan,
|
||||
* find the max cost distribute key of subplan.
|
||||
*/
|
||||
keyCostArray = (Cost*)palloc0(sizeof(Cost) * subPlanNum);
|
||||
|
||||
while (counter < subPlanNum) {
|
||||
List* keyIndex = subPlanKeyArray[counter];
|
||||
|
||||
if (keyIndex == NULL) {
|
||||
counter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (subPlanIndex = 0; subPlanIndex < subPlanNum; subPlanIndex++) {
|
||||
if (equal(subPlanKeyArray[subPlanIndex], keyIndex)) {
|
||||
if (subPlanIndex < counter) {
|
||||
keyCostArray[counter] = 0;
|
||||
break;
|
||||
} else {
|
||||
keyCostArray[counter] += subPlanCostArray[subPlanIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the max cost for each redistributekey.
|
||||
*/
|
||||
for (counter = 0; counter < subPlanNum; counter++) {
|
||||
if (keyCostArray[maxCostIndex] < keyCostArray[counter]) {
|
||||
maxCostIndex = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set other each subplan uesing the max cost redistribute key.
|
||||
*/
|
||||
redistributeKeyIndex = subPlanKeyArray[maxCostIndex];
|
||||
for (subPlanIndex = 0; subPlanIndex < subPlanNum; subPlanIndex++) {
|
||||
if (!equal(subPlanKeyArray[subPlanIndex], redistributeKeyIndex)) {
|
||||
*redistributePlanSetCopy = bms_add_member(*redistributePlanSetCopy, subPlanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
pfree_ext(keyCostArray);
|
||||
keyCostArray = NULL;
|
||||
return redistributeKeyIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* We should get the max cost distribute key of subplan
|
||||
* as the final redistribute key for other subplan.
|
||||
*/
|
||||
List* get_max_cost_distkey_for_nulldistkey(
|
||||
PlannerInfo* root, List* subPlans, int subPlanNum, Cost* subPlanCostArray)
|
||||
|
||||
{
|
||||
Plan* subPlan = NULL;
|
||||
int counter = 0;
|
||||
int maxCostIndex = 0;
|
||||
List* redistributeKeyIndex = NULL;
|
||||
|
||||
/*
|
||||
* Get the max cost for each redistributekey.
|
||||
*/
|
||||
for (counter = 0; counter < subPlanNum; counter++) {
|
||||
if (subPlanCostArray[maxCostIndex] < subPlanCostArray[counter]) {
|
||||
maxCostIndex = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is no distkey, we should choose the max cost distkey. */
|
||||
subPlan = (Plan*)list_nth(subPlans, maxCostIndex);
|
||||
redistributeKeyIndex = make_distkey_for_append(root, subPlan);
|
||||
return redistributeKeyIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct distribute key index according to bias, if we cannot find distribute key.
|
||||
* If the targetlist of subplan has no relid, we should choose the first three target entry
|
||||
* of var for distribute key.
|
||||
*/
|
||||
List* make_distkey_for_append(PlannerInfo* root, Plan* subPlan)
|
||||
{
|
||||
List* grplist = NIL;
|
||||
List* subPlanKeyArray = NIL;
|
||||
const int defaultDistkeyNum = 3;
|
||||
|
||||
/* Construct group clause using targetlist. */
|
||||
grplist = make_groupcl_for_append(root, subPlan->targetlist);
|
||||
if (grplist != NIL) {
|
||||
double multiple;
|
||||
List* distkeys = NIL;
|
||||
|
||||
/* Get distkeys according to bias. */
|
||||
distkeys = get_distributekey_from_tlist(root, subPlan->targetlist, grplist, subPlan->plan_rows, &multiple);
|
||||
if (distkeys != NIL)
|
||||
subPlanKeyArray = distributeKeyIndex(root, distkeys, subPlan->targetlist);
|
||||
|
||||
list_free_ext(grplist);
|
||||
list_free_ext(distkeys);
|
||||
} else {
|
||||
/* Choose the first three target entry of var for distribute key. */
|
||||
int distkeynum = 0;
|
||||
ListCell* teCell = NULL;
|
||||
|
||||
foreach (teCell, subPlan->targetlist) {
|
||||
TargetEntry* teEntry = (TargetEntry*)lfirst(teCell);
|
||||
Node* node = (Node*)teEntry->expr;
|
||||
|
||||
if (!teEntry->resjunk && IsTypeDistributable(exprType(node))) {
|
||||
subPlanKeyArray = lappend_int(subPlanKeyArray, teEntry->resno);
|
||||
distkeynum++;
|
||||
|
||||
if (distkeynum <= defaultDistkeyNum)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subPlanKeyArray;
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* stream_cost.cpp
|
||||
* functions used to calculate stream plan costs.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/gaussdbkernel/porcess/stream/stream_cost.cpp
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include "access/hash.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/dataskew.h"
|
||||
#include "optimizer/planner.h"
|
||||
|
||||
void parallel_stream_info_print(ParallelDesc* smpDesc, StreamType type)
|
||||
{
|
||||
char* distri_type = NULL;
|
||||
|
||||
if (NULL == smpDesc)
|
||||
return;
|
||||
|
||||
/* 设置流类型标签。*/
|
||||
switch (smpDesc->distriType) {
|
||||
case REMOTE_DISTRIBUTE:
|
||||
distri_type = "REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case REMOTE_SPLIT_DISTRIBUTE:
|
||||
distri_type = "SPLIT REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case REMOTE_BROADCAST:
|
||||
distri_type = "BROADCAST";
|
||||
break;
|
||||
|
||||
case REMOTE_SPLIT_BROADCAST:
|
||||
distri_type = "SPLIT BROADCAST";
|
||||
break;
|
||||
|
||||
case LOCAL_DISTRIBUTE:
|
||||
distri_type = "LOCAL REDISTRIBUTE";
|
||||
break;
|
||||
|
||||
case LOCAL_BROADCAST:
|
||||
distri_type = "LOCAL BROADCAST";
|
||||
break;
|
||||
|
||||
case LOCAL_ROUNDROBIN:
|
||||
distri_type = "LOCAL ROUNDROBIN";
|
||||
break;
|
||||
|
||||
default:
|
||||
if (type == STREAM_BROADCAST)
|
||||
distri_type = "BROADCAST";
|
||||
else
|
||||
distri_type = "REDISTRIBUTE";
|
||||
break;
|
||||
}
|
||||
|
||||
/* 打印日志。*/
|
||||
elog(DEBUG1,
|
||||
"Stream cost: SMP INFO: sendDop: %d, receiveDop: %d, distribute type: %s",
|
||||
SET_DOP(smpDesc->producerDop),
|
||||
SET_DOP(smpDesc->consumerDop),
|
||||
distri_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_stream
|
||||
* computer cost of stream a RepOptInfo object
|
||||
*/
|
||||
void cost_stream(StreamPath* stream, int width, bool isJoin)
|
||||
{
|
||||
const double startup_cost_broadcast = 0.0;
|
||||
|
||||
// 断言:流和子计划不能为空
|
||||
AssertEreport(stream != NULL && stream->subpath != NULL, MOD_OPT, "The stream or subplan is invalid");
|
||||
|
||||
// 设置启动成本为广播的启动成本
|
||||
stream->path.startup_cost = startup_cost_broadcast;
|
||||
// 累加子计划的启动成本到流的启动成本
|
||||
stream->path.startup_cost += stream->subpath->startup_cost;
|
||||
// 设置流的总成本为子计划的总成本
|
||||
stream->path.total_cost = stream->subpath->total_cost;
|
||||
// 设置流的成本为子计划的启动成本
|
||||
stream->path.stream_cost = stream->subpath->startup_cost;
|
||||
|
||||
// 计算生产者和消费者数据节点的数量
|
||||
unsigned int producer_num_datanodes = bms_num_members(stream->path.distribution.bms_data_nodeids);
|
||||
unsigned int consumer_num_datanodes = bms_num_members(stream->consumer_distribution.bms_data_nodeids);
|
||||
|
||||
// 调用compute_stream_cost函数计算流的成本
|
||||
compute_stream_cost(stream->type,
|
||||
stream->subpath->locator_type,
|
||||
PATH_LOCAL_ROWS(stream->subpath),
|
||||
stream->subpath->rows,
|
||||
stream->path.multiple,
|
||||
width,
|
||||
isJoin,
|
||||
stream->path.distribute_keys,
|
||||
&stream->path.total_cost,
|
||||
&stream->path.rows,
|
||||
producer_num_datanodes,
|
||||
consumer_num_datanodes,
|
||||
stream->smpDesc,
|
||||
stream->skew_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
List* get_max_cost_distkey_for_hasdistkey(PlannerInfo* root, List* subPlans, int subPlanNum,
|
||||
List** subPlanKeyArray, Cost* subPlanCostArray, Bitmapset** redistributePlanSetCopy)
|
||||
{
|
||||
Cost* keyCostArray = NULL;
|
||||
int counter = 0;
|
||||
int maxCostIndex = 0;
|
||||
int subPlanIndex = 0;
|
||||
List* redistributeKeyIndex = NULL;
|
||||
|
||||
/*
|
||||
* subplan有多个分发键,找出子计划的最大成本分配键。
|
||||
*/
|
||||
keyCostArray = (Cost*)palloc0(sizeof(Cost) * subPlanNum);
|
||||
|
||||
while (counter < subPlanNum) {
|
||||
List* keyIndex = subPlanKeyArray[counter];
|
||||
|
||||
if (keyIndex == NULL) {
|
||||
counter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (subPlanIndex = 0; subPlanIndex < subPlanNum; subPlanIndex++) {
|
||||
if (equal(subPlanKeyArray[subPlanIndex], keyIndex)) {
|
||||
if (subPlanIndex < counter) {
|
||||
keyCostArray[counter] = 0;
|
||||
break;
|
||||
} else {
|
||||
keyCostArray[counter] += subPlanCostArray[subPlanIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
/*
|
||||
* 得到每个redistributekey的最大代价
|
||||
*/
|
||||
for (counter = 0; counter < subPlanNum; counter++) {
|
||||
if (keyCostArray[maxCostIndex] < keyCostArray[counter]) {
|
||||
maxCostIndex = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 使用最大成本重分发键设置其他每个子计划
|
||||
*/
|
||||
redistributeKeyIndex = subPlanKeyArray[maxCostIndex];
|
||||
for (subPlanIndex = 0; subPlanIndex < subPlanNum; subPlanIndex++) {
|
||||
if (!equal(subPlanKeyArray[subPlanIndex], redistributeKeyIndex)) {
|
||||
*redistributePlanSetCopy = bms_add_member(*redistributePlanSetCopy, subPlanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
pfree_ext(keyCostArray);
|
||||
keyCostArray = NULL;
|
||||
return redistributeKeyIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* 我们需要得到子计划的最大成本分配键作为其他子计划的最终重分发键。
|
||||
*/
|
||||
List* get_max_cost_distkey_for_nulldistkey(PlannerInfo* root, List* subPlans, int subPlanNum, Cost* subPlanCostArray)
|
||||
{
|
||||
Plan* subPlan = NULL;
|
||||
int counter = 0;
|
||||
int maxCostIndex = 0;
|
||||
List* redistributeKeyIndex = NULL;
|
||||
|
||||
/*
|
||||
* 找到具有最大成本的重新分发键。
|
||||
*/
|
||||
for (counter = 0; counter < subPlanNum; counter++) {
|
||||
if (subPlanCostArray[maxCostIndex] < subPlanCostArray[counter]) {
|
||||
maxCostIndex = counter;
|
||||
}
|
||||
}
|
||||
|
||||
/* 如果没有分发键,我们应该选择具有最大成本的分发键。 */
|
||||
subPlan = (Plan*)list_nth(subPlans, maxCostIndex);
|
||||
redistributeKeyIndex = make_distkey_for_append(root, subPlan);
|
||||
return redistributeKeyIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
如果找不到分布键,则根据偏差构造分布键索引。
|
||||
如果subplan的targetlist没有空闲,我们应该选择前三个目标条目
|
||||
的var为分发键。
|
||||
*/
|
||||
List* make_distkey_for_append(PlannerInfo* root, Plan* subPlan)
|
||||
{
|
||||
List* grplist = NIL;
|
||||
List* subPlanKeyArray = NIL;
|
||||
const int defaultDistkeyNum = 3;
|
||||
|
||||
/* 使用目标列表构建分组子句。 */
|
||||
grplist = make_groupcl_for_append(root, subPlan->targetlist);
|
||||
if (grplist != NIL) {
|
||||
double multiple;
|
||||
List* distkeys = NIL;
|
||||
|
||||
/* 根据偏差获取分发键。 */
|
||||
distkeys = get_distributekey_from_tlist(root, subPlan->targetlist, grplist, subPlan->plan_rows, &multiple);
|
||||
if (distkeys != NIL)
|
||||
subPlanKeyArray = distributeKeyIndex(root, distkeys, subPlan->targetlist);
|
||||
|
||||
list_free_ext(grplist);
|
||||
list_free_ext(distkeys);
|
||||
} else {
|
||||
/* 选择变量的前三个目标项作为分发键。 */
|
||||
int distkeynum = 0;
|
||||
ListCell* teCell = NULL;
|
||||
|
||||
foreach (teCell, subPlan->targetlist) {
|
||||
TargetEntry* teEntry = (TargetEntry*)lfirst(teCell);
|
||||
Node* node = (Node*)teEntry->expr;
|
||||
|
||||
if (!teEntry->resjunk && IsTypeDistributable(exprType(node))) {
|
||||
subPlanKeyArray = lappend_int(subPlanKeyArray, teEntry->resno);
|
||||
distkeynum++;
|
||||
|
||||
if (distkeynum <= defaultDistkeyNum)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subPlanKeyArray;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
auditfuncs.cpp
|
||||
autonomoustransaction.cpp
|
||||
CMakeLists.txt
|
||||
dest.cpp
|
||||
fastpath.cpp
|
||||
LIST.TXT
|
||||
Makefile
|
||||
postgres.cpp
|
||||
pquery.cpp
|
||||
stmt_retry.cpp
|
||||
utility.cpp
|
||||
新建文本文档.bat
|
||||
|
|
@ -0,0 +1 @@
|
|||
DIR *.* /B >LIST.TXT
|
||||
|
|
@ -2,20 +2,10 @@
|
|||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 2021, openGauss Contributors
|
||||
*
|
||||
* openGauss is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* knl_instance.cpp
|
||||
* Initial functions for instance level global variables.
|
||||
* 实例级全局变量的初始函数
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/gausskernel/process/threadpool/knl_instance.cpp
|
||||
|
|
@ -24,43 +14,46 @@
|
|||
*/
|
||||
#include <c.h>
|
||||
|
||||
#include "access/parallel_recovery/page_redo.h"
|
||||
#include "access/reloptions.h"
|
||||
#include "access/xlog.h"
|
||||
#include "commands/prepare.h"
|
||||
#include "executor/instrument.h"
|
||||
#include "gssignal/gs_signal.h"
|
||||
#include "access/parallel_recovery/page_redo.h"//用于并行恢复操作的头文件
|
||||
#include "access/reloptions.h"//处理表和索引的选项
|
||||
#include "access/xlog.h"//处理事务日志
|
||||
#include "commands/prepare.h"//用于SQL预处理
|
||||
#include "executor/instrument.h"//用于执行计划
|
||||
#include "gssignal/gs_signal.h"//处理操作系统信号
|
||||
#include "instruments/instr_waitevent.h"//用于性能监控等待事件
|
||||
#include "knl/knl_instance.h"//PostgreSQL内核实例
|
||||
#include "libcomm/libcomm.h"// 用于通信库的头文件
|
||||
#include "optimizer/cost.h"//查询优化中的成本估算
|
||||
#include "optimizer/dynsmp.h"//动态并行查询执行
|
||||
#include "optimizer/planmain.h"//查询规划主要头文件
|
||||
#include "optimizer/planner.h"//查询规划器
|
||||
#include "optimizer/streamplan.h"//流式查询计划
|
||||
#include "pgstat.h"//用于性能统计
|
||||
#include "regex/regex.h"//正则表达式
|
||||
#include "utils/memutils.h"//内存管理工具
|
||||
#include "utils/palloc.h"//内存分配
|
||||
#include "workload/workload.h"// 工作负载管理相关
|
||||
#include "instruments/instr_waitevent.h"
|
||||
#include "knl/knl_instance.h"
|
||||
#include "libcomm/libcomm.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/dynsmp.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/streamplan.h"
|
||||
#include "pgstat.h"
|
||||
#include "regex/regex.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "workload/workload.h"
|
||||
#include "instruments/instr_waitevent.h"
|
||||
#include "access/multi_redo_api.h"
|
||||
#include "utils/hotkey.h"
|
||||
#include "lib/lrucache.h"
|
||||
#include "access/multi_redo_api.h"//多线程重做操作
|
||||
#include "utils/hotkey.h"//处理热点键
|
||||
#include "lib/lrucache.h"//LRU缓存
|
||||
|
||||
#ifdef ENABLE_WHITEBOX
|
||||
#include "access/ustore/knl_whitebox_test.h"
|
||||
#endif
|
||||
|
||||
const int SIZE_OF_TWO_UINT64 = 16;
|
||||
|
||||
//常量表示,两个64位整数的总大小为16字节
|
||||
knl_instance_context g_instance;
|
||||
|
||||
const int ALLOCSET_UNDO_MAXSIZE = 300 * UNDO_ZONE_COUNT;
|
||||
|
||||
extern void InitGlobalVecFuncMap();
|
||||
|
||||
//一个外部函数声明
|
||||
static void knl_g_cost_init(knl_g_cost_context* cost_cxt)
|
||||
{
|
||||
/*初始化一个 knl_g_cost_context 结构中的各个成员变量,
|
||||
以便后续的操作可以使用这些值来进行成本估算或其他计算*/
|
||||
cost_cxt->cpu_hash_cost = DEFAULT_CPU_HASH_COST;
|
||||
cost_cxt->send_kdata_cost = DEFAULT_SEND_KDATA_COST;
|
||||
cost_cxt->receive_kdata_cost = DEFAULT_RECEIVE_KDATA_COST;
|
||||
|
|
@ -70,54 +63,83 @@ static void knl_g_cost_init(knl_g_cost_context* cost_cxt)
|
|||
|
||||
static void knl_g_quota_init(knl_g_quota_context* quota_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_quota_context 结构中的各个成员变量
|
||||
Assert(quota_cxt != NULL);
|
||||
//一个断言语句,用于确保 quota_cxt 指针不为空
|
||||
//如果为空,则会触发断言失败,表示出现了错误或非预期的情况
|
||||
quota_cxt->g_quota = 0;
|
||||
quota_cxt->g_quotanofify_ratio = 0;
|
||||
quota_cxt->g_having_quota = true;
|
||||
//当前具有资源配额
|
||||
quota_cxt->g_quota_changing = NULL;
|
||||
//当前没有正在更改的资源配额
|
||||
}
|
||||
|
||||
static void knl_g_localinfo_init(knl_g_localinfo_context* localinfo_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_localinfo_context 结构中的各个成员变量
|
||||
Assert(localinfo_cxt != NULL);
|
||||
//用于确保 localinfo_cxt 指针不为空
|
||||
localinfo_cxt->g_local_host = NULL;
|
||||
//说明本地主机信息尚未设置
|
||||
localinfo_cxt->g_self_nodename = NULL;
|
||||
//说明本地节点名称尚未设置
|
||||
localinfo_cxt->g_local_ctrl_tcp_sock = -1;
|
||||
localinfo_cxt->sock_to_server_loop = -1;
|
||||
localinfo_cxt->gs_krb_keyfile = NULL;
|
||||
//说明本地控制 TCP 套接字尚未建立
|
||||
localinfo_cxt->sock_to_server_loop = -1;
|
||||
//说明与服务器的套接字通信尚未建立
|
||||
localinfo_cxt->gs_krb_keyfile = NULL;
|
||||
//说明Kerberos 密钥文件尚未设置
|
||||
}
|
||||
|
||||
static void knl_g_counters_init(knl_g_counters_context* counters_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_counters_context 结构中的各个成员变量
|
||||
Assert(counters_cxt != NULL);
|
||||
//用于确保 counters_cxt 指针不为空
|
||||
counters_cxt->g_cur_node_num = 0;
|
||||
//表示当前节点数量为0
|
||||
counters_cxt->g_expect_node_num = 0;
|
||||
//表示期望的节点数量为0
|
||||
counters_cxt->g_max_stream_num = 0;
|
||||
//表示最大流的数量为0
|
||||
counters_cxt->g_recv_num = 0;
|
||||
//表示接收的数量为0
|
||||
counters_cxt->g_comm_send_timeout = 0;
|
||||
//表示通信发送超时时间为0
|
||||
}
|
||||
|
||||
static void knl_g_ckpt_init(knl_g_ckpt_context* ckpt_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_ckpt_context 结构中的各个成员变量
|
||||
Assert(ckpt_cxt != NULL);
|
||||
//确保 ckpt_cxt 指针不为空
|
||||
errno_t rc = memset_s(ckpt_cxt, sizeof(knl_g_ckpt_context), 0, sizeof(knl_g_ckpt_context));
|
||||
securec_check(rc, "\0", "\0");
|
||||
//用 memset_s 函数将 knl_g_ckpt_context 结构的内存区域初始化为0
|
||||
securec_check(rc, "\0", "\0");
|
||||
//用于检查 memset_s 调用结果的安全检查,如果调试失败,会触发错误,停止运行
|
||||
SpinLockInit(&(ckpt_cxt->queue_lock));
|
||||
//初始化名为 queue_lock 的自旋锁 (自旋锁是一种用于多线程编程的同步机制,用于保护共享资源免受并发访问的干扰)
|
||||
}
|
||||
|
||||
static void knl_g_wal_init(knl_g_wal_context *const wal_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_wal_context 结构,它包含了一系列成员变量的初始化操作
|
||||
int ret = 0;
|
||||
//用于存储函数返回值或错误码
|
||||
ret = pthread_condattr_init(&wal_cxt->criticalEntryAtt);
|
||||
if (ret != 0) {
|
||||
//初始化条件变量属性 criticalEntryAtt
|
||||
if (ret != 0) {
|
||||
elog(FATAL, "Fail to init conattr for walwrite");
|
||||
}
|
||||
ret = pthread_condattr_setclock(&wal_cxt->criticalEntryAtt, CLOCK_MONOTONIC);
|
||||
if (ret != 0) {
|
||||
//设置条件变量属性的时钟类型为 CLOCK_MONOTONIC
|
||||
if (ret != 0) {
|
||||
elog(FATAL, "Fail to setclock walwrite");
|
||||
}
|
||||
ret = pthread_cond_init(&wal_cxt->criticalEntryCV, &wal_cxt->criticalEntryAtt);
|
||||
if (ret != 0) {
|
||||
//初始化条件变量 criticalEntryCV,并将其关联到条件变量属性 criticalEntryAtt
|
||||
if (ret != 0) {
|
||||
elog(FATAL, "Fail to init cond for walwrite");
|
||||
}
|
||||
|
||||
|
|
@ -143,37 +165,55 @@ static void knl_g_wal_init(knl_g_wal_context *const wal_cxt)
|
|||
wal_cxt->totalXlogIterBytes = 0;
|
||||
wal_cxt->totalXlogIterTimes = 0;
|
||||
wal_cxt->xlogFlushStats = NULL;
|
||||
//初始化了结构中的各个成员变量
|
||||
//上述初始化通常用于数据库系统中与WAL相关的管理和同步
|
||||
}
|
||||
|
||||
static void knl_g_bgwriter_init(knl_g_bgwriter_context *bgwriter_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_bgwriter_context 结构中的各个成员变量
|
||||
Assert(bgwriter_cxt != NULL);
|
||||
//确保 bgwriter_cxt 指针不为空
|
||||
bgwriter_cxt->unlink_rel_hashtbl = NULL;
|
||||
//表示未分配关联的哈希表
|
||||
bgwriter_cxt->rel_hashtbl_lock = NULL;
|
||||
//表示未分配关联的锁
|
||||
bgwriter_cxt->invalid_buf_proc_latch = NULL;
|
||||
bgwriter_cxt->unlink_rel_fork_hashtbl = NULL;
|
||||
bgwriter_cxt->rel_one_fork_hashtbl_lock = NULL;
|
||||
//表示未分配关联的事件触发器
|
||||
bgwriter_cxt->unlink_rel_fork_hashtbl = NULL;
|
||||
bgwriter_cxt->rel_one_fork_hashtbl_lock = NULL;
|
||||
//上述初始化通常用于数据库系统中与数据修复或一致性检查相关的数据结构和同步
|
||||
}
|
||||
|
||||
static void knl_g_repair_init(knl_g_repair_context *repair_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_repair_context 结构中的各个成员变量
|
||||
Assert(repair_cxt != NULL);
|
||||
//确保 repair_cxt 指针不为空
|
||||
repair_cxt->page_repair_hashtbl = NULL;
|
||||
//表示未分配关联的哈希表
|
||||
repair_cxt->page_repair_hashtbl_lock = NULL;
|
||||
repair_cxt->repair_proc_latch = NULL;
|
||||
//表示未分配关联的锁
|
||||
repair_cxt->repair_proc_latch = NULL;
|
||||
//表示未分配关联的锁
|
||||
}
|
||||
|
||||
static void knl_g_startup_init(knl_g_startup_context *starup_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_startup_context 结构中的各个成员变量
|
||||
Assert(starup_cxt != NULL);
|
||||
starup_cxt->remoteReadPageNum = 0;
|
||||
//表示远程读取的页面数量为0
|
||||
starup_cxt->badPageHashTbl = NULL;
|
||||
starup_cxt->current_record = NULL;
|
||||
//表示当前记录为空
|
||||
//上述代码通常用于数据库系统的启动阶段相关的数据结构初始化
|
||||
}
|
||||
|
||||
|
||||
static void knl_g_tests_init(knl_g_tests_context* tests_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_tests_context 结构中的各个成员变量
|
||||
Assert(tests_cxt != NULL);
|
||||
tests_cxt->libcomm_test_current_thread = 0;
|
||||
tests_cxt->libcomm_test_thread_arg = NULL;
|
||||
|
|
@ -184,10 +224,12 @@ static void knl_g_tests_init(knl_g_tests_context* tests_cxt)
|
|||
tests_cxt->libcomm_test_send_once = 0;
|
||||
tests_cxt->libcomm_test_recv_sleep = 0;
|
||||
tests_cxt->libcomm_test_recv_once = 0;
|
||||
//上述多用于进行测试和调试
|
||||
}
|
||||
|
||||
static void knl_g_pollers_init(knl_g_pollers_context* pollers_cxt)
|
||||
{
|
||||
//表示尚未请求关闭轮询(poll)操作
|
||||
Assert(pollers_cxt != NULL);
|
||||
pollers_cxt->g_libcomm_receiver_poller_list = NULL;
|
||||
pollers_cxt->g_r_libcomm_poller_list_lock = NULL;
|
||||
|
|
@ -199,20 +241,30 @@ static void knl_g_pollers_init(knl_g_pollers_context* pollers_cxt)
|
|||
|
||||
static void knl_g_reqcheck_init(knl_g_reqcheck_context* reqcheck_cxt)
|
||||
{
|
||||
// 初始化一个 knl_g_reqcheck_context 结构中的各个成员变量
|
||||
Assert(reqcheck_cxt != NULL);
|
||||
reqcheck_cxt->g_shutdown_requested = false;
|
||||
//表示尚未请求关闭操作
|
||||
reqcheck_cxt->g_cancel_requested = 0;
|
||||
//表示尚未请求取消操作
|
||||
reqcheck_cxt->g_close_poll_requested = false;
|
||||
//表示尚未请求关闭轮询操作
|
||||
}
|
||||
|
||||
static void knl_g_mctcp_init(knl_g_mctcp_context* mctcp_cxt)
|
||||
{
|
||||
//初始化一个 knl_g_mctcp_context 结构中的各个成员变量
|
||||
Assert(mctcp_cxt != NULL);
|
||||
mctcp_cxt->mc_tcp_keepalive_idle = 0;
|
||||
//表示TCP连接的空闲时间(idle time)为0
|
||||
mctcp_cxt->mc_tcp_keepalive_interval = 0;
|
||||
mctcp_cxt->mc_tcp_keepalive_count = 0;
|
||||
//表示TCP保活消息发送的间隔时间为0
|
||||
mctcp_cxt->mc_tcp_keepalive_count = 0;
|
||||
//表示TCP保活消息发送的次数为0
|
||||
mctcp_cxt->mc_tcp_connect_timeout = 0;
|
||||
//表示TCP连接的超时时间为0
|
||||
mctcp_cxt->mc_tcp_send_timeout = 0;
|
||||
//表示TCP发送操作的超时时间为0
|
||||
}
|
||||
|
||||
static void knl_g_commutil_init(knl_g_commutil_context* commutil_cxt)
|
||||
|
|
@ -231,14 +283,19 @@ static void knl_g_commutil_init(knl_g_commutil_context* commutil_cxt)
|
|||
static void knl_g_parallel_redo_init(knl_g_parallel_redo_context* predo_cxt)
|
||||
{
|
||||
Assert(predo_cxt != NULL);
|
||||
//确保 predo_cxt 指针不为空
|
||||
predo_cxt->state = REDO_INIT;
|
||||
//表示并行重做的状态为初始化状态
|
||||
predo_cxt->parallelRedoCtx = NULL;
|
||||
//表示未分配关联的并行重做上下文
|
||||
for (int i = 0; i < MAX_RECOVERY_THREAD_NUM; ++i) {
|
||||
predo_cxt->pageRedoThreadStatusList[i].threadId = 0;
|
||||
predo_cxt->pageRedoThreadStatusList[i].threadState = PAGE_REDO_WORKER_INVALID;
|
||||
}
|
||||
predo_cxt->totalNum = 0;
|
||||
//表示总线程数为0
|
||||
SpinLockInit(&(predo_cxt->rwlock));
|
||||
//初始化自旋锁 rwlock
|
||||
predo_cxt->redoPf.redo_start_ptr = 0;
|
||||
predo_cxt->redoPf.redo_start_time = 0;
|
||||
predo_cxt->redoPf.redo_done_time = 0;
|
||||
|
|
@ -250,13 +307,18 @@ static void knl_g_parallel_redo_init(knl_g_parallel_redo_context* predo_cxt)
|
|||
predo_cxt->redoPf.speed_according_seg = 0;
|
||||
predo_cxt->redoPf.local_max_lsn = 0;
|
||||
predo_cxt->redoPf.oldest_segment = 1;
|
||||
//用于记录重做的进度和性能信息
|
||||
knl_g_set_redo_finish_status(0);
|
||||
//将重做完成状态设置为0
|
||||
predo_cxt->redoType = DEFAULT_REDO;
|
||||
//表示重做类型为默认类型
|
||||
predo_cxt->pre_enable_switch = 0;
|
||||
//将 pre_enable_switch 成员设置为0
|
||||
SpinLockInit(&(predo_cxt->destroy_lock));
|
||||
for (int i = 0; i < NUM_MAX_PAGE_FLUSH_LSN_PARTITIONS; ++i) {
|
||||
pg_atomic_write_u64(&(predo_cxt->max_page_flush_lsn[i]), 0);
|
||||
}
|
||||
//用于记录最大页面刷新LSN(Log Sequence Number)的值
|
||||
predo_cxt->permitFinishRedo = 0;
|
||||
predo_cxt->last_replayed_conflict_csn = 0;
|
||||
predo_cxt->hotStdby = 0;
|
||||
|
|
@ -276,27 +338,35 @@ static void knl_g_parallel_decode_init(knl_g_parallel_decode_context* pdecode_cx
|
|||
{
|
||||
Assert(pdecode_cxt != NULL);
|
||||
pdecode_cxt->state = DECODE_INIT;
|
||||
//表示并行解码的状态为初始化状态
|
||||
pdecode_cxt->parallelDecodeCtx = NULL;
|
||||
//表示未分配关联的并行解码上下文
|
||||
pdecode_cxt->ParallelReaderWorkerStatus.threadId = 0;
|
||||
pdecode_cxt->ParallelReaderWorkerStatus.threadState = PARALLEL_DECODE_WORKER_INVALID;
|
||||
for (int i = 0; i < MAX_PARALLEL_DECODE_NUM; ++i) {
|
||||
//表示并行解码工作者状态为无效
|
||||
for (int i = 0; i < MAX_PARALLEL_DECODE_NUM; ++i) {
|
||||
pdecode_cxt->ParallelDecodeWorkerStatusList[i].threadId = 0;
|
||||
pdecode_cxt->ParallelDecodeWorkerStatusList[i].threadState = PARALLEL_DECODE_WORKER_INVALID;
|
||||
}
|
||||
pdecode_cxt->totalNum = 0;
|
||||
//表示总线程数为0
|
||||
SpinLockInit(&(pdecode_cxt->rwlock));
|
||||
SpinLockInit(&(pdecode_cxt->destroy_lock));
|
||||
//初始化自旋锁 destroy_lock
|
||||
}
|
||||
|
||||
static void knl_g_cache_init(knl_g_cache_context* cache_cxt)
|
||||
{
|
||||
cache_cxt->global_cache_mem = NULL;
|
||||
//表示全局缓存内存未分配
|
||||
for (int i = 0; i < MAX_GLOBAL_CACHEMEM_NUM; ++i)
|
||||
cache_cxt->global_plancache_mem[i] = NULL;
|
||||
//通过循环遍历,对 global_plancache_mem 数组中的每个元素进行初始化
|
||||
}
|
||||
|
||||
void knl_g_cachemem_create()
|
||||
{
|
||||
//创建全局缓存内存上下文,命名为 "GlobalCacheMemory",并设置默认的内存分配参数
|
||||
g_instance.cache_cxt.global_cache_mem = AllocSetContextCreate(g_instance.instance_context,
|
||||
"GlobalCacheMemory",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -305,7 +375,7 @@ void knl_g_cachemem_create()
|
|||
SHARED_CONTEXT,
|
||||
DEFAULT_MEMORY_CONTEXT_MAX_SIZE,
|
||||
false);
|
||||
|
||||
//遍历全局计划缓存内存数组,为每个元素创建内存上下文,命名为 "GlobalPlanCacheMemory",并设置默认的内存分配参数
|
||||
for (int i = 0; i < MAX_GLOBAL_CACHEMEM_NUM; ++i) {
|
||||
g_instance.cache_cxt.global_plancache_mem[i] = AllocSetContextCreate(g_instance.instance_context,
|
||||
"GlobalPlanCacheMemory",
|
||||
|
|
@ -316,6 +386,7 @@ void knl_g_cachemem_create()
|
|||
DEFAULT_MEMORY_CONTEXT_MAX_SIZE,
|
||||
false);
|
||||
}
|
||||
//遍历全局包运行时缓存内存数组,为每个元素创建内存上下文,命名为 "GlobalPackageRuntimeCacheMemory",并设置默认的内存分配参数
|
||||
for (int i = 0; i < MAX_GLOBAL_PRC_NUM; ++i) {
|
||||
g_instance.cache_cxt.global_prc_mem[i] = AllocSetContextCreate(g_instance.instance_context,
|
||||
"GlobalPackageRuntimeCacheMemory",
|
||||
|
|
@ -326,58 +397,72 @@ void knl_g_cachemem_create()
|
|||
DEFAULT_MEMORY_CONTEXT_MAX_SIZE,
|
||||
false);
|
||||
}
|
||||
//创建全局计划缓存对象
|
||||
g_instance.plan_cache = New(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)) GlobalPlanCache();
|
||||
g_instance.global_session_pkg = PLGlobalPackageRuntimeCache::Instance();
|
||||
//创建全局会话包运行时缓存对象
|
||||
g_instance.global_session_pkg = PLGlobalPackageRuntimeCache::Instance();
|
||||
}
|
||||
static void knl_g_comm_init(knl_g_comm_context* comm_cxt)
|
||||
{
|
||||
//断言确保传入的 comm_cxt 指针不为空
|
||||
Assert(comm_cxt != NULL);
|
||||
comm_cxt->gs_wakeup_consumer = NULL;
|
||||
comm_cxt->g_receivers = NULL;
|
||||
comm_cxt->g_senders = NULL;
|
||||
comm_cxt->g_delay_survey_switch = false;
|
||||
comm_cxt->g_unix_path = NULL;
|
||||
comm_cxt->g_ha_shm_data = NULL;
|
||||
comm_cxt->g_usable_streamid = NULL;
|
||||
comm_cxt->g_r_node_sock = NULL;
|
||||
comm_cxt->g_s_node_sock = NULL;
|
||||
comm_cxt->g_delay_info = NULL;
|
||||
comm_cxt->g_c_mailbox = NULL;
|
||||
comm_cxt->g_p_mailbox = NULL;
|
||||
comm_cxt->libcomm_log_timezone = NULL;
|
||||
comm_cxt->force_cal_space_info = false;
|
||||
comm_cxt->cal_all_space_info_in_progress = false;
|
||||
comm_cxt->current_gsrewind_count = 0;
|
||||
comm_cxt->isNeedChangeRole = false;
|
||||
comm_cxt->usedDnSpace = NULL;
|
||||
comm_cxt->request_disaster_cluster = true;
|
||||
comm_cxt->lastArchiveRcvTime = 0;
|
||||
|
||||
//初始化 comm_cxt 结构中的各个成员变量为默认值或 NULL
|
||||
comm_cxt->gs_wakeup_consumer = NULL; //指向唤醒消费者的指针
|
||||
comm_cxt->g_receivers = NULL; //接收者信息
|
||||
comm_cxt->g_senders = NULL; //发送者信息
|
||||
comm_cxt->g_delay_survey_switch = false; //延迟测量开关
|
||||
comm_cxt->g_unix_path = NULL; //UNIX 套接字路径
|
||||
comm_cxt->g_ha_shm_data = NULL; //HA 共享内存数据
|
||||
comm_cxt->g_usable_streamid = NULL; //可用的流 ID
|
||||
comm_cxt->g_r_node_sock = NULL; //接收节点套接字信息
|
||||
comm_cxt->g_s_node_sock = NULL; //发送节点套接字信息
|
||||
comm_cxt->g_delay_info = NULL; //延迟信息
|
||||
comm_cxt->g_c_mailbox = NULL; //CMailbox(通信邮箱)信息
|
||||
comm_cxt->g_p_mailbox = NULL; //PMailbox(通信邮箱)信息
|
||||
comm_cxt->libcomm_log_timezone = NULL; //日志时区
|
||||
comm_cxt->force_cal_space_info = false; //强制计算空间信息标志
|
||||
comm_cxt->cal_all_space_info_in_progress = false; //正在计算所有空间信息的标志
|
||||
comm_cxt->current_gsrewind_count = 0; //当前 GS_REWIND 的计数
|
||||
comm_cxt->isNeedChangeRole = false; //是否需要改变角色
|
||||
comm_cxt->usedDnSpace = NULL; //已用的数据节点空间信息
|
||||
comm_cxt->request_disaster_cluster = true; //请求灾备集群信息的标志
|
||||
comm_cxt->lastArchiveRcvTime = 0; //上次归档接收时间
|
||||
|
||||
#ifdef USE_SSL
|
||||
comm_cxt->libcomm_data_port_list = NULL;
|
||||
comm_cxt->libcomm_ctrl_port_list = NULL;
|
||||
//如果使用 SSL 加密通信,则初始化 SSL 相关的成员变量
|
||||
comm_cxt->libcomm_data_port_list = NULL; //数据端口列表
|
||||
comm_cxt->libcomm_ctrl_port_list = NULL; //控制端口列表
|
||||
#endif
|
||||
|
||||
knl_g_quota_init(&g_instance.comm_cxt.quota_cxt);
|
||||
knl_g_localinfo_init(&g_instance.comm_cxt.localinfo_cxt);
|
||||
knl_g_counters_init(&g_instance.comm_cxt.counters_cxt);
|
||||
knl_g_tests_init(&g_instance.comm_cxt.tests_cxt);
|
||||
knl_g_pollers_init(&g_instance.comm_cxt.pollers_cxt);
|
||||
knl_g_reqcheck_init(&g_instance.comm_cxt.reqcheck_cxt);
|
||||
knl_g_mctcp_init(&g_instance.comm_cxt.mctcp_cxt);
|
||||
knl_g_commutil_init(&g_instance.comm_cxt.commutil_cxt);
|
||||
knl_g_parallel_redo_init(&g_instance.comm_cxt.predo_cxt);
|
||||
//分别调用其他初始化函数来初始化 comm_cxt 中的其他成员变量
|
||||
knl_g_quota_init(&g_instance.comm_cxt.quota_cxt); //初始化配额信息
|
||||
knl_g_localinfo_init(&g_instance.comm_cxt.localinfo_cxt); //初始化本地信息
|
||||
knl_g_counters_init(&g_instance.comm_cxt.counters_cxt); //初始化计数器信息
|
||||
knl_g_tests_init(&g_instance.comm_cxt.tests_cxt); //初始化测试信息
|
||||
knl_g_pollers_init(&g_instance.comm_cxt.pollers_cxt); //初始化轮询器信息
|
||||
knl_g_reqcheck_init(&g_instance.comm_cxt.reqcheck_cxt); //初始化请求检查信息
|
||||
knl_g_mctcp_init(&g_instance.comm_cxt.mctcp_cxt); //初始化 MCTCP 信息
|
||||
knl_g_commutil_init(&g_instance.comm_cxt.commutil_cxt); //初始化通信工具信息
|
||||
knl_g_parallel_redo_init(&g_instance.comm_cxt.predo_cxt); //初始化并行重做信息
|
||||
|
||||
//遍历并初始化并行解码信息(根据最大复制槽数)
|
||||
for (int i = 0; i < g_instance.attr.attr_storage.max_replication_slots; ++i) {
|
||||
knl_g_parallel_decode_init(&g_instance.comm_cxt.pdecode_cxt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void knl_g_conn_init(knl_g_conn_context* conn_cxt)
|
||||
{
|
||||
conn_cxt->CurConnCount = 0;
|
||||
//表示当前连接的数量为0
|
||||
conn_cxt->CurCMAConnCount = 0;
|
||||
//表示当前 CMA连接的数量为0
|
||||
conn_cxt->CurCMAProcCount = 0;
|
||||
//表示当前 CMA 进程的数量为0
|
||||
SpinLockInit(&conn_cxt->ConnCountLock);
|
||||
//用于在多线程环境下保护连接计数信息的并发访问
|
||||
}
|
||||
|
||||
static void knl_g_executor_init(knl_g_executor_context* exec_cxt)
|
||||
|
|
@ -396,29 +481,43 @@ static void knl_g_rto_init(knl_g_rto_context *rto_cxt)
|
|||
|
||||
static void knl_g_xlog_init(knl_g_xlog_context *xlog_cxt)
|
||||
{
|
||||
// 始化 xlog_cxt 结构中的成员变量为默认值或 NULL
|
||||
|
||||
//初始化 num_locks_in_group 为0,表示锁组数量为0
|
||||
xlog_cxt->num_locks_in_group = 0;
|
||||
|
||||
#ifdef ENABLE_MOT
|
||||
//如果启用了 MOT 存储引擎,则初始化 redoCommitCallback 为 NULL
|
||||
xlog_cxt->redoCommitCallback = NULL;
|
||||
#endif
|
||||
|
||||
//初始化 shareStorageXLogCtl 和 shareStorageXLogCtlOrigin 为 NULL
|
||||
xlog_cxt->shareStorageXLogCtl = NULL;
|
||||
xlog_cxt->shareStorageXLogCtlOrigin = NULL;
|
||||
//使用 memset_s 函数将 shareStorageopCtl 结构初始化为0
|
||||
errno_t rc = memset_s(&xlog_cxt->shareStorageopCtl, sizeof(ShareStorageOperateCtl), 0,
|
||||
sizeof(ShareStorageOperateCtl));
|
||||
securec_check(rc, "\0", "\0");
|
||||
//初始化 remain_segs_lock 互斥锁为 NULL
|
||||
pthread_mutex_init(&xlog_cxt->remain_segs_lock, NULL);
|
||||
//初始化 shareStorageLockFd 为 -1,表示共享存储锁的文件描述符为无效值
|
||||
xlog_cxt->shareStorageLockFd = -1;
|
||||
}
|
||||
|
||||
|
||||
static void KnlGUndoInit(knl_g_undo_context *undoCxt)
|
||||
{
|
||||
//使用 undo 命名空间
|
||||
using namespace undo;
|
||||
MemoryContext oldContext;
|
||||
|
||||
//创建 Undo 命名空间的内存上下文,并将其设置为当前上下文
|
||||
g_instance.undo_cxt.undoContext = AllocSetContextCreate(g_instance.instance_context,
|
||||
"Undo", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_UNDO_MAXSIZE, SHARED_CONTEXT);
|
||||
oldContext = MemoryContextSwitchTo(g_instance.undo_cxt.undoContext);
|
||||
/*
|
||||
* Create three bitmaps for undozone with three kinds of tables(permanent, unlogged and temp).
|
||||
* Use -1 to initialize each bit of the bitmap as 1.
|
||||
为每个持久性级别(permanent, unlogged 和 temp)创建三个位图,
|
||||
使用 -1 初始化位图的每个位(bit)为1。
|
||||
*/
|
||||
for (auto i = 0; i < UNDO_PERSISTENCE_LEVELS; i++) {
|
||||
g_instance.undo_cxt.uZoneBitmap[i] = bms_add_member(g_instance.undo_cxt.uZoneBitmap[i], PERSIST_ZONE_COUNT);
|
||||
|
|
@ -426,7 +525,9 @@ static void KnlGUndoInit(knl_g_undo_context *undoCxt)
|
|||
(g_instance.undo_cxt.uZoneBitmap[i])->nwords * sizeof(bitmapword),
|
||||
-1, (g_instance.undo_cxt.uZoneBitmap[i])->nwords * sizeof(bitmapword));
|
||||
}
|
||||
//恢复先前的内存上下文
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
//初始化 undoCxt 结构中的成员变量为默认值
|
||||
undoCxt->undoTotalSize = 0;
|
||||
undoCxt->undoMetaSize = 0;
|
||||
undoCxt->uZoneCount = 0;
|
||||
|
|
@ -443,6 +544,7 @@ static void knl_g_flashback_init(knl_g_flashback_context *flashbackCxt)
|
|||
|
||||
static void knl_g_libpq_init(knl_g_libpq_context* libpq_cxt)
|
||||
{
|
||||
//初始化libpq中各参数值
|
||||
Assert(libpq_cxt != NULL);
|
||||
libpq_cxt->pam_passwd = NULL;
|
||||
libpq_cxt->pam_port_cludge = NULL;
|
||||
|
|
@ -452,6 +554,7 @@ static void knl_g_libpq_init(knl_g_libpq_context* libpq_cxt)
|
|||
|
||||
static void InitHotkeyResources(knl_g_stat_context* stat_cxt)
|
||||
{
|
||||
//如果 hotkeysCxt 为 NULL,则创建一个新的内存上下文 hotkeysCxt
|
||||
if (stat_cxt->hotkeysCxt == NULL) {
|
||||
stat_cxt->hotkeysCxt = AllocSetContextCreate(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER),
|
||||
"HotkeysMemory",
|
||||
|
|
@ -460,48 +563,68 @@ static void InitHotkeyResources(knl_g_stat_context* stat_cxt)
|
|||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
}
|
||||
|
||||
//在 hotkeysCxt 内存上下文中创建 CircularQueue(循环队列)并初始化
|
||||
stat_cxt->hotkeysCollectList = New(stat_cxt->hotkeysCxt) CircularQueue(HOTKEYS_QUEUE_LENGTH, stat_cxt->hotkeysCxt);
|
||||
stat_cxt->hotkeysCollectList->Init();
|
||||
|
||||
//在 hotkeysCxt 内存上下文中创建 LRUCache(最近最少使用缓存)并初始化
|
||||
stat_cxt->lru = New(stat_cxt->hotkeysCxt) LRUCache(LRU_QUEUE_LENGTH, stat_cxt->hotkeysCxt);
|
||||
stat_cxt->lru->Init();
|
||||
|
||||
//初始化 fifo(First-In-First-Out,先进先出)队列为 NULL
|
||||
stat_cxt->fifo = NIL;
|
||||
}
|
||||
|
||||
static void knl_g_stat_init(knl_g_stat_context* stat_cxt)
|
||||
{
|
||||
//初始化等待计数哈希表为 NULL
|
||||
stat_cxt->WaitCountHashTbl = NULL;
|
||||
//初始化等待计数状态列表为 NULL
|
||||
stat_cxt->WaitCountStatusList = NULL;
|
||||
//初始化 pgStatSock 为无效的套接字
|
||||
stat_cxt->pgStatSock = PGINVALID_SOCKET;
|
||||
//初始化 got_SIGHUP 为 false
|
||||
stat_cxt->got_SIGHUP = false;
|
||||
|
||||
//初始化 UniqueSqlContext 为 NULL
|
||||
stat_cxt->UniqueSqlContext = NULL;
|
||||
//初始化 UniqueSQLHashtbl 为 NULL
|
||||
stat_cxt->UniqueSQLHashtbl = NULL;
|
||||
//初始化 InstrUserHTAB 为 NULL
|
||||
stat_cxt->InstrUserHTAB = NULL;
|
||||
//初始化 calculate_on_other_cn 为 false
|
||||
stat_cxt->calculate_on_other_cn = false;
|
||||
//初始化 force_process 为 false
|
||||
stat_cxt->force_process = false;
|
||||
//初始化 RTPERCENTILE 数组的元素为 0
|
||||
stat_cxt->RTPERCENTILE[0] = 0;
|
||||
stat_cxt->RTPERCENTILE[1] = 0;
|
||||
//初始化 NodeStatResetTime 为 0
|
||||
stat_cxt->NodeStatResetTime = 0;
|
||||
//初始化 sql_rt_info_array 为 NULL
|
||||
stat_cxt->sql_rt_info_array = NULL;
|
||||
|
||||
//初始化 gInstanceTimeInfo 数组为 0
|
||||
stat_cxt->gInstanceTimeInfo = (int64*)MemoryContextAllocZero(
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), TOTAL_TIME_INFO_TYPES * sizeof(int64));
|
||||
errno_t rc;
|
||||
rc = memset_s(
|
||||
stat_cxt->gInstanceTimeInfo, TOTAL_TIME_INFO_TYPES * sizeof(int64), 0, TOTAL_TIME_INFO_TYPES * sizeof(int64));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
|
||||
//初始化 snapshot_thread_counter 为 0
|
||||
stat_cxt->snapshot_thread_counter = 0;
|
||||
|
||||
//初始化 fileIOStat 为 0
|
||||
stat_cxt->fileIOStat = (FileIOStat*)MemoryContextAllocZero(
|
||||
INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), sizeof(FileIOStat));
|
||||
rc = memset_s(stat_cxt->fileIOStat, sizeof(FileIOStat), 0, sizeof(FileIOStat));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
//初始化 tableStat 为 0
|
||||
stat_cxt->tableStat = (UHeapPruneStat *) palloc0(sizeof(UHeapPruneStat));
|
||||
rc = memset_s(stat_cxt->tableStat, sizeof(UHeapPruneStat), 0, sizeof(UHeapPruneStat));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
|
||||
stat_cxt->active_sess_hist_arrary = NULL;
|
||||
stat_cxt->ash_appname = NULL;
|
||||
stat_cxt->instr_stmt_is_cleaning = false;
|
||||
|
|
@ -509,11 +632,13 @@ static void knl_g_stat_init(knl_g_stat_context* stat_cxt)
|
|||
stat_cxt->ASHUniqueSQLHashtbl = NULL;
|
||||
stat_cxt->track_context_hash = NULL;
|
||||
stat_cxt->track_memory_info_hash = NULL;
|
||||
|
||||
//创建并初始化用于跟踪内存锁的 rwlock
|
||||
pthread_rwlockattr_t attr;
|
||||
/* set write-first lock for rwlock to avoid hunger of wrlock */
|
||||
(void)pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
(void)pthread_rwlock_init(&(stat_cxt->track_memory_lock), &attr);
|
||||
|
||||
|
||||
//调用 InitHotkeyResources 函数来初始化热点数据统计资源
|
||||
InitHotkeyResources(stat_cxt);
|
||||
}
|
||||
|
||||
|
|
@ -524,17 +649,23 @@ static void knl_g_adv_init(knl_g_advisor_conntext* adv_cxt)
|
|||
adv_cxt->maxMemory = 0;
|
||||
adv_cxt->maxsqlCount = 0;
|
||||
adv_cxt->currentUser = InvalidOid;
|
||||
//表示没有有效的用户标识
|
||||
adv_cxt->currentDB = InvalidOid;
|
||||
//表示没有有效的数据库标识
|
||||
adv_cxt->GWCArray = NULL;
|
||||
|
||||
//表示没有分配内存或初始化该数组
|
||||
|
||||
adv_cxt->SQLAdvisorContext = NULL;
|
||||
//表示没有为 SQL 查询优化提供额外的上下文或资源
|
||||
}
|
||||
|
||||
static void knl_g_pid_init(knl_g_pid_context* pid_cxt)
|
||||
{
|
||||
errno_t rc;
|
||||
errno_t rc;//表示操作是否成功
|
||||
rc = memset_s(pid_cxt, sizeof(knl_g_pid_context), 0, sizeof(knl_g_pid_context));
|
||||
pid_cxt->PageWriterPID = NULL;
|
||||
/*memset_s 是一个安全版本的内存清零函数,它接受四个参数:目标内存地址,目标内存大小,
|
||||
设置的值(这里是0),以及目标内存的大小。这可以确保 pid_cxt 的所有字段都被初始化为0。*/
|
||||
pid_cxt->PageWriterPID = NULL;
|
||||
pid_cxt->CommReceiverPIDS = NULL;
|
||||
pid_cxt->PgAuditPID = NULL;
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
|
@ -542,7 +673,8 @@ static void knl_g_pid_init(knl_g_pid_context* pid_cxt)
|
|||
|
||||
static void knl_g_wlm_init(knl_g_wlm_context* wlm_cxt)
|
||||
{
|
||||
wlm_cxt->parctl_process_memory = 8; // initialize the per query memory as 8 MB
|
||||
wlm_cxt->parctl_process_memory = 8;
|
||||
//将每个查询的内存初始化为8 MB
|
||||
|
||||
wlm_cxt->cluster_state = NULL;
|
||||
wlm_cxt->dnnum_in_cluster_state = 0;
|
||||
|
|
@ -571,10 +703,15 @@ static void knl_g_compaction_init(knl_g_ts_compaction_context* tsc_cxt)
|
|||
Assert(tsc_cxt != NULL);
|
||||
tsc_cxt->totalNum = 0;
|
||||
tsc_cxt->state = Compaction::COMPACTION_INIT;
|
||||
tsc_cxt->drop_db_count = 0;
|
||||
//表示压缩状态为初始化状态
|
||||
tsc_cxt->drop_db_count = 0;
|
||||
//表示要删除的数据库数量
|
||||
tsc_cxt->origin_id = (int*)palloc0(sizeof(int) * Compaction::MAX_TSCOMPATION_NUM);
|
||||
tsc_cxt->compaction_rest = false;
|
||||
//用于跟踪数据库的原始标识
|
||||
tsc_cxt->compaction_rest = false;
|
||||
//表示没有进行压缩的休息状态
|
||||
tsc_cxt->dropdb_id = InvalidOid;
|
||||
//表示要删除的数据库的标识
|
||||
for (int i = 0; i < Compaction::MAX_TSCOMPATION_NUM; i++) {
|
||||
tsc_cxt->compaction_worker_status_list[i].thread_id = 0;
|
||||
tsc_cxt->compaction_worker_status_list[i].thread_state = Compaction::COMPACTION_WORKER_INVALID;
|
||||
|
|
@ -595,10 +732,13 @@ static void knl_g_dw_init(knl_g_dw_context *dw_cxt)
|
|||
errno_t rc = memset_s(dw_cxt, sizeof(knl_g_dw_context), 0, sizeof(knl_g_dw_context));
|
||||
securec_check(rc, "\0", "\0");
|
||||
dw_cxt->closed = 1;
|
||||
|
||||
//表示数据仓库处于关闭状态
|
||||
dw_cxt->old_batch_version = false;
|
||||
//表示使用的是旧批处理版本
|
||||
dw_cxt->recovery_dw_file_num = 0;
|
||||
//表示恢复时的数据仓库文件数量为 0
|
||||
dw_cxt->recovery_dw_file_size = 0;
|
||||
//表示恢复时的数据仓库文件大小为 0
|
||||
}
|
||||
|
||||
static void knl_g_numa_init(knl_g_numa_context* numa_cxt)
|
||||
|
|
@ -627,6 +767,7 @@ static void knl_g_archive_obs_init(knl_g_archive_context *archive_cxt)
|
|||
|
||||
static void knl_g_archive_thread_info_init(knl_g_archive_thread_info *archive_thread_info)
|
||||
{
|
||||
//用于为归档线程信息的全局结构体设置默认值和状态
|
||||
errno_t rc = memset_s(archive_thread_info, sizeof(knl_g_archive_thread_info), 0,
|
||||
sizeof(knl_g_archive_thread_info));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
|
@ -637,6 +778,7 @@ static void knl_g_archive_thread_info_init(knl_g_archive_thread_info *archive_th
|
|||
static void knl_g_mot_init(knl_g_mot_context* mot_cxt)
|
||||
{
|
||||
mot_cxt->jitExecMode = JitExec::JIT_EXEC_MODE_INVALID;
|
||||
//这个字段用于表示 MOT 执行的 JIT(即时编译)模式,此处将其初始化为无效模式
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -672,6 +814,7 @@ static void knl_g_pldebug_init(knl_g_pldebug_context* pldebug_cxt)
|
|||
|
||||
static void knl_g_spi_plan_init(knl_g_spi_plan_context* spi_plan_cxt)
|
||||
{
|
||||
//用于在初始化 SPI 计划上下文时,将其字段初始化为默认值或空值
|
||||
spi_plan_cxt->global_spi_plan_context = NULL;
|
||||
spi_plan_cxt->FPlans = NULL;
|
||||
spi_plan_cxt->nFPlans = 0;
|
||||
|
|
@ -759,11 +902,17 @@ void knl_instance_init()
|
|||
pg_atomic_init_u32(&g_instance.extensionNum, 0);
|
||||
|
||||
/*
|
||||
* Set up the process wise memory context. The memory allocated from this
|
||||
* context will not be released untill it is called free. Meanwhile, the
|
||||
* memory context is visible to all threads but not thread safe, so only
|
||||
* postmaster thread shall use it or use it with lock protection.
|
||||
这个内存上下文是进程级别的,即每个进程都有自己独立的内存上下文,
|
||||
其中分配的内存只有在显式调用释放函数之后才会释放。此外,这个内存
|
||||
上下文对所有线程可见,但不是线程安全的,因此只有主进程(Postmaster
|
||||
线程)应该使用它,或者在使用时需要进行锁保护。
|
||||
*/
|
||||
|
||||
/*
|
||||
这种设置允许进程在运行过程中分配内存,并在需要时手动释放,而不会
|
||||
在每次分配内存时都进行自动释放。这对于需要更细粒度地控制内存管
|
||||
理的情况非常有用,但需要开发人员自行管理内存的生命周期和线程安全性。
|
||||
*/
|
||||
g_instance.instance_context = AllocSetContextCreate((MemoryContext)NULL,
|
||||
"ProcessMemory",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -835,15 +984,20 @@ void knl_instance_init()
|
|||
|
||||
void add_numa_alloc_info(void* numaAddr, size_t length)
|
||||
{
|
||||
//如果已分配的 NUMA 内存信息数量超过了数组的最大长度,就要计算新的数组长度,将数组长度翻倍
|
||||
if (g_instance.numa_cxt.allocIndex >= g_instance.numa_cxt.maxLength) {
|
||||
size_t newLength = g_instance.numa_cxt.maxLength * 2;
|
||||
g_instance.numa_cxt.numaAllocInfos =
|
||||
(NumaMemAllocInfo*)repalloc(g_instance.numa_cxt.numaAllocInfos, newLength * sizeof(NumaMemAllocInfo));
|
||||
//使用repalloc函数重新分配内存,以适应新的数组长度。这将导致分配一个新的数组,同时保留以前的数据
|
||||
g_instance.numa_cxt.maxLength = newLength;
|
||||
}
|
||||
g_instance.numa_cxt.numaAllocInfos[g_instance.numa_cxt.allocIndex].numaAddr = numaAddr;
|
||||
g_instance.numa_cxt.numaAllocInfos[g_instance.numa_cxt.allocIndex].length = length;
|
||||
//将当前索引位置的 NUMA 内存地址设置为传入的 numaAddr
|
||||
g_instance.numa_cxt.numaAllocInfos[g_instance.numa_cxt.allocIndex].length = length;
|
||||
//将当前索引位置的 NUMA 内存块长度设置为传入的 length
|
||||
++g_instance.numa_cxt.allocIndex;
|
||||
//增加 NUMA 内存信息数组的索引,以便下次添加信息时使用新的位置
|
||||
}
|
||||
|
||||
void knl_g_set_redo_finish_status(uint32 status)
|
||||
|
|
@ -867,7 +1021,9 @@ bool knl_g_get_local_redo_finish_status()
|
|||
#if defined(__arm__) || defined(__arm) || defined(__aarch64__) || defined(__aarch64)
|
||||
pg_memory_barrier();
|
||||
#endif
|
||||
//使用原子读取获取 Redo 完成状态
|
||||
uint32 isRedoFinish = pg_atomic_read_u32(&(g_instance.comm_cxt.predo_cxt.isRedoFinish));
|
||||
//检查是否本地 Redo 完成(按位与操作)
|
||||
return (isRedoFinish & REDO_FINISH_STATUS_LOCAL) == REDO_FINISH_STATUS_LOCAL;
|
||||
}
|
||||
|
||||
|
|
@ -876,7 +1032,8 @@ bool knl_g_get_redo_finish_status()
|
|||
#if defined(__arm__) || defined(__arm) || defined(__aarch64__) || defined(__aarch64)
|
||||
pg_memory_barrier();
|
||||
#endif
|
||||
//使用原子读取获取 Redo 完成状态
|
||||
uint32 isRedoFinish = pg_atomic_read_u32(&(g_instance.comm_cxt.predo_cxt.isRedoFinish));
|
||||
//检查是否整体 Redo 完成(按位与操作)
|
||||
return (isRedoFinish & REDO_FINISH_STATUS_CM) == REDO_FINISH_STATUS_CM;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -7,16 +7,12 @@
|
|||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* threadpool_controler.cpp
|
||||
* Controler for thread pool. Class ThreadPoolControler is defined to
|
||||
* initilize thread pool's worker and listener threads, and dispatch
|
||||
* new session from postmaster thread to suitable thread group.
|
||||
* 线程池的控制器。定义了 ThreadPoolControler 类,
|
||||
* 用于初始化线程池的工作线程和监听线程,并将新会话从
|
||||
* 主线程分派到合适的线程组。
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
|
|
@ -71,32 +67,33 @@ ThreadPoolControler* g_threadPoolControler = NULL;
|
|||
|
||||
static const long one_hundred_micro_sec = 100;
|
||||
|
||||
// 线程池控制器的构造函数
|
||||
ThreadPoolControler::ThreadPoolControler()
|
||||
{
|
||||
m_threadPoolContext = NULL;
|
||||
m_sessCtrl = NULL;
|
||||
m_groups = NULL;
|
||||
m_scheduler = NULL;
|
||||
m_groupNum = 1;
|
||||
m_threadNum = 0;
|
||||
m_maxPoolSize = 0;
|
||||
m_maxStreamPoolSize = 0;
|
||||
m_streamProcRatio = 0;
|
||||
m_threadPoolContext = NULL; // 线程池上下文
|
||||
m_sessCtrl = NULL; // 会话控制
|
||||
m_groups = NULL; // 线程组
|
||||
m_scheduler = NULL; // 调度器
|
||||
m_groupNum = 1; // 线程组数量
|
||||
m_threadNum = 0; // 线程数量
|
||||
m_maxPoolSize = 0; // 最大线程池大小
|
||||
m_maxStreamPoolSize = 0; // 最大流线程池大小
|
||||
m_streamProcRatio = 0; // 流处理比例
|
||||
}
|
||||
|
||||
ThreadPoolControler::~ThreadPoolControler()
|
||||
{
|
||||
delete m_scheduler;
|
||||
delete m_sessCtrl;
|
||||
MemoryContextDelete(m_threadPoolContext);
|
||||
m_threadPoolContext = NULL;
|
||||
m_groups = NULL;
|
||||
m_sessCtrl = NULL;
|
||||
delete m_scheduler; // 删除调度器对象
|
||||
delete m_sessCtrl; // 删除会话控制对象
|
||||
MemoryContextDelete(m_threadPoolContext); // 删除线程池上下文
|
||||
m_threadPoolContext = NULL; // 将线程池上下文置为空
|
||||
m_groups = NULL; // 将线程组指针置为空
|
||||
m_sessCtrl = NULL; // 将会话控制指针置为空
|
||||
}
|
||||
|
||||
void ThreadPoolControler::Init(bool enableNumaDistribute)
|
||||
{
|
||||
|
||||
// 创建线程池上下文
|
||||
m_threadPoolContext = AllocSetContextCreate(g_instance.instance_context,
|
||||
"ThreadPoolContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -104,12 +101,18 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
|
|||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
|
||||
// 切换到线程池上下文
|
||||
AutoContextSwitch memSwitch(m_threadPoolContext);
|
||||
|
||||
// 分配线程组数组
|
||||
m_groups = (ThreadPoolGroup**)palloc(sizeof(ThreadPoolGroup*) * m_groupNum);
|
||||
|
||||
// 创建线程池会话控制对象
|
||||
m_sessCtrl = New(CurrentMemoryContext) ThreadPoolSessControl(CurrentMemoryContext);
|
||||
|
||||
// 检查是否绑定 CPU
|
||||
bool bindCpu = CheckCpuBind();
|
||||
// 检查是否绑定 CPU NUMA
|
||||
bool bindCpuNuma = CheckCpuNumaBind();
|
||||
int maxThreadNum = 0;
|
||||
int expectThreadNum = 0;
|
||||
|
|
@ -123,16 +126,14 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
|
|||
while (m_cpuInfo.cpuArrSize[numaId] == 0)
|
||||
numaId++;
|
||||
|
||||
/*
|
||||
* Invoke numa_set_preferred before starting worker thread to make
|
||||
* more memory allocation local to worker thread.
|
||||
*/
|
||||
#ifdef __USE_NUMA
|
||||
// 如果启用 NUMA 分配,则设置首选 NUMA 节点
|
||||
if (enableNumaDistribute) {
|
||||
numa_set_preferred(numaId);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 获取当前 NUMA 节点的 CPU 数量
|
||||
Assert(numaId < m_cpuInfo.totalNumaNum);
|
||||
expectThreadNum = (int)round(
|
||||
(double)m_threadNum * ((double)m_cpuInfo.cpuArrSize[numaId] / (double)m_cpuInfo.activeCpuNum));
|
||||
|
|
@ -143,30 +144,35 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
|
|||
cpuNum = m_cpuInfo.cpuArrSize[numaId];
|
||||
cpuArr = m_cpuInfo.cpuArr[numaId];
|
||||
|
||||
// 增加 NUMA ID,以处理下一个 NUMA 节点
|
||||
numaId++;
|
||||
} else {
|
||||
// 如果未绑定 CPU,则均匀分配线程和流的数量
|
||||
expectThreadNum = m_threadNum / m_groupNum;
|
||||
maxThreadNum = m_maxPoolSize / m_groupNum;
|
||||
maxStreamNum = m_maxPoolSize / m_groupNum;
|
||||
numaId = -1;
|
||||
}
|
||||
|
||||
// 创建线程池组对象并初始化
|
||||
m_groups[i] = New(CurrentMemoryContext)ThreadPoolGroup(maxThreadNum, expectThreadNum,
|
||||
maxStreamNum, i, numaId, cpuNum, cpuArr, bindCpuNuma);
|
||||
m_groups[i]->Init(enableNumaDistribute);
|
||||
}
|
||||
|
||||
// 等待所有线程池组准备就绪
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
m_groups[i]->WaitReady();
|
||||
}
|
||||
|
||||
#ifdef __USE_NUMA
|
||||
if (enableNumaDistribute) {
|
||||
/* Set to interleave mode for other than worker thread */
|
||||
/* 设置为交织模式以供除工作线程之外的其他线程使用 */
|
||||
numa_set_interleave_mask(numa_all_nodes_ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 创建线程池调度器对象并启动
|
||||
m_scheduler = New(CurrentMemoryContext) ThreadPoolScheduler(m_groupNum, m_groups);
|
||||
m_scheduler->StartUp();
|
||||
}
|
||||
|
|
@ -194,9 +200,10 @@ void AdjustThreadAffinity(void) {
|
|||
cpu_set_t m_cpuset;
|
||||
CPU_ZERO(&m_cpuset);
|
||||
|
||||
/* Check if the instance has been attch to some specific CPUs. */
|
||||
// 检查实例是否已绑定到某些特定的 CPU
|
||||
int ret = pthread_getaffinity_np(PostmasterPid, sizeof(cpu_set_t), &m_cpuset);
|
||||
if (ret == 0) {
|
||||
// 如果绑定到 CPU 0 而未绑定到 CPU 1,则重新设置 CPU 亲和性,以避免绑定到单一 CPU
|
||||
if ((CPU_ISSET(0, &m_cpuset)) && (!CPU_ISSET(1, &m_cpuset))) {
|
||||
int num_processors = sysconf(_SC_NPROCESSORS_CONF);
|
||||
CPU_ZERO(&m_cpuset);
|
||||
|
|
@ -204,7 +211,7 @@ void AdjustThreadAffinity(void) {
|
|||
CPU_SET(j, &m_cpuset);
|
||||
}
|
||||
|
||||
// set CPU affinity of a thread
|
||||
// 设置线程的 CPU 亲和性
|
||||
int s = pthread_setaffinity_np(PostmasterPid, sizeof(cpu_set_t), &m_cpuset);
|
||||
if (s != 0) {
|
||||
ereport(WARNING, (errmsg("AdjustThreadAffinity fail to bind thread %lu, errno: %d", PostmasterPid, ret)));
|
||||
|
|
@ -213,17 +220,16 @@ void AdjustThreadAffinity(void) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void ThreadPoolControler::GetInstanceBind(cpu_set_t *cpuset)
|
||||
{
|
||||
/* this function is used to avoid the libgomp bug on some specified OS */
|
||||
void ThreadPoolControler::GetInstanceBind(cpu_set_t *cpuset) {
|
||||
// 修复 libgomp 在某些指定操作系统上的 bug
|
||||
AdjustThreadAffinity();
|
||||
|
||||
/* Check if the instance has been attch to some specific CPUs. */
|
||||
// 检查实例是否已绑定到某些特定的 CPU
|
||||
int ret = pthread_getaffinity_np(PostmasterPid, sizeof(cpu_set_t), cpuset);
|
||||
if (ret == 0) {
|
||||
return;
|
||||
} else {
|
||||
// 初始化 cpuset,以避免未绑定的情况
|
||||
errno_t rc = memset_s(cpuset, sizeof(cpu_set_t), 0, sizeof(cpu_set_t));
|
||||
securec_check(rc, "\0", "\0");
|
||||
}
|
||||
|
|
@ -235,7 +241,7 @@ void ThreadPoolControler::ParseAttr()
|
|||
m_attr.groupNum = DEFAULT_THREAD_POOL_GROUPS;
|
||||
m_attr.bindCpu = NULL;
|
||||
|
||||
/* Do str copy and remove space. */
|
||||
/* 复制字符串并移除空格 */
|
||||
char* attr = TrimStr(g_instance.attr.attr_common.thread_pool_attr);
|
||||
if (IS_NULL_STR(attr))
|
||||
return;
|
||||
|
|
@ -244,13 +250,13 @@ void ThreadPoolControler::ParseAttr()
|
|||
char* psave = NULL;
|
||||
const char* pdelimiter = ",";
|
||||
|
||||
/* Get thread num */
|
||||
/* 获取线程数 */
|
||||
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
|
||||
if (!IS_NULL_STR(ptoken))
|
||||
m_attr.threadNum = pg_strtoint32(ptoken);
|
||||
pfree_ext(ptoken);
|
||||
|
||||
/* Ger group num */
|
||||
/* 获取线程组数 */
|
||||
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
|
||||
if (!IS_NULL_STR(ptoken))
|
||||
m_attr.groupNum = pg_strtoint32(ptoken);
|
||||
|
|
@ -258,15 +264,15 @@ void ThreadPoolControler::ParseAttr()
|
|||
|
||||
if (m_attr.threadNum < 0 || m_attr.threadNum > MAX_THREAD_POOL_SIZE)
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current thread num %d is out of range [%d, %d].", m_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
|
||||
errdetail("当前线程数 %d 超出范围 [%d, %d]。", m_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
|
||||
if (m_attr.groupNum < 0 || m_attr.groupNum > MAX_THREAD_POOL_GROUPS)
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current group num %d is out of range [%d, %d].", m_attr.groupNum, 0, MAX_THREAD_POOL_GROUPS));
|
||||
errdetail("当前线程组数 %d 超出范围 [%d, %d]。", m_attr.groupNum, 0, MAX_THREAD_POOL_GROUPS));
|
||||
|
||||
/* Get attach cpu */
|
||||
/* 获取绑定 CPU */
|
||||
m_attr.bindCpu = TrimStr(psave);
|
||||
ParseBindCpu();
|
||||
|
||||
|
||||
pfree_ext(attr);
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +282,8 @@ void ThreadPoolControler::ParseStreamAttr()
|
|||
m_stream_attr.procRatio = DEFAULT_THREAD_POOL_STREAM_PROC_RATIO;
|
||||
m_stream_attr.groupNum = DEFAULT_THREAD_POOL_GROUPS;
|
||||
m_stream_attr.bindCpu = NULL;
|
||||
|
||||
|
||||
// 获取流式线程池属性配置字符串
|
||||
char* attr = TrimStr(g_instance.attr.attr_common.thread_pool_stream_attr);
|
||||
if (IS_NULL_STR(attr)) {
|
||||
return;
|
||||
|
|
@ -285,36 +292,36 @@ void ThreadPoolControler::ParseStreamAttr()
|
|||
char* ptoken = NULL;
|
||||
char* psave = NULL;
|
||||
const char* pdelimiter = ",";
|
||||
|
||||
/* Get stream_thread_pool_stream max thread num */
|
||||
|
||||
// 解析流式线程池的最大线程数
|
||||
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
|
||||
if (IS_NULL_STR(ptoken) || !isdigit((unsigned char)*ptoken)) {
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current thread_pool_stream_attr format is error, stream_thread_num must be digital."));
|
||||
errdetail("当前 thread_pool_stream_attr 格式错误,stream_thread_num 必须为数字。"));
|
||||
}
|
||||
m_stream_attr.threadNum = pg_strtoint32(ptoken);
|
||||
pfree_ext(ptoken);
|
||||
if (m_stream_attr.threadNum < 0 || m_stream_attr.threadNum > MAX_THREAD_POOL_SIZE) {
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current stream_thread_num %d is out of range [%d, %d].",
|
||||
errdetail("当前 stream_thread_num %d 超出范围 [%d, %d]。",
|
||||
m_stream_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
|
||||
}
|
||||
|
||||
/* Get proc ratio of stream threads */
|
||||
|
||||
// 解析流式线程池的处理比例
|
||||
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
|
||||
if (IS_NULL_STR(ptoken) || !isdigit((unsigned char)*ptoken)) {
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current thread_pool_stream_attr format is error, stream_proc_ratio must be digital."));
|
||||
errdetail("当前 thread_pool_stream_attr 格式错误,stream_proc_ratio 必须为数字。"));
|
||||
}
|
||||
m_stream_attr.procRatio = atof(ptoken);
|
||||
pfree_ext(ptoken);
|
||||
if (m_stream_attr.procRatio <= 0 || m_stream_attr.procRatio > MAX_THREAD_POOL_STREAM_PROC_RATIO) {
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Current stream_proc_ratio %f is out of range (%d, %d].",
|
||||
errdetail("当前 stream_proc_ratio %f 超出范围 (%d, %d]。",
|
||||
m_stream_attr.procRatio, 0, MAX_THREAD_POOL_STREAM_PROC_RATIO));
|
||||
}
|
||||
pfree_ext(attr);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +332,7 @@ void ThreadPoolControler::ParseBindCpu()
|
|||
return;
|
||||
}
|
||||
|
||||
// 复制属性字符串以进行解析
|
||||
char* pattr = pstrdup(m_attr.bindCpu);
|
||||
char* scpu = pattr;
|
||||
char* ptoken = NULL;
|
||||
|
|
@ -332,15 +340,18 @@ void ThreadPoolControler::ParseBindCpu()
|
|||
const char* pdelimiter = ":";
|
||||
int bindNum = 0;
|
||||
|
||||
// 检查属性字符串的格式是否正确
|
||||
if (scpu[0] != '(' || scpu[strlen(scpu) - 1] != ')')
|
||||
INVALID_ATTR_ERROR("Use '(' ')' to indicate cpu bind info.");
|
||||
INVALID_ATTR_ERROR("使用 '(' ')' 来表示 CPU 绑定信息。");
|
||||
|
||||
scpu++;
|
||||
scpu[strlen(scpu) - 1] = '\0';
|
||||
|
||||
// 解析属性字符串并转换为小写
|
||||
ptoken = TrimStr(strtok_r(scpu, pdelimiter, &psave));
|
||||
ptoken = pg_strtolower(ptoken);
|
||||
|
||||
// 根据不同的属性类型进行解析
|
||||
if (strncmp("nobind", ptoken, strlen("nobind")) == 0) {
|
||||
m_cpuInfo.bindType = NO_CPU_BIND;
|
||||
return;
|
||||
|
|
@ -360,16 +371,16 @@ void ThreadPoolControler::ParseBindCpu()
|
|||
m_cpuInfo.isBindCpuNumaArr = (bool*)palloc0(sizeof(bool) * m_cpuInfo.totalCpuNum);
|
||||
bindNum = ParseRangeStr(psave, m_cpuInfo.isBindCpuNumaArr, m_cpuInfo.totalCpuNum, "numabind");
|
||||
} else {
|
||||
INVALID_ATTR_ERROR(errdetail("Only 'nobind', 'allbind', 'cpubind', 'nodebind' and 'numabind' "
|
||||
"are valid attribute."));
|
||||
INVALID_ATTR_ERROR(errdetail("只有 'nobind', 'allbind', 'cpubind', 'nodebind' 和 'numabind' "
|
||||
"是有效的属性。"));
|
||||
}
|
||||
|
||||
// 检查是否找到有效的 CPU 进行线程绑定
|
||||
if (bindNum == 0)
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("Can not find valid CPU for thread binding, there are two possible reasons:\n"
|
||||
"1. These CPUs are not active, use lscpu to check On-line CPU(s) list.\n"
|
||||
"2. The process has been bind to other CPUs and there is no intersection,"
|
||||
"use taskset -pc to check process CPU bind info.\n"));
|
||||
errdetail("无法找到有效的 CPU 进行线程绑定,可能的原因有两个:\n"
|
||||
"1. 这些 CPU 不处于活动状态,请使用 lscpu 命令检查在线 CPU 列表。\n"
|
||||
"2. 进程已绑定到其他 CPU,且没有交集,请使用 taskset -pc 命令检查进程 CPU 绑定信息。\n"));
|
||||
pfree_ext(ptoken);
|
||||
pfree_ext(pattr);
|
||||
}
|
||||
|
|
@ -381,6 +392,7 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
|
|||
const char* pdelimiter = ",";
|
||||
int retNum = 0;
|
||||
|
||||
// 解析属性字符串
|
||||
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
|
||||
|
||||
while (!IS_NULL_STR(ptoken)) {
|
||||
|
|
@ -390,25 +402,29 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
|
|||
int startid = -1;
|
||||
int endid = -1;
|
||||
|
||||
// 解析范围字符串,可能包含起始和结束值
|
||||
pt = TrimStr(strtok_r(ptoken, pd, &ps));
|
||||
if (!IS_NULL_STR(pt))
|
||||
startid = pg_strtoint32(pt);
|
||||
if (!IS_NULL_STR(ps))
|
||||
endid = pg_strtoint32(ps);
|
||||
|
||||
// 检查解析的值是否有效
|
||||
if (startid < 0 && endid < 0)
|
||||
INVALID_ATTR_ERROR(errdetail("Can not parse attribute %s", pt));
|
||||
INVALID_ATTR_ERROR(errdetail("无法解析属性 %s", pt));
|
||||
if (startid >= totalNum)
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("The %s attribute %d is out of valid range [%d, %d]", bindtype, startid, 0, totalNum - 1));
|
||||
errdetail("属性 %s 中的 %d 超出了有效范围 [%d, %d]", bindtype, startid, 0, totalNum - 1));
|
||||
if (endid >= totalNum)
|
||||
INVALID_ATTR_ERROR(
|
||||
errdetail("The %s attribute %d is out of valid range [%d, %d]", bindtype, endid, 0, totalNum - 1));
|
||||
errdetail("属性 %s 中的 %d 超出了有效范围 [%d, %d]", bindtype, endid, 0, totalNum - 1));
|
||||
|
||||
if (endid == -1) {
|
||||
// 单个 CPU 绑定
|
||||
retNum += arr[startid] ? 0 : 1;
|
||||
arr[startid] = true;
|
||||
} else {
|
||||
// 范围内的多个 CPU 绑定
|
||||
if (startid > endid) {
|
||||
int tmpid = startid;
|
||||
startid = endid;
|
||||
|
|
@ -421,7 +437,7 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
|
|||
}
|
||||
}
|
||||
|
||||
/* Don't need to free when error ocurrs, errors here are FATAL level! */
|
||||
// 释放临时字符串内存,不需要在错误发生时释放,这里的错误是致命的!
|
||||
pfree_ext(pt);
|
||||
pfree_ext(ptoken);
|
||||
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
|
||||
|
|
@ -438,9 +454,8 @@ bool* ThreadPoolControler::GetMcsCpuInfo(int totalCpuNum)
|
|||
bool* isMcsCpuArr = (bool*)palloc0(sizeof(bool) * totalCpuNum);
|
||||
|
||||
/*
|
||||
* When the database is deplyed on MCS, we need to read cpuset.cpus to find
|
||||
* available CPUs in this MCS. If we can read this file, then we think all
|
||||
* CPUs are available.
|
||||
* 当数据库部署在MCS上时,我们需要读取cpuset.cpus文件以查找此MCS中可用的CPU。
|
||||
* 如果我们能够读取此文件,那么我们认为所有CPU都是可用的。
|
||||
*/
|
||||
fp = fopen("/sys/fs/cgroup/cpuset/cpuset.cpus", "r");
|
||||
if (fp == NULL) {
|
||||
|
|
@ -473,11 +488,12 @@ bool* ThreadPoolControler::GetMcsCpuInfo(int totalCpuNum)
|
|||
|
||||
void ThreadPoolControler::GetActiveCpu(NumaCpuId *numaCpuIdList, int *num)
|
||||
{
|
||||
*num = 0;
|
||||
*num = 0; // 初始化传出参数 num 为 0
|
||||
char buf[BUFSIZE];
|
||||
FILE* fp = popen("lscpu -b -e=cpu,node", "r");
|
||||
FILE* fp = popen("lscpu -b -e=cpu,node", "r"); // 打开一个用于读取 CPU 信息的流
|
||||
|
||||
if (fp == NULL) {
|
||||
ereport(WARNING, (errmsg("Unable to use 'lscpu' to read CPU info.")));
|
||||
ereport(WARNING, (errmsg("Unable to use 'lscpu' to read CPU info."))); // 如果打开流失败,发出警告消息
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -486,45 +502,49 @@ void ThreadPoolControler::GetActiveCpu(NumaCpuId *numaCpuIdList, int *num)
|
|||
const char* pdelimiter = " ";
|
||||
int cpuid = 0;
|
||||
int numaid = 0;
|
||||
/* try to read the header. */
|
||||
|
||||
// 尝试读取头部信息
|
||||
if (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
ptoken = strtok_r(buf, pdelimiter, &psave);
|
||||
if (!IS_NULL_STR(ptoken)) {
|
||||
cpuid = pg_strtoint32(ptoken);
|
||||
cpuid = pg_strtoint32(ptoken); // 解析 CPU ID
|
||||
}
|
||||
ptoken = strtok_r(NULL, pdelimiter, &psave);
|
||||
if (!IS_NULL_STR(ptoken)) {
|
||||
numaid = pg_strtoint32(ptoken);
|
||||
numaid = pg_strtoint32(ptoken); // 解析 NUMA ID
|
||||
}
|
||||
numaCpuIdList[*num].cpuId = cpuid;
|
||||
numaCpuIdList[*num].numaId = numaid;
|
||||
(*num)++;
|
||||
numaCpuIdList[*num].cpuId = cpuid; // 将 CPU ID 存入传出参数
|
||||
numaCpuIdList[*num].numaId = numaid; // 将 NUMA ID 存入传出参数
|
||||
(*num)++; // 递增传出参数 num
|
||||
}
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
pclose(fp); // 关闭流
|
||||
}
|
||||
|
||||
void ThreadPoolControler::GetSysCpuInfo()
|
||||
{
|
||||
if (m_cpuInfo.totalNumaNum == 0 || m_cpuInfo.totalCpuNum == 0) {
|
||||
ereport(WARNING, (errmsg("Fail to read cpu num or numa num.")));
|
||||
ereport(WARNING, (errmsg("Fail to read cpu num or numa num."))); // 如果没有读取到 CPU 数量或 NUMA 数量,发出警告消息
|
||||
return;
|
||||
}
|
||||
|
||||
m_cpuInfo.isMcsCpuArr = GetMcsCpuInfo(m_cpuInfo.totalCpuNum);
|
||||
m_cpuInfo.isMcsCpuArr = GetMcsCpuInfo(m_cpuInfo.totalCpuNum); // 获取 MCS CPU 信息
|
||||
|
||||
m_cpuInfo.cpuArr = (int**)palloc0(sizeof(int*) * m_cpuInfo.totalNumaNum);
|
||||
m_cpuInfo.cpuArrSize = (int*)palloc0(sizeof(int) * m_cpuInfo.totalNumaNum);
|
||||
int cpu_per_numa = m_cpuInfo.totalCpuNum / m_cpuInfo.totalNumaNum;
|
||||
|
||||
for (int i = 0; i < m_cpuInfo.totalNumaNum; i++) {
|
||||
m_cpuInfo.cpuArr[i] = (int*)palloc0(sizeof(int) * cpu_per_numa);
|
||||
m_cpuInfo.cpuArr[i] = (int*)palloc0(sizeof(int) * cpu_per_numa); // 为每个 NUMA 节点分配内存
|
||||
}
|
||||
|
||||
m_cpuInfo.activeCpuNum = 0;
|
||||
NumaCpuId *sysNumaCpuIdList = (NumaCpuId*)palloc0(sizeof(NumaCpuId) * m_cpuInfo.totalCpuNum);
|
||||
int sysNumaCpuIdNum = 0;
|
||||
GetActiveCpu(sysNumaCpuIdList, &sysNumaCpuIdNum);
|
||||
|
||||
GetActiveCpu(sysNumaCpuIdList, &sysNumaCpuIdNum); // 获取激活的 CPU 信息
|
||||
|
||||
if (sysNumaCpuIdNum == 0) {
|
||||
return;
|
||||
|
|
@ -533,56 +553,58 @@ void ThreadPoolControler::GetSysCpuInfo()
|
|||
for (int i = 0; i < sysNumaCpuIdNum; ++i) {
|
||||
int cpuid = sysNumaCpuIdList[i].cpuId;
|
||||
int numaid = sysNumaCpuIdList[i].numaId;
|
||||
|
||||
if (IsActiveCpu(cpuid, numaid)) {
|
||||
m_cpuInfo.cpuArr[numaid][m_cpuInfo.cpuArrSize[numaid]] = cpuid;
|
||||
m_cpuInfo.cpuArrSize[numaid]++;
|
||||
m_cpuInfo.activeCpuNum++;
|
||||
m_cpuInfo.cpuArr[numaid][m_cpuInfo.cpuArrSize[numaid]] = cpuid; // 将 CPU ID 存入相应 NUMA 节点的数组中
|
||||
m_cpuInfo.cpuArrSize[numaid]++; // 递增相应 NUMA 节点的数组大小
|
||||
m_cpuInfo.activeCpuNum++; // 递增激活的 CPU 数量
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pfree_ext(sysNumaCpuIdList);
|
||||
pfree_ext(sysNumaCpuIdList); // 释放内存
|
||||
|
||||
for (int i = 0; i < m_cpuInfo.totalNumaNum; i++) {
|
||||
if (m_cpuInfo.cpuArrSize[i] > 0)
|
||||
m_cpuInfo.activeNumaNum++;
|
||||
m_cpuInfo.activeNumaNum++; // 统计激活的 NUMA 节点数量
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPoolControler::InitCpuInfo()
|
||||
{
|
||||
m_cpuInfo.totalCpuNum = 0;
|
||||
m_cpuInfo.activeCpuNum = 0;
|
||||
m_cpuInfo.totalNumaNum = 0;
|
||||
m_cpuInfo.activeNumaNum = 0;
|
||||
m_cpuInfo.cpuArrSize = NULL;
|
||||
m_cpuInfo.cpuArr = NULL;
|
||||
m_cpuInfo.totalCpuNum = 0; // 初始化总 CPU 数量为 0
|
||||
m_cpuInfo.activeCpuNum = 0; // 初始化激活的 CPU 数量为 0
|
||||
m_cpuInfo.totalNumaNum = 0; // 初始化总 NUMA 节点数量为 0
|
||||
m_cpuInfo.activeNumaNum = 0; // 初始化激活的 NUMA 节点数量为 0
|
||||
m_cpuInfo.cpuArrSize = NULL; // 初始化 CPU 数组大小为 NULL
|
||||
m_cpuInfo.cpuArr = NULL; // 初始化 CPU 数组为 NULL
|
||||
|
||||
m_cpuInfo.bindType = NO_CPU_BIND;
|
||||
m_cpuInfo.isBindCpuArr = NULL;
|
||||
m_cpuInfo.isBindNumaArr = NULL;
|
||||
m_cpuInfo.isMcsCpuArr = NULL;
|
||||
m_cpuInfo.bindType = NO_CPU_BIND; // 初始化 CPU 绑定类型为 NO_CPU_BIND
|
||||
m_cpuInfo.isBindCpuArr = NULL; // 初始化 CPU 绑定数组为 NULL
|
||||
m_cpuInfo.isBindNumaArr = NULL; // 初始化 NUMA 节点绑定数组为 NULL
|
||||
m_cpuInfo.isMcsCpuArr = NULL; // 初始化 MCS CPU 数组为 NULL
|
||||
}
|
||||
|
||||
void ThreadPoolControler::GetCpuAndNumaNum(int32 *totalCpuNum, int32 *totalNumaNum)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
FILE* fp = NULL;
|
||||
|
||||
// 打开 "lscpu" 命令的输出以获取 CPU 和 NUMA 节点数量信息
|
||||
if ((fp = popen("LANG=en_US.UTF-8;lscpu", "r")) != NULL) {
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
if (strncmp("CPU(s)", buf, strlen("CPU(s)")) == 0 &&
|
||||
strncmp("On-line CPU(s) list", buf, strlen("On-line CPU(s) list")) != 0 &&
|
||||
strncmp("NUMA node", buf, strlen("NUMA node")) != 0) {
|
||||
// 当遇到包含 "CPU(s)" 的行时,解析并获取总 CPU 数量
|
||||
char* loc = strchr(buf, ':');
|
||||
*totalCpuNum = pg_strtoint32(loc + 1);
|
||||
} else if (strncmp("NUMA node(s)", buf, strlen("NUMA node(s)")) == 0) {
|
||||
// 当遇到包含 "NUMA node(s)" 的行时,解析并获取总 NUMA 节点数量
|
||||
char* loc = strchr(buf, ':');
|
||||
*totalNumaNum = pg_strtoint32(loc + 1);
|
||||
}
|
||||
}
|
||||
pclose(fp);
|
||||
pclose(fp); // 关闭文件流
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -591,67 +613,72 @@ bool ThreadPoolControler::IsActiveCpu(int cpuid, int numaid)
|
|||
switch (m_cpuInfo.bindType) {
|
||||
case NO_CPU_BIND:
|
||||
case ALL_CPU_BIND:
|
||||
// 如果未进行 CPU 绑定,或者进行了全局 CPU 绑定,检查 CPU 是否激活
|
||||
return (m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
|
||||
case NODE_BIND:
|
||||
// 如果进行了 NUMA 节点绑定,检查 CPU 和 NUMA 节点是否激活
|
||||
return (m_cpuInfo.isBindNumaArr[numaid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
|
||||
case CPU_BIND:
|
||||
// 如果进行了 CPU 绑定,检查 CPU 是否激活
|
||||
return (m_cpuInfo.isBindCpuArr[cpuid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
|
||||
case NUMA_BIND:
|
||||
// 如果进行了 NUMA 节点和 CPU 绑定,检查 CPU 和 NUMA 节点是否激活
|
||||
return (m_cpuInfo.isBindCpuNumaArr[cpuid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
|
||||
}
|
||||
return false;
|
||||
return false; // 默认情况下,返回 false
|
||||
}
|
||||
|
||||
bool ThreadPoolControler::CheckCpuBind() const
|
||||
{
|
||||
if (m_cpuInfo.bindType == NO_CPU_BIND)
|
||||
return false;
|
||||
return false; // 如果没有进行 CPU 绑定,则返回 false
|
||||
|
||||
if (m_groupNum != m_cpuInfo.activeNumaNum) {
|
||||
ereport(WARNING,
|
||||
(errmsg("Can not bind worker thread to CPU because the "
|
||||
"thread group num must equal to active NUMA num.")));
|
||||
return false;
|
||||
}
|
||||
if (m_cpuInfo.activeCpuNum == 0 || m_cpuInfo.cpuArr == NULL) {
|
||||
ereport(WARNING, (errmsg("Can not bind worker thread to CPU because no valid CPUs.")));
|
||||
return false;
|
||||
(errmsg("无法将工作线程绑定到 CPU,因为线程组数必须等于激活的 NUMA 节点数。")));
|
||||
return false; // 如果线程组数不等于激活的 NUMA 节点数,返回 false
|
||||
}
|
||||
|
||||
return true;
|
||||
if (m_cpuInfo.activeCpuNum == 0 || m_cpuInfo.cpuArr == NULL) {
|
||||
ereport(WARNING, (errmsg("无法将工作线程绑定到 CPU,因为没有有效的 CPU。")));
|
||||
return false; // 如果没有有效的 CPU,返回 false
|
||||
}
|
||||
|
||||
return true; // 其他情况下,返回 true
|
||||
}
|
||||
|
||||
bool ThreadPoolControler::CheckCpuNumaBind() const
|
||||
{
|
||||
return m_cpuInfo.bindType == NUMA_BIND;
|
||||
return m_cpuInfo.bindType == NUMA_BIND; // 如果进行了 NUMA 绑定,返回 true,否则返回 false
|
||||
}
|
||||
|
||||
bool ThreadPoolControler::CheckNumaDistribute(int numaNodeNum) const
|
||||
{
|
||||
if (m_cpuInfo.bindType == NO_CPU_BIND) {
|
||||
ereport(WARNING,
|
||||
(errmsg("allbind should be used to replace nobind in thread_pool_attr when NUMA is activated.")));
|
||||
return false;
|
||||
(errmsg("在激活 NUMA 时,应使用 allbind 来替代 nobind 在 thread_pool_attr 中。")));
|
||||
return false; // 如果未进行 CPU 绑定,给出警告并返回 false
|
||||
}
|
||||
|
||||
if (!CheckCpuBind()) {
|
||||
return false;
|
||||
return false; // 如果 CPU 绑定检查失败,返回 false
|
||||
}
|
||||
|
||||
if (m_cpuInfo.totalNumaNum != numaNodeNum || !m_cpuInfo.cpuArrSize) {
|
||||
ereport(WARNING,
|
||||
(errmsg("Can not activate NUMA distribute because no multiple NUMA nodes or CPUs are available.")));
|
||||
return false;
|
||||
(errmsg("无法激活 NUMA 分布,因为没有多个 NUMA 节点或可用的 CPU。")));
|
||||
return false; // 如果 NUMA 节点数不等于给定的 numaNodeNum,或者 cpuArrSize 为空,返回 false
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_cpuInfo.totalNumaNum; ++i) {
|
||||
if (m_cpuInfo.cpuArrSize[i] <= 0) {
|
||||
ereport(WARNING,
|
||||
(errmsg("Can not activate NUMA distribute because no available cpu in node %d.", i)));
|
||||
return false;
|
||||
(errmsg("无法激活 NUMA 分布,因为节点 %d 中没有可用的 CPU。", i)));
|
||||
return false; // 如果某个 NUMA 节点中没有可用的 CPU,返回 false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true; // 其他情况下,返回 true
|
||||
}
|
||||
|
||||
CPUBindType ThreadPoolControler::GetCpuBindType() const
|
||||
|
|
@ -669,55 +696,54 @@ void ThreadPoolControler::SetGroupAndThreadNum()
|
|||
{
|
||||
if (m_attr.groupNum == 0) {
|
||||
if (m_cpuInfo.totalNumaNum > 0)
|
||||
m_groupNum = m_cpuInfo.activeNumaNum;
|
||||
m_groupNum = m_cpuInfo.activeNumaNum; // 如果未指定线程组数且存在 NUMA 节点,则使用激活的 NUMA 节点数
|
||||
else
|
||||
m_groupNum = DEFAULT_THREAD_POOL_GROUPS;
|
||||
m_groupNum = DEFAULT_THREAD_POOL_GROUPS; // 否则使用默认的线程组数
|
||||
} else {
|
||||
m_groupNum = m_attr.groupNum;
|
||||
m_groupNum = m_attr.groupNum; // 如果指定了线程组数,则使用指定的线程组数
|
||||
}
|
||||
|
||||
if (m_attr.threadNum == 0) {
|
||||
if (m_cpuInfo.activeCpuNum > 0)
|
||||
m_threadNum = m_cpuInfo.activeCpuNum * THREAD_CORE_RATIO;
|
||||
m_threadNum = m_cpuInfo.activeCpuNum * THREAD_CORE_RATIO; // 如果未指定线程数且存在激活的 CPU,则计算线程数
|
||||
else
|
||||
m_threadNum = DEFAULT_THREAD_POOL_SIZE;
|
||||
m_threadNum = DEFAULT_THREAD_POOL_SIZE; // 否则使用默认的线程数
|
||||
} else {
|
||||
m_threadNum = m_attr.threadNum;
|
||||
m_threadNum = m_attr.threadNum; // 如果指定了线程数,则使用指定的线程数
|
||||
}
|
||||
|
||||
ConstrainThreadNum();
|
||||
ConstrainThreadNum(); // 调用 ConstrainThreadNum 方法进行线程数约束
|
||||
}
|
||||
|
||||
void ThreadPoolControler::ConstrainThreadNum()
|
||||
{
|
||||
/* Thread pool size should not be larger than max_connections. */
|
||||
/* 线程池大小不应超过 max_connections。 */
|
||||
if (MAX_THREAD_POOL_SIZE > g_instance.attr.attr_network.MaxConnections) {
|
||||
ereport(LOG, (errcode(ERRCODE_OPERATE_INVALID_PARAM),
|
||||
errmsg("Max thread pool size %d should not be larger than max_connections %d, "
|
||||
"so reduce max thread pool size to max_connections",
|
||||
errmsg("最大线程池大小 %d 不应超过 max_connections %d,因此将最大线程池大小减小到 max_connections",
|
||||
MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections)));
|
||||
}
|
||||
|
||||
m_maxPoolSize = Min(MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections);
|
||||
m_threadNum = Min(m_threadNum, m_maxPoolSize);
|
||||
m_maxPoolSize = Min(MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections); // 最大线程池大小受限于 max_connections
|
||||
m_threadNum = Min(m_threadNum, m_maxPoolSize); // 线程数不应超过最大线程池大小
|
||||
}
|
||||
|
||||
int ThreadPoolControler::GetThreadNum()
|
||||
{
|
||||
return m_maxPoolSize;
|
||||
return m_maxPoolSize; // 返回最大线程池大小
|
||||
}
|
||||
|
||||
ThreadPoolStat* ThreadPoolControler::GetThreadPoolStat(uint32* num)
|
||||
{
|
||||
ThreadPoolStat* result = (ThreadPoolStat*)palloc(m_groupNum * sizeof(ThreadPoolStat));
|
||||
ThreadPoolStat* result = (ThreadPoolStat*)palloc(m_groupNum * sizeof(ThreadPoolStat)); // 分配存储线程池统计信息的内存
|
||||
int i;
|
||||
|
||||
for (i = 0; i < m_groupNum; i++) {
|
||||
m_groups[i]->GetThreadPoolGroupStat(&result[i]);
|
||||
m_groups[i]->GetThreadPoolGroupStat(&result[i]); // 获取每个线程组的统计信息
|
||||
}
|
||||
|
||||
*num = m_groupNum;
|
||||
return result;
|
||||
*num = m_groupNum; // 返回线程组数量
|
||||
return result; // 返回线程池统计信息数组
|
||||
}
|
||||
|
||||
void ThreadPoolControler::CloseAllSessions()
|
||||
|
|
@ -725,14 +751,14 @@ void ThreadPoolControler::CloseAllSessions()
|
|||
ereport(LOG, (errmodule(MOD_THREAD_POOL),
|
||||
errmsg("pmState:%d, start to close all sessions in threadpool.", pmState)));
|
||||
|
||||
m_sessCtrl->MarkAllSessionClose();
|
||||
(void)SignalCancelAllBackEnd();
|
||||
m_sessCtrl->MarkAllSessionClose(); // 标记所有会话为关闭状态
|
||||
(void)SignalCancelAllBackEnd(); // 发送取消信号以取消所有后端任务
|
||||
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
m_groups[i]->GetListener()->SendShutDown();
|
||||
m_groups[i]->GetListener()->SendShutDown(); // 发送关闭信号给所有监听器
|
||||
}
|
||||
|
||||
/* Check until all groups have closed their sessions. */
|
||||
/* 检查直到所有组都关闭了它们的会话。 */
|
||||
bool allclose = false;
|
||||
while (!allclose) {
|
||||
if (m_sessCtrl->IsActiveListEmpty()) {
|
||||
|
|
@ -741,9 +767,9 @@ void ThreadPoolControler::CloseAllSessions()
|
|||
|
||||
allclose = true;
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
allclose = (m_groups[i]->AllSessionClosed() && allclose);
|
||||
allclose = (m_groups[i]->AllSessionClosed() && allclose); // 检查每个组的会话是否都已关闭
|
||||
}
|
||||
pg_usleep(one_hundred_micro_sec);
|
||||
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
|
||||
}
|
||||
|
||||
ereport(LOG, (errmodule(MOD_THREAD_POOL),
|
||||
|
|
@ -753,21 +779,21 @@ void ThreadPoolControler::CloseAllSessions()
|
|||
void ThreadPoolControler::ShutDownThreads(bool forceWait)
|
||||
{
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
m_groups[i]->ShutDownThreads();
|
||||
m_groups[i]->ShutDownThreads(); // 关闭所有线程组中的线程
|
||||
}
|
||||
|
||||
ereport(LOG, (errmodule(MOD_THREAD_POOL),
|
||||
errmsg("pmState:%d, shut down all threadpool threads.", pmState)));
|
||||
|
||||
if (forceWait) {
|
||||
/* Check until all groups have shut down their workers. */
|
||||
/* 检查直到所有组都关闭了它们的工作线程。 */
|
||||
bool allshut = false;
|
||||
while (!allshut) {
|
||||
allshut = true;
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
allshut = (m_groups[i]->AllThreadShutDown() && allshut);
|
||||
allshut = (m_groups[i]->AllThreadShutDown() && allshut); // 检查每个组的线程是否都已关闭
|
||||
}
|
||||
pg_usleep(one_hundred_micro_sec);
|
||||
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
|
||||
}
|
||||
|
||||
ereport(LOG, (errmodule(MOD_THREAD_POOL),
|
||||
|
|
@ -778,16 +804,16 @@ void ThreadPoolControler::ShutDownThreads(bool forceWait)
|
|||
void ThreadPoolControler::ShutDownListeners(bool forceWait)
|
||||
{
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
m_groups[i]->GetListener()->ShutDown();
|
||||
m_groups[i]->GetListener()->ShutDown(); // 关闭所有线程组的监听器
|
||||
}
|
||||
if (forceWait) {
|
||||
bool allshut = false;
|
||||
while (!allshut) {
|
||||
allshut = true;
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
allshut = (m_groups[i]->GetListener()->GetThreadId() == 0) && allshut;
|
||||
allshut = (m_groups[i]->GetListener()->GetThreadId() == 0) && allshut; // 检查监听器线程是否都已关闭
|
||||
}
|
||||
pg_usleep(one_hundred_micro_sec);
|
||||
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -828,20 +854,20 @@ void ThreadPoolControler::AddWorkerIfNecessary()
|
|||
|
||||
ThreadPoolGroup* ThreadPoolControler::FindThreadGroupWithLeastSession()
|
||||
{
|
||||
int idx = 0;
|
||||
float4 least_session = 0.0;
|
||||
float4 session_per_thread = 0.0;
|
||||
int idx = 0; // 用于记录具有最少会话的线程组的索引
|
||||
float4 least_session = 0.0; // 用于记录最少会话数
|
||||
float4 session_per_thread = 0.0; // 用于记录每个线程的平均会话数
|
||||
|
||||
least_session = m_groups[0]->GetSessionPerThread();
|
||||
least_session = m_groups[0]->GetSessionPerThread(); // 获取第一个线程组的平均会话数作为初始值
|
||||
for (int i = 1; i < m_groupNum; i++) {
|
||||
session_per_thread = m_groups[i]->GetSessionPerThread();
|
||||
if (session_per_thread < least_session) {
|
||||
least_session = session_per_thread;
|
||||
idx = i;
|
||||
session_per_thread = m_groups[i]->GetSessionPerThread(); // 获取当前线程组的平均会话数
|
||||
if (session_per_thread < least_session) { // 如果当前线程组的平均会话数更小
|
||||
least_session = session_per_thread; // 更新最少会话数
|
||||
idx = i; // 更新具有最少会话的线程组的索引
|
||||
}
|
||||
}
|
||||
|
||||
return m_groups[idx];
|
||||
return m_groups[idx]; // 返回具有最少会话的线程组的指针
|
||||
}
|
||||
|
||||
bool ThreadPoolControler::StayInAttachMode()
|
||||
|
|
@ -855,15 +881,14 @@ int ThreadPoolControler::DispatchSession(Port* port)
|
|||
knl_session_context* sc = NULL;
|
||||
|
||||
/*
|
||||
* In comm_proxy mode, each accepted fd is combined with a fixed communicator thread in one NUMA group,
|
||||
* we no longer distribute it with old mothod "group with latest sessions",
|
||||
* so just return the communicator's NUMA group.
|
||||
* 在comm_proxy模式下,每个接受的文件描述符(fd)都与一个
|
||||
* 固定的通信线程合并在一个NUMA组中,我们不再使用旧的方法“
|
||||
* 与最新会话的组合”进行分配,因此只需返回通信线程所在的NUMA组即可。
|
||||
*
|
||||
* Note: We assume that each connected user session can be equal-possibily distributed to communicators,
|
||||
* fortunately,it looks like Euler OS can guarantee this(proved),
|
||||
* otherwise we need revisit it.
|
||||
* 注意:我们假设每个连接的用户会话可以等可能地分布给通信器,
|
||||
* 幸运的是,欧拉操作系统似乎可以保证这一点(已经证明),否则我们还需要重新考虑这个问题。
|
||||
*
|
||||
* Performance optimization with comm_proxy when thread_pool m_groupNum same as comm_proxy numa groups
|
||||
* 性能优化:在使用`comm_proxy`时,当`thread_pool`的`m_groupNum`与`comm_proxy`的NUMA组数相同时,可以实现性能优化。
|
||||
*/
|
||||
if (AmIProxyModeSockfd(port->sock) && m_groupNum == g_comm_proxy_config.s_numa_num) {
|
||||
CommSockDesc* comm_sock = g_comm_controller->FdGetCommSockDesc(port->sock);
|
||||
|
|
@ -876,7 +901,7 @@ int ThreadPoolControler::DispatchSession(Port* port)
|
|||
Assert(false);
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
/* if this group is hanged, we don't accept new session */
|
||||
/* 如果这个组挂起了,我们不会接受新的会话。 */
|
||||
if (grp->IsGroupHanged()) {
|
||||
ereport(WARNING,
|
||||
(errmodule(MOD_THREAD_POOL),
|
||||
|
|
@ -893,29 +918,39 @@ int ThreadPoolControler::DispatchSession(Port* port)
|
|||
}
|
||||
|
||||
/*
|
||||
* Bind the specified thread to all the available CPUs.
|
||||
* This is invoked by auxiliary thread, such as WALSender.
|
||||
* 将指定的线程绑定到所有可用的CPU。
|
||||
* 此操作由辅助线程(例如WAL发送者)调用。
|
||||
*/
|
||||
void ThreadPoolControler::BindThreadToAllAvailCpu(ThreadId thread) const
|
||||
{
|
||||
// 如果不需要绑定CPU,直接返回
|
||||
if (!CheckCpuBind()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果绑定方式是ALL_CPU_BIND,也直接返回
|
||||
if (m_cpuInfo.bindType == ALL_CPU_BIND) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建一个CPU集合,初始化为空
|
||||
cpu_set_t availCpuSet;
|
||||
CPU_ZERO(&availCpuSet);
|
||||
|
||||
// 遍历每个NUMA节点
|
||||
for (int numaNo = 0; numaNo < m_cpuInfo.totalNumaNum; ++numaNo) {
|
||||
int cpuNumber = m_cpuInfo.cpuArrSize[numaNo];
|
||||
|
||||
// 将每个NUMA节点上的CPU添加到CPU集合中
|
||||
for (int i = 0; i < cpuNumber; ++i) {
|
||||
CPU_SET(m_cpuInfo.cpuArr[numaNo][i], &availCpuSet);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用pthread_setaffinity_np函数将线程绑定到CPU集合中
|
||||
int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &availCpuSet);
|
||||
|
||||
// 如果绑定失败,输出警告信息
|
||||
if (ret != 0)
|
||||
ereport(WARNING, (errmsg("BindThreadToAllAvailCpu fail to bind thread %lu, errno: %d", thread, ret)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@
|
|||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* threadpool_group.cpp
|
||||
* Thread pool group controls listener and worker threads.
|
||||
* 线程池组控制监听器线程和工作者线程
|
||||
* 监听器线程负责接受新的连接请求,通常用于处理客户端与服务器之间的通信建立和维护。
|
||||
* 工作者线程负责实际的任务处理,通常用于执行数据库查询、事务处理等具体的业务逻辑。
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
|
|
@ -59,12 +61,12 @@
|
|||
ThreadPoolGroup::ThreadPoolGroup(int maxWorkerNum, int expectWorkerNum, int maxStreamNum,
|
||||
int groupId, int numaId, int cpuNum, int* cpuArr, bool enableBindCpuNuma)
|
||||
: m_listener(NULL),
|
||||
m_maxWorkerNum(maxWorkerNum),
|
||||
m_maxStreamNum(maxStreamNum),
|
||||
m_maxWorkerNum(maxWorkerNum),//最大工作者线程数量
|
||||
m_maxStreamNum(maxStreamNum),//最大流线程数量
|
||||
m_defaultWorkerNum(expectWorkerNum),
|
||||
m_workerNum(0),
|
||||
m_listenerNum(0),
|
||||
m_expectWorkerNum(expectWorkerNum),
|
||||
m_expectWorkerNum(expectWorkerNum),//预期工作者线程数量
|
||||
m_idleWorkerNum(0),
|
||||
m_pendingWorkerNum(0),
|
||||
m_streamNum(0),
|
||||
|
|
@ -73,8 +75,8 @@ ThreadPoolGroup::ThreadPoolGroup(int maxWorkerNum, int expectWorkerNum, int maxS
|
|||
m_waitServeSessionCount(0),
|
||||
m_processTaskCount(0),
|
||||
m_hasHanged(0),
|
||||
m_groupId(groupId),
|
||||
m_numaId(numaId),
|
||||
m_groupId(groupId),//线程池组的唯一标识符
|
||||
m_numaId(numaId),//NUMA 节点的标识符
|
||||
m_groupCpuNum(cpuNum),
|
||||
m_groupCpuArr(cpuArr),
|
||||
m_enableNumaDistribute(false),
|
||||
|
|
@ -104,6 +106,7 @@ ThreadPoolGroup::~ThreadPoolGroup()
|
|||
|
||||
void ThreadPoolGroup::Init(bool enableNumaDistribute)
|
||||
{
|
||||
// 创建线程池组上下文
|
||||
m_context = AllocSetContextCreate(g_instance.instance_context,
|
||||
"ThreadPoolGroupContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
|
@ -111,22 +114,27 @@ void ThreadPoolGroup::Init(bool enableNumaDistribute)
|
|||
ALLOCSET_DEFAULT_MAXSIZE,
|
||||
SHARED_CONTEXT);
|
||||
|
||||
// 切换到线程池组上下文
|
||||
AutoContextSwitch acontext(m_context);
|
||||
|
||||
// 创建线程池监听器并启动
|
||||
m_listener = New(CurrentMemoryContext) ThreadPoolListener(this);
|
||||
m_listener->StartUp();
|
||||
|
||||
// 如果启用 CPU 绑定和 NUMA 分配,将指定的 CPU 添加到 CPU 集合中
|
||||
if (m_enableBindCpuNuma) {
|
||||
for (int i = 0; i < m_groupCpuNum; i++) {
|
||||
CPU_SET(m_groupCpuArr[i], &m_CpuNumaSet);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化工作者线程管理
|
||||
InitWorkerSentry();
|
||||
|
||||
// 初始化流线程管理
|
||||
InitStreamSentry();
|
||||
|
||||
/* Prepare the CPU_SET including all of available cpus in this node */
|
||||
// 准备包含本节点所有可用 CPU 的 CPU_SET
|
||||
m_enableNumaDistribute = enableNumaDistribute;
|
||||
for (int i = 0; i < m_groupCpuNum; ++i) {
|
||||
CPU_SET(m_groupCpuArr[i], &m_nodeCpuSet);
|
||||
|
|
@ -135,19 +143,19 @@ void ThreadPoolGroup::Init(bool enableNumaDistribute)
|
|||
|
||||
void ThreadPoolGroup::InitWorkerSentry()
|
||||
{
|
||||
/* Prepare slots in case we need to enlarge this thread group. */
|
||||
/* 为可能需要扩展的线程组准备插槽。 */
|
||||
m_workers = (ThreadWorkerSentry*)palloc0_noexcept(sizeof(ThreadWorkerSentry) * m_maxWorkerNum);
|
||||
if (m_workers == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("内存不足")));
|
||||
}
|
||||
|
||||
/* Init lock for each slot. */
|
||||
/* 为每个插槽初始化锁。 */
|
||||
for (int i = 0; i < m_maxWorkerNum; i++) {
|
||||
pthread_mutex_init(&m_workers[i].mutex, NULL);
|
||||
pthread_cond_init(&m_workers[i].cond, NULL);
|
||||
}
|
||||
|
||||
/* Start up workers. */
|
||||
/* 启动工作者线程。 */
|
||||
for (int i = 0; i < m_expectWorkerNum; i++) {
|
||||
AddWorker(i);
|
||||
}
|
||||
|
|
@ -155,8 +163,10 @@ void ThreadPoolGroup::InitWorkerSentry()
|
|||
|
||||
void ThreadPoolGroup::AddWorker(int i)
|
||||
{
|
||||
// 创建线程池工作者
|
||||
m_workers[i].worker = New(m_context) ThreadPoolWorker(i, this, &m_workers[i].mutex, &m_workers[i].cond);
|
||||
|
||||
// 启动工作者线程
|
||||
int ret = m_workers[i].worker->StartUp();
|
||||
if (ret == STATUS_OK) {
|
||||
m_workers[i].stat.slotStatus = THREAD_SLOT_INUSE;
|
||||
|
|
@ -175,7 +185,7 @@ void ThreadPoolGroup::AddWorker(int i)
|
|||
} else {
|
||||
delete m_workers[i].worker;
|
||||
m_workers[i].worker = NULL;
|
||||
ereport(LOG, (errmsg("Faid to start up thread pool worker: %m")));
|
||||
ereport(LOG, (errmsg("启动线程池工作者失败: %m")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +193,7 @@ void ThreadPoolGroup::ReleaseWorkerSlot(int i)
|
|||
{
|
||||
Assert(m_workers[i].worker != NULL);
|
||||
|
||||
// 加锁以修改工作者数量
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pg_atomic_fetch_sub_u32((volatile uint32*)&m_workerNum, 1);
|
||||
Assert(m_workerNum >= 0);
|
||||
|
|
@ -192,11 +203,12 @@ void ThreadPoolGroup::ReleaseWorkerSlot(int i)
|
|||
|
||||
void ThreadPoolGroup::WaitReady()
|
||||
{
|
||||
// 循环等待直到监听器数量为 1,表示已经就绪
|
||||
while (true) {
|
||||
if (m_listenerNum == 1) {
|
||||
break;
|
||||
}
|
||||
pg_usleep(500);
|
||||
pg_usleep(500); // 等待 500 微秒
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,26 +219,32 @@ float4 ThreadPoolGroup::GetSessionPerThread()
|
|||
|
||||
void ThreadPoolGroup::GetThreadPoolGroupStat(ThreadPoolStat* stat)
|
||||
{
|
||||
stat->groupId = m_groupId;
|
||||
stat->numaId = m_numaId;
|
||||
stat->bindCpuNum = m_groupCpuNum;
|
||||
stat->listenerNum = m_listenerNum;
|
||||
// 将线程池组的状态信息填充到给定的 ThreadPoolStat 结构体中
|
||||
stat->groupId = m_groupId; // 线程池组的 ID
|
||||
stat->numaId = m_numaId; // NUMA 节点的 ID
|
||||
stat->bindCpuNum = m_groupCpuNum; // 绑定的 CPU 数量
|
||||
stat->listenerNum = m_listenerNum; // 监听器数量
|
||||
|
||||
// 填充工作者线程信息
|
||||
int rc = sprintf_s(stat->workerInfo, STATUS_INFO_SIZE,
|
||||
"default: %d new: %d expect: %d actual: %d idle: %d pending: %d",
|
||||
m_defaultWorkerNum, m_expectWorkerNum - m_defaultWorkerNum, m_expectWorkerNum,
|
||||
m_workerNum, m_idleWorkerNum, m_pendingWorkerNum);
|
||||
"default: %d new: %d expect: %d actual: %d idle: %d pending: %d",
|
||||
m_defaultWorkerNum, m_expectWorkerNum - m_defaultWorkerNum, m_expectWorkerNum,
|
||||
m_workerNum, m_idleWorkerNum, m_pendingWorkerNum);
|
||||
securec_check_ss(rc, "", "");
|
||||
|
||||
// 计算运行中和空闲中的会话数量
|
||||
int runSessionNum = m_workerNum - m_idleWorkerNum;
|
||||
int idleSessionNum = m_sessionCount - m_waitServeSessionCount - runSessionNum;
|
||||
idleSessionNum = (idleSessionNum < 0) ? 0 : idleSessionNum;
|
||||
|
||||
// 填充会话信息
|
||||
rc = sprintf_s(stat->sessionInfo, STATUS_INFO_SIZE,
|
||||
"total: %d waiting: %d running:%d idle: %d",
|
||||
m_sessionCount, m_waitServeSessionCount,
|
||||
runSessionNum, idleSessionNum);
|
||||
"total: %d waiting: %d running:%d idle: %d",
|
||||
m_sessionCount, m_waitServeSessionCount,
|
||||
runSessionNum, idleSessionNum);
|
||||
securec_check_ss(rc, "", "");
|
||||
|
||||
// 如果是 PGXC 数据节点,填充流信息,否则将流信息置为空
|
||||
if (IS_PGXC_DATANODE) {
|
||||
rc = sprintf_s(stat->streamInfo, STATUS_INFO_SIZE,
|
||||
"total: %d running: %d idle: %d", m_streamNum, m_streamNum - m_idleStreamNum, m_idleStreamNum);
|
||||
|
|
@ -238,10 +256,12 @@ void ThreadPoolGroup::GetThreadPoolGroupStat(ThreadPoolStat* stat)
|
|||
|
||||
void ThreadPoolGroup::AddWorkerIfNecessary()
|
||||
{
|
||||
// 自动加锁,确保线程安全
|
||||
AutoMutexLock alock(&m_mutex);
|
||||
alock.lock();
|
||||
m_workerNum = (m_workerNum >= 0) ? m_workerNum : 0;
|
||||
|
||||
// 如果当前工作者线程数量小于期望的数量,则添加工作者线程
|
||||
if (m_workerNum < m_expectWorkerNum) {
|
||||
for (int i = 0; i < m_expectWorkerNum; i++) {
|
||||
if (m_workers[i].stat.slotStatus == THREAD_SLOT_UNUSE) {
|
||||
|
|
@ -252,80 +272,84 @@ void ThreadPoolGroup::AddWorkerIfNecessary()
|
|||
}
|
||||
}
|
||||
}
|
||||
alock.unLock();
|
||||
alock.unLock(); // 解锁
|
||||
}
|
||||
|
||||
bool ThreadPoolGroup::EnlargeWorkers(int enlargeNum)
|
||||
{
|
||||
// 自动加锁,确保线程安全
|
||||
AutoMutexLock alock(&m_mutex);
|
||||
alock.lock();
|
||||
|
||||
// 如果期望的工作者线程数量已经等于最大数量,不再扩展
|
||||
if (m_expectWorkerNum == m_maxWorkerNum) {
|
||||
alock.unLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
int num = m_expectWorkerNum;
|
||||
m_expectWorkerNum += enlargeNum;
|
||||
m_expectWorkerNum = Min(m_expectWorkerNum, m_maxWorkerNum);
|
||||
m_expectWorkerNum += enlargeNum; // 增加期望的工作者线程数量
|
||||
m_expectWorkerNum = Min(m_expectWorkerNum, m_maxWorkerNum); // 不超过最大工作者线程数量
|
||||
elog(LOG, "[SCHEDULER] Group %d enlarge worker. Old worker num %d, new worker num %d",
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
int diff = m_expectWorkerNum - num;
|
||||
|
||||
/* Turn pending workers into running workers if we have. */
|
||||
// 如果有待处理的工作者线程,将它们转换为运行中的线程
|
||||
if (m_pendingWorkerNum != 0) {
|
||||
ThreadPoolWorker* worker = NULL;
|
||||
int wakeUpNum = Min(diff, m_pendingWorkerNum);
|
||||
m_pendingWorkerNum -= wakeUpNum;
|
||||
int wakeUpNum = Min(diff, m_pendingWorkerNum); // 计算需要唤醒的数量
|
||||
m_pendingWorkerNum -= wakeUpNum; // 减少待处理的工作者线程数量
|
||||
for (int i = num; i < num + wakeUpNum; i++) {
|
||||
if (m_workers[i].stat.slotStatus == THREAD_SLOT_INUSE) {
|
||||
worker = m_workers[i].worker;
|
||||
if (worker->GetthreadStatus() == THREAD_PENDING) {
|
||||
worker->WakeUpToUpdate(THREAD_RUN);
|
||||
worker->WakeUpToUpdate(THREAD_RUN); // 唤醒待处理线程,使其运行
|
||||
elog(LOG, "[SCHEDULER] Group %d enlarge: wakeup pending worker %lu",
|
||||
m_groupId, m_workers[i].worker->GetThreadId());
|
||||
m_groupId, m_workers[i].worker->GetThreadId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
alock.unLock();
|
||||
alock.unLock(); // 解锁
|
||||
|
||||
/* Start up worker if pending worker is not enough. */
|
||||
// 向 Postmaster 发送信号以启动新的工作者线程
|
||||
SendPostmasterSignal(PMSIGNAL_START_THREADPOOL_WORKER);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::ReduceWorkers(int reduceNum)
|
||||
{
|
||||
// 自动加锁,确保线程安全
|
||||
AutoMutexLock alock(&m_mutex);
|
||||
alock.lock();
|
||||
|
||||
int num = m_expectWorkerNum;
|
||||
m_expectWorkerNum -= reduceNum;
|
||||
m_expectWorkerNum = Max(m_expectWorkerNum, m_defaultWorkerNum);
|
||||
m_expectWorkerNum = Max(m_expectWorkerNum, m_defaultWorkerNum); // 保证不低于默认工作者线程数量
|
||||
if (num - m_expectWorkerNum == 0) {
|
||||
alock.unLock();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingWorkerNum += (num - m_expectWorkerNum);
|
||||
m_pendingWorkerNum += (num - m_expectWorkerNum); // 将多余的工作者线程设置为待处理状态
|
||||
elog(LOG, "[SCHEDULER] Group %d reduce worker. Old worker num %d, new worker num %d",
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
/* only wake up free thread to pending, if we meet working thread, just skip it. */
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
/* 只唤醒空闲线程到待处理状态,如果遇到正在工作的线程,则跳过 */
|
||||
for (int i = m_expectWorkerNum; i < num; i++) {
|
||||
if (m_workers[i].stat.slotStatus == THREAD_SLOT_INUSE) {
|
||||
Assert(m_workers[i].worker != NULL);
|
||||
if (m_workers[i].worker->WakeUpToPendingIfFree()) {
|
||||
elog(LOG, "[SCHEDULER] Group %d reduce: pending worker %lu",
|
||||
m_groupId, m_workers[i].worker->GetThreadId());
|
||||
m_groupId, m_workers[i].worker->GetThreadId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elog(LOG, "[SCHEDULER] Group %d reduce worker end. Old worker num %d, new worker num %d",
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
m_groupId, num, m_expectWorkerNum);
|
||||
alock.unLock();
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::ShutDownPendingWorkers()
|
||||
{
|
||||
if (m_pendingWorkerNum == 0) {
|
||||
|
|
@ -333,7 +357,7 @@ void ThreadPoolGroup::ShutDownPendingWorkers()
|
|||
}
|
||||
|
||||
elog(LOG, "[SCHEDULER] Group %d shut down pending workers start. pending worker num %d, current worker num %d",
|
||||
m_groupId, m_pendingWorkerNum, m_expectWorkerNum);
|
||||
m_groupId, m_pendingWorkerNum, m_expectWorkerNum);
|
||||
|
||||
AutoMutexLock alock(&m_mutex);
|
||||
ThreadPoolWorker* worker = NULL;
|
||||
|
|
@ -342,41 +366,46 @@ void ThreadPoolGroup::ShutDownPendingWorkers()
|
|||
if (m_workers[i].stat.slotStatus == THREAD_SLOT_INUSE) {
|
||||
worker = m_workers[i].worker;
|
||||
if (worker->GetthreadStatus() == THREAD_PENDING) {
|
||||
worker->WakeUpToUpdate(THREAD_EXIT);
|
||||
worker->WakeUpToUpdate(THREAD_EXIT); // 唤醒待处理线程以退出
|
||||
}
|
||||
}
|
||||
}
|
||||
m_pendingWorkerNum = 0;
|
||||
elog(LOG, "[SCHEDULER] Group %d shut down pending workers end. pending worker num %d, current worker num %d",
|
||||
m_groupId, m_pendingWorkerNum, m_expectWorkerNum);
|
||||
m_groupId, m_pendingWorkerNum, m_expectWorkerNum);
|
||||
alock.unLock();
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::ShutDownThreads()
|
||||
{
|
||||
// 自动加锁,确保线程安全
|
||||
AutoMutexLock alock(&m_mutex);
|
||||
alock.lock();
|
||||
// 关闭工作者线程
|
||||
for (int i = 0; i < m_maxWorkerNum; i++) {
|
||||
if (m_workers[i].stat.slotStatus != THREAD_SLOT_UNUSE) {
|
||||
m_workers[i].worker->WakeUpToUpdate(THREAD_EXIT);
|
||||
m_workers[i].worker->WakeUpToUpdate(THREAD_EXIT); // 唤醒工作者线程以退出
|
||||
}
|
||||
}
|
||||
m_pendingWorkerNum = 0;
|
||||
m_pendingWorkerNum = 0; // 重置待处理工作者线程数量
|
||||
|
||||
// 关闭流线程
|
||||
for (int i = 0; i < m_maxStreamNum; i++) {
|
||||
if (m_streams[i].stat.slotStatus != THREAD_SLOT_UNUSE) {
|
||||
m_streams[i].stream->WakeUpToUpdate(THREAD_EXIT);
|
||||
m_streams[i].stream->WakeUpToUpdate(THREAD_EXIT); // 唤醒流线程以退出
|
||||
}
|
||||
}
|
||||
alock.unLock();
|
||||
alock.unLock(); // 解锁
|
||||
}
|
||||
|
||||
bool ThreadPoolGroup::IsGroupHang()
|
||||
{
|
||||
// 检查是否存在正在处理的任务或空闲工作者线程
|
||||
if (pg_atomic_exchange_u32((volatile uint32*)&m_processTaskCount, 0) != 0 ||
|
||||
m_idleWorkerNum != 0)
|
||||
return false;
|
||||
|
||||
// 调用 ThreadPoolListener 的 GetSessIshang 函数检查是否线程组挂起
|
||||
bool ishang = m_listener->GetSessIshang(&m_current_time, &m_sessionId);
|
||||
return ishang;
|
||||
}
|
||||
|
|
@ -394,45 +423,57 @@ bool ThreadPoolGroup::IsGroupHanged()
|
|||
|
||||
void ThreadPoolGroup::AttachThreadToCPU(ThreadId thread, int cpu)
|
||||
{
|
||||
// 创建一个 CPU 集合并将指定的 CPU 添加到集合中
|
||||
cpu_set_t cpuset;
|
||||
int ret = 0;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpu, &cpuset);
|
||||
|
||||
// 使用 pthread_setaffinity_np 函数将线程绑定到指定的 CPU
|
||||
ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
|
||||
if (ret != 0) {
|
||||
// 如果绑定失败,记录警告日志
|
||||
ereport(WARNING, (errmsg("Fail to attach thread %lu to CPU %d", thread, cpu)));
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::AttachThreadToNodeLevel(ThreadId thread) const
|
||||
{
|
||||
// 使用 pthread_setaffinity_np 函数将线程绑定到 NUMA 节点的 CPU 集合
|
||||
int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &m_nodeCpuSet);
|
||||
if (ret != 0)
|
||||
// 如果绑定失败,记录警告日志
|
||||
ereport(WARNING, (errmsg("Fail to attach thread %lu to numa node %d", thread, m_numaId)));
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::AttachThreadToCpuNuma(ThreadId thread)
|
||||
{
|
||||
// 使用 pthread_setaffinity_np 函数将线程绑定到指定的 CPU NUMA 的 CPU 集合
|
||||
int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &m_CpuNumaSet);
|
||||
if (ret != 0) {
|
||||
// 如果绑定失败,记录警告日志
|
||||
ereport(WARNING, (errmsg("Fail to attach thread %lu to CPU NUMA", thread)));
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPoolGroup::InitStreamSentry()
|
||||
{
|
||||
// 分配用于存储线程流的数据结构数组
|
||||
m_streams = (ThreadStreamSentry*)palloc0_noexcept(sizeof(ThreadStreamSentry) * m_maxStreamNum);
|
||||
if (m_streams == NULL) {
|
||||
// 如果内存分配失败,记录错误日志
|
||||
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
|
||||
}
|
||||
|
||||
// 为每个线程流初始化互斥锁和条件变量
|
||||
for (int i = 0; i < m_maxStreamNum; i++) {
|
||||
pthread_mutex_init(&m_streams[i].mutex, NULL);
|
||||
pthread_cond_init(&m_streams[i].cond, NULL);
|
||||
}
|
||||
|
||||
m_freeStreamList = New(CurrentMemoryContext)DllistWithLock();
|
||||
// 创建一个带锁的双向链表
|
||||
m_freeStreamList = New(CurrentMemoryContext) DllistWithLock();
|
||||
}
|
||||
|
||||
ThreadId ThreadPoolGroup::GetStreamFromPool(StreamProducer* producer)
|
||||
|
|
@ -442,23 +483,31 @@ ThreadId ThreadPoolGroup::GetStreamFromPool(StreamProducer* producer)
|
|||
AutoMutexLock alock(&m_mutex);
|
||||
alock.lock();
|
||||
|
||||
// 检查是否有空闲的线程流可用
|
||||
if (m_freeStreamList->IsEmpty()) {
|
||||
// 如果没有空闲的线程流,并且已达到最大线程流数限制,报错
|
||||
if (m_streamNum == m_maxStreamNum) {
|
||||
alock.unLock();
|
||||
ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR),
|
||||
errmsg("Exceed stream thread pool limitation %d in group %d", m_maxStreamNum, m_groupId)));
|
||||
}
|
||||
|
||||
// 分配一个 Postmaster 子进程槽位
|
||||
producer->setChildSlot(AssignPostmasterChildSlot());
|
||||
if (producer->getChildSlot() == -1) {
|
||||
return InvalidTid;
|
||||
}
|
||||
|
||||
// 添加一个新的线程流
|
||||
tid = AddStream(producer);
|
||||
} else {
|
||||
// 如果有空闲的线程流,从空闲线程流列表中获取一个
|
||||
Dlelem* elem = m_freeStreamList->RemoveHead();
|
||||
// 减少空闲线程流计数
|
||||
pg_atomic_fetch_sub_u32((volatile uint32*)&m_idleStreamNum, 1);
|
||||
stream = (ThreadPoolStream*)DLE_VAL(elem);
|
||||
tid = stream->GetThreadId();
|
||||
// 唤醒线程流以处理生产者的任务
|
||||
stream->WakeUpToWork(producer);
|
||||
}
|
||||
return tid;
|
||||
|
|
@ -469,6 +518,8 @@ ThreadId ThreadPoolGroup::AddStream(StreamProducer* producer)
|
|||
ThreadId tid = InvalidTid;
|
||||
ThreadStreamSentry* streamSentry = NULL;
|
||||
int idx = 0;
|
||||
|
||||
// 寻找一个空闲的线程流槽位
|
||||
for (idx = 0; idx < m_maxStreamNum; idx++) {
|
||||
if (m_streams[idx].stat.slotStatus == THREAD_SLOT_UNUSE) {
|
||||
streamSentry = &m_streams[idx];
|
||||
|
|
@ -478,25 +529,31 @@ ThreadId ThreadPoolGroup::AddStream(StreamProducer* producer)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到空闲的槽位,报错
|
||||
if (streamSentry == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("Fail to find a free slot for stream")));
|
||||
}
|
||||
|
||||
ThreadPoolStream *stream = New(m_context) ThreadPoolStream();
|
||||
// 创建一个新的线程流
|
||||
ThreadPoolStream* stream = New(m_context) ThreadPoolStream();
|
||||
tid = stream->StartUp(idx, producer, this, &streamSentry->mutex, &streamSentry->cond);
|
||||
|
||||
if (tid != 0) {
|
||||
// 标记线程流槽位为已使用状态
|
||||
streamSentry->stat.slotStatus = THREAD_SLOT_INUSE;
|
||||
streamSentry->stat.spawntick++;
|
||||
streamSentry->stat.lastSpawnTime = GetCurrentTimestamp();
|
||||
streamSentry->stream = stream;
|
||||
tid = stream->GetThreadId();
|
||||
if (m_groupCpuArr) {
|
||||
// 如果启用了 NUMA 分布,将线程流绑定到 NUMA 节点
|
||||
AttachThreadToNodeLevel(tid);
|
||||
}
|
||||
m_streamNum++;
|
||||
} else {
|
||||
// 启动线程流失败,释放资源并记录日志
|
||||
delete stream;
|
||||
tid = 0;
|
||||
ereport(LOG, (errmsg("Faid to start up thread pool stream: %m")));
|
||||
|
|
@ -524,7 +581,11 @@ void ThreadPoolGroup::RemoveStreamFromPool(Dlelem* elem, int idx)
|
|||
void ThreadPoolGroup::ReduceStreams()
|
||||
{
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
|
||||
// 计算最大可以减少的线程流数量
|
||||
int max_reduce_num = m_streamNum - m_defaultWorkerNum;
|
||||
|
||||
// 如果可以减少线程流数量,执行减少操作
|
||||
if (max_reduce_num > 0) {
|
||||
elog(LOG, "Reduce %d stream thread", max_reduce_num);
|
||||
for (int i = 0; i < max_reduce_num; i++) {
|
||||
|
|
@ -536,5 +597,6 @@ void ThreadPoolGroup::ReduceStreams()
|
|||
stream->WakeUpToUpdate(THREAD_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,9 @@
|
|||
*
|
||||
* threadpool_listener.cpp
|
||||
*
|
||||
* There are multiple tasks for listener thread:
|
||||
* 1. Listen to all connections from client or other componets of this cluster
|
||||
* (like connections from other cn).
|
||||
* 2. Dispatch session to available woker thread.
|
||||
* 监听线程有多个任务:
|
||||
* 1. 监听来自客户端或集群中其他组件(如来自其他计算节点的连接)的所有连接。
|
||||
* 2. 将会话分派给可用的工作线程。
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/gausskernel/process/threadpool/threadpool_listener.cpp
|
||||
|
|
@ -74,7 +73,7 @@ void TpoolListenerMain(ThreadPoolListener* listener)
|
|||
|
||||
(void)gspqsignal(SIGHUP, SIG_IGN);
|
||||
(void)gspqsignal(SIGINT, SIG_IGN);
|
||||
// die with pm
|
||||
// Postmaster进程退出时终止
|
||||
(void)gspqsignal(SIGTERM, SIG_IGN);
|
||||
(void)gspqsignal(SIGQUIT, SIG_IGN);
|
||||
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
||||
|
|
@ -107,31 +106,49 @@ void ThreadPoolListenerIAm()
|
|||
|
||||
ThreadPoolListener::ThreadPoolListener(ThreadPoolGroup* group)
|
||||
{
|
||||
// 将传入的线程池组对象保存到成员变量中
|
||||
m_group = group;
|
||||
|
||||
// 初始化一些成员变量,设置为初始值
|
||||
m_tid = InvalidTid;
|
||||
m_epollFd = INVALID_FD;
|
||||
m_epollEvents = NULL;
|
||||
m_reaperAllSession = false;
|
||||
m_getKilled = false;
|
||||
|
||||
// 创建用于管理空闲工作线程的链表
|
||||
m_freeWorkerList = New(CurrentMemoryContext) DllistWithLock();
|
||||
|
||||
// 创建用于管理准备就绪的会话的链表
|
||||
m_readySessionList = New(CurrentMemoryContext) DllistWithLock();
|
||||
|
||||
// 创建用于管理空闲会话的链表
|
||||
m_idleSessionList = New(CurrentMemoryContext) DllistWithLock();
|
||||
|
||||
// 检查是否启用本地系统缓存,根据情况进行初始化
|
||||
if (EnableLocalSysCache()) {
|
||||
/* see HASH_INDEX, Since the hash table must contain a power-of-2 number of elements */
|
||||
// 根据配置设置会话哈希表的桶数(通常为2的幂)
|
||||
#ifdef ENABLE_LITE_MODE
|
||||
m_session_nbucket = 128;
|
||||
#else
|
||||
m_session_nbucket = MAX_THREAD_POOL_SIZE;
|
||||
#endif
|
||||
|
||||
// 分配会话桶的内存空间
|
||||
m_session_bucket = (Dllist*)palloc0(m_session_nbucket * sizeof(Dllist));
|
||||
|
||||
// 分配会话读写锁的内存空间
|
||||
m_session_rw_locks = (pthread_rwlock_t *)palloc0(m_session_nbucket * sizeof(pthread_rwlock_t));
|
||||
|
||||
// 初始化每个会话桶的读写锁
|
||||
for (int i = 0; i < m_session_nbucket; i++) {
|
||||
PthreadRwLockInit(&m_session_rw_locks[i], NULL);
|
||||
}
|
||||
|
||||
// 初始化用于匹配搜索的标志
|
||||
m_match_search = 0;
|
||||
|
||||
} else {
|
||||
// 如果未启用本地系统缓存,则将相关成员变量设置为初始值
|
||||
m_session_nbucket = 0;
|
||||
m_session_bucket = NULL;
|
||||
m_session_rw_locks = NULL;
|
||||
|
|
@ -141,21 +158,27 @@ ThreadPoolListener::ThreadPoolListener(ThreadPoolGroup* group)
|
|||
|
||||
ThreadPoolListener::~ThreadPoolListener()
|
||||
{
|
||||
// 根据宏定义选择不同的关闭方法
|
||||
if (ENABLE_THREAD_POOL_DN_LOGICCONN) {
|
||||
CommEpollClose(m_epollFd);
|
||||
} else {
|
||||
comm_close(m_epollFd); /* CommProxy support */
|
||||
comm_close(m_epollFd); /* CommProxy 支持 */
|
||||
}
|
||||
|
||||
// 将相关成员变量设置为 NULL,以释放对资源的引用
|
||||
m_group = NULL;
|
||||
m_epollEvents = NULL;
|
||||
m_freeWorkerList = NULL;
|
||||
m_readySessionList = NULL;
|
||||
m_idleSessionList = NULL;
|
||||
|
||||
// 如果启用了本地系统缓存,释放相应的资源
|
||||
if (EnableLocalSysCache()) {
|
||||
pfree_ext(m_session_bucket);
|
||||
pfree_ext(m_session_rw_locks);
|
||||
}
|
||||
|
||||
// 将与本地系统缓存相关的成员变量设置为初始值
|
||||
m_session_nbucket = 0;
|
||||
m_session_bucket = NULL;
|
||||
}
|
||||
|
|
@ -173,27 +196,32 @@ void ThreadPoolListener::NotifyReady()
|
|||
|
||||
void ThreadPoolListener::CreateEpoll()
|
||||
{
|
||||
/* MAX_LISTEN_SESSIONS for epool_create is ignored Since Linux 2.6.8 */
|
||||
// 根据宏定义选择不同的 epoll 创建函数
|
||||
if (ENABLE_THREAD_POOL_DN_LOGICCONN) {
|
||||
// 使用 CommEpollCreate 函数创建 epoll,传入最大会话数
|
||||
m_epollFd = CommEpollCreate(GLOBAL_MAX_SESSION_NUM);
|
||||
} else {
|
||||
// 使用 comm_epoll_create 函数创建 epoll,传入最大会话数
|
||||
CommSetEpollOption(CommEpollThreadPoolListener);
|
||||
m_epollFd = comm_epoll_create(GLOBAL_MAX_SESSION_NUM);
|
||||
}
|
||||
|
||||
// 检查 epoll 创建是否成功
|
||||
if (m_epollFd == INVALID_FD) {
|
||||
ereport(LOG,
|
||||
(errmsg("Fail to create epoll for thread pool listener, "
|
||||
"check if the system is out of memory or "
|
||||
"limit on the total number of open files has been reached.")));
|
||||
proc_exit(0);
|
||||
proc_exit(0); // 退出当前进程
|
||||
}
|
||||
|
||||
// 分配用于存储 epoll 事件的内存
|
||||
m_epollEvents = (struct epoll_event*)palloc0_noexcept(sizeof(struct epoll_event) * GLOBAL_MAX_SESSION_NUM);
|
||||
|
||||
// 检查内存分配是否成功
|
||||
if (m_epollEvents == NULL) {
|
||||
elog(LOG, "Not enough memory for listener epoll");
|
||||
proc_exit(0);
|
||||
proc_exit(0); // 退出当前进程
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,20 +235,19 @@ void ThreadPoolListener::AddEpoll(knl_session_context* session)
|
|||
(errmodule(MOD_THREAD_POOL),
|
||||
errmsg("Add a session:%lu to idleSessionList ", session->session_id)));
|
||||
/*
|
||||
* Because we will dispatch the socket to worker thread once
|
||||
* we find an input event of the socket, so we use one_shot mode.
|
||||
* 因为一旦发现套接字有输入事件,就会将套接字分派给工作线程处理,所以使用 "one_shot"
|
||||
* 模式是合适的。这意味着当一个工作线程处理套接字事件时,其他线程不会同时处理相同的套接字事件,
|
||||
* 从而确保了线程之间的互斥性,避免了竞态条件和数据损坏。
|
||||
*/
|
||||
ev.events = EPOLLRDHUP | EPOLLIN | EPOLLET | EPOLLONESHOT;
|
||||
ev.data.ptr = (void*)session;
|
||||
if (session->status != KNL_SESS_UNINIT) {
|
||||
/* CommProxy Support */
|
||||
if (ENABLE_THREAD_POOL_DN_LOGICCONN) {
|
||||
res = CommEpollCtl(m_epollFd, EPOLL_CTL_MOD, session->proc_cxt.MyProcPort->sock, &ev);
|
||||
} else {
|
||||
res = comm_epoll_ctl(m_epollFd, EPOLL_CTL_MOD, session->proc_cxt.MyProcPort->sock, &ev);
|
||||
}
|
||||
} else {
|
||||
/* CommProxy Support */
|
||||
if (ENABLE_THREAD_POOL_DN_LOGICCONN) {
|
||||
res = CommEpollCtl(m_epollFd, EPOLL_CTL_ADD, session->proc_cxt.MyProcPort->sock, &ev);
|
||||
} else {
|
||||
|
|
@ -244,10 +271,14 @@ void ThreadPoolListener::AddEpoll(knl_session_context* session)
|
|||
bool ThreadPoolListener::TryFeedWorker(ThreadPoolWorker* worker)
|
||||
{
|
||||
Dlelem* sc = GetReadySession(worker);
|
||||
//获取一个准备就绪的会话
|
||||
if (sc != NULL) {
|
||||
worker->SetSession((knl_session_context*)sc->dle_val);
|
||||
//将该会话设置给工作线程
|
||||
pg_atomic_fetch_sub_u32((volatile uint32*)&m_group->m_waitServeSessionCount, 1);
|
||||
//减少等待服务会话的计数,表示该会话已被分配
|
||||
pg_atomic_fetch_add_u32((volatile uint32*)&m_group->m_processTaskCount, 1);
|
||||
//增加正在处理任务的计数,表示工作线程正在处理任务。
|
||||
return true;
|
||||
} else {
|
||||
if (EnableLocalSysCache()) {
|
||||
|
|
@ -271,9 +302,10 @@ bool ThreadPoolListener::TryFeedWorker(ThreadPoolWorker* worker)
|
|||
|
||||
void ThreadPoolListener::AddNewSession(knl_session_context* session)
|
||||
{
|
||||
AddEpoll(session);
|
||||
AddEpoll(session);//将指定的会话 session 添加到 epoll 监听中
|
||||
(void)pg_atomic_fetch_add_u32((volatile uint32*)&m_group->m_sessionCount, 1);
|
||||
ereport(DEBUG2,
|
||||
//使用原子操作增加线程池组的会话计数,表示成功添加了一个新的会话
|
||||
ereport(DEBUG2,
|
||||
(errmodule(MOD_THREAD_POOL),
|
||||
errmsg("This group add a session, and now sessionCount is %d, ",
|
||||
m_group->m_sessionCount)));
|
||||
|
|
@ -298,9 +330,8 @@ void ThreadPoolListener::ReaperAllSession()
|
|||
|
||||
while (m_group->m_sessionCount > 0) {
|
||||
/*
|
||||
* There is a very rare case that all thread pool workers happen to
|
||||
* encounter FATAL and exit before close session.
|
||||
* Under such scenarios, we choose to exit directly.
|
||||
* 在非常罕见的情况下,所有线程池工作者都遇到了致命错误(FATAL)并在关闭会话之前退出。
|
||||
* 在这种情况下,我们选择直接退出程序。
|
||||
*/
|
||||
if (m_group->m_workerNum <= 0 && m_group->m_sessionCount > 0) {
|
||||
ereport(WARNING,
|
||||
|
|
@ -309,8 +340,7 @@ void ThreadPoolListener::ReaperAllSession()
|
|||
" encounter FATAL problems before session close.")));
|
||||
abort();
|
||||
}
|
||||
/* m_sessionCount should be sum of the list length of m_idleSessionList and m_readySessionList
|
||||
and worker's attached session */
|
||||
/* m_sessionCount 应该是 m_idleSessionList 和 m_readySessionList 的列表长度之和,以及与工作线程关联的会话的数量之和 */
|
||||
pg_memory_barrier();
|
||||
if (m_idleSessionList->IsEmpty() && m_readySessionList->IsEmpty() &&
|
||||
m_group->m_workerNum - m_group->m_idleWorkerNum == 0) {
|
||||
|
|
@ -348,10 +378,10 @@ void ThreadPoolListener::WaitTask()
|
|||
while (true) {
|
||||
if (unlikely(m_getKilled)) {
|
||||
m_getKilled = false;
|
||||
proc_exit(0);
|
||||
proc_exit(0); // 如果需要退出,直接退出进程
|
||||
}
|
||||
if (unlikely(m_reaperAllSession)) {
|
||||
ReaperAllSession();
|
||||
ReaperAllSession(); // 如果需要清理所有会话,执行清理操作
|
||||
}
|
||||
|
||||
/* as we specify timeout -1, so 0 will not be return, either > 0 or < 0 */
|
||||
|
|
@ -360,6 +390,8 @@ void ThreadPoolListener::WaitTask()
|
|||
} else {
|
||||
nevents = comm_epoll_wait(m_epollFd, m_epollEvents, GLOBAL_MAX_SESSION_NUM, -1); /* CommProxy Support */
|
||||
}
|
||||
|
||||
// 处理收到的事件
|
||||
if (nevents > 0 && nevents <= GLOBAL_MAX_SESSION_NUM) {
|
||||
HandleConnEvent(nevents);
|
||||
continue;
|
||||
|
|
@ -367,9 +399,9 @@ void ThreadPoolListener::WaitTask()
|
|||
ereport(PANIC,
|
||||
(errmsg("epoll receive %d events which exceed the limitation %d", nevents, GLOBAL_MAX_SESSION_NUM)));
|
||||
} else if (nevents == -1 && errno == EINTR) {
|
||||
continue;
|
||||
continue; // 如果是中断信号,继续等待事件
|
||||
} else {
|
||||
ereport(LOG, (errmsg("listener wait event encounter some error :%d", errno)));
|
||||
ereport(LOG, (errmsg("listener wait event encounter some error :%d", errno))); // 其他错误情况下输出日志
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -384,10 +416,10 @@ void ThreadPoolListener::HandleConnEvent(int nevets)
|
|||
session = GetSessionBaseOnEvent(tmp_event);
|
||||
|
||||
if (session == NULL) {
|
||||
continue;
|
||||
continue; // 如果没有获取到会话,继续处理下一个事件
|
||||
}
|
||||
|
||||
DispatchSession(session);
|
||||
DispatchSession(session); // 处理会话的分派
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,20 +434,18 @@ knl_session_context* ThreadPoolListener::GetSessionBaseOnEvent(struct epoll_even
|
|||
} else {
|
||||
session->status = KNL_SESS_CLOSE;
|
||||
}
|
||||
return session;
|
||||
return session; // 如果发生错误或会话关闭事件,则返回该会话
|
||||
} else if (ev->events & EPOLLIN) {
|
||||
return session;
|
||||
return session; // 如果是输入事件,返回该会话
|
||||
}
|
||||
return NULL;
|
||||
return NULL; // 其他情况下返回 NULL,表示没有需要处理的会话
|
||||
}
|
||||
|
||||
void ThreadPoolListener::DispatchSession(knl_session_context* session)
|
||||
{
|
||||
m_idleSessionList->Remove(&session->elem);
|
||||
/*
|
||||
* If the sock, idx, and streamid parameters of the current session
|
||||
* do not meet the requirements for logical connection parameters,
|
||||
* skip this dispatch operation.
|
||||
* 如果当前会话的 sock、idx 和 streamid 参数不符合逻辑连接参数的要求,则跳过此分派操作。
|
||||
*/
|
||||
if (session->proc_cxt.MyProcPort->sock == NO_SOCKET &&
|
||||
session->proc_cxt.MyProcPort->gs_sock.idx == 0 &&
|
||||
|
|
@ -443,7 +473,7 @@ void ThreadPoolListener::DispatchSession(knl_session_context* session)
|
|||
__func__, session->session_id)));
|
||||
INSTR_TIME_SET_CURRENT(session->last_access_time);
|
||||
|
||||
/* Add new session to the head so the connection request can be quickly processed. */
|
||||
/* 将新会话添加到头部,以便可以快速处理连接请求 */
|
||||
if (session->status == KNL_SESS_UNINIT) {
|
||||
AddIdleSessionToHead(session);
|
||||
} else {
|
||||
|
|
@ -457,10 +487,15 @@ void ThreadPoolListener::DispatchSession(knl_session_context* session)
|
|||
|
||||
void ThreadPoolListener::DelSessionFromEpoll(knl_session_context* session)
|
||||
{
|
||||
//检查是否启用了线程池的逻辑连接支持
|
||||
if (ENABLE_THREAD_POOL_DN_LOGICCONN) {
|
||||
struct epoll_event ev = {0};
|
||||
//创建一个名为 ev 的 epoll 事件结构,并初始化所有字段为零
|
||||
struct epoll_event ev = {0};
|
||||
//设置 epoll 事件的关注事件
|
||||
ev.events = EPOLLRDHUP | EPOLLIN | EPOLLET | EPOLLONESHOT;
|
||||
ev.data.ptr = (void*)session;
|
||||
//将 ev 事件的数据指针设置为指向当前会话 (session) 的指针
|
||||
ev.data.ptr = (void*)session;
|
||||
//使用 CommEpollCtl 函数从 epoll 中删除套接字事件
|
||||
CommEpollCtl(m_epollFd, EPOLL_CTL_DEL, session->proc_cxt.MyProcPort->sock, &ev);
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
} else {
|
||||
|
|
@ -469,6 +504,7 @@ void ThreadPoolListener::DelSessionFromEpoll(knl_session_context* session)
|
|||
#endif
|
||||
comm_epoll_ctl(m_epollFd, EPOLL_CTL_DEL, session->proc_cxt.MyProcPort->sock, NULL);
|
||||
}
|
||||
//使用原子操作将会话计数减少 1
|
||||
(void)pg_atomic_fetch_sub_u32((volatile uint32*)&m_group->m_sessionCount, 1);
|
||||
}
|
||||
|
||||
|
|
@ -479,37 +515,52 @@ void ThreadPoolListener::RemoveWorkerFromList(ThreadPoolWorker* worker)
|
|||
|
||||
bool ThreadPoolListener::GetSessIshang(instr_time* current_time, uint64* sessionId)
|
||||
{
|
||||
// 初始化 ishang 为 true
|
||||
bool ishang = true;
|
||||
|
||||
// 获取就绪会话列表的锁
|
||||
m_readySessionList->GetLock();
|
||||
|
||||
// 获取就绪会话列表的头元素
|
||||
Dlelem* elem = m_readySessionList->GetHead();
|
||||
|
||||
// 如果头元素为空,释放锁并返回 false
|
||||
if (elem == NULL) {
|
||||
m_readySessionList->ReleaseLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将头元素转换为 knl_session_context 指针
|
||||
knl_session_context* head_sess = (knl_session_context *)(elem->dle_val);
|
||||
|
||||
// 检查就绪会话的时间戳和会话ID是否与传入的值匹配
|
||||
if (INSTR_TIME_GET_MICROSEC(head_sess->last_access_time) == INSTR_TIME_GET_MICROSEC(*current_time) &&
|
||||
head_sess->session_id == *sessionId) {
|
||||
ishang = true;
|
||||
ishang = true; // 如果匹配,ishang 保持 true
|
||||
} else {
|
||||
// 更新传入的时间戳和会话ID
|
||||
*current_time = head_sess->last_access_time;
|
||||
*sessionId = head_sess->session_id;
|
||||
ishang = false;
|
||||
ishang = false; // ishang 设为 false 表示不再挂起状态
|
||||
}
|
||||
|
||||
// 释放就绪会话列表的锁
|
||||
m_readySessionList->ReleaseLock();
|
||||
|
||||
// 返回 ishang,指示监听器是否挂起
|
||||
return ishang;
|
||||
}
|
||||
|
||||
Dlelem *ThreadPoolListener::GetFreeWorker(knl_session_context* session)
|
||||
{
|
||||
/* only lite mode need find right threadworker,
|
||||
* otherwise since there are so many requests, we dont have any freeworkers. so optimization is not necessary */
|
||||
/* 只有在 "轻量级模式" 下才需要找到正确的线程工作线程,否则由于有如此
|
||||
多的请求,我们没有任何空闲工作线程,因此不需要进行优化。*/
|
||||
#ifdef ENABLE_LITE_MODE
|
||||
if (!EnableLocalSysCache()) {
|
||||
return m_freeWorkerList->RemoveHead();
|
||||
}
|
||||
|
||||
/* sess is not init, we dont know how to hit the cache */
|
||||
/* 如果会话未初始化,我们不知道如何命中缓存 */
|
||||
if (session->status != KNL_SESS_ATTACH && session->status != KNL_SESS_DETACH) {
|
||||
return m_freeWorkerList->RemoveTail();
|
||||
}
|
||||
|
|
@ -518,16 +569,16 @@ Dlelem *ThreadPoolListener::GetFreeWorker(knl_session_context* session)
|
|||
return m_freeWorkerList->RemoveTail();
|
||||
}
|
||||
|
||||
/* for lite_mode, threadworkers are a small amount, so it is quickly to traverse the list */
|
||||
/* 对于轻量级模式,线程工作者数量较少,因此迅速遍历列表是可行的。 */
|
||||
m_freeWorkerList->GetLock();
|
||||
for (Dlelem *elt = m_freeWorkerList->GetHead(); elt != NULL; elt = DLGetSucc(elt)) {
|
||||
ThreadPoolWorker *worker = (ThreadPoolWorker *)DLE_VAL(elt);
|
||||
LocalSysDBCache *lsc = worker->GetThreadContextPtr()->lsc_cxt.lsc;
|
||||
/* uninited lsc are addtotail of the list, so when see one uninited, the follow all are uninited. just break */
|
||||
/* 未初始化的本地系统缓存(lsc)被添加到列表的末尾,因此当遇到一个未初始化的时候,后续的所有也都是未初始化的。因此可以直接中断(break)。 */
|
||||
if (unlikely(lsc == NULL || lsc->my_database_id == InvalidOid)) {
|
||||
break;
|
||||
}
|
||||
/* cache hit */
|
||||
/* 缓存命中 */
|
||||
if (likely(lsc->my_database_id == session->proc_cxt.MyDatabaseId)) {
|
||||
m_freeWorkerList->Remove(elt);
|
||||
m_freeWorkerList->ReleaseLock();
|
||||
|
|
@ -535,7 +586,8 @@ Dlelem *ThreadPoolListener::GetFreeWorker(knl_session_context* session)
|
|||
}
|
||||
}
|
||||
m_freeWorkerList->ReleaseLock();
|
||||
/* dont find, use tail instead head, because head of the list has syscache of other db */
|
||||
/* 建议不要从链表的头部查找,而是从尾部开始查找,因为链表的头部可能包含了
|
||||
来自其他数据库的系统缓存(syscache)数据 */
|
||||
return m_freeWorkerList->RemoveTail();
|
||||
#else
|
||||
return m_freeWorkerList->RemoveHead();
|
||||
|
|
@ -544,12 +596,12 @@ Dlelem *ThreadPoolListener::GetFreeWorker(knl_session_context* session)
|
|||
|
||||
static Dlelem *GetHeadUnInitSession(DllistWithLock* m_readySessionList)
|
||||
{
|
||||
/* uninit session needs be replied first */
|
||||
/* 未初始化的会话应该首先得到回复 */
|
||||
m_readySessionList->GetLock();
|
||||
Dlelem *head = m_readySessionList->GetHead();
|
||||
if (likely(head != NULL)) {
|
||||
if (((knl_session_context *)DLE_VAL(head))->status != KNL_SESS_UNINIT) {
|
||||
/* go cache hit branch, set it null */
|
||||
/* 程序在执行过程中进入了“缓存命中分支”,并且将某个值设置为了 null */
|
||||
head = NULL;
|
||||
} else {
|
||||
head = m_readySessionList->RemoveHeadNoLock();
|
||||
|
|
@ -574,11 +626,11 @@ Dlelem *ThreadPoolListener::GetSessFromReadySessionList(ThreadPoolWorker *worker
|
|||
break;
|
||||
}
|
||||
LocalSysDBCache *lsc = worker->GetThreadContextPtr()->lsc_cxt.lsc;
|
||||
// worker not init, any session is matched
|
||||
// 如果工作线程尚未初始化,那么任何会话都不会被匹配或关联
|
||||
if (unlikely(lsc == NULL || lsc->my_database_id == InvalidOid)) {
|
||||
break;
|
||||
}
|
||||
// now we try to reuse workers syscache
|
||||
// 现在我们尝试重用工作线程的系统缓存
|
||||
Index hash_index = HASH_INDEX(lsc->my_database_id, (uint32)m_session_nbucket);
|
||||
ResourceOwner owner = LOCAL_SYSDB_RESOWNER;
|
||||
PthreadRWlockRdlock(owner, &m_session_rw_locks[hash_index]);
|
||||
|
|
@ -588,7 +640,7 @@ Dlelem *ThreadPoolListener::GetSessFromReadySessionList(ThreadPoolWorker *worker
|
|||
break;
|
||||
}
|
||||
if (!m_readySessionList->RemoveConfirm(&((knl_session_context *)DLE_VAL(elt))->elem)) {
|
||||
// someone remove it already
|
||||
// 已经将他移除了
|
||||
PthreadRWlockUnlock(owner, &m_session_rw_locks[hash_index]);
|
||||
break;
|
||||
}
|
||||
|
|
@ -606,14 +658,17 @@ Dlelem *ThreadPoolListener::GetReadySession(ThreadPoolWorker *worker)
|
|||
if (!EnableLocalSysCache()) {
|
||||
return m_readySessionList->RemoveHead();
|
||||
}
|
||||
// 如果不启用本地系统缓存,直接从就绪会话列表的头部移除并返回一个会话。
|
||||
Dlelem *elt = GetSessFromReadySessionList(worker);
|
||||
if (elt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 从本地系统缓存中获取一个会话。
|
||||
knl_session_context *session = (knl_session_context *)DLE_VAL(elt);
|
||||
Oid cur_dbid = session->proc_cxt.MyDatabaseId;
|
||||
Index hash_index = HASH_INDEX(cur_dbid, (uint32)m_session_nbucket);
|
||||
ResourceOwner owner = LOCAL_SYSDB_RESOWNER;
|
||||
// 获取本地系统缓存的资源锁,并从就绪会话列表中移除该会话。
|
||||
PthreadRWlockWrlock(owner, &m_session_rw_locks[hash_index]);
|
||||
DLRemove(&session->elem2);
|
||||
PthreadRWlockUnlock(owner, &m_session_rw_locks[hash_index]);
|
||||
|
|
@ -626,8 +681,10 @@ void ThreadPoolListener::AddIdleSessionToTail(knl_session_context* session)
|
|||
m_readySessionList->AddTail(&session->elem);
|
||||
return;
|
||||
}
|
||||
// 如果不启用本地系统缓存,将会话添加到就绪会话列表的尾部。
|
||||
Index hash_index = HASH_INDEX(session->proc_cxt.MyDatabaseId, (uint32)m_session_nbucket);
|
||||
ResourceOwner owner = LOCAL_SYSDB_RESOWNER;
|
||||
// 获取本地系统缓存的资源锁,并将会话添加到本地系统缓存和就绪会话列表的尾部。
|
||||
PthreadRWlockWrlock(owner, &m_session_rw_locks[hash_index]);
|
||||
DLAddTail(&m_session_bucket[hash_index], &session->elem2);
|
||||
PthreadRWlockUnlock(owner, &m_session_rw_locks[hash_index]);
|
||||
|
|
@ -640,10 +697,12 @@ void ThreadPoolListener::AddIdleSessionToHead(knl_session_context* session)
|
|||
m_readySessionList->AddHead(&session->elem);
|
||||
return;
|
||||
}
|
||||
// 如果不启用本地系统缓存,将会话添加到就绪会话列表的头部。
|
||||
Index hash_index = HASH_INDEX(session->proc_cxt.MyDatabaseId, (uint32)m_session_nbucket);
|
||||
ResourceOwner owner = LOCAL_SYSDB_RESOWNER;
|
||||
// 获取本地系统缓存的资源锁,并将会话添加到本地系统缓存和就绪会话列表的头部。
|
||||
PthreadRWlockWrlock(owner, &m_session_rw_locks[hash_index]);
|
||||
DLAddHead(&m_session_bucket[hash_index], &session->elem2);
|
||||
PthreadRWlockUnlock(owner, &m_session_rw_locks[hash_index]);
|
||||
m_readySessionList->AddHead(&session->elem);
|
||||
}
|
||||
}
|
||||
|
|
@ -42,35 +42,40 @@
|
|||
#include "utils/guc.h"
|
||||
#include "replication/syncrep.h"
|
||||
|
||||
#define SCHEDULER_TIME_UNIT 1000000 //us
|
||||
// 定义常量,表示时间单位为微秒
|
||||
#define SCHEDULER_TIME_UNIT 1000000 //微秒
|
||||
#define ENLARGE_THREAD_TIME 5
|
||||
#define MAX_HANG_TIME 100
|
||||
#define REDUCE_THREAD_TIME 100
|
||||
#define SHUTDOWN_THREAD_TIME 1000
|
||||
#define GPC_CLEAN_TIME 300
|
||||
|
||||
// 定义处理SIGKILL信号的函数
|
||||
static void SchedulerSIGKILLHandler(SIGNAL_ARGS)
|
||||
{
|
||||
t_thrd.threadpool_cxt.scheduler->m_getKilled = true;
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的SigHupHandler方法,用于处理SIGHUP信号
|
||||
void ThreadPoolScheduler::SigHupHandler()
|
||||
{
|
||||
m_getSIGHUP = true;
|
||||
}
|
||||
|
||||
// 定义reloadConfigFileIfNecessary函数,如果需要的话重新加载配置文件
|
||||
static void reloadConfigFileIfNecessary()
|
||||
{
|
||||
if (unlikely(t_thrd.threadpool_cxt.scheduler->m_getSIGHUP)) {
|
||||
t_thrd.threadpool_cxt.scheduler->m_getSIGHUP = false;
|
||||
ProcessConfigFile(PGC_SIGHUP);
|
||||
/* Update most_available_sync if it's modified dynamically. */
|
||||
most_available_sync = (volatile bool) u_sess->attr.attr_storage.guc_most_available_sync;
|
||||
/* 如果动态修改了most_available_sync,则更新它。 */
|
||||
most_available_sync = (volatile bool)u_sess->attr.attr_storage.guc_most_available_sync;
|
||||
SyncRepUpdateSyncStandbysDefined();
|
||||
}
|
||||
}
|
||||
|
||||
void TpoolSchedulerMain(ThreadPoolScheduler *scheduler)
|
||||
// 定义TpoolSchedulerMain函数,这是线程池调度器的主要执行函数
|
||||
void TpoolSchedulerMain(ThreadPoolScheduler* scheduler)
|
||||
{
|
||||
int gpc_count = 0;
|
||||
|
||||
|
|
@ -99,18 +104,20 @@ void TpoolSchedulerMain(ThreadPoolScheduler *scheduler)
|
|||
proc_exit(0);
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的构造函数,接受线程池组数量和线程池组数组作为参数
|
||||
ThreadPoolScheduler::ThreadPoolScheduler(int groupNum, ThreadPoolGroup** groups)
|
||||
:m_groupNum(groupNum), m_groups(groups), m_has_shutdown(false)
|
||||
: m_groupNum(groupNum), m_groups(groups), m_has_shutdown(false)
|
||||
{
|
||||
m_tid = 0;
|
||||
m_hangTestCount = (uint *)palloc0(sizeof(uint) * groupNum);
|
||||
m_freeTestCount = (uint *)palloc0(sizeof(uint) * groupNum);
|
||||
m_freeStreamCount = (uint *)palloc0(sizeof(uint) * groupNum);
|
||||
m_hangTestCount = (uint*)palloc0(sizeof(uint) * groupNum);
|
||||
m_freeTestCount = (uint*)palloc0(sizeof(uint) * groupNum);
|
||||
m_freeStreamCount = (uint*)palloc0(sizeof(uint) * groupNum);
|
||||
m_gpcContext = NULL;
|
||||
m_getSIGHUP = false;
|
||||
m_canAdjustPool = true;
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的析构函数
|
||||
ThreadPoolScheduler::~ThreadPoolScheduler()
|
||||
{
|
||||
m_groups = NULL;
|
||||
|
|
@ -119,12 +126,14 @@ ThreadPoolScheduler::~ThreadPoolScheduler()
|
|||
m_freeTestCount = NULL;
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的StartUp方法,用于初始化调度器并启动其主循环
|
||||
int ThreadPoolScheduler::StartUp()
|
||||
{
|
||||
m_tid = initialize_util_thread(THREADPOOL_SCHEDULER, (void*)this);
|
||||
return ((m_tid == 0) ? STATUS_ERROR : STATUS_OK);
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的DynamicAdjustThreadPool方法,用于动态调整线程池
|
||||
void ThreadPoolScheduler::DynamicAdjustThreadPool()
|
||||
{
|
||||
for (int i = 0; i < m_groupNum; i++) {
|
||||
|
|
@ -135,6 +144,7 @@ void ThreadPoolScheduler::DynamicAdjustThreadPool()
|
|||
}
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的GPCScheduleCleaner方法,用于清理全局计划缓存(GPC)条目
|
||||
void ThreadPoolScheduler::GPCScheduleCleaner(int* gpc_count)
|
||||
{
|
||||
if (ENABLE_GPC && *gpc_count == GPC_CLEAN_TIME) {
|
||||
|
|
@ -152,16 +162,18 @@ void ThreadPoolScheduler::GPCScheduleCleaner(int* gpc_count)
|
|||
(*gpc_count)++;
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的ShutDown方法,用于发送SIGKILL信号以关闭线程池调度器
|
||||
void ThreadPoolScheduler::ShutDown() const
|
||||
{
|
||||
if (m_tid != 0)
|
||||
gs_signal_send(m_tid, SIGKILL);
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的AdjustWorkerPool方法,用于调整工作线程池
|
||||
void ThreadPoolScheduler::AdjustWorkerPool(int idx)
|
||||
{
|
||||
ThreadPoolGroup* group = m_groups[idx];
|
||||
/* When no idle worker and no task has been processed, the system may hang. */
|
||||
/* 当没有空闲工作线程且没有任务被处理时,系统可能会挂起。 */
|
||||
if (group->IsGroupHang()) {
|
||||
m_hangTestCount[idx]++;
|
||||
m_freeTestCount[idx] = 0;
|
||||
|
|
@ -174,56 +186,5 @@ void ThreadPoolScheduler::AdjustWorkerPool(int idx)
|
|||
}
|
||||
}
|
||||
|
||||
void ThreadPoolScheduler::AdjustStreamPool(int idx)
|
||||
{
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
ThreadPoolGroup* group = m_groups[idx];
|
||||
|
||||
if (group->HasFreeStream()) {
|
||||
m_freeStreamCount[idx]++;
|
||||
if (m_freeStreamCount[idx] == SHUTDOWN_THREAD_TIME) {
|
||||
group->ReduceStreams();
|
||||
m_freeStreamCount[idx] = 0;
|
||||
}
|
||||
} else {
|
||||
m_freeStreamCount[idx] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadPoolScheduler::EnlargeWorkerIfNecessage(int groupIdx)
|
||||
{
|
||||
ThreadPoolGroup *group = m_groups[groupIdx];
|
||||
if (m_hangTestCount[groupIdx] >= ENLARGE_THREAD_TIME && m_hangTestCount[groupIdx] < MAX_HANG_TIME) {
|
||||
if (group->EnlargeWorkers(THREAD_SCHEDULER_STEP)) {
|
||||
m_hangTestCount[groupIdx] = 0;
|
||||
}
|
||||
} else if (m_hangTestCount[groupIdx] == MAX_HANG_TIME) {
|
||||
elog(WARNING, "[SCHEDULER] Detect the system has hang %d seconds, "
|
||||
"and the thread num in pool exceed maximum, "
|
||||
"so we need to close all new sessions.", MAX_HANG_TIME);
|
||||
/* set flag for don't accept new session */
|
||||
group->SetGroupHanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPoolScheduler::ReduceWorkerIfNecessary(int groupIdx)
|
||||
{
|
||||
ThreadPoolGroup *group = m_groups[groupIdx];
|
||||
|
||||
if (group->m_expectWorkerNum == group->m_defaultWorkerNum &&
|
||||
group->m_pendingWorkerNum == 0) {
|
||||
m_freeTestCount[groupIdx] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_freeTestCount[groupIdx] % REDUCE_THREAD_TIME == 0) {
|
||||
group->ReduceWorkers(THREAD_SCHEDULER_STEP);
|
||||
}
|
||||
|
||||
if (m_freeTestCount[groupIdx] == SHUTDOWN_THREAD_TIME) {
|
||||
group->ShutDownPendingWorkers();
|
||||
m_freeTestCount[groupIdx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义ThreadPoolScheduler类的AdjustStreamPool方法,用于调整流线程池
|
||||
void ThreadPoolScheduler::AdjustStreamPool(int
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,7 +26,6 @@
|
|||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "distributelayer/streamMain.h"
|
||||
#include "distributelayer/streamProducer.h"
|
||||
#include "executor/executor.h"
|
||||
|
|
@ -37,8 +36,10 @@
|
|||
#include "utils/guc.h"
|
||||
#include "utils/postinit.h"
|
||||
|
||||
// 重置流状态的静态函数
|
||||
static void ResetStreamStatus();
|
||||
|
||||
// 线程池流的构造函数
|
||||
ThreadPoolStream::ThreadPoolStream()
|
||||
{
|
||||
m_tid = InvalidTid;
|
||||
|
|
@ -51,10 +52,12 @@ ThreadPoolStream::ThreadPoolStream()
|
|||
m_threadStatus = THREAD_UNINIT;
|
||||
}
|
||||
|
||||
// 线程池流的析构函数
|
||||
ThreadPoolStream::~ThreadPoolStream()
|
||||
{
|
||||
}
|
||||
|
||||
// 启动线程池流
|
||||
int ThreadPoolStream::StartUp(int idx, StreamProducer* producer, ThreadPoolGroup* group,
|
||||
pthread_mutex_t* mutex, pthread_cond_t* cond)
|
||||
{
|
||||
|
|
@ -73,6 +76,7 @@ int ThreadPoolStream::StartUp(int idx, StreamProducer* producer, ThreadPoolGroup
|
|||
return m_tid;
|
||||
}
|
||||
|
||||
// 等待任务
|
||||
void ThreadPoolStream::WaitMission()
|
||||
{
|
||||
PreventSignal();
|
||||
|
|
@ -95,6 +99,7 @@ void ThreadPoolStream::WaitMission()
|
|||
AllowSignal();
|
||||
}
|
||||
|
||||
// 唤醒线程池流以开始工作
|
||||
void ThreadPoolStream::WakeUpToWork(StreamProducer* producer)
|
||||
{
|
||||
pthread_mutex_lock(m_mutex);
|
||||
|
|
@ -103,6 +108,7 @@ void ThreadPoolStream::WakeUpToWork(StreamProducer* producer)
|
|||
pthread_mutex_unlock(m_mutex);
|
||||
}
|
||||
|
||||
// 唤醒线程池流以更新线程状态
|
||||
void ThreadPoolStream::WakeUpToUpdate(ThreadStatus status)
|
||||
{
|
||||
pthread_mutex_lock(m_mutex);
|
||||
|
|
@ -111,6 +117,7 @@ void ThreadPoolStream::WakeUpToUpdate(ThreadStatus status)
|
|||
pthread_mutex_unlock(m_mutex);
|
||||
}
|
||||
|
||||
// 初始化流
|
||||
void ThreadPoolStream::InitStream()
|
||||
{
|
||||
knl_session_context* sc =
|
||||
|
|
@ -122,7 +129,7 @@ void ThreadPoolStream::InitStream()
|
|||
u_sess = sc;
|
||||
SelfMemoryContext = u_sess->self_mem_cxt;
|
||||
|
||||
/* Switch context to Session context. */
|
||||
/* 切换上下文到会话上下文 */
|
||||
AutoContextSwitch memSwitch(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR));
|
||||
|
||||
SetStreamWorkerInfo(m_producer);
|
||||
|
|
@ -133,17 +140,17 @@ void ThreadPoolStream::InitStream()
|
|||
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
/* Init GUC option for this session. */
|
||||
/* 为此会话初始化GUC选项 */
|
||||
InitializeGUCOptions();
|
||||
/* Read in remaining GUC variables */
|
||||
/* 读取剩余的GUC变量 */
|
||||
read_nondefault_variables();
|
||||
|
||||
/* Do local initialization of file, storage and buffer managers */
|
||||
/* 执行文件、存储和缓冲管理器的本地初始化 */
|
||||
ReBuildLSC();
|
||||
InitFileAccess();
|
||||
smgrinit();
|
||||
|
||||
/* Init Stream thread user and database */
|
||||
/* 初始化流线程的用户和数据库 */
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(
|
||||
u_sess->stream_cxt.producer_obj->getDbName(), InvalidOid, u_sess->stream_cxt.producer_obj->getUserName());
|
||||
t_thrd.proc_cxt.PostInit->InitStreamSession();
|
||||
|
|
@ -162,6 +169,7 @@ void ThreadPoolStream::InitStream()
|
|||
m_producer->getKey().smpIdentifier);
|
||||
}
|
||||
|
||||
// 清理流
|
||||
void ThreadPoolStream::CleanUp()
|
||||
{
|
||||
m_producer = NULL;
|
||||
|
|
@ -171,15 +179,17 @@ void ThreadPoolStream::CleanUp()
|
|||
m_group->ReturnStreamToPool(&m_elem);
|
||||
}
|
||||
|
||||
// 关闭流
|
||||
void ThreadPoolStream::ShutDown()
|
||||
{
|
||||
m_producer = NULL;
|
||||
m_group->RemoveStreamFromPool(&m_elem, m_idx);
|
||||
}
|
||||
|
||||
// 重置流状态
|
||||
static void ResetStreamStatus()
|
||||
{
|
||||
/* Add the pg_delete_audit operation to audit log */
|
||||
/* 添加pg_delete_audit操作到审计日志 */
|
||||
t_thrd.audit.Audit_delete = false;
|
||||
t_thrd.postgres_cxt.debug_query_string = NULL;
|
||||
t_thrd.postgres_cxt.g_NoAnalyzeRelNameList = NIL;
|
||||
|
|
@ -193,8 +203,7 @@ static void ResetStreamStatus()
|
|||
}
|
||||
|
||||
/*
|
||||
* Reset extended-query-message flag, so that any errors
|
||||
* encountered in "idle" state don't provoke skip.
|
||||
* 重置extended-query-message标志,以防止在“空闲”状态下遇到的任何错误引发跳过。
|
||||
*/
|
||||
u_sess->postgres_cxt.doing_extended_query_message = false;
|
||||
u_sess->debug_query_id = 0;
|
||||
|
|
@ -202,23 +211,30 @@ static void ResetStreamStatus()
|
|||
u_sess->misc_cxt.AuthenticatedUserId = InvalidOid;
|
||||
u_sess->analyze_cxt.is_under_analyze = false;
|
||||
|
||||
/* We don't have a transaction command open anymore */
|
||||
/* 不再存在事务命令 */
|
||||
t_thrd.postgres_cxt.xact_started = false;
|
||||
/*
|
||||
* Reset top transaction in case we have received parent transaction
|
||||
* from main worker thread, which has already been release.
|
||||
* 重置顶级事务状态,以防我们已经接收到主工作线程中已释放的父事务。
|
||||
*/
|
||||
InitTopTransactionState();
|
||||
InitCurrentTransactionState();
|
||||
|
||||
if (!IS_PGSTATE_TRACK_UNDEFINE) {
|
||||
volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry;
|
||||
beentry->st_queryid = 0;
|
||||
pgstat_report_unique_sql_id(true);
|
||||
beentry->st_sessionid = 0;
|
||||
beentry->st_parent_sessionid = 0;
|
||||
beentry->st_thread_level = 0;
|
||||
beentry->st_smpid = 0;
|
||||
}
|
||||
// 如果不是未定义的状态跟踪(PGSTATE_TRACK_UNDEFINE是一个宏,可能表示状态跟踪是否已启用)
|
||||
if (!IS_PGSTATE_TRACK_UNDEFINE) {
|
||||
// 获取当前线程的后端状态(用volatile修饰,表示它可能在任何时候被更改)
|
||||
volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry;
|
||||
// 将查询ID重置为0
|
||||
beentry->st_queryid = 0;
|
||||
// 报告唯一SQL ID,参数为true表示清除先前的SQL ID
|
||||
pgstat_report_unique_sql_id(true);
|
||||
// 重置会话ID为0
|
||||
beentry->st_sessionid = 0;
|
||||
// 重置父会话ID为0
|
||||
beentry->st_parent_sessionid = 0;
|
||||
// 重置线程级别为0
|
||||
beentry->st_thread_level = 0;
|
||||
// 重置SMP(Symmetric Multi-Processing) ID为0
|
||||
beentry->st_smpid = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,64 +75,95 @@ static void SendSessionIdxToClient();
|
|||
static void ResetSignalHandle();
|
||||
static void SessionSetBackendOptions();
|
||||
|
||||
// 线程池工作线程的构造函数,初始化各个成员变量
|
||||
ThreadPoolWorker::ThreadPoolWorker(uint idx, ThreadPoolGroup* group, pthread_mutex_t* mutex, pthread_cond_t* cond)
|
||||
{
|
||||
// 工作线程的索引
|
||||
m_idx = idx;
|
||||
// 所属线程池组
|
||||
m_group = group;
|
||||
// 线程ID初始化为无效值
|
||||
m_tid = InvalidTid;
|
||||
// 线程状态初始化为未初始化
|
||||
m_threadStatus = THREAD_UNINIT;
|
||||
// 当前会话初始化为NULL
|
||||
m_currentSession = NULL;
|
||||
// 互斥锁和条件变量用于线程同步
|
||||
m_mutex = mutex;
|
||||
m_cond = cond;
|
||||
// 等待状态初始化为未定义
|
||||
m_waitState = STATE_WAIT_UNDEFINED;
|
||||
// 初始化双向链表元素
|
||||
DLInitElem(&m_elem, this);
|
||||
// 设置线程的私有数据指针为全局线程私有数据
|
||||
m_thrd = &t_thrd;
|
||||
}
|
||||
|
||||
// 线程池工作线程的析构函数,清理资源
|
||||
ThreadPoolWorker::~ThreadPoolWorker()
|
||||
{
|
||||
// 释放资源,将成员变量置为NULL
|
||||
m_currentSession = NULL;
|
||||
m_group = NULL;
|
||||
m_mutex = NULL;
|
||||
m_cond = NULL;
|
||||
}
|
||||
|
||||
// 关闭工作线程的函数
|
||||
void ThreadPoolWorker::ShutDown()
|
||||
{
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 设置线程状态为退出
|
||||
m_threadStatus = THREAD_EXIT;
|
||||
// 清理当前会话的资源
|
||||
CleanUpSession(true);
|
||||
/* Remove the worker if it is in the free worker list. */
|
||||
/* 如果工作线程在空闲线程列表中,将其移除 */
|
||||
m_group->GetListener()->RemoveWorkerFromList(this);
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
// 释放工作线程占用的槽位
|
||||
m_group->ReleaseWorkerSlot(m_idx);
|
||||
}
|
||||
|
||||
// 通知工作线程准备就绪的函数
|
||||
void ThreadPoolWorker::NotifyReady()
|
||||
{
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 如果线程状态是退出,则保持为退出状态,否则设置为运行状态
|
||||
m_threadStatus = (m_threadStatus == THREAD_EXIT) ? THREAD_EXIT : THREAD_RUN;
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
}
|
||||
|
||||
// 启动工作线程的函数
|
||||
int ThreadPoolWorker::StartUp()
|
||||
{
|
||||
Port port;
|
||||
// 初始化端口结构体,并设置可以接受连接
|
||||
int ss_rc = memset_s(&port, sizeof(port), 0, sizeof(port));
|
||||
securec_check(ss_rc, "\0", "\0");
|
||||
|
||||
port.canAcceptConnections = CAC_OK;
|
||||
port.sock = PGINVALID_SOCKET;
|
||||
port.gs_sock = GS_INVALID_GSOCK;
|
||||
/* Calculate cancel key which will be assigned to backend. */
|
||||
|
||||
/* 计算将分配给后端的取消键值。 */
|
||||
GenerateCancelKey(false);
|
||||
// 分配后端槽位
|
||||
t_thrd.proc_cxt.MyPMChildSlot = AssignPostmasterChildSlot();
|
||||
if (t_thrd.proc_cxt.MyPMChildSlot == -1) {
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
// 创建后端进程
|
||||
Backend* bn = CreateBackend();
|
||||
// 初始化工作线程并获取线程ID
|
||||
m_tid = initialize_worker_thread(THREADPOOL_WORKER, &port, (void*)this);
|
||||
// 重置槽位
|
||||
t_thrd.proc_cxt.MyPMChildSlot = 0;
|
||||
|
||||
// 如果线程ID无效,释放槽位,返回错误状态
|
||||
if (m_tid == InvalidTid) {
|
||||
ReleasePostmasterChildSlot(bn->child_slot);
|
||||
bn->pid = 0;
|
||||
|
|
@ -140,9 +171,11 @@ int ThreadPoolWorker::StartUp()
|
|||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
// 设置后端的进程ID和角色
|
||||
bn->pid = m_tid;
|
||||
bn->role = THREADPOOL_WORKER;
|
||||
Assert(bn->child_slot != 0);
|
||||
// 添加后端到全局后端列表
|
||||
AddBackend(bn);
|
||||
|
||||
return STATUS_OK;
|
||||
|
|
@ -159,13 +192,13 @@ void PreventSignal()
|
|||
void AllowSignal()
|
||||
{
|
||||
t_thrd.int_cxt.ignoreBackendSignal = false;
|
||||
/* now we can accept signal. out of this, we rely on signal handle. */
|
||||
/* 现在我们可以接收信号了,为此,我们依赖于信号句柄。 */
|
||||
RESUME_INTERRUPTS();
|
||||
}
|
||||
|
||||
void ThreadPoolWorker::WaitMission()
|
||||
{
|
||||
/* Return if we still in a transaction block. */
|
||||
/* 如果仍在事务块中,则返回 */
|
||||
if (!WorkerThreadCanSeekAnotherMission(&m_reason)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -180,22 +213,22 @@ void ThreadPoolWorker::WaitMission()
|
|||
errmsg("InterruptHoldoffCount should be zero when get next session.")));
|
||||
}
|
||||
/*
|
||||
* prevent any signal execep siguit.
|
||||
* reset any pending signal and timer.
|
||||
* before we serve next session we must keep us clean.
|
||||
防止任何信号,除了信号。
|
||||
复位任何挂起的信号和定时器。
|
||||
在我们下次发球前,我们必须保持干净
|
||||
*/
|
||||
PreventSignal();
|
||||
|
||||
MemoryContext old = CurrentMemoryContext;
|
||||
while (true) {
|
||||
/* we should keep the thread clean for next Session. */
|
||||
/* 我们应该为下一个会话保持线程干净. */
|
||||
CleanThread();
|
||||
/* Get next session. */
|
||||
/* 获取下一个会话. */
|
||||
WaitNextSession();
|
||||
Assert(m_currentSession != NULL);
|
||||
isRawSession = (m_currentSession->status == KNL_SESS_UNINIT);
|
||||
/* do the binding process ,binding the connection and thread */
|
||||
/* return to worker pool if binding fail. */
|
||||
/* 做绑扎过程,绑扎的连接和线程 */
|
||||
/* 如果绑定失败,返回工作池 */
|
||||
if (AttachSessionToThread()) {
|
||||
if (isRawSession) {
|
||||
if (t_thrd.libpq_cxt.PqRecvPointer == t_thrd.libpq_cxt.PqRecvLength) {
|
||||
|
|
@ -213,7 +246,7 @@ void ThreadPoolWorker::WaitMission()
|
|||
/*
|
||||
* CommProxy Support
|
||||
*
|
||||
* session attach thread success, we record relation of sock with worker
|
||||
* 会话连接线程成功,记录sock与worker的关系
|
||||
*/
|
||||
if (AmIProxyModeSockfd(m_currentSession->proc_cxt.MyProcPort->sock)) {
|
||||
g_comm_controller->SetCommSockActive(m_currentSession->proc_cxt.MyProcPort->sock, m_idx);
|
||||
|
|
@ -226,64 +259,83 @@ void ThreadPoolWorker::WaitMission()
|
|||
}
|
||||
MemoryContextSwitchTo(old);
|
||||
(void)disable_session_sig_alarm();
|
||||
/* now we can accept signal. out of this, we rely on signal handle. */
|
||||
/* 现在我们可以接收信号了。为此,我们依赖于信号句柄。 */
|
||||
AllowSignal();
|
||||
ShutDownIfNecessary();
|
||||
}
|
||||
|
||||
// 唤醒工作线程来处理特定会话的函数
|
||||
bool ThreadPoolWorker::WakeUpToWork(knl_session_context* session)
|
||||
{
|
||||
bool succ = true;
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 如果线程状态不是退出或挂起
|
||||
if (likely(m_threadStatus != THREAD_EXIT && m_threadStatus != THREAD_PENDING)) {
|
||||
// 设置当前会话
|
||||
m_currentSession = session;
|
||||
// 发送条件信号,唤醒线程处理会话
|
||||
pthread_cond_signal(m_cond);
|
||||
} else {
|
||||
// 如果线程状态是退出或挂起,返回失败
|
||||
succ = false;
|
||||
}
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
return succ;
|
||||
}
|
||||
|
||||
// 唤醒工作线程来更新线程状态的函数
|
||||
void ThreadPoolWorker::WakeUpToUpdate(ThreadStatus status)
|
||||
{
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 如果线程状态不是退出
|
||||
if (m_threadStatus != THREAD_EXIT) {
|
||||
// 更新线程状态
|
||||
m_threadStatus = status;
|
||||
// 发送条件信号,唤醒线程更新状态
|
||||
pthread_cond_signal(m_cond);
|
||||
}
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
}
|
||||
|
||||
// 唤醒工作线程来挂起线程(如果线程是自由的)的函数
|
||||
bool ThreadPoolWorker::WakeUpToPendingIfFree()
|
||||
{
|
||||
bool ans = false;
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 如果线程状态不是退出、挂起,并且当前会话为空
|
||||
if (m_threadStatus != THREAD_EXIT && m_threadStatus != THREAD_PENDING && m_currentSession == NULL) {
|
||||
// 设置线程状态为挂起
|
||||
m_threadStatus = THREAD_PENDING;
|
||||
// 发送条件信号,唤醒线程挂起
|
||||
pthread_cond_signal(m_cond);
|
||||
ans = true;
|
||||
ans = true; // 返回成功
|
||||
} else {
|
||||
ans = false;
|
||||
ans = false; // 返回失败
|
||||
}
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some variable are session level, however they are used by some opensource
|
||||
* component like postgis, we can not move them to knl_session_context directly.
|
||||
* To solve this problem, providing two interface: RestoreThreadVariable and
|
||||
* SaveThreadVariable.
|
||||
有些变量是会话级别的,但是它们被一些开源程序使用
|
||||
像postgis这样的组件,我们不能直接将它们移动到knl_session_context。
|
||||
为了解决这个问题,提供了两个接口:RestoreThreadVariable和
|
||||
SaveThreadVariable
|
||||
*/
|
||||
void ThreadPoolWorker::RestoreThreadVariable()
|
||||
{
|
||||
Assert(m_currentSession != NULL);
|
||||
|
||||
/* use values in session to set local thread GUC */
|
||||
/* 使用会话中的值来设置本地线程GUC */
|
||||
SetThreadLocalGUC(m_currentSession);
|
||||
|
||||
/* use values in session to set other thread local variables */
|
||||
/* 使用会话中的值来设置其他线程局部变量 */
|
||||
pg_reset_srand48(m_currentSession->rand_cxt.rand48_seed);
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +343,7 @@ void ThreadPoolWorker::RestoreLocaleInfo()
|
|||
{
|
||||
if (strcmp(NameStr(m_currentSession->mb_cxt.datcollate), NameStr(t_thrd.port_cxt.cur_datcollate)) == 0 &&
|
||||
strcmp(NameStr(m_currentSession->mb_cxt.datctype), NameStr(t_thrd.port_cxt.cur_datctype)) == 0) {
|
||||
/* no need set again. */
|
||||
/* 不用再设置了. */
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -325,14 +377,16 @@ void ThreadPoolWorker::RestoreLocaleInfo()
|
|||
NAMEDATALEN);
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
/* Use the right encoding in translated messages */
|
||||
/* 在翻译后的消息中使用正确的编码 */
|
||||
#ifdef ENABLE_NLS
|
||||
pg_bind_textdomain_codeset(textdomain(NULL));
|
||||
#endif
|
||||
}
|
||||
|
||||
// 恢复会话变量的函数
|
||||
void ThreadPoolWorker::RestoreSessionVariable()
|
||||
{
|
||||
// 恢复各个会话变量的初始值
|
||||
m_currentSession->attr.attr_sql.default_statistics_target = default_statistics_target;
|
||||
m_currentSession->attr.attr_common.session_timezone = session_timezone;
|
||||
m_currentSession->attr.attr_common.log_timezone = log_timezone;
|
||||
|
|
@ -344,28 +398,33 @@ void ThreadPoolWorker::RestoreSessionVariable()
|
|||
m_currentSession->attr.attr_network.comm_client_bind = comm_client_bind;
|
||||
m_currentSession->attr.attr_network.comm_ackchk_time = comm_ackchk_time;
|
||||
|
||||
// 恢复随机数发生器的种子
|
||||
unsigned short* rand48 = pg_get_srand48();
|
||||
m_currentSession->rand_cxt.rand48_seed[0] = rand48[0];
|
||||
m_currentSession->rand_cxt.rand48_seed[1] = rand48[1];
|
||||
m_currentSession->rand_cxt.rand48_seed[2] = rand48[2];
|
||||
}
|
||||
|
||||
// 设置会话信息的函数
|
||||
void ThreadPoolWorker::SetSessionInfo()
|
||||
{
|
||||
/*
|
||||
* The proc and pgxact are more likely thread level variable, maybe we need to
|
||||
* reconsider if it's better to put it in knl_thread_context.
|
||||
*/
|
||||
// 获取当前线程的进程和PGXACT结构
|
||||
struct PGPROC* thread_proc = t_thrd.proc;
|
||||
// 设置数据库ID和角色ID
|
||||
thread_proc->databaseId = m_currentSession->proc_cxt.MyDatabaseId;
|
||||
thread_proc->roleId = m_currentSession->proc_cxt.MyRoleId;
|
||||
Assert(thread_proc->pid == t_thrd.proc_cxt.MyProcPid);
|
||||
// 设置会话ID和全局会话ID
|
||||
thread_proc->sessionid = m_currentSession->session_id;
|
||||
thread_proc->globalSessionId = m_currentSession->globalSessionId;
|
||||
// 设置工作线程的版本号
|
||||
thread_proc->workingVersionNum = m_currentSession->proc_cxt.MyProcPort->SessionVersionNum;
|
||||
// 设置会话的附属进程ID
|
||||
m_currentSession->attachPid = thread_proc->pid;
|
||||
|
||||
// 如果PGXACT结构不为空且当前会话是Redis工作者
|
||||
if (t_thrd.pgxact != NULL && m_currentSession->proc_cxt.Isredisworker) {
|
||||
// 获取进程数组锁,设置进程为Redis工作者
|
||||
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||
t_thrd.pgxact->vacuumFlags |= PROC_IS_REDIST;
|
||||
LWLockRelease(ProcArrayLock);
|
||||
|
|
@ -377,15 +436,15 @@ void ThreadPoolWorker::WaitNextSession()
|
|||
if (EnableLocalSysCache()) {
|
||||
g_instance.global_sysdbcache.GSCMemThresholdCheck();
|
||||
}
|
||||
/* Return worker to pool unless we can get a task right now. */
|
||||
/* 除非我们现在能找到工作,否则就把工人送回池子里 */
|
||||
ThreadPoolListener* lsn = m_group->GetListener();
|
||||
Assert(lsn != NULL);
|
||||
|
||||
while (true) {
|
||||
/* Wait if the thread was turned into pending mode. */
|
||||
/* 如果线程变成挂起模式,则等待 */
|
||||
if (unlikely(m_threadStatus == THREAD_PENDING)) {
|
||||
Pending();
|
||||
/* pending thread must don't have session on it */
|
||||
/* 挂起的线程必须没有会话 */
|
||||
if (m_currentSession != NULL) {
|
||||
u_sess = m_currentSession;
|
||||
ereport(FATAL,
|
||||
|
|
@ -399,9 +458,9 @@ void ThreadPoolWorker::WaitNextSession()
|
|||
break;
|
||||
}
|
||||
|
||||
/* Wait for listener dispatch. */
|
||||
/* 等待侦听器调度 */
|
||||
if (!lsn->TryFeedWorker(this)) {
|
||||
/* report thread status. */
|
||||
/* 报告线程状态 */
|
||||
u_sess = t_thrd.fake_session;
|
||||
WaitState oldStatus = pgstat_report_waitstatus(STATE_WAIT_COMM);
|
||||
|
||||
|
|
@ -426,24 +485,35 @@ void ThreadPoolWorker::WaitNextSession()
|
|||
}
|
||||
}
|
||||
|
||||
// 挂起线程的函数
|
||||
void ThreadPoolWorker::Pending()
|
||||
{
|
||||
// 将工作线程数量减一(原子操作)
|
||||
pg_atomic_fetch_sub_u32((volatile uint32*)&m_group->m_workerNum, 1);
|
||||
// 获取互斥锁
|
||||
pthread_mutex_lock(m_mutex);
|
||||
// 如果线程状态是挂起
|
||||
while (m_threadStatus == THREAD_PENDING) {
|
||||
// 等待条件信号,挂起线程
|
||||
pthread_cond_wait(m_cond, m_mutex);
|
||||
}
|
||||
// 释放互斥锁
|
||||
pthread_mutex_unlock(m_mutex);
|
||||
// 将工作线程数量加一(原子操作)
|
||||
pg_atomic_fetch_add_u32((volatile uint32*)&m_group->m_workerNum, 1);
|
||||
|
||||
// 如果线程状态是退出,执行关闭操作
|
||||
if (m_threadStatus == THREAD_EXIT) {
|
||||
ShutDownIfNecessary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果需要的话,关闭线程的函数
|
||||
void ThreadPoolWorker::ShutDownIfNecessary()
|
||||
{
|
||||
// 如果线程状态是退出
|
||||
if (unlikely(m_threadStatus == THREAD_EXIT)) {
|
||||
// 如果当前会话为空,使用虚拟会话
|
||||
if (!m_currentSession) {
|
||||
use_fake_session();
|
||||
m_currentSession = t_thrd.fake_session;
|
||||
|
|
@ -451,11 +521,12 @@ void ThreadPoolWorker::ShutDownIfNecessary()
|
|||
u_sess = m_currentSession;
|
||||
}
|
||||
|
||||
// 恢复线程变量
|
||||
RestoreThreadVariable();
|
||||
// 退出进程
|
||||
proc_exit(0);
|
||||
}
|
||||
/* there is time window which the cancle signal has arrived but ignored by prevent signal called before,
|
||||
* so we rebuild the signal status here in case that happens. */
|
||||
/* 在防止信号被调用之前,取消信号已经到达但被忽略的时间窗口,所以在这里重建信号状态以防发生这种情况。 */
|
||||
if (unlikely(m_currentSession != NULL && m_currentSession->status == KNL_SESS_CLOSE)) {
|
||||
t_thrd.int_cxt.ClientConnectionLost = true;
|
||||
ereport(ERROR, (errmodule(MOD_THREAD_POOL),
|
||||
|
|
@ -466,8 +537,8 @@ void ThreadPoolWorker::ShutDownIfNecessary()
|
|||
void ThreadPoolWorker::CleanThread()
|
||||
{
|
||||
/*
|
||||
* In thread pool mode, ensure that packet transmission must be completed before thread switchover.
|
||||
* Otherwise, packet format disorder may occurs.
|
||||
在线程池模式下,请确保在切换线程前完成报文传输,
|
||||
否则可能导致数据包格式混乱
|
||||
*/
|
||||
if (m_currentSession != NULL && t_thrd.libpq_cxt.PqSendPointer > 0) {
|
||||
int res = pq_flush();
|
||||
|
|
@ -477,12 +548,12 @@ void ThreadPoolWorker::CleanThread()
|
|||
}
|
||||
|
||||
/*
|
||||
* Clean up Allocated descs incase long jump happend
|
||||
* and they are not cleaned up in AtEOXact_Files.
|
||||
* 如果跳远发生,清理分配的位置
|
||||
* 并且它们不会在AtEOXact_Files中被清理
|
||||
*/
|
||||
FreeAllAllocatedDescs();
|
||||
|
||||
/* we should abandon this session. */
|
||||
/* 我们应该放弃这次会议 */
|
||||
if (t_thrd.int_cxt.ClientConnectionLost || t_thrd.threadpool_cxt.reaper_dead_session) {
|
||||
t_thrd.int_cxt.ClientConnectionLost = false;
|
||||
t_thrd.threadpool_cxt.reaper_dead_session = false;
|
||||
|
|
@ -510,17 +581,18 @@ void ThreadPoolWorker::CleanThread()
|
|||
}
|
||||
}
|
||||
|
||||
// 将会话从线程中分离的函数
|
||||
void ThreadPoolWorker::DetachSessionFromThread()
|
||||
{
|
||||
/* session attach thread success, we record relation of sock with worker */
|
||||
/* 会话成功附加到线程上,我们记录套接字和工作线程的关系 */
|
||||
if (AmIProxyModeSockfd(m_currentSession->proc_cxt.MyProcPort->sock)) {
|
||||
g_comm_controller->SetCommSockIdle(m_currentSession->proc_cxt.MyProcPort->sock);
|
||||
}
|
||||
|
||||
/* If some error occur at session initialization, we need to close it. */
|
||||
/* 如果在会话初始化时发生错误,我们需要关闭会话。 */
|
||||
if (m_currentSession->status == KNL_SESS_UNINIT) {
|
||||
m_currentSession->status = KNL_SESS_CLOSERAW;
|
||||
/* cache may be in wrong stat, rebuild is ok */
|
||||
/* 缓存可能处于错误状态,重新构建是可以的 */
|
||||
ReBuildLSC();
|
||||
CleanUpSession(false);
|
||||
m_currentSession = NULL;
|
||||
|
|
@ -540,33 +612,44 @@ void ThreadPoolWorker::DetachSessionFromThread()
|
|||
m_currentSession->pcache_cxt.gpc_in_ddl = false;
|
||||
}
|
||||
RestoreSessionVariable();
|
||||
// 从性能统计中取消关联当前会话
|
||||
pgstat_couple_decouple_session(false);
|
||||
// 取消关联会话的性能统计
|
||||
pgstat_deinitialize_session();
|
||||
m_currentSession->attachPid = (ThreadId)-1;
|
||||
|
||||
/* should restore the data before return to listener. */
|
||||
/* 在返回到监听器之前,应该还原数据。 */
|
||||
m_group->GetListener()->AddEpoll(m_currentSession);
|
||||
m_currentSession = NULL;
|
||||
u_sess = NULL;
|
||||
}
|
||||
|
||||
// 将会话附加到线程上的函数
|
||||
bool ThreadPoolWorker::AttachSessionToThread()
|
||||
{
|
||||
// 断言当前会话不为空,并且当前线程没有事务资源所有者
|
||||
Assert(m_currentSession != NULL);
|
||||
Assert(t_thrd.utils_cxt.TopTransactionResourceOwner == NULL);
|
||||
|
||||
// 设置会话信息并恢复线程变量
|
||||
SetSessionInfo();
|
||||
RestoreThreadVariable();
|
||||
|
||||
// 如果会话状态为 KNL_SESS_DETACH,则还原本地化信息
|
||||
if (m_currentSession->status == KNL_SESS_DETACH) {
|
||||
RestoreLocaleInfo();
|
||||
}
|
||||
|
||||
// 将当前会话设置为活动会话
|
||||
u_sess = m_currentSession;
|
||||
// 设置输出发送目标为远程
|
||||
t_thrd.postgres_cxt.whereToSendOutput = DestRemote;
|
||||
// 将当前内存上下文设置为会话自身内存上下文
|
||||
SelfMemoryContext = u_sess->self_mem_cxt;
|
||||
|
||||
/*
|
||||
* Since thread pool worker may start earlier than startup finishing recovery,
|
||||
* init xlog access if necessary.
|
||||
* 由于线程池工作线程可能早于恢复完成之前启动,
|
||||
* 如果需要,初始化 xlog 访问。
|
||||
*/
|
||||
PG_TRY();
|
||||
{
|
||||
|
|
@ -574,31 +657,33 @@ bool ThreadPoolWorker::AttachSessionToThread()
|
|||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
/* if init xlog has error, should throw fatal this thread */
|
||||
/* 如果初始化 xlog 出错,应该为该线程抛出致命错误 */
|
||||
ereport(FATAL, (errmsg("init xlog failed, throw fatal for this thread")));
|
||||
}
|
||||
PG_END_TRY();
|
||||
#ifdef ENABLE_QUNIT
|
||||
set_qunit_case_number_hook(u_sess->utils_cxt.qunit_case_number, NULL);
|
||||
#endif
|
||||
|
||||
// 根据会话状态执行相应的操作
|
||||
switch (m_currentSession->status) {
|
||||
case KNL_SESS_UNINIT: {
|
||||
// 如果会话初始化成功
|
||||
if (InitSession(m_currentSession)) {
|
||||
/* Registering backend_version */
|
||||
/* 注册后端版本信息 */
|
||||
if (t_thrd.proc && contain_backend_version(t_thrd.proc->workingVersionNum)) {
|
||||
register_backend_version(t_thrd.proc->workingVersionNum);
|
||||
}
|
||||
// 将会话状态设置为附加
|
||||
m_currentSession->status = KNL_SESS_ATTACH;
|
||||
// 断言当前数据库匹配
|
||||
Assert(CheckMyDatabaseMatch());
|
||||
} else {
|
||||
// 如果会话初始化失败
|
||||
m_currentSession->status = KNL_SESS_CLOSE;
|
||||
/* clean up mess. */
|
||||
/* 清理混乱状态。 */
|
||||
CleanUpSession(false);
|
||||
m_currentSession = NULL;
|
||||
u_sess = NULL;
|
||||
}
|
||||
/* init port will change the signal handle */
|
||||
/* 初始化端口会改变信号处理 */
|
||||
ResetSignalHandle();
|
||||
} break;
|
||||
|
||||
|
|
@ -608,6 +693,7 @@ bool ThreadPoolWorker::AttachSessionToThread()
|
|||
int rcs = 0;
|
||||
Port *port = m_currentSession->proc_cxt.MyProcPort;
|
||||
|
||||
// 设置线程名称为当前会话用户名,以区分不同类型的线程
|
||||
if (t_thrd.role == WORKER) {
|
||||
rcs = snprintf_truncated_s(thr_name, sizeof(thr_name), "w:%s", port->user_name);
|
||||
securec_check_ss(rcs, "\0", "\0");
|
||||
|
|
@ -618,24 +704,27 @@ bool ThreadPoolWorker::AttachSessionToThread()
|
|||
(void)pthread_setname_np(gs_thread_self(), thr_name);
|
||||
}
|
||||
#endif
|
||||
// 初始化性能统计信息
|
||||
pgstat_initialize_session();
|
||||
// 关联当前会话的性能统计信息
|
||||
pgstat_couple_decouple_session(true);
|
||||
/* Postgres init thread syscache. */
|
||||
/* Postgres 初始化线程的系统缓存。 */
|
||||
t_thrd.proc_cxt.PostInit->InitLoadLocalSysCache(u_sess->proc_cxt.MyDatabaseId,
|
||||
u_sess->proc_cxt.MyProcPort->database_name);
|
||||
Assert(CheckMyDatabaseMatch());
|
||||
// 将会话状态设置为附加
|
||||
m_currentSession->status = KNL_SESS_ATTACH;
|
||||
} break;
|
||||
|
||||
case KNL_SESS_CLOSERAW:
|
||||
case KNL_SESS_CLOSE: {
|
||||
/* unified auditing logout */
|
||||
/* 统一审计登出 */
|
||||
audit_processlogout_unified();
|
||||
|
||||
/* clean up tmp schema */
|
||||
/* 清理临时模式 */
|
||||
RemoveTempNamespace();
|
||||
|
||||
/* clean up mess. */
|
||||
/* 清理混乱状态。 */
|
||||
CleanUpSession(false);
|
||||
m_currentSession = NULL;
|
||||
u_sess = NULL;
|
||||
|
|
@ -648,11 +737,13 @@ bool ThreadPoolWorker::AttachSessionToThread()
|
|||
|
||||
default:
|
||||
Assert(false);
|
||||
// 未定义的会话状态,应该抛出 PANIC 错误
|
||||
ereport(PANIC,
|
||||
(errcode(ERRCODE_INVALID_ATTRIBUTE),
|
||||
errmsg("undefined state %d for session attach", m_currentSession->status)));
|
||||
}
|
||||
|
||||
// 如果当前会话状态为附加,则返回 true,否则使用虚假会话并返回 false
|
||||
if (m_currentSession && m_currentSession->status == KNL_SESS_ATTACH) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -662,12 +753,14 @@ bool ThreadPoolWorker::AttachSessionToThread()
|
|||
}
|
||||
}
|
||||
|
||||
// 释放会话锁定资源
|
||||
void ThreadPoolWorker::CleanUpSessionWithLock()
|
||||
{
|
||||
if (m_currentSession == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是 Redis 工作线程,释放对应的锁定资源
|
||||
if (t_thrd.pgxact != NULL && m_currentSession->proc_cxt.Isredisworker) {
|
||||
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||
t_thrd.pgxact->vacuumFlags &= ~PROC_IS_REDIST;
|
||||
|
|
@ -675,17 +768,20 @@ void ThreadPoolWorker::CleanUpSessionWithLock()
|
|||
}
|
||||
}
|
||||
|
||||
// 清理会话资源
|
||||
void ThreadPoolWorker::CleanUpSession(bool threadexit)
|
||||
{
|
||||
if (m_currentSession == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果当前会话处于虚假状态,且线程状态为 THREAD_EXIT,则直接返回
|
||||
if (m_currentSession->status == KNL_SESS_FAKE) {
|
||||
Assert(m_threadStatus == THREAD_EXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果当前会话状态不是 KNL_SESS_END_PHASE1,则执行必要的清理工作
|
||||
if (m_currentSession->status != KNL_SESS_END_PHASE1) {
|
||||
InitThreadLocalWhenSessionExit();
|
||||
|
||||
|
|
@ -694,9 +790,10 @@ void ThreadPoolWorker::CleanUpSession(bool threadexit)
|
|||
DecreaseUserCount(m_currentSession->proc_cxt.MyRoleId);
|
||||
}
|
||||
|
||||
/* Close Session. */
|
||||
/* 关闭会话 */
|
||||
m_group->GetListener()->DelSessionFromEpoll(m_currentSession);
|
||||
|
||||
// 如果会话超过连接限制,则减少连接数
|
||||
if (m_currentSession->proc_cxt.PassConnLimit) {
|
||||
SpinLockAcquire(&g_instance.conn_cxt.ConnCountLock);
|
||||
g_instance.conn_cxt.CurConnCount--;
|
||||
|
|
@ -705,34 +802,31 @@ void ThreadPoolWorker::CleanUpSession(bool threadexit)
|
|||
}
|
||||
|
||||
/*
|
||||
* Record this state in case we reenter this function because
|
||||
* ERROR/FATAL occurs in sess_exit().
|
||||
* 在 ERROR/FATAL 在 sess_exit() 函数中再次进入此函数之前,记录此状态。
|
||||
*/
|
||||
m_currentSession->status = KNL_SESS_END_PHASE1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If clean up work already be done at proc_exit(), then we don't need to
|
||||
* call sess_exit() anymore, otherwise, there will be double free.
|
||||
* 如果已经在 proc_exit() 函数中完成了清理工作,则不需要再次调用 sess_exit() 函数,否则会导致重复释放资源。
|
||||
*/
|
||||
if (!t_thrd.proc_cxt.proc_exit_inprogress) {
|
||||
sess_exit(0);
|
||||
}
|
||||
|
||||
/* clear pgstat slot */
|
||||
/* 清理 pgstat 插槽 */
|
||||
pgstat_release_session_memory_entry();
|
||||
pgstat_deinitialize_session();
|
||||
pgstat_beshutdown_session(m_currentSession->session_ctr_index);
|
||||
localeconv_deinitialize_session();
|
||||
|
||||
/* clean gpc refcount and plancache in shared memory */
|
||||
/* 清理 GPC 引用计数和共享内存中的计划缓存 */
|
||||
if (ENABLE_DN_GPC)
|
||||
CleanSessGPCPtr(m_currentSession);
|
||||
|
||||
/*
|
||||
* clear invalid msg slot
|
||||
* If called during pool worker thread exit, session's invalid msg slot has already
|
||||
* been cleared along with that of pool worker in shmem_exit.
|
||||
* 清理无效消息槽
|
||||
* 如果在池工作线程退出期间调用,则会话的无效消息槽已经随着池工作线程的消息槽一起在 shmem_exit 中被清理。
|
||||
*/
|
||||
if (!t_thrd.proc_cxt.proc_exit_inprogress) {
|
||||
CleanupWorkSessionInvalidation();
|
||||
|
|
@ -745,6 +839,7 @@ void ThreadPoolWorker::CleanUpSession(bool threadexit)
|
|||
m_currentSession = NULL;
|
||||
}
|
||||
|
||||
// 创建后端进程
|
||||
Backend* ThreadPoolWorker::CreateBackend()
|
||||
{
|
||||
Backend* bn = AssignFreeBackEnd(t_thrd.proc_cxt.MyPMChildSlot);
|
||||
|
|
@ -754,6 +849,7 @@ Backend* ThreadPoolWorker::CreateBackend()
|
|||
return bn;
|
||||
}
|
||||
|
||||
// 将后端进程添加到全局后端列表中
|
||||
void ThreadPoolWorker::AddBackend(Backend* bn)
|
||||
{
|
||||
bn->is_autovacuum = false;
|
||||
|
|
@ -761,6 +857,7 @@ void ThreadPoolWorker::AddBackend(Backend* bn)
|
|||
DLAddHead(g_instance.backend_list, &bn->elem);
|
||||
}
|
||||
|
||||
// 初始化会话共享内存
|
||||
static void init_session_share_memory()
|
||||
{
|
||||
TableSpaceUsageManager::Init();
|
||||
|
|
@ -769,21 +866,22 @@ static void init_session_share_memory()
|
|||
#endif
|
||||
}
|
||||
|
||||
// 如果需要,初始化 BSQL 插件钩子
|
||||
#ifndef ENABLE_MULTIPLE_NODES
|
||||
extern void InitBSqlPluginHookIfNeeded();
|
||||
#endif
|
||||
|
||||
// 初始化会话(参数:会话上下文)
|
||||
static bool InitSession(knl_session_context* session)
|
||||
{
|
||||
/* non't send ereport to client now */
|
||||
/* 非实时向客户端发送错误信息 */
|
||||
t_thrd.postgres_cxt.whereToSendOutput = DestNone;
|
||||
/* Switch context to Session context. */
|
||||
/* 切换上下文到会话上下文 */
|
||||
AutoContextSwitch memSwitch(session->mcxt_group->GetMemCxtGroup(MEMORY_CONTEXT_DEFAULT));
|
||||
|
||||
/*
|
||||
* Set thread version to the latest working version number for
|
||||
* InitializeGUCOptions.
|
||||
* This is ugly and can not avoid all race conditions during online upgrade.
|
||||
* 为 InitializeGUCOptions 设置当前线程版本号。
|
||||
* 这是丑陋的,无法避免在线升级期间所有竞态条件。
|
||||
*/
|
||||
t_thrd.proc->workingVersionNum = pg_atomic_read_u32(&WorkingGrandVersionNum);
|
||||
|
||||
|
|
@ -793,55 +891,55 @@ static bool InitSession(knl_session_context* session)
|
|||
(void)pg_atomic_add_fetch_u32(&g_instance.comm_cxt.current_gsrewind_count, 1);
|
||||
}
|
||||
|
||||
/* Init GUC option for this session. */
|
||||
/* 初始化会话的 GUC 选项 */
|
||||
InitializeGUCOptions();
|
||||
|
||||
/* Read in remaining GUC variables */
|
||||
/* 读取剩余的 GUC 变量 */
|
||||
read_nondefault_variables();
|
||||
|
||||
/* now safe to ereport to client */
|
||||
/* 现在安全地向客户端发送错误报告 */
|
||||
t_thrd.postgres_cxt.whereToSendOutput = DestRemote;
|
||||
|
||||
/* Init port and connection. */
|
||||
/* 初始化端口和连接 */
|
||||
if (!InitPort(session->proc_cxt.MyProcPort)) {
|
||||
/* reset some status below */
|
||||
/* 重置以下状态 */
|
||||
if (!disable_sig_alarm(false)) {
|
||||
ereport(FATAL, (errmsg("could not disable timer for startup packet timeout")));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* switch version number to that gotten from port */
|
||||
/* 将版本号切换到从端口获得的版本号 */
|
||||
t_thrd.proc->workingVersionNum = session->proc_cxt.MyProcPort->SessionVersionNum;
|
||||
|
||||
/* add process definer mode */
|
||||
/* 添加进程定义模式 */
|
||||
Reset_Pseudo_CurrentUserId();
|
||||
|
||||
SetProcessingMode(InitProcessing);
|
||||
|
||||
SessionSetBackendOptions();
|
||||
|
||||
/* initialize guc variables which need to be sended to stream threads */
|
||||
/* 初始化需要发送给流线程的 GUC 变量 */
|
||||
#ifdef PGXC
|
||||
if (IS_PGXC_DATANODE && IsUnderPostmaster) {
|
||||
init_sync_guc_variables();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We need to allow SIGINT, etc during the initial transaction */
|
||||
/* 我们需要在初始事务期间允许 SIGINT 等信号 */
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
|
||||
/* init invalid msg slot */
|
||||
/* 初始化无效消息槽 */
|
||||
SharedInvalBackendInit(false, true);
|
||||
|
||||
/* init pgstat slot */
|
||||
/* 初始化 pgstat 插槽 */
|
||||
pgstat_initialize_session();
|
||||
|
||||
/* Do local initialization of file, storage and buffer managers */
|
||||
/* 执行文件、存储和缓冲管理器的本地初始化 */
|
||||
InitFileAccess();
|
||||
smgrinit();
|
||||
|
||||
/* openGauss init. */
|
||||
/* openGauss 初始化 */
|
||||
char* dbname = session->proc_cxt.MyProcPort->database_name;
|
||||
char* username = session->proc_cxt.MyProcPort->user_name;
|
||||
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username);
|
||||
|
|
@ -863,12 +961,12 @@ static bool InitSession(knl_session_context* session)
|
|||
|
||||
SendSessionIdxToClient();
|
||||
|
||||
/* init param hash table for sending set message */
|
||||
/* 初始化用于发送设置消息的参数哈希表 */
|
||||
if (IS_PGXC_COORDINATOR) {
|
||||
init_set_params_htab();
|
||||
}
|
||||
|
||||
/* check if memory already reach the max_dynamic_memory */
|
||||
/* 检查内存是否已经达到最大动态内存 */
|
||||
if (t_thrd.utils_cxt.gs_mp_inited && processMemInChunks > maxChunksPerProcess) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY),
|
||||
|
|
@ -883,9 +981,10 @@ static bool InitSession(knl_session_context* session)
|
|||
return true;
|
||||
}
|
||||
|
||||
// 初始化端口(参数:端口指针)
|
||||
static bool InitPort(Port* port)
|
||||
{
|
||||
/* session version number is initialized to process version number */
|
||||
/* 会话版本号初始化为进程版本号 */
|
||||
port->SessionVersionNum = pg_atomic_read_u32(&WorkingGrandVersionNum);
|
||||
|
||||
PortInitialize(port, NULL);
|
||||
|
|
@ -903,6 +1002,7 @@ static bool InitPort(Port* port)
|
|||
return true;
|
||||
}
|
||||
|
||||
// 向客户端发送会话索引
|
||||
static void SendSessionIdxToClient()
|
||||
{
|
||||
GenerateCancelKey(true);
|
||||
|
|
@ -917,17 +1017,19 @@ static void SendSessionIdxToClient()
|
|||
}
|
||||
}
|
||||
|
||||
// 重置信号处理函数
|
||||
static void ResetSignalHandle()
|
||||
{
|
||||
// may change during thread init port(accept new connection)
|
||||
// 可能在初始化端口时(接受新连接)发生变化
|
||||
(void)gspqsignal(SIGALRM, handle_sig_alarm);
|
||||
(void)gspqsignal(SIGQUIT, quickdie); /* hard crash time */
|
||||
(void)gspqsignal(SIGTERM, die); /* cancel current query and exit */
|
||||
(void)gspqsignal(SIGQUIT, quickdie); /* 强制崩溃时 */
|
||||
(void)gspqsignal(SIGTERM, die); /* 取消当前查询并退出 */
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
||||
/* not necessary, unblock for sure */
|
||||
/* 不是必需的,确保信号解除阻塞 */
|
||||
gs_signal_unblock_sigusr2();
|
||||
}
|
||||
|
||||
// 设置会话后端选项
|
||||
static void SessionSetBackendOptions()
|
||||
{
|
||||
char** av = NULL;
|
||||
|
|
@ -935,11 +1037,10 @@ static void SessionSetBackendOptions()
|
|||
int ac = 0;
|
||||
|
||||
/*
|
||||
* Now, build the argv vector that will be given to PostgresMain.
|
||||
* 现在,构建将传递给 PostgresMain 的 argv 向量。
|
||||
*
|
||||
* The maximum possible number of commandline arguments that could come
|
||||
* from ExtraOptions is (strlen(ExtraOptions) + 1) / 2; see
|
||||
* pg_split_opts().
|
||||
* 来自 ExtraOptions 的可能的命令行参数的最大数量是 (strlen(ExtraOptions) + 1) / 2;
|
||||
* 请参阅 pg_split_opts()。
|
||||
*/
|
||||
maxac = (strlen(g_instance.ExtraOptions) + 1) / 2 + 2;
|
||||
|
||||
|
|
@ -949,6 +1050,6 @@ static void SessionSetBackendOptions()
|
|||
pg_split_opts(av, &ac, g_instance.ExtraOptions);
|
||||
av[ac] = NULL;
|
||||
|
||||
/* Parse command-line options. */
|
||||
/* 解析命令行选项 */
|
||||
process_postgres_switches(ac, av, PGC_POSTMASTER, NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"condition_variable": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
// 包含的必要的头文件
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
|
@ -23,83 +24,88 @@
|
|||
|
||||
#include "replication/slot.h"
|
||||
|
||||
|
||||
size_t ArchiveRead(const char* fileName, const int offset, char *buffer, const int length,
|
||||
ArchiveConfig *archive_config)
|
||||
// 定义的函数,用于从归档储存中读取数据
|
||||
size_t ArchiveRead(const char *fileName, const int offset, char *buffer, const int length,
|
||||
ArchiveConfig *archive_config) // archive_config是用于存储归档存储配置信息的数据结构。
|
||||
{
|
||||
if (archive_config == NULL) {
|
||||
if (archive_config == NULL) { // 如果未指定archive_config,则无法读取数据
|
||||
return 0;
|
||||
}
|
||||
if (archive_config->media_type == ARCHIVE_OBS) {
|
||||
return obsRead(fileName, offset, buffer, length, archive_config);
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) {
|
||||
return NasRead(fileName, offset, buffer, length, archive_config);
|
||||
// 根据归档存储介质类型执行相应的读取函数
|
||||
if (archive_config->media_type == ARCHIVE_OBS) { // 判断归档存储介质的类型——对象储存
|
||||
return obsRead(fileName, offset, buffer, length, archive_config); // OBS储存介质的读取函数
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) { // 用于区分归档存储操作是在网络附加存储上进行的
|
||||
return NasRead(fileName, offset, buffer, length, archive_config); // NSA储存介质的读取函数
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0; // 如果介质类型未知,返回0
|
||||
}
|
||||
|
||||
int ArchiveWrite(const char* fileName, const char *buffer, const int bufferLength, ArchiveConfig *archive_config)
|
||||
// 定义了一个函数用于向归档存储中写入数据
|
||||
int ArchiveWrite(const char *fileName, const char *buffer, const int bufferLength, ArchiveConfig *archive_config)
|
||||
{
|
||||
int ret = -1;
|
||||
if (archive_config == NULL) {
|
||||
return ret;
|
||||
return ret; // 如果归档配置为空,直接返回 -1 表示写入失败
|
||||
}
|
||||
|
||||
// 根据归档存储介质类型执行相应的写入函数
|
||||
if (archive_config->media_type == ARCHIVE_OBS) {
|
||||
ret = obsWrite(fileName, buffer, bufferLength, archive_config);
|
||||
ret = obsWrite(fileName, buffer, bufferLength, archive_config); // OBS储存介质的写入函数
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) {
|
||||
ret = NasWrite(fileName, buffer, bufferLength, archive_config);
|
||||
ret = NasWrite(fileName, buffer, bufferLength, archive_config); // NSA储存介质的写入函数
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret; // 根据返回结果可以判断是否写入成功,成功(非负数),失败(-1)
|
||||
}
|
||||
|
||||
int ArchiveDelete(const char* fileName, ArchiveConfig *archive_config)
|
||||
// 定义了一个函数用于删除归档存储中的数据
|
||||
int ArchiveDelete(const char *fileName, ArchiveConfig *archive_config)
|
||||
{
|
||||
int ret = -1;
|
||||
if (archive_config == NULL) {
|
||||
return ret;
|
||||
return ret; // 如果归档配置为空,直接返回 -1 表示删除失败
|
||||
}
|
||||
|
||||
// 根据归档存储介质类型执行相应的删除函数
|
||||
if (archive_config->media_type == ARCHIVE_OBS) {
|
||||
ret = obsDelete(fileName, archive_config);
|
||||
ret = obsDelete(fileName, archive_config); // 调用OBS存储的删除函数
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) {
|
||||
ret = NasDelete(fileName, archive_config);
|
||||
ret = NasDelete(fileName, archive_config); // 调用NAS存储的删除函数
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret; // 根据返回结果可以判断是否删除成功,成功(非负数),失败(-1)
|
||||
}
|
||||
|
||||
List* ArchiveList(const char* prefix, ArchiveConfig *archive_config, bool reportError, bool shortenConnTime)
|
||||
// 定义了一个函数,用于列出归档存储中的文件列表
|
||||
List *ArchiveList(const char *prefix, ArchiveConfig *archive_config, bool reportError, bool shortenConnTime)
|
||||
{
|
||||
List* fileNameList = NIL;
|
||||
List *fileNameList = NIL;
|
||||
if (archive_config == NULL) {
|
||||
return fileNameList;
|
||||
}
|
||||
|
||||
// 根据归档存储介质类型执行相应的列出文件列表函数
|
||||
if (archive_config->media_type == ARCHIVE_OBS) {
|
||||
fileNameList = obsList(prefix, archive_config, reportError, shortenConnTime);
|
||||
// 调用OBS储存的文件列表,传入reporError(指定是否在列出文件列表过程中报告错误)以及shortenConnTime(指定是否缩短连接时间)
|
||||
// 从而提供更多的控制和灵活性,从而灵活的获得文件列表
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) {
|
||||
fileNameList = NasList(prefix, archive_config);
|
||||
fileNameList = NasList(prefix, archive_config); // 调用NAS存储的列出文件列表函数
|
||||
}
|
||||
|
||||
return fileNameList;
|
||||
return fileNameList; // 返回文件名列表
|
||||
}
|
||||
|
||||
bool ArchiveFileExist(const char* file_path, ArchiveConfig *archive_config)
|
||||
// 定义了一个函数用于检查归档存储中的文件是否存在
|
||||
bool ArchiveFileExist(const char *file_path, ArchiveConfig *archive_config)
|
||||
{
|
||||
bool ret = false;
|
||||
if (archive_config == NULL) {
|
||||
ereport(WARNING, (errmsg("when check file exist, the archive config is null")));
|
||||
ereport(
|
||||
WARNING,
|
||||
(errmsg("when check file exist, the archive config is null"))); // 如果归档配置为空,会发出警告并返回false
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 根据归档存储介质类型执行相应的检查文件存在函数
|
||||
if (archive_config->media_type == ARCHIVE_OBS) {
|
||||
ret = checkOBSFileExist(file_path, archive_config);
|
||||
ret = checkOBSFileExist(file_path, archive_config); // 调用OBS存储的检查文件存在函数
|
||||
} else if (archive_config->media_type == ARCHIVE_NAS) {
|
||||
ret = checkNASFileExist(file_path, archive_config);
|
||||
ret = checkNASFileExist(file_path, archive_config); // 调用NAS存储的检查文件存在函数
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
return ret; // 返回检查结果,可能是存在(true)或不存在(false)
|
||||
}
|
||||
|
|
@ -46,11 +46,22 @@
|
|||
#include "postmaster/alarmchecker.h"
|
||||
#include "replication/walreceiver.h"
|
||||
|
||||
//定义文件路径的最大长度,使代码更加清晰和易于维护
|
||||
#define MAX_PATH_LEN 1024
|
||||
//定义了一个头部长度
|
||||
static int headerLen = 22;
|
||||
|
||||
// 定义一个函数,用于从NAS存储中读取文件数据
|
||||
size_t NasRead(const char* fileName, const int offset, char *buffer, const int length, ArchiveConfig *nas_config)
|
||||
{
|
||||
/*
|
||||
fileName:要读取的文件名。
|
||||
offset:读取的起始偏移量。
|
||||
buffer:用于存储读取的数据的缓冲区。
|
||||
length:要读取的数据长度。
|
||||
nas_config:归档配置信息的指针,指向 ArchiveConfig 结构,包含了有关存储位置和设置的信息。
|
||||
*/
|
||||
//初始化
|
||||
size_t readLength = 0;
|
||||
ArchiveConfig *archive_nas = NULL;
|
||||
char file_path[MAXPGPATH] = {0};
|
||||
|
|
@ -58,40 +69,45 @@ size_t NasRead(const char* fileName, const int offset, char *buffer, const int l
|
|||
FILE *fp = NULL;
|
||||
struct stat statbuf;
|
||||
|
||||
if ((fileName == NULL) || (buffer == NULL)) {
|
||||
if ((fileName == NULL) || (buffer == NULL)) {//如果文件名和缓冲区为空会报错
|
||||
ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||
errmsg("The parameter cannot be NULL")));
|
||||
}
|
||||
|
||||
//获取NAS存储的配置信息
|
||||
if (nas_config != NULL) {
|
||||
archive_nas = nas_config;
|
||||
} else {
|
||||
archive_nas = getArchiveConfig();
|
||||
}
|
||||
|
||||
if (archive_nas == NULL) {
|
||||
if (archive_nas == NULL) {//无法获取归档配置信息时报错
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("Cannot get archive config from replication slots")));
|
||||
}
|
||||
|
||||
if (strncmp(fileName, "global_barrier_records", headerLen) != 0) {
|
||||
if (strncmp(fileName, "global_barrier_records", headerLen) != 0) {//构建文件的完整路径
|
||||
//调用snprintf_s函数构建一个完整的文件路径,并将结果存储在 file_path 变量中
|
||||
ret = snprintf_s(file_path, MAXPGPATH, MAXPGPATH - 1, "%s/%s", archive_nas->archive_prefix, fileName);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
// ret用于储存snprintf_s 函数的返回值。这个返回值表示已格式化字符串的长度。
|
||||
securec_check_ss(ret, "\0", "\0");//如果发生错误将会在 ret 中返回 '\0' ,从而帮助定位错误的位置。
|
||||
//用于检查 snprintf_s函数的返回值并进行安全性检查,以确保没有发生缓冲区溢出或格式化错误
|
||||
} else {
|
||||
char pathPrefix[MAXPGPATH] = {0};
|
||||
char pathPrefix[MAXPGPATH] = {0};//生成一个空的char数组作为pathPrefix(用于储存归档配置的存档前缀)
|
||||
ret = strcpy_s(pathPrefix, MAXPGPATH, archive_nas->archive_prefix);
|
||||
//复制 archive_nas->archive_prefix 到 pathPrefix
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
if (!IS_PGXC_COORDINATOR) {
|
||||
char *p = strrchr(pathPrefix, '/');
|
||||
char *p = strrchr(pathPrefix, '/');//在pathPrefix中找到最后一个斜杠的位置
|
||||
if (p == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Obs path prefix is invalid")));
|
||||
}
|
||||
*p = '\0';
|
||||
*p = '\0';//将最后一个斜杠替换为'\0'
|
||||
}
|
||||
ret = snprintf_s(file_path, MAXPGPATH, MAXPGPATH - 1, "%s/%s", pathPrefix, fileName);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
// 构建完整路径并检查 snprintf_s 函数的返回值
|
||||
}
|
||||
|
||||
//检查文件是否存在
|
||||
if (stat(file_path, &statbuf)) {
|
||||
if (errno != ENOENT) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", fileName)));
|
||||
|
|
@ -99,9 +115,10 @@ size_t NasRead(const char* fileName, const int offset, char *buffer, const int l
|
|||
ereport(ERROR, (errcode_for_file_access(), errmsg("The file \"%s\" not exists", fileName)));
|
||||
return readLength;
|
||||
}
|
||||
|
||||
//打开文件并读取数据
|
||||
canonicalize_path(file_path);
|
||||
fp = fopen(file_path, "rb");
|
||||
//对于打开文件产生的错误进行处理
|
||||
if (fp == NULL) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", fileName)));
|
||||
return readLength;
|
||||
|
|
@ -111,90 +128,99 @@ size_t NasRead(const char* fileName, const int offset, char *buffer, const int l
|
|||
ereport(ERROR, (errcode_for_file_access(), errmsg("file size is wrong, \"%s\": %m", fileName)));
|
||||
return readLength;
|
||||
}
|
||||
|
||||
|
||||
//读取数据到缓冲区中
|
||||
readLength = fread(buffer, 1, statbuf.st_size, fp);
|
||||
|
||||
fclose(fp);
|
||||
return readLength;
|
||||
}
|
||||
|
||||
//该函数用于将数据写入文件,并在必要时进行备份和重命名
|
||||
int NasWrite(const char* fileName, const char *buffer, const int bufferLength, ArchiveConfig *nas_config)
|
||||
{
|
||||
int ret = 0;
|
||||
ArchiveConfig *archive_nas = NULL;
|
||||
char file_path[MAXPGPATH] = {0};
|
||||
char file_path_bak[MAXPGPATH] = {0};
|
||||
char *origin_file_path = NULL;
|
||||
char *base_path = NULL;
|
||||
FILE *fp = NULL;
|
||||
|
||||
ArchiveConfig *archive_nas = NULL;//存储NAS配置的指针
|
||||
char file_path[MAXPGPATH] = {0};//存储构建的文件路
|
||||
char file_path_bak[MAXPGPATH] = {0};//存储备份文件路径
|
||||
char *origin_file_path = NULL;//存储规范化的文件路径的副本
|
||||
char *base_path = NULL;//存储文件的基础路径
|
||||
FILE *fp = NULL;//文件指针,用于操作文件
|
||||
|
||||
//检查传入的参数是否为空,文件名和缓冲器为空时报错
|
||||
if ((fileName == NULL) || (buffer == NULL)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||
errmsg("The parameter cannot be NULL")));
|
||||
}
|
||||
|
||||
//获取归档配置信息,如果传入的归档配置参数为NULL,则尝试从默认位置获取
|
||||
if (nas_config != NULL) {
|
||||
archive_nas = nas_config;
|
||||
} else {
|
||||
archive_nas = getArchiveConfig();
|
||||
}
|
||||
|
||||
//检查获取到的归档配置信息是否有效,无效时报错
|
||||
if (archive_nas == NULL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("Cannot get archive config from replication slots")));
|
||||
}
|
||||
|
||||
if (strncmp(fileName, "global_barrier_records", headerLen) != 0) {
|
||||
//根据文件名构建完整的文件路径
|
||||
if (strncmp(fileName, "global_barrier_records", headerLen) != 0) {//判断文件名是否为"global_barrier_records"
|
||||
ret = snprintf_s(file_path, MAXPGPATH, MAXPGPATH - 1, "%s/%s", archive_nas->archive_prefix, fileName);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
//构建完整路径并检查snprintf_s函数的返回值
|
||||
} else {
|
||||
char pathPrefix[MAXPGPATH] = {0};
|
||||
ret = strcpy_s(pathPrefix, MAXPGPATH, archive_nas->archive_prefix);
|
||||
char pathPrefix[MAXPGPATH] = {0};//用于存储路径前缀
|
||||
ret = strcpy_s(pathPrefix, MAXPGPATH, archive_nas->archive_prefix);//复制 archive_nas->archive_prefix 到 pathPrefix
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
if (!IS_PGXC_COORDINATOR) {
|
||||
char *p = strrchr(pathPrefix, '/');
|
||||
if (p == NULL) {
|
||||
if (!IS_PGXC_COORDINATOR) {// 如果不是协调器节点
|
||||
char *p = strrchr(pathPrefix, '/');//在pathPrefix中查找最后一个'/'
|
||||
if (p == NULL) {//没有找到'/'则会报错
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Obs path prefix is invalid")));
|
||||
}
|
||||
*p = '\0';
|
||||
*p = '\0';//将最后一个斜杠替换为'\0'
|
||||
}
|
||||
ret = snprintf_s(file_path, MAXPGPATH, MAXPGPATH - 1, "%s/%s", pathPrefix, fileName);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
// 构建完整路径并检查 snprintf_s 函数的返回值
|
||||
}
|
||||
|
||||
canonicalize_path(file_path);
|
||||
canonicalize_path(file_path);//规范化文件路径,去除多余的字符,如'.'、'..'等
|
||||
|
||||
origin_file_path = pstrdup(file_path);
|
||||
base_path = dirname(origin_file_path);
|
||||
origin_file_path = pstrdup(file_path);//复制文件路径到origin_file_path
|
||||
base_path = dirname(origin_file_path);//获取origin_file_path(文件的完整路径)的父级路径,即基础路径
|
||||
//检查基础路径是否存在,如果不存在则创建
|
||||
if (!isDirExist(base_path)) {
|
||||
if (pg_mkdir_p(base_path, S_IRWXU) != 0) {
|
||||
pfree_ext(origin_file_path);
|
||||
// 调用 pg_mkdir_p 函数尝试创建目录,S_IRWXU 是指定权限参数
|
||||
if (pg_mkdir_p(base_path, S_IRWXU) != 0) {//如果返回值不为0,则说明创建目录失败
|
||||
// 如果创建目录失败,释放内存并报告错误
|
||||
pfree_ext(origin_file_path);//释放其占用的内存空间
|
||||
ereport(LOG, (errmsg("could not create path \"%s\"", base_path)));
|
||||
return -1;
|
||||
return -1;//返回 -1 表示创建目录失败
|
||||
}
|
||||
}
|
||||
|
||||
//构建备份文件的路径,将 ".bak" 添加到 file_path
|
||||
ret = snprintf_s(file_path_bak, MAXPGPATH, MAXPGPATH - 1, "%s.bak", file_path);
|
||||
securec_check_ss(ret, "\0", "\0");
|
||||
fp = fopen(file_path_bak, "wb");
|
||||
if (fp == NULL) {
|
||||
fp = fopen(file_path_bak, "wb");//打开备份文件,以二进制写入模式
|
||||
if (fp == NULL) {//如果打开备份文件失败,释放内存并报告错误
|
||||
pfree_ext(origin_file_path);
|
||||
ereport(LOG, (errmsg("could not create file \"%s\": %m", fileName)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//将数据写入备份文件
|
||||
if (fwrite(buffer, bufferLength, 1, fp) != 1) {
|
||||
ereport(LOG, (errmsg("could not write file \"%s\": %m", fileName)));
|
||||
pfree_ext(origin_file_path);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
//刷新文件缓冲区,确保数据写入磁盘
|
||||
if (fflush(fp) != 0) {
|
||||
ereport(LOG, (errmsg("could not fflush file \"%s\": %m", fileName)));
|
||||
(void)fclose(fp);
|
||||
pfree_ext(origin_file_path);
|
||||
return -1;
|
||||
}
|
||||
//将备份文件重命名为正式文件
|
||||
if (rename(file_path_bak, file_path) < 0) {
|
||||
ereport(LOG, (errmsg("could not rename file \"%s\": %m", fileName)));
|
||||
(void)fclose(fp);
|
||||
|
|
@ -202,8 +228,8 @@ int NasWrite(const char* fileName, const char *buffer, const int bufferLength, A
|
|||
return -1;
|
||||
}
|
||||
|
||||
pfree_ext(origin_file_path);
|
||||
fclose(fp);
|
||||
pfree_ext(origin_file_path);//释放被占用的内存空间
|
||||
fclose(fp);//关闭文件
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -420,4 +446,4 @@ bool checkNASFileExist(const char* file_path, ArchiveConfig *nas_config)
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
//包含所必要的头文件
|
||||
#include "storage/dfs/dfscache_mgr.h"
|
||||
|
||||
#include "postgres.h"
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
#include "postmaster/pagewriter.h"
|
||||
#include "postmaster/bgwriter.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
// 用于控制缓冲区的队列插槽的数量的倍增因子
|
||||
const int PAGE_QUEUE_SLOT_MULTI_NBUFFERS = 5;
|
||||
|
||||
/*
|
||||
|
|
@ -59,37 +60,39 @@ const int PAGE_QUEUE_SLOT_MULTI_NBUFFERS = 5;
|
|||
* Pins must be released before end of transaction. For efficiency the
|
||||
* shared refcount isn't increased if a individual backend pins a buffer
|
||||
* multiple times. Check the PrivateRefCount infrastructure in bufmgr.c.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* Initialize shared buffer pool
|
||||
*
|
||||
* This is called once during shared-memory initialization (either in the
|
||||
* postmaster, or in a standalone backend).
|
||||
*/
|
||||
// 初始化共享缓冲池
|
||||
void InitBufferPool(void)
|
||||
{
|
||||
// 用于指示是否找到共享内存结构的变量
|
||||
bool found_bufs = false;
|
||||
bool found_descs = false;
|
||||
bool found_buf_ckpt = false;
|
||||
uint64 buffer_size;
|
||||
|
||||
//在共享内容中初始化缓冲区描述符
|
||||
t_thrd.storage_cxt.BufferDescriptors = (BufferDescPadded *)CACHELINEALIGN(
|
||||
ShmemInitStruct("Buffer Descriptors",
|
||||
TOTAL_BUFFER_NUM * sizeof(BufferDescPadded) + PG_CACHE_LINE_SIZE,
|
||||
&found_descs));
|
||||
|
||||
/* Init candidate buffer list and candidate buffer free map */
|
||||
// 初始化候选缓冲区列表和候选缓冲区空闲映射
|
||||
candidate_buf_init();
|
||||
|
||||
#ifdef __aarch64__
|
||||
#ifdef __aarch64__// 在共享内存中分配缓冲块的内存
|
||||
buffer_size = TOTAL_BUFFER_NUM * (Size)BLCKSZ + PG_CACHE_LINE_SIZE;
|
||||
t_thrd.storage_cxt.BufferBlocks =
|
||||
(char *)CACHELINEALIGN(ShmemInitStruct("Buffer Blocks", buffer_size, &found_bufs));
|
||||
#else
|
||||
#else//在非aarch64时执行以下代码,用于为缓冲区分配共享内存。
|
||||
buffer_size = TOTAL_BUFFER_NUM * (Size)BLCKSZ;
|
||||
t_thrd.storage_cxt.BufferBlocks = (char *)ShmemInitStruct("Buffer Blocks", buffer_size, &found_bufs);
|
||||
#endif
|
||||
|
||||
// 检查是否需要对共享缓冲区进行黑名单处理
|
||||
if (BBOX_BLACKLIST_SHARE_BUFFER) {
|
||||
/* Segment Buffer is exclued from the black list, as it contains many critical information for debug */
|
||||
bbox_blacklist_add(SHARED_BUFFER, t_thrd.storage_cxt.BufferBlocks, NORMAL_SHARED_BUFFER_NUM * (Size)BLCKSZ);
|
||||
|
|
@ -102,24 +105,30 @@ void InitBufferPool(void)
|
|||
* the checkpointer is restarted, memory allocation failures would be
|
||||
* painful.
|
||||
*/
|
||||
// 在共享内存中初始化用于排序检查点缓冲区 ID 的数组
|
||||
g_instance.ckpt_cxt_ctl->CkptBufferIds =
|
||||
(CkptSortItem *)ShmemInitStruct("Checkpoint BufferIds",
|
||||
TOTAL_BUFFER_NUM * sizeof(CkptSortItem), &found_buf_ckpt);
|
||||
|
||||
|
||||
//如果启用了增量检查点,并且脏页队列尚未分配,就分配并初始化该队列。
|
||||
if (ENABLE_INCRE_CKPT && g_instance.ckpt_cxt_ctl->dirty_page_queue == NULL) {
|
||||
// 计算脏页队列所需的大小
|
||||
g_instance.ckpt_cxt_ctl->dirty_page_queue_size = TOTAL_BUFFER_NUM *
|
||||
PAGE_QUEUE_SLOT_MULTI_NBUFFERS;
|
||||
// 切换内存上下文以分配队列
|
||||
MemoryContext oldcontext = MemoryContextSwitchTo(g_instance.increCheckPoint_context);
|
||||
|
||||
// 计算脏页队列的总内存大小
|
||||
Size queue_mem_size = g_instance.ckpt_cxt_ctl->dirty_page_queue_size * sizeof(DirtyPageQueueSlot);
|
||||
// 使用 'palloc_huge' 为脏页队列分配内存
|
||||
g_instance.ckpt_cxt_ctl->dirty_page_queue =
|
||||
(DirtyPageQueueSlot *)palloc_huge(CurrentMemoryContext, queue_mem_size);
|
||||
|
||||
/* The memory of the memset sometimes exceeds 2 GB. so, memset_s cannot be used. */
|
||||
MemSet((char*)g_instance.ckpt_cxt_ctl->dirty_page_queue, 0, queue_mem_size);
|
||||
// 切换回原始的内存上下文
|
||||
(void)MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
// 初始化后台写入程序的哈希表
|
||||
if (g_instance.bgwriter_cxt.unlink_rel_hashtbl == NULL) {
|
||||
g_instance.bgwriter_cxt.unlink_rel_hashtbl = relfilenode_hashtbl_create("unlink_rel_hashtbl", true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,13 @@
|
|||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
*这段代码实现了将BufferTags映射到缓冲区索引的相关功能。
|
||||
*它定义了一些用于在哈希表中查找、插入和删除缓冲区映射的函数。
|
||||
*这些函数用于管理缓冲区标签与实际缓冲区索引之间的映射关系,
|
||||
*从而允许系统根据缓冲区标签快速定位相应的缓冲区。
|
||||
*
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -71,15 +71,15 @@ const int MAX_RETRY_TIMES = 1000;
|
|||
const float NEED_DELAY_RETRY_GET_BUF = 0.8;
|
||||
|
||||
/* Prototypes for internal functions */
|
||||
static BufferDesc* GetBufferFromRing(BufferAccessStrategy strategy, uint32* buf_state);
|
||||
static void AddBufferToRing(BufferAccessStrategy strategy, volatile BufferDesc* buf);
|
||||
void PageListBackWrite(uint32* bufList, int32 n,
|
||||
/* buffer list, bufs to scan, */
|
||||
uint32 flags = 0, /* opt flags */
|
||||
SMgrRelation use_smgrReln = NULL, /* opt relation */
|
||||
int32* bufs_written = NULL, /* opt written count returned */
|
||||
int32* bufs_reusable = NULL); /* opt reusable count returned */
|
||||
static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32* buf_state);
|
||||
static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy, uint32 *buf_state);
|
||||
static void AddBufferToRing(BufferAccessStrategy strategy, volatile BufferDesc *buf);
|
||||
void PageListBackWrite(uint32 *bufList, int32 n,
|
||||
/* buffer list, bufs to scan, */
|
||||
uint32 flags = 0, /* opt flags */
|
||||
SMgrRelation use_smgrReln = NULL, /* opt relation */
|
||||
int32 *bufs_written = NULL, /* opt written count returned */
|
||||
int32 *bufs_reusable = NULL); /* opt reusable count returned */
|
||||
static BufferDesc *get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32 *buf_state);
|
||||
|
||||
static void perform_delay(StrategyDelayStatus *status)
|
||||
{
|
||||
|
|
@ -99,7 +99,6 @@ static void perform_delay(StrategyDelayStatus *status)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ClockSweepTick - Helper routine for StrategyGetBuffer()
|
||||
*
|
||||
|
|
@ -177,7 +176,7 @@ static inline uint32 ClockSweepTick(int max_nbuffer_can_use)
|
|||
* If the fraction is too small, we will increase dynamiclly to avoid elog(ERROR)
|
||||
* in `Startup' process because of ERROR will promote to FATAL.
|
||||
*/
|
||||
BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint32* buf_state)
|
||||
BufferDesc *StrategyGetBuffer(BufferAccessStrategy strategy, uint32 *buf_state)
|
||||
{
|
||||
BufferDesc *buf = NULL;
|
||||
int bgwproc_no;
|
||||
|
|
@ -185,8 +184,8 @@ BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint32* buf_state)
|
|||
uint32 local_buf_state = 0; /* to avoid repeated (de-)referencing */
|
||||
int max_buffer_can_use;
|
||||
bool am_standby = RecoveryInProgress();
|
||||
StrategyDelayStatus retry_lock_status = { 0, 0 };
|
||||
StrategyDelayStatus retry_buf_status = { 0, 0 };
|
||||
StrategyDelayStatus retry_lock_status = {0, 0};
|
||||
StrategyDelayStatus retry_buf_status = {0, 0};
|
||||
|
||||
/*
|
||||
* If given a strategy object, see whether it can select a buffer. We
|
||||
|
|
@ -352,7 +351,7 @@ int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc)
|
|||
* Additionally add the number of wraparounds that happened before
|
||||
* completePasses could be incremented. C.f. ClockSweepTick().
|
||||
*/
|
||||
*complete_passes += next_victim_buffer / (unsigned int) NORMAL_SHARED_BUFFER_NUM;
|
||||
*complete_passes += next_victim_buffer / (unsigned int)NORMAL_SHARED_BUFFER_NUM;
|
||||
}
|
||||
|
||||
if (num_buf_alloc != NULL) {
|
||||
|
|
@ -487,7 +486,7 @@ BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
|
|||
break;
|
||||
case BAS_VACUUM:
|
||||
ring_size = g_instance.attr.attr_storage.NBuffers / 32 /
|
||||
Max(g_instance.attr.attr_storage.autovacuum_max_workers, 1);
|
||||
Max(g_instance.attr.attr_storage.autovacuum_max_workers, 1);
|
||||
break;
|
||||
case BAS_REPAIR:
|
||||
ring_size = Min(g_instance.attr.attr_storage.NBuffers, MIN_REPAIR_FILE_SLOT_NUM);
|
||||
|
|
@ -609,7 +608,7 @@ RETRY:
|
|||
if (retry_times < Min(MAX_RETRY_RING_TIMES, strategy->ring_size * MAX_RETRY_RING_PCT)) {
|
||||
goto RETRY;
|
||||
} else if (get_curr_candidate_nums(false) >= (uint32)g_instance.attr.attr_storage.NBuffers *
|
||||
u_sess->attr.attr_storage.candidate_buf_percent_target){
|
||||
u_sess->attr.attr_storage.candidate_buf_percent_target) {
|
||||
strategy->current_was_in_ring = false;
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -617,8 +616,7 @@ RETRY:
|
|||
|
||||
local_buf_state = LockBufHdr(buf);
|
||||
if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 && BUF_STATE_GET_USAGECOUNT(local_buf_state) <= 1 &&
|
||||
(backend_can_flush_dirty_page() || !(local_buf_state & BM_DIRTY)) &&
|
||||
!(local_buf_state & BM_IS_META)) {
|
||||
(backend_can_flush_dirty_page() || !(local_buf_state & BM_DIRTY)) && !(local_buf_state & BM_IS_META)) {
|
||||
strategy->current_was_in_ring = true;
|
||||
*buf_state = local_buf_state;
|
||||
return buf;
|
||||
|
|
@ -673,26 +671,59 @@ bool StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:根据缓冲区访问策略获取环形预取的数量和触发器值
|
||||
*
|
||||
* 参数列表:
|
||||
* strategy:缓冲区访问策略
|
||||
* quantity:用于存储预取数量的指针
|
||||
* trigger:用于存储预取触发器值的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于确定环形预取的数量和触发器值,以优化磁盘I/O性能。
|
||||
* 如果传入的策略为空或者不是BAS_BULKREAD类型,则不进行预取设置。
|
||||
* 预取数量和触发器值将基于环形缓冲区的大小和配置参数来计算。
|
||||
*/
|
||||
void StrategyGetRingPrefetchQuantityAndTrigger(BufferAccessStrategy strategy, int *quantity, int *trigger)
|
||||
{
|
||||
int threshold;
|
||||
int prefetch_trigger = u_sess->attr.attr_storage.prefetch_quantity;
|
||||
int prefetch_trigger = u_sess->attr.attr_storage.prefetch_quantity; // 获取预取触发器的值,从配置参数中获取
|
||||
|
||||
if (strategy == NULL || strategy->btype != BAS_BULKREAD) {
|
||||
return;
|
||||
return; // 如果传入的策略为空或者不是BAS_BULKREAD类型,直接返回,不进行预取设置
|
||||
}
|
||||
|
||||
// 预取阈值设置为缓冲策略的环形缓冲区大小的1/4
|
||||
threshold = strategy->ring_size / 4;
|
||||
|
||||
if (quantity != NULL) {
|
||||
// 如果传入的quantity参数不为空,则设置quantity为配置参数中的prefetch_quantity或者threshold中的较小值
|
||||
*quantity = (threshold > u_sess->attr.attr_storage.prefetch_quantity)
|
||||
? u_sess->attr.attr_storage.prefetch_quantity
|
||||
: threshold;
|
||||
}
|
||||
|
||||
if (trigger != NULL) {
|
||||
// 如果传入的trigger参数不为空,则设置trigger为prefetch_trigger或者threshold中的较小值
|
||||
*trigger = (threshold > prefetch_trigger) ? prefetch_trigger : threshold;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:唤醒页写入线程
|
||||
*
|
||||
* 参数列表:
|
||||
* 无
|
||||
*
|
||||
* 返回值:
|
||||
* 无
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于唤醒页写入线程,以便其继续处理待写入的数据。
|
||||
* 它检查页写入线程是否存在,如果存在则触发线程的处理。
|
||||
*/
|
||||
void wakeup_pagewriter_thread()
|
||||
{
|
||||
PageWriterProc *pgwr = &g_instance.ckpt_cxt_ctl->pgwr_procs.writer_proc[0];
|
||||
|
|
@ -703,45 +734,66 @@ void wakeup_pagewriter_thread()
|
|||
return;
|
||||
}
|
||||
|
||||
const int CANDIDATE_DIRTY_LIST_LEN = 100;
|
||||
const float HIGH_WATER = 0.75;
|
||||
static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32* buf_state)
|
||||
const int CANDIDATE_DIRTY_LIST_LEN = 100; // 定义候选脏页列表的最大长度
|
||||
const float HIGH_WATER = 0.75; // 定义脏页高水位线比例
|
||||
/*
|
||||
* 功能:从候选列表中获取缓冲区描述符
|
||||
*
|
||||
* 参数列表:
|
||||
* strategy:缓冲区访问策略
|
||||
* buf_state:指向存储缓冲区状态的变量的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 指向缓冲区描述符的指针,如果没有可用缓冲区则返回 NULL
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于从候选列表中获取缓冲区描述符,并根据策略选择合适的缓冲区。
|
||||
* 在获取缓冲区之前,它会检查缓冲区的可用性和状态,并可能触发页写入线程。
|
||||
*/
|
||||
static BufferDesc *get_buf_from_candidate_list(BufferAccessStrategy strategy, uint32 *buf_state)
|
||||
{
|
||||
BufferDesc* buf = NULL;
|
||||
uint32 local_buf_state;
|
||||
int buf_id = 0;
|
||||
int list_num = g_instance.ckpt_cxt_ctl->pgwr_procs.sub_num;
|
||||
int list_id = 0;
|
||||
volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry;
|
||||
Buffer *candidate_dirty_list = NULL;
|
||||
int dirty_list_num = 0;
|
||||
bool enable_available = false;
|
||||
bool need_push_dirst_list = false;
|
||||
BufferDesc *buf = NULL; // 缓冲区描述符指针
|
||||
uint32 local_buf_state; // 本地缓冲区状态
|
||||
int buf_id = 0; // 缓冲区ID
|
||||
int list_num = g_instance.ckpt_cxt_ctl->pgwr_procs.sub_num; // 子进程数量
|
||||
int list_id = 0; // 列表ID
|
||||
volatile PgBackendStatus *beentry = t_thrd.shemem_ptr_cxt.MyBEEntry; // 获取当前线程的后端状态信息
|
||||
Buffer *candidate_dirty_list = NULL; // 存储候选脏页的列表
|
||||
int dirty_list_num = 0; // 候选脏页列表中的脏页数量
|
||||
bool enable_available = false; // 是否可用标志
|
||||
bool need_push_dirst_list = false; // 是否需要将缓冲区添加到候选脏页列表的标志
|
||||
bool need_scan_dirty =
|
||||
(g_instance.ckpt_cxt_ctl->actual_dirty_page_num / (float)(g_instance.attr.attr_storage.NBuffers) > HIGH_WATER)
|
||||
&& backend_can_flush_dirty_page();
|
||||
(g_instance.ckpt_cxt_ctl->actual_dirty_page_num / (float)(g_instance.attr.attr_storage.NBuffers) >
|
||||
HIGH_WATER) &&
|
||||
backend_can_flush_dirty_page(); // 是否需要扫描脏页的标志,根据脏页占用比例和是否允许刷新脏页决定
|
||||
|
||||
if (need_scan_dirty) {
|
||||
/*Not return the dirty page when there are few dirty pages */
|
||||
candidate_dirty_list = (Buffer*)palloc0(sizeof(Buffer) * CANDIDATE_DIRTY_LIST_LEN);
|
||||
/* 分配用于保存脏页的候选列表 */
|
||||
candidate_dirty_list = (Buffer *)palloc0(sizeof(Buffer) * CANDIDATE_DIRTY_LIST_LEN);
|
||||
}
|
||||
|
||||
/* 计算列表 ID */
|
||||
list_id = beentry->st_tid > 0 ? (beentry->st_tid % list_num) : (beentry->st_sessionid % list_num);
|
||||
|
||||
/* 遍历候选列表 */
|
||||
for (int i = 0; i < list_num; i++) {
|
||||
/* the pagewriter sub thread store normal buffer pool, sub thread starts from 1 */
|
||||
/* 子进程的ID,从1开始 */
|
||||
int thread_id = (list_id + i) % list_num + 1;
|
||||
Assert(thread_id > 0 && thread_id <= list_num);
|
||||
|
||||
while (candidate_buf_pop(&buf_id, thread_id)) {
|
||||
Assert(buf_id < SegmentBufferStartID);
|
||||
buf = GetBufferDescriptor(buf_id);
|
||||
local_buf_state = LockBufHdr(buf);
|
||||
buf = GetBufferDescriptor(buf_id); // 获取缓冲区描述符
|
||||
local_buf_state = LockBufHdr(buf); // 锁定缓冲区头部
|
||||
|
||||
if (g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id]) {
|
||||
g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id] = false;
|
||||
enable_available = BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 && !(local_buf_state & BM_IS_META);
|
||||
need_push_dirst_list = need_scan_dirty && dirty_list_num < CANDIDATE_DIRTY_LIST_LEN &&
|
||||
free_space_enough(buf_id);
|
||||
need_push_dirst_list =
|
||||
need_scan_dirty && dirty_list_num < CANDIDATE_DIRTY_LIST_LEN && free_space_enough(buf_id);
|
||||
|
||||
if (enable_available) {
|
||||
/* 如果缓冲区可用,将其添加到策略环中 */
|
||||
if (NEED_CONSIDER_USECOUNT && BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0) {
|
||||
local_buf_state -= BUF_USAGECOUNT_ONE;
|
||||
} else if (!(local_buf_state & BM_DIRTY)) {
|
||||
|
|
@ -758,10 +810,11 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui
|
|||
}
|
||||
}
|
||||
}
|
||||
UnlockBufHdr(buf, local_buf_state);
|
||||
UnlockBufHdr(buf, local_buf_state); // 解锁缓冲区头部
|
||||
}
|
||||
}
|
||||
|
||||
/* 唤醒 PageWriter 线程 */
|
||||
wakeup_pagewriter_thread();
|
||||
|
||||
if (need_scan_dirty) {
|
||||
|
|
@ -769,9 +822,11 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui
|
|||
buf_id = candidate_dirty_list[i];
|
||||
buf = GetBufferDescriptor(buf_id);
|
||||
local_buf_state = LockBufHdr(buf);
|
||||
enable_available = (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0) && !(local_buf_state & BM_IS_META)
|
||||
&& free_space_enough(buf_id);
|
||||
enable_available = (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0) && !(local_buf_state & BM_IS_META) &&
|
||||
free_space_enough(buf_id);
|
||||
|
||||
if (enable_available) {
|
||||
/* 如果缓冲区可用,将其添加到策略环中 */
|
||||
if (strategy != NULL) {
|
||||
AddBufferToRing(strategy, buf);
|
||||
}
|
||||
|
|
@ -779,13 +834,13 @@ static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, ui
|
|||
pfree(candidate_dirty_list);
|
||||
return buf;
|
||||
}
|
||||
UnlockBufHdr(buf, local_buf_state);
|
||||
UnlockBufHdr(buf, local_buf_state); // 解锁缓冲区头部
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate_dirty_list != NULL) {
|
||||
pfree(candidate_dirty_list);
|
||||
pfree(candidate_dirty_list); // 释放候选脏页列表内存
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL; // 返回 NULL,表示没有可用的缓冲区
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ void LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum, BlockNumber bloc
|
|||
InitLocalBuffers();
|
||||
|
||||
/* See if the desired buffer already exists */
|
||||
hresult = (LocalBufferLookupEnt*)hash_search(u_sess->storage_cxt.LocalBufHash, (void*)&new_tag, HASH_FIND, NULL);
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&new_tag, HASH_FIND, NULL);
|
||||
if (hresult != NULL) {
|
||||
/* Yes, so nothing to do */
|
||||
return;
|
||||
|
|
@ -95,7 +95,17 @@ void LocalBufferFlushForExtremRTO(BufferDesc *bufHdr)
|
|||
}
|
||||
FlushBuffer(bufHdr, NULL, WITH_LOCAL_CACHE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:刷新所有本地缓冲区中的脏缓冲区
|
||||
*
|
||||
* 参数列表:无
|
||||
*
|
||||
* 返回值:无
|
||||
*
|
||||
* 注意:
|
||||
* 此函数遍历所有本地缓冲区,检查并刷新所有脏缓冲区。
|
||||
* 本地缓冲区是特定于本地文件系统的缓冲区。
|
||||
*/
|
||||
void LocalBufferFlushAllBuffer()
|
||||
{
|
||||
int i;
|
||||
|
|
@ -104,25 +114,45 @@ void LocalBufferFlushAllBuffer()
|
|||
BufferDesc *bufHdr = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
uint32 buf_state;
|
||||
|
||||
/* 获取缓冲区状态 */
|
||||
buf_state = pg_atomic_read_u32(&bufHdr->state);
|
||||
|
||||
/* 断言:本地引用计数为0,确保没有被其他地方引用 */
|
||||
Assert(u_sess->storage_cxt.LocalRefCount[i] == 0);
|
||||
|
||||
/* 如果缓冲区是有效的且脏的 */
|
||||
if ((buf_state & BM_VALID) && (buf_state & BM_DIRTY)) {
|
||||
/* 执行极端RTO时的本地缓冲区刷新 */
|
||||
LocalBufferFlushForExtremRTO(bufHdr);
|
||||
|
||||
/* 清除脏标志位 */
|
||||
buf_state &= ~BM_DIRTY;
|
||||
pg_atomic_write_u32(&bufHdr->state, buf_state);
|
||||
|
||||
/* 更新统计信息:本地块写入计数 */
|
||||
u_sess->instr_cxt.pg_buffer_usage->local_blks_written++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查本地缓冲区的一致性
|
||||
*
|
||||
* 参数列表:
|
||||
* tag1:第一个缓冲区标签
|
||||
* tag2:第二个缓冲区标签
|
||||
*
|
||||
* 返回值:无
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查两个本地缓冲区标签是否相等,如果不相等,则引发数据损坏错误。
|
||||
* 本地缓冲区标签用于标识特定的本地缓冲区。
|
||||
*/
|
||||
static void LocalBufferSanityCheck(BufferTag tag1, BufferTag tag2)
|
||||
{
|
||||
/* 如果两个缓冲区标签不相等 */
|
||||
if (!BUFFERTAGS_EQUAL(tag1, tag2)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
|
||||
(errmsg("local buffer hash tag mismatch."))));
|
||||
/* 引发数据损坏错误 */
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash tag mismatch."))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +165,22 @@ static void LocalBufferSanityCheck(BufferTag tag1, BufferTag tag2)
|
|||
* does not get set. Lastly, we support only default access strategy
|
||||
* (hence, usage_count is always advanced).
|
||||
*/
|
||||
/*
|
||||
* 功能:分配本地缓冲区并返回缓冲区描述符
|
||||
*
|
||||
* 参数列表:
|
||||
* smgr:存储管理器关系
|
||||
* forkNum:分叉号
|
||||
* blockNum:块号
|
||||
* foundPtr:指向存储是否找到的指针的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 指向缓冲区描述符的指针,如果未找到可用缓冲区则返回 NULL
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于分配本地缓冲区,并返回与给定块标识符关联的缓冲区描述符。
|
||||
* 如果缓冲区已存在,则直接返回,否则分配新的缓冲区。
|
||||
*/
|
||||
BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, bool *foundPtr)
|
||||
{
|
||||
BufferTag new_tag; /* identity of requested block */
|
||||
|
|
@ -152,7 +198,7 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber
|
|||
InitLocalBuffers();
|
||||
|
||||
/* See if the desired buffer already exists */
|
||||
hresult = (LocalBufferLookupEnt*)hash_search(u_sess->storage_cxt.LocalBufHash, (void*)&new_tag, HASH_FIND, NULL);
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&new_tag, HASH_FIND, NULL);
|
||||
if (hresult != NULL) {
|
||||
b = hresult->id;
|
||||
buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[b];
|
||||
|
|
@ -257,8 +303,8 @@ BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber
|
|||
pg_atomic_write_u32(&buf_desc->state, buf_state);
|
||||
}
|
||||
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&new_tag, HASH_ENTER,
|
||||
&found);
|
||||
hresult =
|
||||
(LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&new_tag, HASH_ENTER, &found);
|
||||
if (found) /* shouldn't happen */
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted."))));
|
||||
hresult->id = b;
|
||||
|
|
@ -325,8 +371,8 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B
|
|||
int i;
|
||||
|
||||
for (i = 0; i < u_sess->storage_cxt.NLocBuffer; i++) {
|
||||
BufferDesc* buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
LocalBufferLookupEnt* hresult = NULL;
|
||||
BufferDesc *buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
LocalBufferLookupEnt *hresult = NULL;
|
||||
uint32 buf_state;
|
||||
|
||||
buf_state = pg_atomic_read_u32(&buf_desc->state);
|
||||
|
|
@ -335,15 +381,14 @@ void DropRelFileNodeLocalBuffers(const RelFileNode &rnode, ForkNumber forkNum, B
|
|||
buf_desc->tag.forkNum == forkNum && buf_desc->tag.blockNum >= firstDelBlock) {
|
||||
if (u_sess->storage_cxt.LocalRefCount[i] != 0) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_BUFFER_REFERENCE),
|
||||
(errmsg("block %u of %s is still referenced (local %d)",
|
||||
buf_desc->tag.blockNum,
|
||||
relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum),
|
||||
u_sess->storage_cxt.LocalRefCount[i]))));
|
||||
(errcode(ERRCODE_INVALID_BUFFER_REFERENCE),
|
||||
(errmsg("block %u of %s is still referenced (local %d)", buf_desc->tag.blockNum,
|
||||
relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum),
|
||||
u_sess->storage_cxt.LocalRefCount[i]))));
|
||||
}
|
||||
/* Remove entry from hashtable */
|
||||
hresult = (LocalBufferLookupEnt*)hash_search(
|
||||
u_sess->storage_cxt.LocalBufHash, (void*)&buf_desc->tag, HASH_REMOVE, NULL);
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&buf_desc->tag,
|
||||
HASH_REMOVE, NULL);
|
||||
if (hresult == NULL) /* shouldn't happen */
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted."))));
|
||||
/* Mark buffer invalid */
|
||||
|
|
@ -367,8 +412,8 @@ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < u_sess->storage_cxt.NLocBuffer; i++) {
|
||||
BufferDesc* buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
LocalBufferLookupEnt* hresult = NULL;
|
||||
BufferDesc *buf_desc = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
LocalBufferLookupEnt *hresult = NULL;
|
||||
uint32 buf_state;
|
||||
|
||||
buf_state = pg_atomic_read_u32(&buf_desc->state);
|
||||
|
|
@ -380,15 +425,14 @@ void DropRelFileNodeAllLocalBuffers(const RelFileNode &rnode)
|
|||
errmsg("fork number should not be less than zero")));
|
||||
}
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_BUFFER_REFERENCE),
|
||||
(errmsg("block %u of %s is still referenced (local %d)",
|
||||
buf_desc->tag.blockNum,
|
||||
relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum),
|
||||
u_sess->storage_cxt.LocalRefCount[i]))));
|
||||
(errcode(ERRCODE_INVALID_BUFFER_REFERENCE),
|
||||
(errmsg("block %u of %s is still referenced (local %d)", buf_desc->tag.blockNum,
|
||||
relpathbackend(buf_desc->tag.rnode, BackendIdForTempRelations, buf_desc->tag.forkNum),
|
||||
u_sess->storage_cxt.LocalRefCount[i]))));
|
||||
}
|
||||
/* Remove entry from hashtable */
|
||||
hresult = (LocalBufferLookupEnt*)hash_search(
|
||||
u_sess->storage_cxt.LocalBufHash, (void*)&buf_desc->tag, HASH_REMOVE, NULL);
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(u_sess->storage_cxt.LocalBufHash, (void *)&buf_desc->tag,
|
||||
HASH_REMOVE, NULL);
|
||||
if (hresult == NULL) /* shouldn't happen */
|
||||
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), (errmsg("local buffer hash table corrupted."))));
|
||||
/* Mark buffer invalid */
|
||||
|
|
@ -413,12 +457,12 @@ static void InitLocalBuffers(void)
|
|||
int i;
|
||||
|
||||
/* Allocate and zero buffer headers and auxiliary arrays */
|
||||
u_sess->storage_cxt.LocalBufferDescriptors = (BufferDesc*)MemoryContextAllocZero(
|
||||
u_sess->storage_cxt.LocalBufferDescriptors = (BufferDesc *)MemoryContextAllocZero(
|
||||
SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), (unsigned int)nbufs * sizeof(BufferDesc));
|
||||
u_sess->storage_cxt.LocalBufferBlockPointers = (Block*)MemoryContextAllocZero(
|
||||
u_sess->storage_cxt.LocalBufferBlockPointers = (Block *)MemoryContextAllocZero(
|
||||
SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), (unsigned int)nbufs * sizeof(Block));
|
||||
u_sess->storage_cxt.LocalRefCount = (int32*)MemoryContextAllocZero(
|
||||
SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), (unsigned int)nbufs * sizeof(int32));
|
||||
u_sess->storage_cxt.LocalRefCount = (int32 *)MemoryContextAllocZero(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE),
|
||||
(unsigned int)nbufs * sizeof(int32));
|
||||
if (!u_sess->storage_cxt.LocalBufferDescriptors || !u_sess->storage_cxt.LocalBufferBlockPointers ||
|
||||
!u_sess->storage_cxt.LocalRefCount)
|
||||
ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
|
||||
|
|
@ -427,7 +471,7 @@ static void InitLocalBuffers(void)
|
|||
|
||||
/* initialize fields that need to start off nonzero */
|
||||
for (i = 0; i < nbufs; i++) {
|
||||
BufferDesc* buf = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
BufferDesc *buf = &u_sess->storage_cxt.LocalBufferDescriptors[i];
|
||||
|
||||
/*
|
||||
* negative to indicate local buffer. This is tricky: shared buffers
|
||||
|
|
@ -446,12 +490,11 @@ static void InitLocalBuffers(void)
|
|||
info.hash = tag_hash;
|
||||
info.hcxt = SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE);
|
||||
|
||||
u_sess->storage_cxt.LocalBufHash = hash_create(
|
||||
"Local Buffer Lookup Table", nbufs, &info, HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);
|
||||
u_sess->storage_cxt.LocalBufHash =
|
||||
hash_create("Local Buffer Lookup Table", nbufs, &info, HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);
|
||||
|
||||
if (!u_sess->storage_cxt.LocalBufHash) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INITIALIZE_FAILED),
|
||||
(errmsg("could not initialize local buffer hash table."))));
|
||||
ereport(ERROR, (errcode(ERRCODE_INITIALIZE_FAILED), (errmsg("could not initialize local buffer hash table."))));
|
||||
}
|
||||
|
||||
/* Initialization done, mark buffers allocated */
|
||||
|
|
@ -483,11 +526,9 @@ static Block GetLocalBufferStorage(void)
|
|||
* output. Create the context on first use.
|
||||
*/
|
||||
if (u_sess->storage_cxt.LocalBufferContext == NULL)
|
||||
u_sess->storage_cxt.LocalBufferContext = AllocSetContextCreate(u_sess->top_mem_cxt,
|
||||
"LocalBufferContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
u_sess->storage_cxt.LocalBufferContext =
|
||||
AllocSetContextCreate(u_sess->top_mem_cxt, "LocalBufferContext", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
/* Start with a 16-buffer request; subsequent ones double each time */
|
||||
num_bufs = Max(u_sess->storage_cxt.num_bufs_in_block * 2, 16);
|
||||
|
|
@ -497,7 +538,7 @@ static Block GetLocalBufferStorage(void)
|
|||
num_bufs = Min((unsigned int)(num_bufs), MaxAllocSize / BLCKSZ);
|
||||
|
||||
u_sess->storage_cxt.cur_block =
|
||||
(char*)MemoryContextAlloc(u_sess->storage_cxt.LocalBufferContext, num_bufs * BLCKSZ);
|
||||
(char *)MemoryContextAlloc(u_sess->storage_cxt.LocalBufferContext, num_bufs * BLCKSZ);
|
||||
u_sess->storage_cxt.next_buf_in_block = 0;
|
||||
u_sess->storage_cxt.num_bufs_in_block = num_bufs;
|
||||
}
|
||||
|
|
@ -560,10 +601,10 @@ void AtProcExit_LocalBuffers(void)
|
|||
void ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum)
|
||||
{
|
||||
SMgrRelation smgr = smgropen(rnode, t_thrd.proc_cxt.MyBackendId);
|
||||
BufferTag tag; /* identity of target block */
|
||||
BufferTag tag; /* identity of target block */
|
||||
LocalBufferLookupEnt *hresult;
|
||||
BufferDesc *bufHdr;
|
||||
uint32 bufState;
|
||||
uint32 bufState;
|
||||
|
||||
/*
|
||||
* If somehow this is the first request in the session, there's nothing to
|
||||
|
|
@ -577,8 +618,7 @@ void ForgetLocalBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockN
|
|||
INIT_BUFFERTAG(tag, smgr->smgr_rnode.node, forkNum, blockNum);
|
||||
|
||||
/* see if the block is in the local buffer pool */
|
||||
hresult = (LocalBufferLookupEnt *)
|
||||
hash_search(t_thrd.storage_cxt.LocalBufHash, (void *) &tag, HASH_REMOVE, NULL);
|
||||
hresult = (LocalBufferLookupEnt *)hash_search(t_thrd.storage_cxt.LocalBufHash, (void *)&tag, HASH_REMOVE, NULL);
|
||||
|
||||
/* didn't find it, so nothing to do */
|
||||
if (!hresult) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -49,16 +49,38 @@ char *generate_unique_cache_name_prefix(Oid oid, uint32 distSessionKey)
|
|||
tmpname[MAXPGPATH - 1] = '\0';
|
||||
return pstrdup(tmpname);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:生成唯一的缓存名称前缀
|
||||
*
|
||||
* 参数列表:
|
||||
* relname:关系名称,用于构建缓存名称的一部分
|
||||
*
|
||||
* 返回值:
|
||||
* 指向生成的缓存名称前缀的指针
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于创建一个唯一的缓存名称前缀,通常用于标识缓存数据的唯一性。
|
||||
* 缓存名称前缀包括关系名、拷贝ID和进程ID。
|
||||
*/
|
||||
char *generate_unique_cache_name_prefix(const char *relname)
|
||||
{
|
||||
// 创建一个临时字符数组用于存储缓存名称前缀
|
||||
char tmpname[MAXPGPATH];
|
||||
|
||||
// 生成一个唯一的拷贝ID,通常用于标识数据副本
|
||||
uint32 copyId = generate_unique_id(>_copyId);
|
||||
;
|
||||
|
||||
// 使用snprintf_s函数构造缓存名称前缀字符串,包括关系名、拷贝ID和进程ID
|
||||
int rc = snprintf_s(tmpname, sizeof(tmpname), sizeof(tmpname) - 1, "%s.%u.%lu.cache", relname, copyId,
|
||||
t_thrd.proc_cxt.MyProcPid);
|
||||
|
||||
// 检查snprintf_s的返回值,确保格式化操作没有发生错误
|
||||
securec_check_ss(rc, "", "");
|
||||
|
||||
// 将tmpname字符串的最后一个字符设为终止符,以确保字符串的正确终止
|
||||
tmpname[MAXPGPATH - 1] = '\0';
|
||||
|
||||
// 使用pstrdup函数为生成的字符串分配内存并返回
|
||||
return pstrdup(tmpname);
|
||||
}
|
||||
|
||||
|
|
@ -90,185 +112,340 @@ void unlink_local_cache_file(const char *prefix, const uint32 smpId)
|
|||
UnlinkCacheFile(localfile);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将BaseError对象的数据序列化到StringInfo缓冲区中
|
||||
*
|
||||
* 参数列表:
|
||||
* buf:用于存储序列化数据的StringInfo缓冲区
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将BaseError对象的数据序列化,以便后续传输或存储。它首先处理最大值,
|
||||
* 然后对每个属性进行处理,根据属性的类型和长度发送相应的数据到缓冲区中。
|
||||
*/
|
||||
void BaseError::Serialize(StringInfo buf)
|
||||
{
|
||||
// 获取关系描述和属性数组
|
||||
Form_pg_attribute *attrs = m_desc->attrs;
|
||||
int natts = m_desc->natts;
|
||||
|
||||
// 断言属性数量与最大值匹配
|
||||
Assert(m_desc->natts == MaxNumOfValue);
|
||||
|
||||
// 调用 serializeMaxNumOfValue 函数,将某种限制值序列化到缓冲区
|
||||
serializeMaxNumOfValue(buf);
|
||||
|
||||
// 循环处理每个属性
|
||||
for (int i = 0; i < natts; ++i) {
|
||||
Datum attr = m_values[i];
|
||||
Datum attr = m_values[i]; // 获取属性的数据
|
||||
if (m_isNull[i]) {
|
||||
// 如果属性为NULL,发送特殊标志 UNSIGNED_MINUS_ONE
|
||||
pq_sendint32(buf, UNSIGNED_MINUS_ONE);
|
||||
continue;
|
||||
}
|
||||
if (attrs[i]->attlen > 0 && attrs[i]->attlen <= 8) {
|
||||
pq_sendint32(buf, attrs[i]->attlen);
|
||||
pq_sendbytes(buf, (char *)&attr, attrs[i]->attlen);
|
||||
// 如果属性长度大于0且小于等于8,发送属性长度和属性数据
|
||||
pq_sendint32(buf, attrs[i]->attlen); // 发送属性长度
|
||||
pq_sendbytes(buf, (char *)&attr, attrs[i]->attlen); // 发送属性数据
|
||||
} else if (attrs[i]->attlen > 8) {
|
||||
pq_sendint32(buf, attrs[i]->attlen);
|
||||
pq_sendbytes(buf, DatumGetPointer(attr), attrs[i]->attlen);
|
||||
// 如果属性长度大于8,发送属性长度和属性数据
|
||||
pq_sendint32(buf, attrs[i]->attlen); // 发送属性长度
|
||||
pq_sendbytes(buf, DatumGetPointer(attr), attrs[i]->attlen); // 发送属性数据
|
||||
} else {
|
||||
pq_sendint32(buf, VARSIZE(attr) - VARHDRSZ);
|
||||
pq_sendbytes(buf, VARDATA(attr), VARSIZE(attr) - VARHDRSZ);
|
||||
// 处理变长属性,发送属性数据的长度和属性数据
|
||||
pq_sendint32(buf, VARSIZE(attr) - VARHDRSZ); // 发送属性数据的长度
|
||||
pq_sendbytes(buf, VARDATA(attr), VARSIZE(attr) - VARHDRSZ); // 发送属性数据
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:从StringInfo缓冲区中反序列化数据到BaseError对象
|
||||
*
|
||||
* 参数列表:
|
||||
* buf:包含序列化数据的StringInfo缓冲区
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于从StringInfo缓冲区中读取序列化数据,并将其反序列化到BaseError对象中。
|
||||
*/
|
||||
void BaseError::Deserialize(StringInfo buf)
|
||||
{
|
||||
int natts;
|
||||
Form_pg_attribute *attrs = m_desc->attrs;
|
||||
int natts; // 属性数量
|
||||
Form_pg_attribute *attrs = m_desc->attrs; // 关系描述中的属性数组
|
||||
|
||||
// 从缓冲区中获取属性数量
|
||||
natts = pq_getmsgint(buf, 2);
|
||||
|
||||
// 检查属性数量是否合法
|
||||
if (natts != MaxNumOfValue || natts != m_desc->natts) {
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED), errmsg("Found invalid error recored")));
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED), errmsg("Found invalid error record")));
|
||||
}
|
||||
|
||||
// 循环处理每个属性
|
||||
for (int i = 0; i < natts; ++i) {
|
||||
int len = pq_getmsgint(buf, 4);
|
||||
int len = pq_getmsgint(buf, 4); // 获取属性数据的长度
|
||||
|
||||
if (len == -1) {
|
||||
// 如果长度为-1,表示属性为NULL
|
||||
m_isNull[i] = true;
|
||||
m_values[i] = (Datum)0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(len < 0)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error recored: negative length that is not -1.")));
|
||||
// 长度小于0是无效的,抛出错误
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error record: negative length that is not -1.")));
|
||||
}
|
||||
|
||||
m_isNull[i] = false;
|
||||
|
||||
if (attrs[i]->attlen > 0 && attrs[i]->attlen <= 8) {
|
||||
// 处理定长属性
|
||||
if (unlikely(len != m_desc->attrs[i]->attlen)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error recored: length is not the same as the attribute length.")));
|
||||
// 长度不等于属性长度是无效的,抛出错误
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error record: length is not the same as the attribute length.")));
|
||||
}
|
||||
pq_copymsgbytes(buf, (char*)&m_values[i], len);
|
||||
pq_copymsgbytes(buf, (char *)&m_values[i], len); // 从缓冲区复制属性数据
|
||||
} else if (attrs[i]->attlen > 8) {
|
||||
// 处理变长属性
|
||||
if (unlikely(len != m_desc->attrs[i]->attlen)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error recored: length is not the same as the attribute length.")));
|
||||
// 长度不等于属性长度是无效的,抛出错误
|
||||
ereport(ERROR, (errcode(ERRCODE_OPERATE_RESULT_NOT_EXPECTED),
|
||||
errmsg("Found invalid error record: length is not the same as the attribute length.")));
|
||||
}
|
||||
m_values[i] = (Datum)palloc(len);
|
||||
pq_copymsgbytes(buf, DatumGetPointer(m_values[i]), len);
|
||||
m_values[i] = (Datum)palloc(len); // 分配内存以存储属性数据
|
||||
pq_copymsgbytes(buf, DatumGetPointer(m_values[i]), len); // 从缓冲区复制属性数据
|
||||
} else {
|
||||
m_values[i] = (Datum)palloc(VARHDRSZ + len);
|
||||
SET_VARSIZE(m_values[i], VARHDRSZ + len);
|
||||
pq_copymsgbytes(buf, VARDATA(m_values[i]), len);
|
||||
// 处理变长属性
|
||||
m_values[i] = (Datum)palloc(VARHDRSZ + len); // 分配内存以存储属性数据(包括长度信息)
|
||||
SET_VARSIZE(m_values[i], VARHDRSZ + len); // 设置变长数据的长度信息
|
||||
pq_copymsgbytes(buf, VARDATA(m_values[i]), len); // 从缓冲区复制属性数据
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置BaseError对象的状态
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将BaseError对象的状态重置,包括将所有属性的IsNull标志和值清零。
|
||||
*/
|
||||
void BaseError::Reset()
|
||||
{
|
||||
errno_t rc;
|
||||
|
||||
// 使用memset_s函数将 m_isNull 数组的所有元素清零
|
||||
rc = memset_s(m_isNull, sizeof(bool) * MaxNumOfValue, 0, sizeof(bool) * MaxNumOfValue);
|
||||
securec_check(rc, "", "");
|
||||
|
||||
// 使用memset_s函数将 m_values 数组的所有元素清零
|
||||
rc = memset_s(m_values, sizeof(Datum) * MaxNumOfValue, 0, sizeof(Datum) * MaxNumOfValue);
|
||||
securec_check(rc, "", "");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将最大值(MaxNumOfValue)序列化到StringInfo缓冲区中
|
||||
*
|
||||
* 参数列表:
|
||||
* buf:用于存储序列化数据的StringInfo缓冲区
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将预定义的最大值(MaxNumOfValue)序列化到给定的StringInfo缓冲区中,
|
||||
* 以便在序列化错误信息时使用。
|
||||
*/
|
||||
void ImportError::serializeMaxNumOfValue(StringInfo buf)
|
||||
{
|
||||
// 使用 pq_sendint16 函数将 MaxNumOfValue 序列化为一个16位整数,并写入缓冲区
|
||||
pq_sendint16(buf, MaxNumOfValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将最大值(MaxNumOfValue)序列化到StringInfo缓冲区中
|
||||
*
|
||||
* 参数列表:
|
||||
* buf:用于存储序列化数据的StringInfo缓冲区
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将预定义的最大值(MaxNumOfValue)序列化到给定的StringInfo缓冲区中,
|
||||
* 以便在序列化拷贝错误信息时使用。
|
||||
*/
|
||||
void CopyError::serializeMaxNumOfValue(StringInfo buf)
|
||||
{
|
||||
// 使用 pq_sendint 函数将 MaxNumOfValue 序列化为一个2字节整数,并写入缓冲区
|
||||
pq_sendint(buf, MaxNumOfValue, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:从文件中读取错误记录并将其填充到BaseError对象中
|
||||
*
|
||||
* 参数列表:
|
||||
* edata:用于存储错误记录的BaseError对象
|
||||
*
|
||||
* 返回值:
|
||||
* 0 表示成功,EOF 表示已读取到文件末尾,其他值表示出错
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于从文件中读取错误记录,将其反序列化,并将结果填充到给定的BaseError对象中。
|
||||
* 如果读取或处理出错,将抛出相应的错误。
|
||||
*/
|
||||
int BaseErrorLogger::FetchError(BaseError *edata)
|
||||
{
|
||||
AutoContextSwitch memGuard(m_memCxt);
|
||||
int nread = 0;
|
||||
uint32 len = 0;
|
||||
Assert(m_buffer != NULL && edata != NULL);
|
||||
MemoryContextReset(m_memCxt);
|
||||
resetStringInfo(m_buffer);
|
||||
AutoContextSwitch memGuard(m_memCxt); // 自动内存上下文切换
|
||||
int nread = 0; // 读取的字节数
|
||||
uint32 len = 0; // 错误记录的长度
|
||||
|
||||
Assert(m_buffer != NULL && edata != NULL); // 断言检查输入参数的有效性
|
||||
MemoryContextReset(m_memCxt); // 重置内存上下文
|
||||
resetStringInfo(m_buffer); // 重置StringInfo缓冲区
|
||||
|
||||
// 从文件中读取错误记录的长度(4字节)
|
||||
nread = FilePRead(m_fd, (char *)&len, 4, m_offset);
|
||||
if (nread == 0) {
|
||||
return EOF;
|
||||
return EOF; // 已读取到文件末尾
|
||||
} else if (nread < 0) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fetch error record:%m")));
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fetch error record:%m"))); // 文件读取错误
|
||||
} else if (nread < 4) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fetch expected length:%m")));
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(), errmsg("could not fetch expected length:%m"))); // 未读取到预期的长度
|
||||
}
|
||||
|
||||
m_offset += 4;
|
||||
len = ntohl(len);
|
||||
enlargeStringInfo(m_buffer, len + 1);
|
||||
m_offset += 4; // 更新文件偏移量
|
||||
len = ntohl(len); // 将长度从网络字节顺序转换为主机字节顺序
|
||||
enlargeStringInfo(m_buffer, len + 1); // 扩展StringInfo缓冲区以容纳错误记录
|
||||
|
||||
// 从文件中读取错误记录数据,直到缓冲区长度达到错误记录长度
|
||||
while ((uint32)m_buffer->len < len) {
|
||||
nread = FilePRead(m_fd, m_buffer->data + m_buffer->len, len - m_buffer->len, m_offset);
|
||||
if (nread == 0) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("incomplete error record")));
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("incomplete error record"))); // 未完整读取错误记录
|
||||
} else if (nread < 0) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fetch error record:%m")));
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not fetch error record:%m"))); // 文件读取错误
|
||||
}
|
||||
m_buffer->len += nread;
|
||||
m_offset += nread;
|
||||
m_buffer->len += nread; // 更新缓冲区长度
|
||||
m_offset += nread; // 更新文件偏移量
|
||||
}
|
||||
|
||||
m_buffer->data[m_buffer->len] = '\0';
|
||||
edata->Deserialize(m_buffer);
|
||||
return 0;
|
||||
m_buffer->data[m_buffer->len] = '\0'; // 将缓冲区的最后一个字符设置为'\0',以便作为字符串使用
|
||||
edata->Deserialize(m_buffer); // 反序列化错误记录并填充到BaseError对象中
|
||||
return 0; // 成功读取并处理错误记录
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将BaseError对象的错误信息保存到文件中
|
||||
*
|
||||
* 参数列表:
|
||||
* edata:包含错误信息的BaseError对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将给定的BaseError对象的错误信息序列化并保存到文件中。
|
||||
* 函数通过将错误信息的长度前置到数据之前,并写入文件来实现保存操作。
|
||||
*/
|
||||
void BaseErrorLogger::SaveError(BaseError *edata)
|
||||
{
|
||||
int nwrite = 0;
|
||||
int len = 0;
|
||||
int nwrite = 0; // 写入的字节数
|
||||
int len = 0; // 当前已写入的字节数
|
||||
|
||||
Assert(m_buffer != NULL && edata != NULL);
|
||||
resetStringInfo(m_buffer);
|
||||
appendStringInfoSpaces(m_buffer, 4);
|
||||
edata->Serialize(m_buffer);
|
||||
*((uint32 *)m_buffer->data) = htonl((uint32)(m_buffer->len - 4));
|
||||
Assert(m_buffer != NULL && edata != NULL); // 断言检查输入参数的有效性
|
||||
resetStringInfo(m_buffer); // 重置StringInfo缓冲区
|
||||
appendStringInfoSpaces(m_buffer, 4); // 在缓冲区前添加4个空字节(用于存储错误信息的长度)
|
||||
edata->Serialize(m_buffer); // 序列化错误信息并追加到缓冲区
|
||||
*((uint32 *)m_buffer->data) = htonl((uint32)(m_buffer->len - 4)); // 将错误信息的长度写入缓冲区
|
||||
|
||||
// 循环将错误信息写入文件,直到所有数据都写入
|
||||
while (len < m_buffer->len) {
|
||||
nwrite = FilePWrite(m_fd, m_buffer->data + len, m_buffer->len - len, m_offset);
|
||||
if (nwrite == 0 || nwrite < 0) {
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not cache error info:%m")));
|
||||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not cache error info:%m"))); // 文件写入错误
|
||||
}
|
||||
len += nwrite;
|
||||
m_offset += nwrite;
|
||||
len += nwrite; // 更新已写入的字节数
|
||||
m_offset += nwrite; // 更新文件偏移量
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化 ImportErrorLogger 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* output:输出参数(未使用)
|
||||
* errDesc:错误描述的元组描述符
|
||||
* errInfo:错误日志信息(未使用)
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于初始化 ImportErrorLogger 对象,设置相关的上下文和属性。
|
||||
* 输出参数 `output` 和错误日志信息 `errInfo` 目前未被使用。
|
||||
*/
|
||||
void ImportErrorLogger::Initialize(const void *output, TupleDesc errDesc, ErrLogInfo &errInfo)
|
||||
{
|
||||
// 创建内存上下文 m_memCxt
|
||||
m_memCxt = AllocSetContextCreate(CurrentMemoryContext, "Import Error Context", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
// 设置错误描述的元组描述符 m_errDesc
|
||||
m_errDesc = errDesc;
|
||||
|
||||
// 忽略未使用的 errInfo 参数
|
||||
(void)errInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 ImportErrorLogger 对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于销毁 ImportErrorLogger 对象,释放相关的资源和内存上下文。
|
||||
*/
|
||||
void ImportErrorLogger::Destroy()
|
||||
{
|
||||
// 删除内存上下文 m_memCxt,同时释放相关资源
|
||||
MemoryContextDelete(m_memCxt);
|
||||
|
||||
// 将错误描述的元组描述符 m_errDesc 设置为 NULL
|
||||
m_errDesc = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化 GDSErrorLogger 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* output:输出参数,通常是一个 GDSStream 指针
|
||||
* errDesc:错误描述的元组描述符
|
||||
* errInfo:错误日志信息
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于初始化 GDSErrorLogger 对象,设置相关的属性和上下文。
|
||||
* 输出参数 `output` 通常是一个 GDSStream 指针,`errDesc` 用于错误描述的元组描述符,
|
||||
* `errInfo` 用于错误日志信息(未使用)。
|
||||
*/
|
||||
void GDSErrorLogger::Initialize(const void *output, TupleDesc errDesc, ErrLogInfo &errInfo)
|
||||
{
|
||||
// 将输出参数强制转换为 GDSStream 指针并存储在 m_output 中
|
||||
m_output = (GDSStream *)output;
|
||||
|
||||
// 复制错误描述的元组描述符的名称并存储在 m_name 中
|
||||
m_name = pstrdup((char *)errDesc);
|
||||
|
||||
// 调用 ImportErrorLogger 的 Initialize 函数初始化对象
|
||||
ImportErrorLogger::Initialize(output, NULL, errInfo);
|
||||
|
||||
// 在使用断言检查时,初始化计数器 m_counter 为 0
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
m_counter = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 GDSErrorLogger 对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于销毁 GDSErrorLogger 对象,同时输出日志信息(当使用断言检查时)。
|
||||
*/
|
||||
void GDSErrorLogger::Destroy()
|
||||
{
|
||||
// 在使用断言检查时,输出日志信息,显示已发送的错误日志行数
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
ereport(LOG, (errcode(ERRCODE_LOG), errmsg("Error Log send %d lines", m_counter)));
|
||||
#endif
|
||||
|
||||
// 调用 ImportErrorLogger 的 Destroy 函数销毁对象
|
||||
ImportErrorLogger::Destroy();
|
||||
}
|
||||
|
||||
|
|
@ -280,65 +457,144 @@ int GDSErrorLogger::FetchError(BaseError *edata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:保存错误信息到 GDSStream
|
||||
*
|
||||
* 参数列表:
|
||||
* edata:包含错误信息的BaseError对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于将给定的错误信息保存到 GDSStream 中,用于远程日志记录。
|
||||
* 它通过获取错误信息的原始数据,将其封装为 CmdRemoteLog 结构并序列化,
|
||||
* 然后将序列化后的数据写入 GDSStream 的输出缓冲区,并刷新缓冲区。
|
||||
* 在使用断言检查时,增加计数器 m_counter。
|
||||
*/
|
||||
void GDSErrorLogger::SaveError(BaseError *edata)
|
||||
{
|
||||
CmdRemoteLog dat;
|
||||
Datum rawdata;
|
||||
CmdRemoteLog dat; // 用于远程日志记录的数据结构
|
||||
Datum rawdata; // 原始数据
|
||||
|
||||
Assert(edata != NULL);
|
||||
Assert(edata != NULL); // 断言检查输入参数的有效性
|
||||
|
||||
// 如果错误信息的原始数据为NULL,则不进行保存
|
||||
if (edata->m_isNull[BaseError::RawDataIdx])
|
||||
return;
|
||||
|
||||
// 获取错误信息的原始数据
|
||||
rawdata = edata->m_values[BaseError::RawDataIdx];
|
||||
|
||||
// 填充远程日志记录数据结构
|
||||
dat.m_type = CMD_TYPE_REMOTELOG;
|
||||
dat.m_datasize = VARSIZE_ANY_EXHDR(DatumGetPointer(rawdata));
|
||||
dat.m_data = VARDATA_ANY(DatumGetPointer(rawdata));
|
||||
dat.m_name = pstrdup(m_name);
|
||||
dat.m_name = pstrdup(m_name); // 复制错误描述的名称
|
||||
|
||||
// 序列化数据结构并将其写入 GDSStream 的输出缓冲区
|
||||
SerializeCmd(&dat, m_output->m_outBuf);
|
||||
|
||||
// 刷新输出缓冲区,将数据发送到远程日志服务器
|
||||
m_output->Flush();
|
||||
|
||||
// 在使用断言检查时,增加计数器,用于记录已发送的错误日志行数
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
++m_counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:生成导入错误的错误信息
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:拷贝状态信息
|
||||
* begintime:导入开始时间
|
||||
* edata:导入错误信息的BaseError对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于生成导入错误的错误信息,将其存储在给定的BaseError对象中。
|
||||
* 它首先将BaseError对象重置,然后根据拷贝状态中的信息生成原始数据,
|
||||
* 并将其存储在BaseError对象中。同时,将一些标志位设置为指示错误信息是否为空。
|
||||
*/
|
||||
void GDSErrorLogger::FormError(CopyState cstate, Datum begintime, ImportError *edata)
|
||||
{
|
||||
Assert(edata != NULL);
|
||||
Assert(edata != NULL); // 断言检查输入参数的有效性
|
||||
|
||||
// 重置BaseError对象的状态
|
||||
edata->Reset();
|
||||
|
||||
errno_t rc;
|
||||
// 使用 memset_s 函数将 BaseError 对象的 isNull 数组的所有元素设置为 1(表示空值)
|
||||
rc = memset_s(edata->m_isNull, sizeof(bool) * ImportError::MaxNumOfValue, 1,
|
||||
sizeof(bool) * ImportError::MaxNumOfValue);
|
||||
securec_check(rc, "", "");
|
||||
|
||||
if (cstate->line_buf.len > 0) {
|
||||
int len = cstate->line_buf.len + VARHDRSZ + 1;
|
||||
|
||||
// 分配内存用于存储原始数据,并设置其长度
|
||||
edata->m_values[ImportError::RawDataIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(edata->m_values[ImportError::RawDataIdx], len);
|
||||
|
||||
// 将拷贝状态中的数据复制到原始数据中,并添加换行符
|
||||
rc = memcpy_s(((char *)edata->m_values[ImportError::RawDataIdx]) + VARHDRSZ, len - VARHDRSZ,
|
||||
cstate->line_buf.data, cstate->line_buf.len);
|
||||
securec_check(rc, "", "");
|
||||
((char *)edata->m_values[ImportError::RawDataIdx])[len - 1] = '\n';
|
||||
|
||||
// 将原始数据的空值标志设置为 false(表示非空)
|
||||
edata->m_isNull[ImportError::RawDataIdx] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化 LocalErrorLogger 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* filename:缓存文件的文件名
|
||||
* errDesc:错误描述的元组描述符
|
||||
* errInfo:错误日志信息
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于初始化 LocalErrorLogger 对象,设置相关的属性和上下文。
|
||||
* 它通过生成本地缓存文件的文件名,并打开该文件来存储错误日志。
|
||||
* 然后,它调用 ImportErrorLogger 的 Initialize 函数初始化对象。
|
||||
*/
|
||||
void LocalErrorLogger::Initialize(const void *filename, TupleDesc errDesc, ErrLogInfo &errInfo)
|
||||
{
|
||||
char cache_file[MAXPGPATH];
|
||||
char cache_file[MAXPGPATH]; // 本地缓存文件的文件名
|
||||
|
||||
// 生成本地缓存文件的文件名
|
||||
generate_local_cache_file((const char *)filename, errInfo.smp_id, cache_file);
|
||||
|
||||
// 打开缓存文件,获取文件描述符
|
||||
m_fd = OpenCacheFile(cache_file, errInfo.unlink_owner);
|
||||
|
||||
// 创建 StringInfo 缓冲区
|
||||
m_buffer = makeStringInfo();
|
||||
|
||||
// 调用 ImportErrorLogger 的 Initialize 函数初始化对象,传递缓存文件的文件名
|
||||
ImportErrorLogger::Initialize(cache_file, errDesc, errInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 LocalErrorLogger 对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于销毁 LocalErrorLogger 对象,同时释放相关的资源。
|
||||
* 它首先调用 ImportErrorLogger 的 Destroy 函数销毁对象,然后释放
|
||||
* StringInfo 缓冲区的内存,关闭缓存文件的文件描述符,但不删除物理文件。
|
||||
*/
|
||||
void LocalErrorLogger::Destroy()
|
||||
{
|
||||
// 调用 ImportErrorLogger 的 Destroy 函数销毁对象
|
||||
ImportErrorLogger::Destroy();
|
||||
|
||||
// 如果 StringInfo 缓冲区不为空
|
||||
if (m_buffer != NULL) {
|
||||
// 如果缓冲区的数据不为空,释放数据内存
|
||||
if (m_buffer->data != NULL)
|
||||
pfree(m_buffer->data);
|
||||
|
||||
// 释放 StringInfo 缓冲区的内存
|
||||
pfree(m_buffer);
|
||||
m_buffer = NULL;
|
||||
}
|
||||
|
|
@ -351,82 +607,161 @@ void LocalErrorLogger::Destroy()
|
|||
FileClose(m_fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:生成本地错误日志的错误信息
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:拷贝状态信息
|
||||
* begintime:导入开始时间
|
||||
* ierror:导入错误信息的ImportError对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于生成本地错误日志的错误信息,将其存储在给定的ImportError对象中。
|
||||
* 它首先通过CopyErrorData函数获取错误信息,然后将相关信息填充到ImportError对象中。
|
||||
* 如果有原始数据,将其存储在RawData字段中,如果有详细信息,将其存储在Detail字段中。
|
||||
*/
|
||||
void LocalErrorLogger::FormError(CopyState cstate, Datum begintime, ImportError *ierror)
|
||||
{
|
||||
ErrorData *edata = NULL;
|
||||
int len = 0;
|
||||
const char *detail = NULL;
|
||||
int sqlerrcode;
|
||||
ErrorData *edata = NULL; // 错误数据
|
||||
int len = 0; // 长度
|
||||
const char *detail = NULL; // 详细信息
|
||||
int sqlerrcode; // SQL错误代码
|
||||
errno_t rc;
|
||||
|
||||
Assert(ierror != NULL);
|
||||
Assert(ierror != NULL); // 断言检查输入参数的有效性
|
||||
|
||||
// 获取错误信息
|
||||
edata = CopyErrorData();
|
||||
|
||||
// 获取错误详细信息和SQL错误代码
|
||||
detail = edata->message;
|
||||
sqlerrcode = edata->sqlerrcode;
|
||||
|
||||
// 重置ImportError对象的状态
|
||||
ierror->Reset();
|
||||
|
||||
// 设置ImportError对象的描述符
|
||||
ierror->m_desc = m_errDesc;
|
||||
|
||||
// 填充ImportError对象的字段值
|
||||
ierror->m_values[ImportError::NodeIdIdx] = u_sess->pgxc_cxt.PGXCNodeId;
|
||||
ierror->m_values[ImportError::StartTimeIdx] = begintime;
|
||||
|
||||
// 填充文件名字段
|
||||
len = strlen(cstate->filename) + VARHDRSZ;
|
||||
ierror->m_values[ImportError::FileNameIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[ImportError::FileNameIdx], len);
|
||||
rc = memcpy_s(((char *)ierror->m_values[ImportError::FileNameIdx]) + VARHDRSZ, len - VARHDRSZ, cstate->filename,
|
||||
len - VARHDRSZ);
|
||||
securec_check(rc, "", "");
|
||||
|
||||
// 填充行号字段
|
||||
ierror->m_values[ImportError::LineNOIdx] = cstate->cur_lineno;
|
||||
|
||||
// 如果存在原始数据并且SQL错误代码不表示字符不在编码中或无法转换字符
|
||||
if (cstate->line_buf.len > 0 && sqlerrcode != ERRCODE_CHARACTER_NOT_IN_REPERTOIRE &&
|
||||
sqlerrcode != ERRCODE_UNTRANSLATABLE_CHARACTER) {
|
||||
char *rawDataVal = NULL;
|
||||
int rawDataValLen = 0;
|
||||
|
||||
// 获取限制长度的原始数据
|
||||
rawDataVal = limit_printout_length(cstate->line_buf.data);
|
||||
rawDataValLen = strlen(rawDataVal);
|
||||
|
||||
// 填充原始数据字段
|
||||
len = cstate->line_buf.len + VARHDRSZ;
|
||||
ierror->m_values[ImportError::RawDataIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[ImportError::RawDataIdx], len);
|
||||
rc = memcpy_s(((char *)ierror->m_values[ImportError::RawDataIdx]) + VARHDRSZ, len - VARHDRSZ,
|
||||
cstate->line_buf.data, rawDataValLen);
|
||||
securec_check(rc, "", "");
|
||||
} else
|
||||
} else {
|
||||
// 原始数据为空
|
||||
ierror->m_isNull[ImportError::RawDataIdx] = true;
|
||||
}
|
||||
|
||||
// 如果存在详细信息,填充详细信息字段
|
||||
if (detail != NULL) {
|
||||
char *detailVal = limit_printout_length(detail);
|
||||
int leng = strlen(detailVal);
|
||||
|
||||
ierror->m_values[ImportError::DetailIdx] = (Datum)palloc(leng + VARHDRSZ);
|
||||
SET_VARSIZE(ierror->m_values[ImportError::DetailIdx], leng + VARHDRSZ);
|
||||
rc = memcpy_s(((char *)ierror->m_values[ImportError::DetailIdx]) + VARHDRSZ, leng, detail, leng);
|
||||
securec_check(rc, "", "");
|
||||
} else
|
||||
} else {
|
||||
// 详细信息为空
|
||||
ierror->m_isNull[ImportError::DetailIdx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化 CopyErrorLogger 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:拷贝状态信息
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于初始化 CopyErrorLogger 对象,设置相关的属性和上下文。
|
||||
* 它通过生成一个唯一的缓存文件名,并打开该文件来存储拷贝错误日志。
|
||||
* 同时,创建 StringInfo 缓冲区,分配内存上下文,并获取错误表的元组描述符。
|
||||
*/
|
||||
void CopyErrorLogger::Initialize(CopyState cstate)
|
||||
{
|
||||
/* Get ourselves a cache file */
|
||||
char *cache_file = generate_unique_cache_name_prefix(RelationGetRelationName(cstate->rel));
|
||||
|
||||
// 打开缓存文件,获取文件描述符,并设置自动删除标志为 true
|
||||
m_fd = OpenCacheFile(cache_file, true);
|
||||
|
||||
// 创建 StringInfo 缓冲区
|
||||
m_buffer = makeStringInfo();
|
||||
|
||||
// 创建内存上下文
|
||||
m_memCxt = AllocSetContextCreate(CurrentMemoryContext, "Copy Import Error Context", ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
// 获取错误表的元组描述符
|
||||
m_errDesc = RelationGetDescr(cstate->err_table);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置 CopyErrorLogger 对象的状态
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于重置 CopyErrorLogger 对象的状态,包括缓冲区、内存上下文和偏移量。
|
||||
* 它将 StringInfo 缓冲区重置为空,重置内存上下文,并将偏移量设置为 0。
|
||||
*/
|
||||
void CopyErrorLogger::Reset()
|
||||
{
|
||||
// 重置 StringInfo 缓冲区为空
|
||||
resetStringInfo(m_buffer);
|
||||
|
||||
// 重置内存上下文
|
||||
MemoryContextReset(m_memCxt);
|
||||
|
||||
// 将偏移量设置为 0
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 CopyErrorLogger 对象
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于销毁 CopyErrorLogger 对象,释放相关资源,包括内存上下文、缓冲区、错误描述符和文件描述符。
|
||||
* 它会删除内存上下文、释放缓冲区的内存、关闭文件描述符。
|
||||
*/
|
||||
void CopyErrorLogger::Destroy()
|
||||
{
|
||||
// 删除内存上下文
|
||||
MemoryContextDelete(m_memCxt);
|
||||
|
||||
// 清空错误描述符
|
||||
m_errDesc = NULL;
|
||||
|
||||
// 释放缓冲区相关的内存
|
||||
if (m_buffer != NULL) {
|
||||
if (m_buffer->data != NULL)
|
||||
pfree(m_buffer->data);
|
||||
|
||||
pfree(m_buffer);
|
||||
m_buffer = NULL;
|
||||
}
|
||||
|
|
@ -438,8 +773,21 @@ void CopyErrorLogger::Destroy()
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:根据错误信息生成 CopyError 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:当前的复制状态
|
||||
* begintime:开始时间
|
||||
* ierror:指向 CopyError 对象的指针,用于存储生成的错误信息
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于生成 CopyError 对象,将错误信息填充到 CopyError 中。
|
||||
* 它根据复制状态、开始时间、错误描述等信息创建错误记录。
|
||||
*/
|
||||
void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ierror)
|
||||
{
|
||||
// 声明 ErrorData 指针和其他变量
|
||||
ErrorData *edata = NULL;
|
||||
int len = 0;
|
||||
const char *detail = NULL;
|
||||
|
|
@ -448,28 +796,38 @@ void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ie
|
|||
errno_t rc;
|
||||
char *source = NULL;
|
||||
|
||||
// 断言确保传入的 CopyError 对象非空
|
||||
Assert(ierror != NULL);
|
||||
|
||||
// 获取当前错误信息
|
||||
edata = CopyErrorData();
|
||||
detail = edata->message;
|
||||
sqlerrcode = edata->sqlerrcode;
|
||||
|
||||
// 重置 CopyError 对象,清空之前的数据
|
||||
ierror->Reset();
|
||||
|
||||
// 设置 CopyError 的描述符为错误描述符
|
||||
ierror->m_desc = m_errDesc;
|
||||
|
||||
// 构建关系名,格式为 "命名空间.表名"
|
||||
relname = (char *)palloc(MAX_NAME_LEN);
|
||||
rc = snprintf_s(relname, MAX_NAME_LEN, MAX_NAME_LEN - 1, "%s.%s", cstate->logger->m_namespace,
|
||||
RelationGetRelationName(cstate->rel));
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
|
||||
// 分配并设置关系名
|
||||
len = strlen(relname) + VARHDRSZ;
|
||||
ierror->m_values[CopyError::RelNameIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::RelNameIdx], len);
|
||||
rc = memcpy_s(((char *)ierror->m_values[CopyError::RelNameIdx]) + VARHDRSZ, len - VARHDRSZ, relname,
|
||||
len - VARHDRSZ);
|
||||
rc =
|
||||
memcpy_s(((char *)ierror->m_values[CopyError::RelNameIdx]) + VARHDRSZ, len - VARHDRSZ, relname, len - VARHDRSZ);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
// 设置开始时间
|
||||
ierror->m_values[CopyError::StartTimeIdx] = begintime;
|
||||
|
||||
// 设置文件名
|
||||
if (cstate->filename)
|
||||
source = cstate->filename;
|
||||
else {
|
||||
|
|
@ -477,13 +835,15 @@ void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ie
|
|||
source = "STDIN";
|
||||
}
|
||||
|
||||
// 分配并设置文件名
|
||||
len = strlen(source) + VARHDRSZ;
|
||||
ierror->m_values[CopyError::FileNameIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::FileNameIdx], len);
|
||||
rc = memcpy_s(((char *)ierror->m_values[CopyError::FileNameIdx]) + VARHDRSZ, len - VARHDRSZ, source,
|
||||
len - VARHDRSZ);
|
||||
rc =
|
||||
memcpy_s(((char *)ierror->m_values[CopyError::FileNameIdx]) + VARHDRSZ, len - VARHDRSZ, source, len - VARHDRSZ);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
// 设置行号
|
||||
ierror->m_values[CopyError::LineNOIdx] = cstate->cur_lineno;
|
||||
|
||||
/* save the raw data here */
|
||||
|
|
@ -503,6 +863,7 @@ void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ie
|
|||
ierror->m_isNull[CopyError::RawDataIdx] = true;
|
||||
}
|
||||
|
||||
// 保存详细信息(如果可用),注意限制数据长度
|
||||
if (detail != NULL) {
|
||||
char *detailVal = limit_printout_length(detail);
|
||||
int leng = strlen(detailVal);
|
||||
|
|
@ -513,30 +874,65 @@ void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ie
|
|||
} else
|
||||
ierror->m_isNull[CopyError::DetailIdx] = true;
|
||||
|
||||
// 释放关系名的内存
|
||||
pfree(relname);
|
||||
}
|
||||
|
||||
void CopyErrorLogger::FormWhenLog(CopyState cstate, Datum begintime, CopyError *ierror)
|
||||
/*
|
||||
* 功能:根据错误信息生成 CopyError 对象
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:当前的复制状态
|
||||
* begintime:开始时间
|
||||
* ierror:指向 CopyError 对象的指针,用于存储生成的错误信息
|
||||
*
|
||||
* 注意:
|
||||
* 该函数用于生成 CopyError 对象,将错误信息填充到 CopyError 中。
|
||||
* 它根据复制状态、开始时间、错误描述等信息创建错误记录。
|
||||
*/
|
||||
void CopyErrorLogger::FormError(CopyState cstate, Datum begintime, CopyError *ierror)
|
||||
{
|
||||
// 声明 ErrorData 指针和其他变量
|
||||
ErrorData *edata = NULL;
|
||||
int len = 0;
|
||||
const char *detail = NULL;
|
||||
char *relname = NULL;
|
||||
int sqlerrcode;
|
||||
errno_t rc;
|
||||
char *source = NULL;
|
||||
|
||||
// 断言确保传入的 CopyError 对象非空
|
||||
Assert(ierror != NULL);
|
||||
detail = "COPY_WHEN_ROWS";
|
||||
|
||||
// 获取当前错误信息
|
||||
edata = CopyErrorData();
|
||||
detail = edata->message;
|
||||
sqlerrcode = edata->sqlerrcode;
|
||||
|
||||
// 重置 CopyError 对象,清空之前的数据
|
||||
ierror->Reset();
|
||||
|
||||
// 设置 CopyError 的描述符为错误描述符
|
||||
ierror->m_desc = m_errDesc;
|
||||
|
||||
// 构建关系名,格式为 "命名空间.表名"
|
||||
relname = (char *)palloc(MAX_NAME_LEN);
|
||||
rc = snprintf_s(relname, MAX_NAME_LEN, MAX_NAME_LEN - 1, "%s.%s", cstate->logger->m_namespace,
|
||||
RelationGetRelationName(cstate->rel));
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
|
||||
ierror->m_values[CopyError::RelNameIdx] = PointerGetDatum(cstring_to_text_with_len(relname, strlen(relname)));
|
||||
// 分配并设置关系名
|
||||
len = strlen(relname) + VARHDRSZ;
|
||||
ierror->m_values[CopyError::RelNameIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::RelNameIdx], len);
|
||||
rc =
|
||||
memcpy_s(((char *)ierror->m_values[CopyError::RelNameIdx]) + VARHDRSZ, len - VARHDRSZ, relname, len - VARHDRSZ);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
// 设置开始时间
|
||||
ierror->m_values[CopyError::StartTimeIdx] = begintime;
|
||||
|
||||
// 设置文件名
|
||||
if (cstate->filename)
|
||||
source = cstate->filename;
|
||||
else {
|
||||
|
|
@ -544,25 +940,45 @@ void CopyErrorLogger::FormWhenLog(CopyState cstate, Datum begintime, CopyError *
|
|||
source = "STDIN";
|
||||
}
|
||||
|
||||
ierror->m_values[CopyError::FileNameIdx] = PointerGetDatum(cstring_to_text_with_len(source, strlen(source)));
|
||||
// 分配并设置文件名
|
||||
len = strlen(source) + VARHDRSZ;
|
||||
ierror->m_values[CopyError::FileNameIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::FileNameIdx], len);
|
||||
rc =
|
||||
memcpy_s(((char *)ierror->m_values[CopyError::FileNameIdx]) + VARHDRSZ, len - VARHDRSZ, source, len - VARHDRSZ);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
|
||||
// 设置行号
|
||||
ierror->m_values[CopyError::LineNOIdx] = cstate->cur_lineno;
|
||||
|
||||
/* save the raw data here */
|
||||
if (cstate->line_buf.len > 0 && cstate->logErrorsData) {
|
||||
if (cstate->line_buf.len > 0 && sqlerrcode != ERRCODE_CHARACTER_NOT_IN_REPERTOIRE &&
|
||||
sqlerrcode != ERRCODE_UNTRANSLATABLE_CHARACTER && cstate->logErrorsData) {
|
||||
char *rawDataVal = NULL;
|
||||
int rawDataValLen = 0;
|
||||
rawDataVal = limit_printout_length(cstate->line_buf.data);
|
||||
ierror->m_values[CopyError::RawDataIdx] =
|
||||
PointerGetDatum(cstring_to_text_with_len(rawDataVal, strlen(rawDataVal)));
|
||||
pfree(rawDataVal);
|
||||
rawDataValLen = strlen(rawDataVal);
|
||||
len = rawDataValLen + VARHDRSZ;
|
||||
ierror->m_values[CopyError::RawDataIdx] = (Datum)palloc(len);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::RawDataIdx], len);
|
||||
rc = memcpy_s(((char *)ierror->m_values[CopyError::RawDataIdx]) + VARHDRSZ, len - VARHDRSZ, rawDataVal,
|
||||
rawDataValLen);
|
||||
securec_check(rc, "", "");
|
||||
} else {
|
||||
ierror->m_isNull[CopyError::RawDataIdx] = true;
|
||||
}
|
||||
|
||||
// 保存详细信息(如果可用),注意限制数据长度
|
||||
if (detail != NULL) {
|
||||
ierror->m_values[CopyError::DetailIdx] = PointerGetDatum(cstring_to_text_with_len(detail, strlen(detail)));
|
||||
} else {
|
||||
char *detailVal = limit_printout_length(detail);
|
||||
int leng = strlen(detailVal);
|
||||
ierror->m_values[CopyError::DetailIdx] = (Datum)palloc(leng + VARHDRSZ);
|
||||
SET_VARSIZE(ierror->m_values[CopyError::DetailIdx], leng + VARHDRSZ);
|
||||
rc = memcpy_s(((char *)ierror->m_values[CopyError::DetailIdx]) + VARHDRSZ, leng, detail, leng);
|
||||
securec_check_c(rc, "\0", "\0");
|
||||
} else
|
||||
ierror->m_isNull[CopyError::DetailIdx] = true;
|
||||
}
|
||||
|
||||
// 释放关系名的内存
|
||||
pfree(relname);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -32,7 +32,14 @@ template bool getNextRoach<true>(CopyState cstate);
|
|||
template bool getNextRoach<false>(CopyState cstate);
|
||||
template void initRoachState<true>(CopyState cstate, const char *filename, List *totalTask);
|
||||
template void initRoachState<false>(CopyState cstate, const char *filename, List *totalTask);
|
||||
|
||||
/*
|
||||
* Function: initRoachRoutine
|
||||
*
|
||||
* Initializes a RoachRoutine by looking up the "roach_handler" function and
|
||||
* checking its return type.
|
||||
*
|
||||
* Returns: A RoachRoutine pointer.
|
||||
*/
|
||||
RoachRoutine *initRoachRoutine()
|
||||
{
|
||||
Datum datum;
|
||||
|
|
@ -50,7 +57,16 @@ RoachRoutine *initRoachRoutine()
|
|||
|
||||
return routine;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: getNextRoach
|
||||
*
|
||||
* Gets the next Roach data to import or export in a COPY operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
*
|
||||
* Returns: true if successful, false if there's no more data to process.
|
||||
*/
|
||||
template <bool import>
|
||||
bool getNextRoach(CopyState cstate)
|
||||
{
|
||||
|
|
@ -78,7 +94,19 @@ bool getNextRoach(CopyState cstate)
|
|||
cstate->roach_context = roach_context;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: copyGetRoachData
|
||||
*
|
||||
* Reads Roach data for a COPY operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
* - databuf: The data buffer to read into.
|
||||
* - minread: The minimum number of bytes to read.
|
||||
* - maxread: The maximum number of bytes to read.
|
||||
*
|
||||
* Returns: The number of bytes read.
|
||||
*/
|
||||
int copyGetRoachData(CopyState cstate, void *databuf, int minread, int maxread)
|
||||
{
|
||||
Assert(cstate->roach_routine);
|
||||
|
|
@ -91,7 +119,16 @@ int copyGetRoachData(CopyState cstate, void *databuf, int minread, int maxread)
|
|||
|
||||
return bytesread;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: initRoachState
|
||||
*
|
||||
* Initializes the state for a Roach COPY operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
* - filename: The Roach file to import/export.
|
||||
* - totalTask: The total tasks to process.
|
||||
*/
|
||||
template <bool import>
|
||||
void initRoachState(CopyState cstate, const char *filename, List *totalTask)
|
||||
{
|
||||
|
|
@ -117,8 +154,8 @@ void initRoachState(CopyState cstate, const char *filename, List *totalTask)
|
|||
char roachPath[PATH_MAX + 1];
|
||||
const char *pos = strstr(filename, ROACH_PREFIX);
|
||||
pos += ROACH_PREFIX_LEN;
|
||||
errno_t ret = snprintf_s(roachPath, sizeof(roachPath), PATH_MAX, "%s/%s", pos,
|
||||
g_instance.attr.attr_common.PGXCNodeName);
|
||||
errno_t ret =
|
||||
snprintf_s(roachPath, sizeof(roachPath), PATH_MAX, "%s/%s", pos, g_instance.attr.attr_common.PGXCNodeName);
|
||||
securec_check_ss(ret, "", "");
|
||||
roachPath[PATH_MAX] = '\0';
|
||||
|
||||
|
|
@ -137,7 +174,14 @@ void initRoachState(CopyState cstate, const char *filename, List *totalTask)
|
|||
(void)getNextRoach<import>(cstate);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: endRoachBulkLoad
|
||||
*
|
||||
* Ends a Roach bulk load operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
*/
|
||||
void endRoachBulkLoad(CopyState cstate)
|
||||
{
|
||||
if (IS_PGXC_DATANODE) {
|
||||
|
|
@ -149,7 +193,14 @@ void endRoachBulkLoad(CopyState cstate)
|
|||
ereport(ERROR, (errcode_for_file_access(), errmsg("could not close roach %s", cstate->filename)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: exportRoach
|
||||
*
|
||||
* Exports data to Roach in a COPY operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
*/
|
||||
void exportRoach(CopyState cstate)
|
||||
{
|
||||
Assert(cstate->copy_dest == COPY_ROACH);
|
||||
|
|
@ -196,7 +247,15 @@ void exportRoach(CopyState cstate)
|
|||
|
||||
resetStringInfo(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function: exportRoachFlushOut
|
||||
*
|
||||
* Flushes the data to Roach in a COPY operation.
|
||||
*
|
||||
* Parameters:
|
||||
* - cstate: The COPY operation's state.
|
||||
* - isWholeLineAtEnd: Indicates if the whole line is at the end.
|
||||
*/
|
||||
void exportRoachFlushOut(CopyState cstate, bool isWholeLineAtEnd)
|
||||
{
|
||||
Assert(cstate->roach_routine);
|
||||
|
|
|
|||
|
|
@ -52,40 +52,88 @@ extern void SyncBulkloadStates(CopyState cstate);
|
|||
extern void CleanBulkloadStates(); // all stuffs used for bulkload(end).
|
||||
|
||||
// Try to save importing error if needed
|
||||
/*
|
||||
* 功能:尝试保存导入错误
|
||||
*
|
||||
* 参数列表:
|
||||
* importState:导入执行状态指针,包含导入过程中的相关信息
|
||||
* node:外部扫描状态指针,用于访问外部扫描相关信息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功保存导入错误,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 这个函数用于尝试保存导入错误,它检查错误码是否为数据异常(Data Exception)并且是否可以接受错误。
|
||||
* 如果满足条件,它会保存错误记录,并在必要时清除错误状态。
|
||||
*/
|
||||
bool TrySaveImportError(DistImportExecutionState *importState, ForeignScanState *node)
|
||||
{
|
||||
// 检查当前错误码是否为数据异常(Data Exception)
|
||||
if ((ERRCODE_TO_CATEGORY((unsigned int)geterrcode()) == ERRCODE_DATA_EXCEPTION) && DoAcceptOneError(importState)) {
|
||||
// 如果是数据异常并且DoAcceptOneError返回true,表示可以接受错误
|
||||
|
||||
// 检查错误码是否为字符不在字符集中或者无法翻译的字符
|
||||
if (geterrcode() == ERRCODE_CHARACTER_NOT_IN_REPERTOIRE || geterrcode() == ERRCODE_UNTRANSLATABLE_CHARACTER)
|
||||
t_thrd.bulk_cxt.illegal_character_err_cnt++;
|
||||
|
||||
ListCell *lc = NULL;
|
||||
|
||||
// 遍历错误记录器列表,处理每个错误记录
|
||||
foreach (lc, importState->elogger) {
|
||||
ImportErrorLogger *elogger = (ImportErrorLogger *)lfirst(lc);
|
||||
FormAndSaveImportError(importState, importState->errLogRel, importState->beginTime, elogger);
|
||||
}
|
||||
|
||||
// clear error state
|
||||
//
|
||||
// 清除错误状态
|
||||
FlushErrorStateWithoutDeleteChildrenContext();
|
||||
return true;
|
||||
return true; // 返回true表示成功保存导入错误
|
||||
}
|
||||
return false;
|
||||
return false; // 返回false表示没有保存导入错误
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:尝试保存导入错误
|
||||
*
|
||||
* 参数列表:
|
||||
* cstate:复制状态指针,包含复制过程中的相关信息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功保存导入错误,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 这个函数用于尝试保存导入错误,它检查错误码是否为数据异常(Data Exception)并且是否可以接受错误。
|
||||
* 如果满足条件,它会保存错误记录,并在必要时清除错误状态。
|
||||
*/
|
||||
bool TrySaveImportError(CopyState cstate)
|
||||
{
|
||||
// 增加错误行数计数器
|
||||
cstate->errorrows++;
|
||||
if ((ERRCODE_TO_CATEGORY((unsigned int)geterrcode()) == ERRCODE_DATA_EXCEPTION) && DoAcceptOneError(cstate)) {
|
||||
FormAndSaveImportError(cstate, cstate->err_table, cstate->copy_beginTime, cstate->logger);
|
||||
// clear error state
|
||||
//
|
||||
FlushErrorStateWithoutDeleteChildrenContext();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查当前错误码是否为数据异常(Data Exception)
|
||||
if ((ERRCODE_TO_CATEGORY((unsigned int)geterrcode()) == ERRCODE_DATA_EXCEPTION) && DoAcceptOneError(cstate)) {
|
||||
// 如果是数据异常并且DoAcceptOneError返回true,表示可以接受错误
|
||||
|
||||
// 调用函数保存导入错误
|
||||
FormAndSaveImportError(cstate, cstate->err_table, cstate->copy_beginTime, cstate->logger);
|
||||
|
||||
// 清除错误状态
|
||||
FlushErrorStateWithoutDeleteChildrenContext();
|
||||
return true; // 返回true表示成功保存导入错误
|
||||
}
|
||||
return false; // 返回false表示没有保存导入错误
|
||||
}
|
||||
/*
|
||||
* 功能:执行向量化外部数据导入
|
||||
*
|
||||
* 参数列表:
|
||||
* node:向量化外部扫描状态指针,包含导入过程中的相关信息
|
||||
*
|
||||
* 返回值:
|
||||
* 返回包含导入数据的向量批次(VectorBatch)。
|
||||
*
|
||||
* 注意:
|
||||
* 这个函数用于执行向量化的外部数据导入操作。它从外部数据源中读取数据并将其填充到VectorBatch中。
|
||||
* 同时,它还处理错误并保存导入错误记录。
|
||||
*/
|
||||
VectorBatch *distExecVecImport(VecForeignScanState *node)
|
||||
{
|
||||
DistImportExecutionState *importState = (DistImportExecutionState *)node->fdw_state;
|
||||
|
|
@ -97,60 +145,45 @@ VectorBatch *distExecVecImport(VecForeignScanState *node)
|
|||
MemoryContext oldMemoryContext;
|
||||
MemoryContext scanMcxt = node->scanMcxt;
|
||||
|
||||
/* Set up callback to identify error line number. */
|
||||
/* 设置错误回调以识别错误行号 */
|
||||
errcontext.callback = BulkloadErrorCallback;
|
||||
errcontext.arg = (void *)importState;
|
||||
errcontext.previous = t_thrd.log_cxt.error_context_stack;
|
||||
t_thrd.log_cxt.error_context_stack = &errcontext;
|
||||
|
||||
/*
|
||||
* The protocol for loading a virtual tuple into a slot is first
|
||||
* ExecClearTuple, then fill the values/isnull arrays, then
|
||||
* ExecStoreVirtualTuple. If we don't find another row in the file, we
|
||||
* just skip the last step, leaving the slot empty as required.
|
||||
*
|
||||
* We can pass ExprContext = NULL because we read all columns from the
|
||||
* file, so no need to evaluate default expressions.
|
||||
*
|
||||
* We can also pass tupleOid = NULL because we don't allow oids for
|
||||
* foreign tables.
|
||||
*/
|
||||
batch->Reset(true);
|
||||
|
||||
if (node->m_done) {
|
||||
/* Remove error callback. */
|
||||
/* 移除错误回调 */
|
||||
t_thrd.log_cxt.error_context_stack = errcontext.previous;
|
||||
return batch;
|
||||
}
|
||||
|
||||
MemoryContextReset(scanMcxt);
|
||||
oldMemoryContext = MemoryContextSwitchTo(scanMcxt);
|
||||
#ifndef ENABLE_LITE_MODE
|
||||
SetObsMemoryContext(((CopyState)importState)->copycontext);
|
||||
#endif
|
||||
|
||||
for (batch->m_rows = 0; batch->m_rows < BatchMaxSize; batch->m_rows++) {
|
||||
retry:
|
||||
retry:
|
||||
PG_TRY();
|
||||
{
|
||||
/*
|
||||
* Synchronize the current bulkload states.
|
||||
*/
|
||||
/* 同步当前批量加载状态 */
|
||||
SyncBulkloadStates((CopyState)importState);
|
||||
|
||||
// 从外部数据源中读取下一行数据
|
||||
found = NextCopyFrom((CopyState)importState, NULL, values, nulls, NULL);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
/*
|
||||
* Clean the current bulkload states.
|
||||
*/
|
||||
CleanBulkloadStates();
|
||||
/* 清理当前批量加载状态 */
|
||||
|
||||
// 尝试保存导入错误,如果成功则重试
|
||||
if (TrySaveImportError(importState, node)) {
|
||||
(void)MemoryContextSwitchTo(scanMcxt);
|
||||
MemoryContextReset(scanMcxt);
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
goto retry;
|
||||
} else {
|
||||
/* clean copy state and re throw */
|
||||
/* 清理复制状态并重新抛出异常 */
|
||||
importState->isExceptionShutdown = true;
|
||||
EndDistImport(importState);
|
||||
PG_RE_THROW();
|
||||
|
|
@ -158,13 +191,13 @@ retry:
|
|||
}
|
||||
PG_END_TRY();
|
||||
|
||||
/*
|
||||
* Clean the current bulkload states.
|
||||
*/
|
||||
/* 清理当前批量加载状态 */
|
||||
CleanBulkloadStates();
|
||||
|
||||
if (found) {
|
||||
int rows = batch->m_rows;
|
||||
|
||||
// 将读取的数据填充到VectorBatch中
|
||||
for (int i = 0; i < batch->m_cols; i++) {
|
||||
ScalarVector *vec = &(batch->m_arr[i]);
|
||||
if (nulls[i]) {
|
||||
|
|
@ -187,7 +220,8 @@ retry:
|
|||
}
|
||||
|
||||
(void)MemoryContextSwitchTo(oldMemoryContext);
|
||||
/* Remove error callback. */
|
||||
|
||||
/* 移除错误回调 */
|
||||
t_thrd.log_cxt.error_context_stack = errcontext.previous;
|
||||
|
||||
return batch;
|
||||
|
|
|
|||
|
|
@ -93,12 +93,18 @@ int CacheMgrNumLocks(int64 cache_size, uint32 each_block_size)
|
|||
*/
|
||||
int64 CacheMgrCalcSizeByType(MgrCacheType type)
|
||||
{
|
||||
// 计算元数据缓存大小为cstore缓冲区大小的1/4,但最大不超过2G
|
||||
/*
|
||||
* g_instance.attr.attr_storage.cstore_buffers:这是一个配置参数。
|
||||
* 表示 cstore 存储引擎的缓冲区大小,以块为单位。
|
||||
*/
|
||||
int64 size = g_instance.attr.attr_storage.cstore_buffers * 1024LL * 1 / 4;
|
||||
int64 cache_size = Min(size, MAX_METADATA_CACHE_SIZE);
|
||||
if (!g_instance.attr.attr_sql.enable_orc_cache) {
|
||||
//如果不启用 ORC 缓存(由配置参数 enable_orc_cache 控制),则将缓存大小设置为 1MB。
|
||||
if (!g_instance.attr.attr_sql.enable_orc_cache) {
|
||||
cache_size = 1024 * 1024;
|
||||
}
|
||||
|
||||
// 如果缓存类型为数据缓存,将缓存大小设置为剩余的大小
|
||||
if (type == MGR_CACHE_TYPE_DATA) {
|
||||
cache_size = g_instance.attr.attr_storage.cstore_buffers * 1024LL - cache_size;
|
||||
}
|
||||
|
|
@ -118,19 +124,36 @@ void CacheMgr::Init(int64 cache_size, uint32 each_block_size, MgrCacheType type,
|
|||
int i = 0;
|
||||
int trancheId = LWTRANCHE_UNKNOWN;
|
||||
int32 total_slots = 0;
|
||||
|
||||
// 设置缓存类型
|
||||
m_cache_type = type;
|
||||
/* Must be greater than 0 */
|
||||
m_CaccheSlotMax = 1;
|
||||
|
||||
m_cstoreCurrentSize = 0;
|
||||
m_cstoreMaxSize = cache_size;
|
||||
|
||||
m_cstoreCurrentSize = 0;//表示当前缓存的实际大小,初始值为 0,表示在初始化时缓存尚未使用。
|
||||
m_cstoreMaxSize = cache_size;//表示缓存的最大可用大小,它由传递给函数的 cache_size 参数指定,表示系统允许的最大缓存大小。
|
||||
|
||||
// 计算可用的缓存槽的总数,限制最大数量为 MAX_CACHE_SLOT_COUNT
|
||||
total_slots = Min(cache_size / each_block_size, MAX_CACHE_SLOT_COUNT);
|
||||
|
||||
/*
|
||||
*这行代码分配了一个连续的内存块,用于存储缓存槽的数据。
|
||||
* total_slots 表示缓存槽的总数
|
||||
* each_slot_length 表示每个缓存槽的长度,乘积得到了需要的总内存大小
|
||||
* palloc0 是一个内存分配函数,它将分配的内存块初始化为零
|
||||
* 这个数组将被用于存储每个缓存槽的数据。
|
||||
*/
|
||||
m_CacheSlots = (char *)palloc0(total_slots * each_slot_length);
|
||||
|
||||
//这行代码分配了一个数组,用于存储缓存描述符。
|
||||
m_CacheDesc = (CacheDesc *)palloc0(total_slots * sizeof(CacheDesc));
|
||||
|
||||
//这行代码将 m_CacheSlotsNum 设置为缓存槽的总数,以便后续的缓存管理操作可以使用这个值。
|
||||
m_CacheSlotsNum = total_slots;
|
||||
|
||||
//这行代码将 m_slot_length 设置为每个缓存槽的长度,以便在后续操作中可以正确处理每个缓存槽的数据。
|
||||
m_slot_length = each_slot_length;
|
||||
|
||||
// 遍历所有的缓存槽,初始化相关参数
|
||||
for (i = 0; i < total_slots; ++i) {
|
||||
m_CacheDesc[i].m_usage_count = 0;
|
||||
m_CacheDesc[i].m_refcount = 0;
|
||||
|
|
@ -144,11 +167,18 @@ void CacheMgr::Init(int64 cache_size, uint32 each_block_size, MgrCacheType type,
|
|||
} else if (type == MGR_CACHE_TYPE_INDEX) {
|
||||
trancheId = (int)LWTRANCHE_META_CACHE;
|
||||
}
|
||||
|
||||
//分别为当前缓存描述符中的 m_iobusy_lock 和 m_compress_lock 字段分配了与缓存类型相关的轻量级锁。
|
||||
m_CacheDesc[i].m_iobusy_lock = LWLockAssign(trancheId);
|
||||
m_CacheDesc[i].m_compress_lock = LWLockAssign(trancheId);
|
||||
|
||||
//这行代码将缓存描述符中的 m_refreshing 字段初始化为 false。这个字段表示当前缓存块是否正在刷新中,刷新是指重新加载缓存块的数据。
|
||||
m_CacheDesc[i].m_refreshing = false;
|
||||
|
||||
//将缓存描述符中的 m_datablock_size 字段初始化为 0。这个字段用于记录缓存块中数据的大小。
|
||||
m_CacheDesc[i].m_datablock_size = 0;
|
||||
|
||||
|
||||
//初始化了缓存描述符中的自旋锁 m_slot_hdr_lock,该锁用于在缓存管理操作中保护缓存槽的头部信息。
|
||||
SpinLockInit(&m_CacheDesc[i].m_slot_hdr_lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -460,28 +460,34 @@ void CStoreFreeSpace::Push(const CStoreFreeSpaceDesc& desc)
|
|||
m_descs[i] = desc;
|
||||
}
|
||||
|
||||
void CStoreFreeSpace::PopDescWithMaxSize(CStoreFreeSpaceDesc& desc)
|
||||
/*
|
||||
* 功能:弹出最大大小的空闲空间描述
|
||||
*
|
||||
* 参数:
|
||||
* desc:用于存储弹出的空闲空间描述
|
||||
*/
|
||||
void CStoreFreeSpace::PopDescWithMaxSize(CStoreFreeSpaceDesc &desc)
|
||||
{
|
||||
CStoreFreeSpaceDesc tmp;
|
||||
int i = 1;
|
||||
int subi = 2;
|
||||
|
||||
if (m_descNum == 0)
|
||||
return;
|
||||
return; // 如果没有空闲空间描述,直接返回
|
||||
|
||||
desc = m_descs[1];
|
||||
tmp = m_descs[m_descNum--];
|
||||
desc = m_descs[1]; // 将根节点的空闲空间描述赋值给传入的参数 desc
|
||||
tmp = m_descs[m_descNum--]; // 取出最后一个空闲空间描述,并减少描述数量
|
||||
|
||||
while (subi <= m_descNum) {
|
||||
if (subi < m_descNum && m_descs[subi].size < m_descs[subi + 1].size)
|
||||
subi++;
|
||||
subi++; // 如果右子节点比左子节点大,选择右子节点
|
||||
if (tmp.size >= m_descs[subi].size)
|
||||
break;
|
||||
m_descs[i] = m_descs[subi];
|
||||
break; // 如果当前节点比子节点大,退出循环
|
||||
m_descs[i] = m_descs[subi]; // 否则将子节点上移
|
||||
i = subi;
|
||||
subi *= 2;
|
||||
}
|
||||
m_descs[i] = tmp;
|
||||
m_descs[i] = tmp; // 将原根节点(最大的)放入空闲空间描述数组的正确位置
|
||||
}
|
||||
|
||||
void CStoreFreeSpace::GetDescWithMaxSize(_out_ CStoreFreeSpaceDesc& desc)
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
#include "utils/plog.h"
|
||||
|
||||
#if defined(__LP64__) || defined(__64BIT__)
|
||||
typedef unsigned int GS_UINT32;
|
||||
typedef unsigned int GS_UINT32;
|
||||
#else
|
||||
typedef unsigned long GS_UINT32;
|
||||
typedef unsigned long GS_UINT32;
|
||||
#endif
|
||||
|
||||
#define OBS_NOT_IMPLEMENT \
|
||||
|
|
@ -41,25 +41,50 @@
|
|||
HTAB *OBSConnectorCache = NULL;
|
||||
|
||||
namespace dfs {
|
||||
/*
|
||||
* 功能:创建一个 OBS 连接器对象
|
||||
*
|
||||
* 参数列表:
|
||||
* ctx:内存上下文,用于内存管理
|
||||
* foreignTableId:外部表的对象标识符
|
||||
*/
|
||||
OBSConnector::OBSConnector(MemoryContext ctx, Oid foreignTableId)
|
||||
: m_memcontext(ctx), m_handler(NULL), srvType(T_INVALID)
|
||||
{
|
||||
// build obs handler
|
||||
m_handler = searchConnectorCache(foreignTableId);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建一个 OBS 连接器对象
|
||||
*
|
||||
* 参数列表:
|
||||
* ctx:内存上下文,用于内存管理
|
||||
* obsOptions:OBS 连接选项,包含连接到 OBS 所需的配置信息
|
||||
*/
|
||||
OBSConnector::OBSConnector(MemoryContext ctx, ObsOptions *obsOptions)
|
||||
: m_memcontext(ctx), m_handler(NULL), srvType(T_INVALID)
|
||||
{
|
||||
// build obs handler
|
||||
m_handler = createRWHandler(obsOptions);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 OBS 连接器对象
|
||||
*
|
||||
* 注意:在对象销毁时,调用了 Destroy() 函数以执行必要的清理操作。
|
||||
*/
|
||||
OBSConnector::~OBSConnector()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 OBS 连接器对象
|
||||
*
|
||||
* 注意:
|
||||
* - 这个函数用于销毁 OBS 连接器对象,包括清理与对象相关的资源。
|
||||
* - 在销毁前,将 m_handler->m_object_info.key 设置为 NULL,以避免悬挂指针。
|
||||
* - 调用 DestroyObsReadWriteHandler() 函数销毁 OBS 读写处理程序,并传递 true 参数以确保释放所有相关资源。
|
||||
* - 最后,将 m_handler 指针设置为 NULL,防止重复销毁。
|
||||
*/
|
||||
void OBSConnector::Destroy()
|
||||
{
|
||||
// IMPORT: m_handler->m_prefix not alloc on this class 's memeory context
|
||||
|
|
@ -315,47 +340,99 @@ List *OBSConnector::listObjectsStat(char *searchPath, const char *primitivePrefi
|
|||
|
||||
return objectList;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取指定文件的 DFS 块信息
|
||||
*
|
||||
* 参数列表:
|
||||
* filePath:要查询块信息的文件路径
|
||||
*
|
||||
* 注意:
|
||||
* - 该函数尚未实现,只是一个占位符。在实现此功能之前,它返回 NULL。
|
||||
* - 一旦实现了获取文件的 DFS 块信息的功能,应该更新此函数以提供正确的实现。
|
||||
* - DFS 块信息通常包括块的位置、大小等相关信息。
|
||||
*/
|
||||
DFSBlockInfo *OBSConnector::getBlockLocations(char *filePath)
|
||||
{
|
||||
// 使用 OBS_NOT_IMPLEMENT 标记函数未实现
|
||||
OBS_NOT_IMPLEMENT;
|
||||
// 返回 NULL,因为函数未实现
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:删除指定路径的目录(未实现)
|
||||
*
|
||||
* 参数列表:
|
||||
* path:要删除的目录路径
|
||||
* recursive:是否递归删除子目录(1 表示递归删除,0 表示不递归)
|
||||
*/
|
||||
int OBSConnector::dropDirectory(const char *path, int recursive)
|
||||
{
|
||||
// 使用 OBS_NOT_IMPLEMENT 标记函数未实现
|
||||
OBS_NOT_IMPLEMENT;
|
||||
// 返回 0,表示删除成功(占位返回值,实际删除未实现)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建指定路径的目录(未实现)
|
||||
*
|
||||
* 参数列表:
|
||||
* path:要创建的目录路径
|
||||
*/
|
||||
int OBSConnector::createDirectory(const char *path)
|
||||
{
|
||||
// 使用 OBS_NOT_IMPLEMENT 标记函数未实现
|
||||
OBS_NOT_IMPLEMENT;
|
||||
// 返回 0,表示创建成功(占位返回值,实际创建未实现)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打开指定路径的文件
|
||||
*
|
||||
* 参数列表:
|
||||
* path:要打开的文件路径
|
||||
* flag:文件打开标志,O_RDONLY 表示只读,其他表示其他操作
|
||||
*/
|
||||
int OBSConnector::openFile(const char *path, int flag)
|
||||
{
|
||||
// 声明 bucket 和 prefix 字符串指针,并初始化为 NULL
|
||||
char *bucket = NULL;
|
||||
char *prefix = NULL;
|
||||
|
||||
// 通过 FetchUrlPropertiesForQuery 函数获取路径中的 bucket 和 prefix
|
||||
FetchUrlPropertiesForQuery(path, &bucket, &prefix);
|
||||
|
||||
// 设置存储处理器的对象信息的 key 为 prefix
|
||||
m_handler->m_object_info.key = prefix;
|
||||
|
||||
// 如果存储处理器的选项中的桶名称为空,将其设置为 bucket
|
||||
if (m_handler->m_option.bucket_options.bucket_name == NULL)
|
||||
m_handler->m_option.bucket_options.bucket_name = bucket;
|
||||
|
||||
// 根据打开标志设置存储处理器的类型为读取或写入
|
||||
if (flag == O_RDONLY)
|
||||
ObsReadWriteHandlerSetType(m_handler, OBS_READ);
|
||||
else
|
||||
ObsReadWriteHandlerSetType(m_handler, OBS_WRITE);
|
||||
|
||||
// 返回 0,表示打开成功
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:删除指定路径的文件或目录
|
||||
*
|
||||
* 参数列表:
|
||||
* path:要删除的文件或目录的路径
|
||||
* recursive:递归删除标志,1 表示递归删除,0 表示只删除指定文件或目录
|
||||
*
|
||||
* 返回值:
|
||||
* 0 表示删除成功,其他值表示删除失败
|
||||
*/
|
||||
int OBSConnector::deleteFile(const char *path, int recursive)
|
||||
{
|
||||
int ret = 0;
|
||||
// 调用 deleteOBSObject 函数删除指定路径的文件或目录,并将结果赋给 ret
|
||||
ret = deleteOBSObject(m_handler);
|
||||
// 返回删除操作的结果
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -435,27 +512,57 @@ bool OBSConnector::existsFile(const char *path)
|
|||
|
||||
return isExists;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否存在有效文件
|
||||
*
|
||||
* 返回值:
|
||||
* false 表示不存在有效文件
|
||||
*/
|
||||
bool OBSConnector::hasValidFile() const
|
||||
{
|
||||
/* OBS_NOT_IMPLEMENT; */
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将数据写入当前文件
|
||||
*
|
||||
* 参数列表:
|
||||
* buffer:要写入的数据的缓冲区
|
||||
* length:要写入的数据的长度
|
||||
*
|
||||
* 返回值:
|
||||
* 0 表示写入成功,其他值表示写入失败
|
||||
*/
|
||||
int OBSConnector::writeCurrentFile(const char *buffer, int length)
|
||||
{
|
||||
int ret = 0;
|
||||
// 调用 writeObsTempFile 函数将数据写入当前文件,并将结果赋给 ret
|
||||
ret = writeObsTempFile(m_handler, buffer, length);
|
||||
|
||||
// 返回写入操作的结果
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:从当前文件中读取数据到缓冲区,直到读取指定长度的数据或达到文件末尾
|
||||
*
|
||||
* 参数列表:
|
||||
* buffer:用于存储读取数据的缓冲区
|
||||
* length:要读取的数据的长度
|
||||
* offset:读取数据的起始偏移量(未使用)
|
||||
*
|
||||
* 返回值:
|
||||
* 0 表示成功读取指定长度的数据,-1 表示读取失败或达到文件末尾
|
||||
*/
|
||||
int OBSConnector::readCurrentFileFully(char *buffer, int length, int64 offset)
|
||||
{
|
||||
int ret = 0;
|
||||
// 调用 read_bucket_object 函数从当前文件中读取数据到缓冲区,返回已读取的数据长度
|
||||
int readSize = (int)read_bucket_object(m_handler, buffer, length);
|
||||
// 如果已读取的数据长度不等于指定的长度,将返回值设置为-1,表示读取失败或达到文件末尾
|
||||
if (readSize != length) {
|
||||
ret = -1;
|
||||
}
|
||||
// 返回读取操作的结果
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -533,19 +640,37 @@ OBSReadWriteHandler *OBSConnector::searchConnectorCache(Oid foreignTableId)
|
|||
|
||||
return handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建 OBS 读写处理程序
|
||||
*
|
||||
* 参数列表:
|
||||
* obsOptions:包含 OBS 配置信息的选项结构
|
||||
*
|
||||
* 返回值:
|
||||
* 返回新创建的 OBS 读写处理程序的指针
|
||||
*/
|
||||
OBSReadWriteHandler *OBSConnector::createRWHandler(ObsOptions *obsOptions)
|
||||
{
|
||||
// 切换内存上下文到当前类的内存上下文
|
||||
AutoContextSwitch memGuard(m_memcontext);
|
||||
|
||||
OBSReadWriteHandler *handler = NULL;
|
||||
|
||||
// 创建 OBS 读写处理程序,并为查询进行配置
|
||||
handler = CreateObsReadWriteHandlerForQuery(obsOptions);
|
||||
handler->in_computing = true;
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* 从缓存中获取 OBS 连接选项
|
||||
*
|
||||
* 参数列表:
|
||||
* foreignTableId:外部表的 OID
|
||||
*
|
||||
* 返回值:
|
||||
* 返回 ObsOptions 结构的指针,包含 OBS 连接选项
|
||||
*/
|
||||
ObsOptions *OBSConnector::getOptionFromCache(Oid foreignTableId)
|
||||
{
|
||||
bool found = false;
|
||||
|
|
@ -565,6 +690,7 @@ ObsOptions *OBSConnector::getOptionFromCache(Oid foreignTableId)
|
|||
if (found && (entry && entry->access_key != NULL && entry->secret_access_key != NULL && entry->address != NULL)) {
|
||||
GS_UINT32 outPutLen;
|
||||
ObsOptions *retOption = copyObsOptions(entry);
|
||||
// 解码加密的秘密访问密钥
|
||||
pfree(retOption->secret_access_key);
|
||||
retOption->secret_access_key = SEC_decodeBase64(entry->secret_access_key, &outPutLen);
|
||||
return retOption;
|
||||
|
|
@ -576,9 +702,11 @@ ObsOptions *OBSConnector::getOptionFromCache(Oid foreignTableId)
|
|||
/* Put the new connector info into the hash table. */
|
||||
(void)LWLockAcquire(DfsConnectorCacheLock, LW_EXCLUSIVE);
|
||||
entry = (ObsOptions *)hash_search(OBSConnectorCache, (void *)&key, HASH_ENTER, &found);
|
||||
// 如果未能成功添加到哈希表,报错
|
||||
if (entry == NULL)
|
||||
ereport(PANIC, (errcode(ERRCODE_UNDEFINED_OBJECT), errmodule(MOD_OBS),
|
||||
errmsg("build global OBS connect cache hash table failed")));
|
||||
// 如果在哈希表中未找到连接选项,将外部表的选项复制到哈希表中
|
||||
if (!found) {
|
||||
Assert(obsOptions != NULL);
|
||||
entry->address = obsOptions->address;
|
||||
|
|
@ -591,6 +719,7 @@ ObsOptions *OBSConnector::getOptionFromCache(Oid foreignTableId)
|
|||
}
|
||||
LWLockRelease(DfsConnectorCacheLock);
|
||||
|
||||
// 返回连接选项的副本,并解码加密的秘密访问密钥
|
||||
ObsOptions *retOption = copyObsOptions(entry);
|
||||
GS_UINT32 outPutLen;
|
||||
pfree(retOption->secret_access_key);
|
||||
|
|
@ -608,52 +737,82 @@ int OBSConnector::getType()
|
|||
{
|
||||
return (int)OBS_CONNECTOR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:用于数据结构(如哈希表或搜索树)中,以帮助确定元素的位置或查找元素
|
||||
* 参数说明:
|
||||
* key1:指向第一个Oid的指针。
|
||||
* key2:指向第二个Oid的指针。
|
||||
* keySize:表示Oid的大小,通常是sizeof(Oid)。
|
||||
*/
|
||||
static int matchOid(const void *key1, const void *key2, Size keySize)
|
||||
{
|
||||
return (int)(*(Oid *)key1 - *(Oid *)key2);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化OBS连接器缓存锁
|
||||
*
|
||||
* 无参数
|
||||
*/
|
||||
void InitOBSConnectorCacheLock()
|
||||
{
|
||||
HASHCTL ctl;
|
||||
errno_t rc = 0;
|
||||
HASHCTL ctl; // 哈希表的控制结构
|
||||
errno_t rc = 0; // 错误码
|
||||
|
||||
if (OBSConnectorCache == NULL) {
|
||||
// 清零控制结构
|
||||
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
|
||||
securec_check(rc, "\0", "\0");
|
||||
ctl.hcxt = g_instance.instance_context;
|
||||
ctl.keysize = sizeof(Oid);
|
||||
ctl.entrysize = sizeof(ObsOptions);
|
||||
ctl.match = (HashCompareFunc)matchOid;
|
||||
ctl.hash = (HashValueFunc)oid_hash;
|
||||
OBSConnectorCache = hash_create("OBS connector cache", 50, &ctl,
|
||||
HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_SHRCTX);
|
||||
ctl.hcxt = g_instance.instance_context; // 分配内存的上下文
|
||||
ctl.keysize = sizeof(Oid); // 键的大小,通常是Oid的大小
|
||||
ctl.entrysize = sizeof(ObsOptions); // 哈希表中每个条目的大小
|
||||
ctl.match = (HashCompareFunc)matchOid; // 用于比较键的函数
|
||||
ctl.hash = (HashValueFunc)oid_hash; // 计算哈希值的函数
|
||||
// 创建哈希表,带有指定的控制选项
|
||||
OBSConnectorCache =
|
||||
hash_create("OBS connector cache", 50, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_SHRCTX);
|
||||
// 如果哈希表创建失败,发出 PANIC 级别的错误信息
|
||||
if (OBSConnectorCache == NULL)
|
||||
ereport(PANIC, (errmodule(MOD_HDFS), errmsg("could not initialize OBS connector hash table")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:使 OBS 连接器缓存无效
|
||||
*
|
||||
* 参数列表:
|
||||
* serverOid:外部表所在的服务器的对象标识符(OID)
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功清理缓存,则返回 true;否则返回 false
|
||||
*/
|
||||
bool InvalidOBSConnectorCache(Oid serverOid)
|
||||
{
|
||||
bool realClean = false;
|
||||
bool realClean = false; // 用于标记是否真正清理了缓存
|
||||
// 获取 DfsConnectorCacheLock 锁,以独占模式
|
||||
(void)LWLockAcquire(DfsConnectorCacheLock, LW_EXCLUSIVE);
|
||||
// 在哈希表 OBSConnectorCache 中查找并移除指定的键(serverOid)
|
||||
ObsOptions *entry = (ObsOptions *)hash_search(OBSConnectorCache, (void *)&serverOid, HASH_REMOVE, NULL);
|
||||
|
||||
// 如果找到了指定的键(entry 不为 NULL),则进行清理操作
|
||||
if (entry != NULL) {
|
||||
// 如果 access_key 不为 NULL,释放其内存
|
||||
if (entry->access_key != NULL) {
|
||||
pfree_ext(entry->access_key);
|
||||
}
|
||||
// 如果 secret_access_key 不为 NULL,使用 OPENSSL_free 释放其内存,并将其置为 NULL
|
||||
if (entry->secret_access_key != NULL) {
|
||||
OPENSSL_free(entry->secret_access_key);
|
||||
entry->secret_access_key = NULL;
|
||||
}
|
||||
// 如果 address 不为 NULL,释放其内存
|
||||
if (entry->address != NULL) {
|
||||
pfree_ext(entry->address);
|
||||
}
|
||||
// 标记已经成功清理缓存
|
||||
realClean = true;
|
||||
}
|
||||
// 释放 DfsConnectorCacheLock 锁
|
||||
LWLockRelease(DfsConnectorCacheLock);
|
||||
// 返回是否成功清理缓存的标志
|
||||
return realClean;
|
||||
}
|
||||
} // namespace dfs
|
||||
|
|
|
|||
|
|
@ -34,58 +34,96 @@ void DoradoReadCtlInfo(ShareStorageXLogCtl *ctlInfo);
|
|||
int DoradoWriteXLog(XLogRecPtr startLsn, char *buf, int writeLen);
|
||||
int DoradoReadXLog(XLogRecPtr startLsn, char *buf, int expectReadLen);
|
||||
void DoradoFsync();
|
||||
|
||||
/*
|
||||
* 功能:定义一个静态常量结构体,并初始化其成员函数指针
|
||||
*/
|
||||
static const ShareStorageOperateIf doradoOperateIf = {
|
||||
DoradoReadCtlInfo, DoradoWriteCtlInfo, DoradoReadXLog, DoradoWriteXLog, DoradoFsync,
|
||||
};
|
||||
|
||||
/*
|
||||
* 功能:计算 Xlog 位置
|
||||
*
|
||||
* 参数列表:
|
||||
* expect:表示期望的 Xlog 位置
|
||||
*/
|
||||
static inline uint64 GetXlogPos(uint64 expect)
|
||||
{
|
||||
// 计算 Xlog 位置,通过将期望的位置加上常量 DORADO_XLOG_START_POS 得到
|
||||
return (expect + DORADO_XLOG_START_POS);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化 Dorado 存储
|
||||
*
|
||||
* 参数列表:
|
||||
* filePath:Xlog 文件的路径
|
||||
* fileSize:Xlog 文件的大小
|
||||
*/
|
||||
void InitDoradoStorage(char *filePath, uint64 fileSize)
|
||||
{
|
||||
// 断言确保共享存储操作控制块没有被初始化
|
||||
Assert(!g_instance.xlog_cxt.shareStorageopCtl.isInit);
|
||||
// 设置 Xlog 文件路径为传入的 filePath
|
||||
g_instance.xlog_cxt.shareStorageopCtl.xlogFilePath = filePath;
|
||||
|
||||
|
||||
// 设置块大小为 MEMORY_ALIGNED_SIZE(一个常量)
|
||||
g_instance.xlog_cxt.shareStorageopCtl.blkSize = MEMORY_ALIGNED_SIZE;
|
||||
// 设置操作接口为 doradoOperateIf(之前定义的结构体)
|
||||
g_instance.xlog_cxt.shareStorageopCtl.opereateIf = &doradoOperateIf;
|
||||
|
||||
// 规范化文件路径,确保它是绝对路径
|
||||
canonicalize_path(filePath);
|
||||
// 打开 Xlog 文件,以读写方式打开,二进制模式,使用 O_DIRECT 标志
|
||||
// S_IRUSR | S_IWUSR 表示文件权限,S_IRUSR 表示用户有读权限,S_IWUSR 表示用户有写权限
|
||||
g_instance.xlog_cxt.shareStorageopCtl.fd = open(filePath, O_RDWR | PG_BINARY | O_DIRECT, S_IRUSR | S_IWUSR);
|
||||
// 如果文件打开失败,报告错误并且终止程序(PANIC)
|
||||
if (g_instance.xlog_cxt.shareStorageopCtl.fd < 0) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("could not open xlog file \"%s\" : %m", filePath)));
|
||||
}
|
||||
|
||||
// 设置共享存储操作控制块的初始化标志为 true
|
||||
g_instance.xlog_cxt.shareStorageopCtl.isInit = true;
|
||||
// 如果正在进行数据库初始化(IsInitdb 为真),设置 Xlog 文件大小为传入的 fileSize
|
||||
if (IsInitdb) {
|
||||
g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize = fileSize;
|
||||
} else {
|
||||
// 否则,如果不是初始化数据库,断言确保共享存储 XLog 控制块不为空
|
||||
Assert(g_instance.xlog_cxt.shareStorageXLogCtl != NULL);
|
||||
// 调用 DoradoReadCtlInfo 函数读取共享存储 XLog 控制块的信息,并设置 Xlog 文件大小
|
||||
DoradoReadCtlInfo(g_instance.xlog_cxt.shareStorageXLogCtl);
|
||||
g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize = g_instance.xlog_cxt.shareStorageXLogCtl->xlogFileSize;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将共享存储控制信息写入文件
|
||||
*
|
||||
* 参数列表:
|
||||
* ctlInfo:共享存储 XLog 控制块的指针
|
||||
*/
|
||||
void DoradoWriteCtlInfo(const ShareStorageXLogCtl *ctlInfo)
|
||||
{
|
||||
// 断言确保文件描述符大于 0,即文件已经成功打开
|
||||
Assert(g_instance.xlog_cxt.shareStorageopCtl.fd > 0);
|
||||
|
||||
// 检查共享存储块大小是否与控制块大小对齐,如果不对齐,报告错误并终止程序
|
||||
if (!IS_TYPE_ALIGINED(g_instance.xlog_cxt.shareStorageopCtl.blkSize, ctlInfo)) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("dorado write control info ptr(%p) is not match,mask is %X",
|
||||
ctlInfo, g_instance.xlog_cxt.shareStorageopCtl.blkSize)));
|
||||
}
|
||||
// 检查控制块的魔数和检验码是否匹配,如果不匹配,报告致命错误
|
||||
if (ctlInfo->magic != SHARE_STORAGE_CTL_MAGIC || ctlInfo->checkNumber != SHARE_STORAGE_CTL_CHCK_NUMBER) {
|
||||
ereport(FATAL, (errmsg("ShareStorageXLogCtl info in memory maybe damaged")));
|
||||
}
|
||||
// 计算控制块的 CRC 校验值
|
||||
pg_crc32c crc = CalShareStorageCtlInfoCrc(ctlInfo);
|
||||
// 检查计算得到的 CRC 是否与控制块中的 CRC 相匹配,如果不匹配,报告致命错误
|
||||
if (!EQ_CRC32C(crc, ctlInfo->crc)) {
|
||||
ereport(FATAL, (errmsg("crc check fail for ShareStorageXLogCtl in DoradoWriteCtlInfo")));
|
||||
}
|
||||
|
||||
// write 512bytes
|
||||
// 使用 pwrite 函数将控制块写入文件,写入大小为 DORADO_CTL_WRITE_SIZE 字节,偏移量为 0
|
||||
ssize_t actualBytes = pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, ctlInfo, DORADO_CTL_WRITE_SIZE, 0);
|
||||
// 检查实际写入的字节数是否与期望的字节数相匹配,如果不匹配,报告错误
|
||||
if (actualBytes != (ssize_t)DORADO_CTL_WRITE_SIZE) {
|
||||
/* if write didn't set errno, assume no disk space */
|
||||
if (errno == 0) {
|
||||
|
|
@ -97,41 +135,68 @@ void DoradoWriteCtlInfo(const ShareStorageXLogCtl *ctlInfo)
|
|||
(unsigned long)actualBytes, (unsigned long)DORADO_CTL_WRITE_SIZE)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:从文件中读取共享存储控制信息
|
||||
*
|
||||
* 参数列表:
|
||||
* ctlInfo:共享存储 XLog 控制块的指针
|
||||
*/
|
||||
void DoradoReadCtlInfo(ShareStorageXLogCtl *ctlInfo)
|
||||
{
|
||||
// 断言确保文件描述符大于 0,即文件已经成功打开
|
||||
Assert(g_instance.xlog_cxt.shareStorageopCtl.fd > 0);
|
||||
// 检查共享存储块大小是否与控制块大小对齐,如果不对齐,报告错误并终止程序
|
||||
if (!IS_TYPE_ALIGINED(g_instance.xlog_cxt.shareStorageopCtl.blkSize, ctlInfo)) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("dorado read control info ptr(%p) is not match,mask is %X",
|
||||
ctlInfo, g_instance.xlog_cxt.shareStorageopCtl.blkSize)));
|
||||
}
|
||||
|
||||
// 使用 pread 函数从已打开的文件中读取控制块信息,读取大小为 DORADO_CTL_WRITE_SIZE 字节,偏移量为 0
|
||||
ssize_t actualBytes = pread(g_instance.xlog_cxt.shareStorageopCtl.fd, ctlInfo, DORADO_CTL_WRITE_SIZE, 0);
|
||||
// 检查实际读取的字节数是否与期望的字节数相匹配,如果不匹配,报告错误并终止程序
|
||||
if (actualBytes != (ssize_t)DORADO_CTL_WRITE_SIZE) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("could not read dorado ctl info: %m")));
|
||||
}
|
||||
|
||||
// 检查控制块的魔数和检验码是否匹配,如果不匹配,报告致命错误
|
||||
if (ctlInfo->magic != SHARE_STORAGE_CTL_MAGIC || ctlInfo->checkNumber != SHARE_STORAGE_CTL_CHCK_NUMBER) {
|
||||
ereport(FATAL, (errmsg("dorado ctl info maybe damaged")));
|
||||
}
|
||||
|
||||
// 计算控制块的 CRC 校验值
|
||||
pg_crc32c crc = CalShareStorageCtlInfoCrc(ctlInfo);
|
||||
// 检查计算得到的 CRC 是否与控制块中的 CRC 相匹配,如果不匹配,报告致命错误
|
||||
if (!EQ_CRC32C(crc, ctlInfo->crc)) {
|
||||
ereport(FATAL, (errmsg("crc check fail for ShareStorageXLogCtl in DoradoReadCtlInfo")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:从文件中读取 XLog 数据
|
||||
*
|
||||
* 参数列表:
|
||||
* startLsn:开始读取的 XLog 记录位置
|
||||
* buf:用于存储读取数据的缓冲区
|
||||
* expectReadLen:期望读取的数据长度
|
||||
*
|
||||
* 返回值:成功读取的字节数,或者错误时返回负值
|
||||
*/
|
||||
int DoradoReadXLog(XLogRecPtr startLsn, char *buf, int expectReadLen)
|
||||
{
|
||||
// 断言确保文件描述符大于 0,即文件已经成功打开
|
||||
Assert(g_instance.xlog_cxt.shareStorageopCtl.fd > 0);
|
||||
// 检查共享存储块大小是否与缓冲区大小对齐,如果不对齐,报告错误并终止程序
|
||||
if (!IS_TYPE_ALIGINED(g_instance.xlog_cxt.shareStorageopCtl.blkSize, buf)) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("dorado read xlog ptr(%p) is not match,mask is %X", buf,
|
||||
g_instance.xlog_cxt.shareStorageopCtl.blkSize)));
|
||||
}
|
||||
|
||||
// 计算起始位置,即 startLsn 对 XLog 文件大小取模
|
||||
uint64 startPos = startLsn % g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize;
|
||||
// 如果期望读取的数据不会超过文件末尾
|
||||
if ((startPos + expectReadLen) <= g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize) {
|
||||
// 使用 pread 函数从已打开的文件中读取数据,读取大小为 expectReadLen 字节,起始位置为 GetXlogPos(startPos)
|
||||
ssize_t actualBytes = pread(g_instance.xlog_cxt.shareStorageopCtl.fd, buf, expectReadLen, GetXlogPos(startPos));
|
||||
// 如果读取失败,报告错误并终止程序
|
||||
if (actualBytes < 0) {
|
||||
uint32 shiftSize = 32;
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("read xlog(start:%X/%X, pos:%lu len:%d) failed : %m",
|
||||
|
|
@ -139,11 +204,16 @@ int DoradoReadXLog(XLogRecPtr startLsn, char *buf, int expectReadLen)
|
|||
static_cast<uint32>(startLsn), startPos, expectReadLen)));
|
||||
}
|
||||
|
||||
// 返回成功读取的字节数
|
||||
return static_cast<int>(actualBytes);
|
||||
} else {
|
||||
} else { // 如果期望读取的数据会超过文件末尾,需要分两次读取
|
||||
// 第一次读取的大小,从 startPos 到文件末尾
|
||||
int firstReadSize = g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize - startPos;
|
||||
// 第二次读取的大小,剩余部分
|
||||
int secondReadSize = expectReadLen - firstReadSize;
|
||||
// 第一次读取数据,起始位置为 GetXlogPos(startPos)
|
||||
ssize_t actualBytes = pread(g_instance.xlog_cxt.shareStorageopCtl.fd, buf, firstReadSize, GetXlogPos(startPos));
|
||||
// 如果第一次读取失败,报告错误并终止程序
|
||||
if (actualBytes < 0) {
|
||||
uint32 shiftSize = 32;
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("first read xlog(start:%X/%X, pos:%lu len:%d) failed:%m",
|
||||
|
|
@ -151,11 +221,14 @@ int DoradoReadXLog(XLogRecPtr startLsn, char *buf, int expectReadLen)
|
|||
static_cast<uint32>(startLsn), startPos, firstReadSize)));
|
||||
}
|
||||
|
||||
// 如果第一次读取的字节数不足,返回实际读取的字节数
|
||||
if (actualBytes < firstReadSize) {
|
||||
return static_cast<int>(actualBytes);
|
||||
}
|
||||
|
||||
// 第二次读取数据,起始位置为 GetXlogPos(0)
|
||||
actualBytes = pread(t_thrd.xlog_cxt.openLogFile, buf + firstReadSize, secondReadSize, GetXlogPos(0));
|
||||
// 如果第二次读取失败,报告错误并终止程序
|
||||
if (actualBytes < 0) {
|
||||
uint32 shiftSize = 32;
|
||||
XLogRecPtr nextStartLsn = startLsn + firstReadSize;
|
||||
|
|
@ -164,23 +237,40 @@ int DoradoReadXLog(XLogRecPtr startLsn, char *buf, int expectReadLen)
|
|||
static_cast<uint32>(nextStartLsn), secondReadSize)));
|
||||
}
|
||||
|
||||
// 返回成功读取的字节数,包括第一次和第二次读取的部分
|
||||
return static_cast<int>(actualBytes + firstReadSize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:向文件中写入 XLog 数据
|
||||
*
|
||||
* 参数列表:
|
||||
* startLsn:要写入的 XLog 记录的起始位置
|
||||
* buf:包含要写入的数据的缓冲区
|
||||
* writeLen:要写入的数据长度
|
||||
*
|
||||
* 返回值:成功写入的数据长度,或者错误时返回负值
|
||||
*/
|
||||
int DoradoWriteXLog(XLogRecPtr startLsn, char *buf, int writeLen)
|
||||
{
|
||||
// 断言确保文件描述符大于 0,即文件已经成功打开
|
||||
Assert(g_instance.xlog_cxt.shareStorageopCtl.fd > 0);
|
||||
// 计算起始位置,即 startLsn 对 XLog 文件大小取模
|
||||
uint64 startPos = startLsn % g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize;
|
||||
|
||||
// 检查共享存储块大小是否与缓冲区大小对齐,如果不对齐,报告错误并终止程序
|
||||
if (!IS_TYPE_ALIGINED(g_instance.xlog_cxt.shareStorageopCtl.blkSize, buf)) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("dorado write xlog ptr(%p) is not match,mask is %X", buf,
|
||||
g_instance.xlog_cxt.shareStorageopCtl.blkSize)));
|
||||
}
|
||||
|
||||
// 如果写入的数据不会超过文件末尾
|
||||
if ((startPos + writeLen) <= g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize) {
|
||||
// 使用 pwrite 函数向已打开的文件中写入数据,写入大小为 writeLen 字节,起始位置为 GetXlogPos(startPos)
|
||||
ssize_t actualBytes = pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, buf, writeLen, GetXlogPos(startPos));
|
||||
// 如果写入的字节数不等于期望的字节数,报告错误并终止程序
|
||||
if (actualBytes != writeLen) {
|
||||
// 如果写入未设置 errno,假定是磁盘空间不足
|
||||
if (errno == 0) {
|
||||
errno = ENOSPC;
|
||||
}
|
||||
|
|
@ -190,13 +280,18 @@ int DoradoWriteXLog(XLogRecPtr startLsn, char *buf, int writeLen)
|
|||
static_cast<uint32>(startLsn >> shiftSize),
|
||||
static_cast<uint32>(startLsn), startPos, writeLen)));
|
||||
}
|
||||
} else {
|
||||
} else { // 如果写入的数据会超过文件末尾,需要分两次写入
|
||||
// 第一次写入的大小,从 startPos 到文件末尾
|
||||
int firstWriteSize = g_instance.xlog_cxt.shareStorageopCtl.xlogFileSize - startPos;
|
||||
// 第二次写入的大小,剩余部分
|
||||
int secondWriteSize = writeLen - firstWriteSize;
|
||||
|
||||
ssize_t actualBytes = pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, buf, firstWriteSize,
|
||||
GetXlogPos(startPos));
|
||||
// 第一次写入数据,起始位置为 GetXlogPos(startPos)
|
||||
ssize_t actualBytes =
|
||||
pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, buf, firstWriteSize, GetXlogPos(startPos));
|
||||
// 如果第一次写入的字节数不等于第一次写入的大小,报告错误并终止程序
|
||||
if (actualBytes != firstWriteSize) {
|
||||
// 如果写入未设置 errno,假定是磁盘空间不足
|
||||
if (errno == 0) {
|
||||
errno = ENOSPC;
|
||||
}
|
||||
|
|
@ -207,9 +302,12 @@ int DoradoWriteXLog(XLogRecPtr startLsn, char *buf, int writeLen)
|
|||
static_cast<uint32>(startLsn), startPos, firstWriteSize)));
|
||||
}
|
||||
|
||||
actualBytes = pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, buf + firstWriteSize, secondWriteSize,
|
||||
GetXlogPos(0));
|
||||
// 第二次写入数据,起始位置为 GetXlogPos(0)
|
||||
actualBytes =
|
||||
pwrite(g_instance.xlog_cxt.shareStorageopCtl.fd, buf + firstWriteSize, secondWriteSize, GetXlogPos(0));
|
||||
// 如果第二次写入的字节数不等于第二次写入的大小,报告错误并终止程序
|
||||
if (actualBytes != secondWriteSize) {
|
||||
// 如果写入未设置 errno,假定是磁盘空间不足
|
||||
if (errno == 0) {
|
||||
errno = ENOSPC;
|
||||
}
|
||||
|
|
@ -222,12 +320,20 @@ int DoradoWriteXLog(XLogRecPtr startLsn, char *buf, int writeLen)
|
|||
}
|
||||
}
|
||||
|
||||
// 返回成功写入的数据长度
|
||||
return writeLen;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将共享存储文件的数据同步到磁盘
|
||||
*/
|
||||
|
||||
void DoradoFsync()
|
||||
{
|
||||
// 断言确保文件描述符大于 0,即文件已经成功打开
|
||||
Assert(g_instance.xlog_cxt.shareStorageopCtl.fd > 0);
|
||||
|
||||
// 使用 fsync 函数将共享存储文件的数据同步到磁盘
|
||||
if (fsync(g_instance.xlog_cxt.shareStorageopCtl.fd) != 0) {
|
||||
ereport(PANIC, (errcode_for_file_access(), errmsg("could not fsync dorado file %s: %m",
|
||||
g_instance.xlog_cxt.shareStorageopCtl.xlogFilePath)));
|
||||
|
|
|
|||
|
|
@ -54,20 +54,55 @@ OccTransactionManager::OccTransactionManager()
|
|||
OccTransactionManager::~OccTransactionManager()
|
||||
{}
|
||||
|
||||
/*
|
||||
* 功能:初始化事务管理器
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功初始化事务管理器,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化事务管理器的状态和属性,但当前版本尚未执行具体的初始化操作。
|
||||
* 在将来的版本中,应根据需求添加更多的初始化代码。
|
||||
*/
|
||||
bool OccTransactionManager::Init()
|
||||
{
|
||||
bool result = true;
|
||||
return result;
|
||||
bool result = true; // 创建一个名为 result 的布尔变量并初始化为 true。
|
||||
|
||||
return result; // 返回 result 变量的值,这里始终返回 true。
|
||||
}
|
||||
|
||||
bool OccTransactionManager::CheckVersion(const Access* access)
|
||||
/*
|
||||
* 功能:检查事务版本
|
||||
*
|
||||
* 参数列表:
|
||||
* access:访问对象指针,包含要检查的事务信息
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查给定事务是否与访问对象中的事务ID匹配。通常用于验证已提交的行的版本。
|
||||
* 如果事务版本匹配,返回 true,否则返回 false。
|
||||
*/
|
||||
bool OccTransactionManager::CheckVersion(const Access *access)
|
||||
{
|
||||
// We always validate on committed rows!
|
||||
const Row* row = access->GetRowFromHeader();
|
||||
const Row *row = access->GetRowFromHeader();
|
||||
|
||||
// 检查行的事务序列号(CSN)是否与访问对象中的事务ID(TID)匹配
|
||||
return (row->m_rowHeader.GetCSN() == access->m_tid);
|
||||
}
|
||||
|
||||
bool OccTransactionManager::QuickHeaderValidation(const Access* access)
|
||||
/*
|
||||
* 功能:快速验证事务头信息
|
||||
*
|
||||
* 参数列表:
|
||||
* access:访问对象指针,包含要验证的事务头信息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果事务头信息验证通过,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于快速验证访问对象中的事务头信息,以决定是否可以继续处理事务。
|
||||
* 具体验证逻辑包括检查操作类型、事务版本和其他条件,根据不同的情况进行验证。
|
||||
*/
|
||||
bool OccTransactionManager::QuickHeaderValidation(const Access *access)
|
||||
{
|
||||
if (access->m_type != INS) {
|
||||
// For WR/DEL/RD_FOR_UPDATE lets verify CSN
|
||||
|
|
@ -76,7 +111,7 @@ bool OccTransactionManager::QuickHeaderValidation(const Access* access)
|
|||
// Lets verify the inserts
|
||||
// For upgrade we verify the row
|
||||
// csn has not changed!
|
||||
Sentinel* sent = access->m_origSentinel;
|
||||
Sentinel *sent = access->m_origSentinel;
|
||||
if (access->m_params.IsUpgradeInsert()) {
|
||||
if (access->m_params.IsDummyDeletedRow()) {
|
||||
// Check is sentinel is deleted and CSN is VALID - ABA problem
|
||||
|
|
@ -104,78 +139,144 @@ bool OccTransactionManager::QuickHeaderValidation(const Access* access)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OccTransactionManager::ValidateReadSet(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:验证读取集合
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待验证的读取集合
|
||||
*
|
||||
* 返回值:
|
||||
* 如果读取集合中的所有读取操作都验证通过,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于验证事务的读取集合中的读取操作是否有效。
|
||||
* 它遍历有序的访问集合,对每个读取操作检查是否有效。
|
||||
* 如果所有读取操作都验证通过,返回true,否则返回false。
|
||||
*/
|
||||
bool OccTransactionManager::ValidateReadSet(TxnManager *txMan)
|
||||
{
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto &raPair : orderedSet) {
|
||||
const Access *ac = raPair.second;
|
||||
if (ac->m_type != RD) {
|
||||
continue;
|
||||
}
|
||||
// 验证读取操作的有效性
|
||||
if (!ac->GetRowFromHeader()->m_rowHeader.ValidateRead(ac->m_tid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OccTransactionManager::ValidateWriteSet(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:验证写入集合
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待验证的写入集合
|
||||
*
|
||||
* 返回值:
|
||||
* 如果写入集合中的所有写入操作都验证通过,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于验证事务的写入集合中的写入操作是否有效。
|
||||
* 它遍历有序的访问集合,对每个写入操作执行快速验证。
|
||||
* 如果所有写入操作都验证通过,返回true,否则返回false。
|
||||
*/
|
||||
bool OccTransactionManager::ValidateWriteSet(TxnManager *txMan)
|
||||
{
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto &raPair : orderedSet) {
|
||||
const Access *ac = raPair.second;
|
||||
if (ac->m_type == RD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 执行快速验证
|
||||
if (!QuickHeaderValidation(ac)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RC OccTransactionManager::LockRows(TxnManager* txMan, uint32_t& numRowsLock)
|
||||
/*
|
||||
* 功能:锁定行
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待锁定的行
|
||||
* numRowsLock:用于存储锁定的行数的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 返回操作结果的代码 (RC),通常是 RC_OK 表示成功。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于锁定事务中需要修改的行。
|
||||
* 它遍历有序的访问集合,对每个需要锁定的行执行锁定操作。
|
||||
* 在锁定行后,会将锁定的行数存储在 numRowsLock 引用中,并进行断言以验证锁定状态。
|
||||
*/
|
||||
RC OccTransactionManager::LockRows(TxnManager *txMan, uint32_t &numRowsLock)
|
||||
{
|
||||
RC rc = RC_OK;
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
numRowsLock = 0;
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
if (ac->m_type == RD) {
|
||||
RC rc = RC_OK; // 初始化返回代码为 RC_OK
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
numRowsLock = 0; // 初始化锁定的行数为 0
|
||||
|
||||
// 遍历有序的访问集合
|
||||
for (const auto &raPair : orderedSet) {
|
||||
const Access *ac = raPair.second;
|
||||
|
||||
if (ac->m_type == RD) { // 如果操作类型是读取,则跳过
|
||||
continue;
|
||||
}
|
||||
if (ac->m_params.IsPrimarySentinel()) {
|
||||
Row* row = ac->GetRowFromHeader();
|
||||
row->m_rowHeader.Lock();
|
||||
numRowsLock++;
|
||||
MOT_ASSERT(row->GetPrimarySentinel()->IsLocked() == true);
|
||||
|
||||
if (ac->m_params.IsPrimarySentinel()) { // 如果是主要的 Sentinel
|
||||
Row *row = ac->GetRowFromHeader(); // 获取行对象
|
||||
row->m_rowHeader.Lock(); // 锁定行
|
||||
numRowsLock++; // 增加锁定的行数计数
|
||||
MOT_ASSERT(row->GetPrimarySentinel()->IsLocked() == true); // 断言验证锁定状态
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc; // 返回操作结果的代码
|
||||
}
|
||||
|
||||
bool OccTransactionManager::LockHeadersNoWait(TxnManager* txMan, uint32_t& numSentinelsLock)
|
||||
/*
|
||||
* 功能:无等待锁定头信息
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待锁定的头信息
|
||||
* numSentinelsLock:用于存储锁定的 Sentinel 数量的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功锁定所有头信息,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于无等待方式锁定事务中需要修改的头信息。
|
||||
* 它遍历有序的访问集合,对每个需要锁定的头信息执行锁定操作。
|
||||
* 如果锁定失败,会进行重试,如果重试次数超过阈值或者发生异常,则返回false。
|
||||
*/
|
||||
bool OccTransactionManager::LockHeadersNoWait(TxnManager *txMan, uint32_t &numSentinelsLock)
|
||||
{
|
||||
uint64_t sleepTime = 1;
|
||||
uint64_t thdId = txMan->GetThdId();
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
numSentinelsLock = 0;
|
||||
while (numSentinelsLock != m_writeSetSize) {
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
if (ac->m_type == RD) {
|
||||
uint64_t sleepTime = 1; // 初始休眠时间为1
|
||||
uint64_t thdId = txMan->GetThdId(); // 获取线程ID
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
numSentinelsLock = 0; // 初始化锁定的 Sentinel 数量为0
|
||||
|
||||
while (numSentinelsLock != m_writeSetSize) { // 当锁定的 Sentinel 数量不等于写入集合大小时进行循环
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *ac = raPair.second;
|
||||
|
||||
if (ac->m_type == RD) { // 如果操作类型是读取,则跳过
|
||||
continue;
|
||||
}
|
||||
Sentinel* sent = ac->m_origSentinel;
|
||||
if (!sent->TryLock(thdId)) {
|
||||
|
||||
Sentinel *sent = ac->m_origSentinel; // 获取原始 Sentinel 对象
|
||||
|
||||
if (!sent->TryLock(thdId)) { // 尝试锁定 Sentinel
|
||||
break;
|
||||
}
|
||||
numSentinelsLock++;
|
||||
if (ac->m_params.IsPrimaryUpgrade()) {
|
||||
ac->m_auxRow->m_rowHeader.Lock();
|
||||
|
||||
numSentinelsLock++; // 增加锁定的 Sentinel 数量计数
|
||||
|
||||
if (ac->m_params.IsPrimaryUpgrade()) { // 如果是主要升级操作
|
||||
ac->m_auxRow->m_rowHeader.Lock(); // 锁定辅助行的头信息
|
||||
}
|
||||
// New insert row is already committed!
|
||||
// Check if row has changed in sentinel
|
||||
|
|
@ -184,217 +285,308 @@ bool OccTransactionManager::LockHeadersNoWait(TxnManager* txMan, uint32_t& numSe
|
|||
}
|
||||
}
|
||||
|
||||
if (numSentinelsLock != m_writeSetSize) {
|
||||
ReleaseHeaderLocks(txMan, numSentinelsLock);
|
||||
numSentinelsLock = 0;
|
||||
if (m_preAbort) {
|
||||
for (const auto& acPair : orderedSet) {
|
||||
const Access* ac = acPair.second;
|
||||
if (numSentinelsLock != m_writeSetSize) { // 如果锁定的 Sentinel 数量不等于写入集合大小
|
||||
ReleaseHeaderLocks(txMan, numSentinelsLock); // 释放已锁定的头信息
|
||||
numSentinelsLock = 0; // 重置已锁定的 Sentinel 数量
|
||||
if (m_preAbort) { // 如果是预终止事务
|
||||
for (const auto &acPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *ac = acPair.second;
|
||||
if (!QuickHeaderValidation(ac)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sleepTime > LOCK_TIME_OUT) {
|
||||
if (sleepTime > LOCK_TIME_OUT) { // 如果休眠时间超过阈值
|
||||
return false;
|
||||
} else {
|
||||
if (IsHighContention() == false) {
|
||||
CpuCyclesLevelTime::Sleep(5);
|
||||
if (IsHighContention() == false) { // 如果不是高竞争环境
|
||||
CpuCyclesLevelTime::Sleep(5); // 休眠5个CPU周期
|
||||
} else {
|
||||
usleep(m_dynamicSleep);
|
||||
usleep(m_dynamicSleep); // 否则休眠指定的微秒数
|
||||
}
|
||||
sleepTime = sleepTime << 1;
|
||||
sleepTime = sleepTime << 1; // 休眠时间翻倍
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true; // 返回锁定成功
|
||||
}
|
||||
|
||||
RC OccTransactionManager::LockHeaders(TxnManager* txMan, uint32_t& numSentinelsLock)
|
||||
/*
|
||||
* 功能:锁定头信息
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待锁定的头信息
|
||||
* numSentinelsLock:用于存储锁定的 Sentinel 数量的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 返回操作结果的代码 (RC),通常是 RC_OK 表示成功。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于锁定事务中需要修改的头信息。
|
||||
* 它遍历有序的访问集合,对每个需要锁定的头信息执行锁定操作。
|
||||
* 如果锁定失败,会进行重试,如果重试次数超过阈值或者发生异常,则返回相应的错误代码。
|
||||
*/
|
||||
RC OccTransactionManager::LockHeaders(TxnManager *txMan, uint32_t &numSentinelsLock)
|
||||
{
|
||||
RC rc = RC_OK;
|
||||
uint64_t thdId = txMan->GetThdId();
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
numSentinelsLock = 0;
|
||||
if (m_validationNoWait) {
|
||||
if (!LockHeadersNoWait(txMan, numSentinelsLock)) {
|
||||
rc = RC_ABORT;
|
||||
goto final;
|
||||
RC rc = RC_OK; // 初始化返回代码为 RC_OK
|
||||
uint64_t thdId = txMan->GetThdId(); // 获取线程ID
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
numSentinelsLock = 0; // 初始化锁定的 Sentinel 数量为0
|
||||
|
||||
if (m_validationNoWait) { // 如果启用无等待锁定
|
||||
if (!LockHeadersNoWait(txMan, numSentinelsLock)) { // 调用无等待锁定函数
|
||||
rc = RC_ABORT; // 锁定失败,设置返回代码为 RC_ABORT
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
} else {
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
if (ac->m_type == RD) {
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *ac = raPair.second;
|
||||
|
||||
if (ac->m_type == RD) { // 如果操作类型是读取,则跳过
|
||||
continue;
|
||||
}
|
||||
Sentinel* sent = ac->m_origSentinel;
|
||||
sent->Lock(thdId);
|
||||
numSentinelsLock++;
|
||||
if (ac->m_params.IsPrimaryUpgrade()) {
|
||||
ac->m_auxRow->m_rowHeader.Lock();
|
||||
|
||||
Sentinel *sent = ac->m_origSentinel; // 获取原始 Sentinel 对象
|
||||
sent->Lock(thdId); // 锁定 Sentinel
|
||||
numSentinelsLock++; // 增加锁定的 Sentinel 数量计数
|
||||
|
||||
if (ac->m_params.IsPrimaryUpgrade()) { // 如果是主要升级操作
|
||||
ac->m_auxRow->m_rowHeader.Lock(); // 锁定辅助行的头信息
|
||||
}
|
||||
// New insert row is already committed!
|
||||
// Check if row has chained in sentinel
|
||||
if (!QuickHeaderValidation(ac)) {
|
||||
rc = RC_ABORT;
|
||||
goto final;
|
||||
if (!QuickHeaderValidation(ac)) { // 调用快速验证函数
|
||||
rc = RC_ABORT; // 验证失败,设置返回代码为 RC_ABORT
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
}
|
||||
}
|
||||
final:
|
||||
return rc;
|
||||
|
||||
final: // final 标签,用于处理最后的清理和返回
|
||||
return rc; // 返回操作结果的代码
|
||||
}
|
||||
|
||||
bool OccTransactionManager::PreAllocStableRow(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:预分配稳定行
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要预分配稳定行的信息
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功预分配稳定行,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 如果启用检查点,此函数用于在提交事务前预分配稳定行。
|
||||
* 它遍历有序的访问集合,对每个需要预分配稳定行的操作执行预分配操作。
|
||||
* 如果预分配失败,会回滚已分配的稳定行并返回false。
|
||||
*/
|
||||
bool OccTransactionManager::PreAllocStableRow(TxnManager *txMan)
|
||||
{
|
||||
if (GetGlobalConfiguration().m_enableCheckpoint) {
|
||||
GetCheckpointManager()->BeginCommit(txMan);
|
||||
if (GetGlobalConfiguration().m_enableCheckpoint) { // 如果启用检查点
|
||||
GetCheckpointManager()->BeginCommit(txMan); // 开始提交检查点
|
||||
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type == RD) {
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second;
|
||||
if (access->m_type == RD) { // 如果操作类型是读取,则跳过
|
||||
continue;
|
||||
}
|
||||
if (access->m_params.IsPrimarySentinel()) {
|
||||
if (!GetCheckpointManager()->PreAllocStableRow(txMan, access->GetRowFromHeader(), access->m_type)) {
|
||||
GetCheckpointManager()->FreePreAllocStableRows(txMan);
|
||||
GetCheckpointManager()->EndCommit(txMan);
|
||||
return false;
|
||||
if (access->m_params.IsPrimarySentinel()) { // 如果是主要 Sentinel
|
||||
if (!GetCheckpointManager()->PreAllocStableRow(txMan, access->GetRowFromHeader(),
|
||||
access->m_type)) { // 预分配稳定行
|
||||
GetCheckpointManager()->FreePreAllocStableRows(txMan); // 释放已分配的稳定行
|
||||
GetCheckpointManager()->EndCommit(txMan); // 结束提交检查点
|
||||
return false; // 预分配失败,返回false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true; // 返回预分配成功
|
||||
}
|
||||
|
||||
bool OccTransactionManager::QuickVersionCheck(TxnManager* txMan, uint32_t& readSetSize)
|
||||
/*
|
||||
* 功能:快速版本检查
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要进行版本检查的信息
|
||||
* readSetSize:用于存储读取集合大小的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 如果版本检查成功,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于快速执行版本检查以确定事务的读取集合大小。
|
||||
* 它遍历有序的访问集合,根据访问类型和隔离级别更新集合大小信息。
|
||||
* 如果启用预终止,还会进行头信息验证。
|
||||
*/
|
||||
bool OccTransactionManager::QuickVersionCheck(TxnManager *txMan, uint32_t &readSetSize)
|
||||
{
|
||||
int isolationLevel = txMan->GetTxnIsoLevel();
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
readSetSize = 0;
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* ac = raPair.second;
|
||||
int isolationLevel = txMan->GetTxnIsoLevel(); // 获取事务的隔离级别
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
readSetSize = 0; // 初始化读取集合大小为0
|
||||
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *ac = raPair.second; // 获取访问对象
|
||||
|
||||
if (ac->m_params.IsPrimarySentinel()) {
|
||||
m_rowsSetSize++;
|
||||
m_rowsSetSize++; // 如果是主要 Sentinel,则增加行集合大小计数
|
||||
}
|
||||
|
||||
switch (ac->m_type) {
|
||||
case RD_FOR_UPDATE:
|
||||
case WR:
|
||||
m_writeSetSize++;
|
||||
m_writeSetSize++; // 如果是写入类型,增加写集合大小计数
|
||||
break;
|
||||
case DEL:
|
||||
m_writeSetSize++;
|
||||
m_deleteSetSize++;
|
||||
m_writeSetSize++; // 如果是删除类型,增加写集合大小计数
|
||||
m_deleteSetSize++; // 同时增加删除集合大小计数
|
||||
break;
|
||||
case INS:
|
||||
m_insertSetSize++;
|
||||
m_writeSetSize++;
|
||||
m_insertSetSize++; // 如果是插入类型,增加插入集合大小计数
|
||||
m_writeSetSize++; // 同时增加写集合大小计数
|
||||
break;
|
||||
case RD:
|
||||
if (isolationLevel > READ_COMMITED) {
|
||||
readSetSize++;
|
||||
if (isolationLevel > READ_COMMITTED) {
|
||||
readSetSize++; // 如果是读取类型且隔离级别较高,增加读取集合大小计数
|
||||
} else {
|
||||
continue;
|
||||
continue; // 否则跳过当前操作
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_preAbort) {
|
||||
if (!QuickHeaderValidation(ac)) {
|
||||
if (m_preAbort) { // 如果启用预终止
|
||||
if (!QuickHeaderValidation(ac)) { // 进行头信息验证,如果失败则返回false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true; // 返回版本检查成功
|
||||
}
|
||||
|
||||
RC OccTransactionManager::ValidateOcc(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:验证 OCC(Optimistic Concurrency Control)
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含待验证的 OCC 信息
|
||||
*
|
||||
* 返回值:
|
||||
* 返回操作结果的代码 (RC),通常是 RC_OK 表示成功。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于验证 OCC 事务,包括执行快速版本检查、锁定头信息、验证读取集合和写入集合、预分配稳定行等步骤。
|
||||
* 它计算事务的各个集合大小,并在验证成功时锁定相关的头信息。
|
||||
* 如果验证失败,会根据错误代码进行相应处理。
|
||||
*/
|
||||
RC OccTransactionManager::ValidateOcc(TxnManager *txMan)
|
||||
{
|
||||
uint32_t numSentinelLock = 0;
|
||||
m_rowsLocked = false;
|
||||
TxnAccess* tx = txMan->m_accessMgr.Get();
|
||||
RC rc = RC_OK;
|
||||
const uint32_t rowCount = tx->m_rowCnt;
|
||||
uint32_t numSentinelLock = 0; // 初始化锁定的 Sentinel 数量为0
|
||||
m_rowsLocked = false; // 初始化行是否已锁定为 false
|
||||
TxnAccess *tx = txMan->m_accessMgr.Get(); // 获取事务的访问对象
|
||||
RC rc = RC_OK; // 初始化返回代码为 RC_OK
|
||||
const uint32_t rowCount = tx->m_rowCnt; // 获取行数
|
||||
|
||||
m_writeSetSize = 0;
|
||||
m_rowsSetSize = 0;
|
||||
m_deleteSetSize = 0;
|
||||
m_insertSetSize = 0;
|
||||
m_txnCounter++;
|
||||
m_writeSetSize = 0; // 初始化写入集合大小为0
|
||||
m_rowsSetSize = 0; // 初始化行集合大小为0
|
||||
m_deleteSetSize = 0; // 初始化删除集合大小为0
|
||||
m_insertSetSize = 0; // 初始化插入集合大小为0
|
||||
m_txnCounter++; // 事务计数器加1
|
||||
|
||||
if (rowCount == 0) {
|
||||
if (rowCount == 0) { // 如果行数为0,则为只读事务
|
||||
// READONLY
|
||||
return rc;
|
||||
return rc; // 返回 RC_OK
|
||||
}
|
||||
|
||||
uint32_t readSetSize = 0;
|
||||
TxnOrderedSet_t& orderedSet = tx->GetOrderedRowSet();
|
||||
MOT_ASSERT(rowCount == orderedSet.size());
|
||||
uint32_t readSetSize = 0; // 初始化读取集合大小为0
|
||||
TxnOrderedSet_t &orderedSet = tx->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
MOT_ASSERT(rowCount == orderedSet.size()); // 断言行数和有序集合大小相等
|
||||
|
||||
/* Perform Quick Version check */
|
||||
if (!QuickVersionCheck(txMan, readSetSize)) {
|
||||
rc = RC_ABORT;
|
||||
goto final;
|
||||
if (!QuickVersionCheck(txMan, readSetSize)) { // 调用快速版本检查函数
|
||||
rc = RC_ABORT; // 检查失败,设置返回代码为 RC_ABORT
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
|
||||
MOT_LOG_DEBUG("Validate OCC rowCnt=%u RD=%u WR=%u\n", tx->m_rowCnt, tx->m_rowCnt - m_writeSetSize, m_writeSetSize);
|
||||
rc = LockHeaders(txMan, numSentinelLock);
|
||||
if (rc != RC_OK) {
|
||||
goto final;
|
||||
MOT_LOG_DEBUG("Validate OCC rowCnt=%u RD=%u WR=%u\n", tx->m_rowCnt, tx->m_rowCnt - m_writeSetSize,
|
||||
m_writeSetSize); // 调试日志
|
||||
|
||||
rc = LockHeaders(txMan, numSentinelLock); // 锁定头信息
|
||||
if (rc != RC_OK) { // 如果锁定失败
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
|
||||
// Validate rows in the read set and write set
|
||||
if (readSetSize > 0) {
|
||||
if (!ValidateReadSet(txMan)) {
|
||||
rc = RC_ABORT;
|
||||
goto final;
|
||||
if (readSetSize > 0) { // 如果读取集合大小大于0
|
||||
if (!ValidateReadSet(txMan)) { // 调用验证读取集合函数
|
||||
rc = RC_ABORT; // 验证失败,设置返回代码为 RC_ABORT
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
}
|
||||
|
||||
if (!ValidateWriteSet(txMan)) {
|
||||
rc = RC_ABORT;
|
||||
goto final;
|
||||
if (!ValidateWriteSet(txMan)) { // 验证写入集合
|
||||
rc = RC_ABORT; // 验证失败,设置返回代码为 RC_ABORT
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
|
||||
// Pre-allocate stable row according to the checkpoint state.
|
||||
if (!PreAllocStableRow(txMan)) {
|
||||
rc = RC_MEMORY_ALLOCATION_ERROR;
|
||||
goto final;
|
||||
if (!PreAllocStableRow(txMan)) { // 调用预分配稳定行函数
|
||||
rc = RC_MEMORY_ALLOCATION_ERROR; // 预分配失败,设置返回代码为 RC_MEMORY_ALLOCATION_ERROR
|
||||
goto final; // 跳转到 final 标签
|
||||
}
|
||||
|
||||
final:
|
||||
if (likely(rc == RC_OK)) {
|
||||
MOT_ASSERT(numSentinelLock == m_writeSetSize);
|
||||
m_rowsLocked = true;
|
||||
} else {
|
||||
ReleaseHeaderLocks(txMan, numSentinelLock);
|
||||
if (likely(rc == RC_ABORT)) {
|
||||
m_abortsCounter++;
|
||||
final: // final 标签,用于处理最后的清理和返回
|
||||
if (likely(rc == RC_OK)) { // 如果返回代码为 RC_OK
|
||||
MOT_ASSERT(numSentinelLock == m_writeSetSize); // 断言锁定的 Sentinel 数量与写入集合大小相等
|
||||
m_rowsLocked = true; // 设置行已锁定为 true
|
||||
} else { // 如果返回代码不为 RC_OK
|
||||
ReleaseHeaderLocks(txMan, numSentinelLock); // 释放头信息锁定
|
||||
if (likely(rc == RC_ABORT)) { // 如果返回代码为 RC_ABORT
|
||||
m_abortsCounter++; // 增加中止计数器
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc; // 返回操作结果的代码
|
||||
}
|
||||
|
||||
void OccTransactionManager::RollbackInserts(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:回滚插入操作
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要回滚的插入操作
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于回滚事务中的插入操作,通过调用事务管理器的 UndoInserts 函数实现。
|
||||
* 插入操作的回滚通常涉及释放分配的资源,将其状态恢复到事务开始之前的状态。
|
||||
*/
|
||||
void OccTransactionManager::RollbackInserts(TxnManager *txMan)
|
||||
{
|
||||
return txMan->UndoInserts();
|
||||
}
|
||||
|
||||
void OccTransactionManager::ApplyWrite(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:应用写入操作
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要应用的写入操作
|
||||
*
|
||||
* 注意:
|
||||
* 如果启用检查点,此函数用于应用事务中的写入操作。
|
||||
* 它遍历有序的访问集合,对每个需要应用的写入操作执行应用操作。
|
||||
* 对于主要 Sentinel,传递实际的全局行(access->GetRowFromHeader()),
|
||||
* 以便稳定行具有与修改之前的原始行相同的 CSN、rowid 等属性。
|
||||
*/
|
||||
void OccTransactionManager::ApplyWrite(TxnManager *txMan)
|
||||
{
|
||||
if (GetGlobalConfiguration().m_enableCheckpoint) {
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type == RD) {
|
||||
if (GetGlobalConfiguration().m_enableCheckpoint) { // 如果启用检查点
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type == RD) { // 如果操作类型是读取,则跳过
|
||||
continue;
|
||||
}
|
||||
if (access->m_params.IsPrimarySentinel()) {
|
||||
if (access->m_params.IsPrimarySentinel()) { // 如果是主要 Sentinel
|
||||
// Pass the actual global row (access->GetRowFromHeader()), so that the stable row will have the
|
||||
// same CSN, rowid, etc as the original row before the modifications are applied.
|
||||
GetCheckpointManager()->ApplyWrite(txMan, access->GetRowFromHeader(), access->m_type);
|
||||
|
|
@ -402,32 +594,44 @@ void OccTransactionManager::ApplyWrite(TxnManager* txMan)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OccTransactionManager::WriteChanges(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:写入事务中的更改
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要写入的更改
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于写入事务中的更改,包括锁定行、应用写入、更新全局行的 CSN 以及处理插入操作等步骤。
|
||||
* 它会遍历有序的访问集合,对每个访问执行相应的更改操作。
|
||||
*/
|
||||
void OccTransactionManager::WriteChanges(TxnManager *txMan)
|
||||
{
|
||||
if (m_writeSetSize == 0 && m_insertSetSize == 0) {
|
||||
if (m_writeSetSize == 0 && m_insertSetSize == 0) { // 如果写入集合和插入集合大小都为0,则直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
LockRows(txMan, m_rowsSetSize);
|
||||
LockRows(txMan, m_rowsSetSize); // 锁定行
|
||||
|
||||
// Stable rows for checkpoint needs to be created (copied from original row) before modifying the global rows.
|
||||
ApplyWrite(txMan);
|
||||
ApplyWrite(txMan); // 应用写入
|
||||
|
||||
TxnOrderedSet_t& orderedSet = txMan->m_accessMgr->GetOrderedRowSet();
|
||||
TxnOrderedSet_t &orderedSet = txMan->m_accessMgr->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
|
||||
// Update CSN with all relevant information on global rows
|
||||
// For deletes invalidate sentinels - rows still locked!
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
access->GetRowFromHeader()->m_rowHeader.WriteChangesToRow(access, txMan->GetCommitSequenceNumber());
|
||||
// 更新全局行的 CSN,对于删除操作,使 Sentinel 失效(仍然锁定)
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
access->GetRowFromHeader()->m_rowHeader.WriteChangesToRow(
|
||||
access, txMan->GetCommitSequenceNumber()); // 更新全局行的 CSN
|
||||
}
|
||||
|
||||
// Treat Inserts
|
||||
if (m_insertSetSize > 0) {
|
||||
for (const auto& raPair : orderedSet) {
|
||||
Access* access = raPair.second;
|
||||
if (access->m_type != INS) {
|
||||
// 处理插入操作
|
||||
if (m_insertSetSize > 0) { // 如果插入集合大小大于0
|
||||
for (const auto &raPair : orderedSet) { // 再次遍历有序的访问集合
|
||||
Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type != INS) { // 如果操作类型不是插入,则跳过
|
||||
continue;
|
||||
}
|
||||
MOT_ASSERT(access->m_origSentinel->IsLocked() == true);
|
||||
|
|
@ -453,15 +657,12 @@ void OccTransactionManager::WriteChanges(TxnManager* txMan)
|
|||
* Save previous row in the access!
|
||||
* We need it for the row release!
|
||||
*/
|
||||
Row* row = access->GetRowFromHeader();
|
||||
Row *row = access->GetRowFromHeader();
|
||||
access->m_localInsertRow = row;
|
||||
access->m_origSentinel->SetNextPtr(access->m_auxRow);
|
||||
// Add row to GC!
|
||||
txMan->GetGcSession()->GcRecordObject(row->GetTable()->GetPrimaryIndex()->GetIndexId(),
|
||||
row,
|
||||
nullptr,
|
||||
Row::RowDtor,
|
||||
ROW_SIZE_FROM_POOL(row->GetTable()));
|
||||
txMan->GetGcSession()->GcRecordObject(row->GetTable()->GetPrimaryIndex()->GetIndexId(), row,
|
||||
nullptr, Row::RowDtor, ROW_SIZE_FROM_POOL(row->GetTable()));
|
||||
} else {
|
||||
// Set Sentinel for
|
||||
access->m_origSentinel->SetNextPtr(access->m_auxRow->GetPrimarySentinel());
|
||||
|
|
@ -475,37 +676,49 @@ void OccTransactionManager::WriteChanges(TxnManager* txMan)
|
|||
}
|
||||
|
||||
// Treat Inserts
|
||||
if (m_insertSetSize > 0) {
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type != INS) {
|
||||
if (m_insertSetSize > 0) { // 如果插入集合大小大于0
|
||||
for (const auto &raPair : orderedSet) { // 再次遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type != INS) { // 如果操作类型不是插入,则跳过
|
||||
continue;
|
||||
}
|
||||
access->m_origSentinel->UnSetDirty();
|
||||
access->m_origSentinel->UnSetDirty(); // 取消设置脏标志
|
||||
}
|
||||
}
|
||||
|
||||
CleanRowsFromIndexes(txMan);
|
||||
CleanRowsFromIndexes(txMan); // 从索引中清除行
|
||||
}
|
||||
|
||||
void OccTransactionManager::CleanRowsFromIndexes(TxnManager* txMan)
|
||||
/*
|
||||
* 功能:从索引中清除行
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要清除的行
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从索引中清除事务中的行,特别是删除操作。
|
||||
* 它遍历有序的访问集合,对每个删除操作执行清除操作。
|
||||
* 清除操作包括从表中减少行数、从索引中移除键等。
|
||||
*/
|
||||
void OccTransactionManager::CleanRowsFromIndexes(TxnManager *txMan)
|
||||
{
|
||||
if (m_deleteSetSize == 0) {
|
||||
if (m_deleteSetSize == 0) { // 如果删除集合大小为0,则直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
TxnAccess* tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t& orderedSet = tx->GetOrderedRowSet();
|
||||
TxnAccess *tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t &orderedSet = tx->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
uint32_t numOfDeletes = m_deleteSetSize;
|
||||
// use local counter to optimize
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type == DEL) {
|
||||
// 使用本地计数器进行优化
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type == DEL) { // 如果操作类型是删除
|
||||
numOfDeletes--;
|
||||
access->GetTxnRow()->GetTable()->UpdateRowCount(-1);
|
||||
access->GetTxnRow()->GetTable()->UpdateRowCount(-1); // 从表中减少行数
|
||||
MOT_ASSERT(access->m_params.IsUpgradeInsert() == false);
|
||||
// Use Txn Row as row may change INSERT after DELETE leaves residue
|
||||
txMan->RemoveKeyFromIndex(access->GetTxnRow(), access->m_origSentinel);
|
||||
// 使用事务行,因为在删除操作后,插入操作可能会留下残余
|
||||
txMan->RemoveKeyFromIndex(access->GetTxnRow(), access->m_origSentinel); // 从索引中移除键
|
||||
}
|
||||
if (!numOfDeletes) {
|
||||
break;
|
||||
|
|
@ -513,24 +726,36 @@ void OccTransactionManager::CleanRowsFromIndexes(TxnManager* txMan)
|
|||
}
|
||||
}
|
||||
|
||||
void OccTransactionManager::ReleaseHeaderLocks(TxnManager* txMan, uint32_t numOfLocks)
|
||||
/*
|
||||
* 功能:释放头信息的锁
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要释放锁的头信息
|
||||
* numOfLocks:需要释放的锁的数量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于释放事务中头信息的锁,特别是在发生回滚或释放锁时。
|
||||
* 它遍历有序的访问集合,对每个需要释放锁的头信息执行释放操作。
|
||||
*/
|
||||
void OccTransactionManager::ReleaseHeaderLocks(TxnManager *txMan, uint32_t numOfLocks)
|
||||
{
|
||||
if (numOfLocks == 0) {
|
||||
if (numOfLocks == 0) { // 如果需要释放的锁的数量为0,则直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
TxnAccess* tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t& orderedSet = tx->GetOrderedRowSet();
|
||||
TxnAccess *tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t &orderedSet = tx->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
// use local counter to optimize
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type == RD) {
|
||||
// 使用本地计数器进行优化
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type == RD) { // 如果操作类型是读取,则继续下一次循环
|
||||
continue;
|
||||
} else {
|
||||
numOfLocks--;
|
||||
access->m_origSentinel->Release();
|
||||
access->m_origSentinel->Release(); // 释放 Sentinel 锁
|
||||
if (access->m_params.IsPrimaryUpgrade()) {
|
||||
access->m_auxRow->m_rowHeader.Release();
|
||||
access->m_auxRow->m_rowHeader.Release(); // 释放辅助行的锁
|
||||
}
|
||||
}
|
||||
if (!numOfLocks) {
|
||||
|
|
@ -539,29 +764,39 @@ void OccTransactionManager::ReleaseHeaderLocks(TxnManager* txMan, uint32_t numOf
|
|||
}
|
||||
}
|
||||
|
||||
void OccTransactionManager::ReleaseRowsLocks(TxnManager* txMan, uint32_t numOfLocks)
|
||||
/*
|
||||
* 功能:释放行的锁
|
||||
*
|
||||
* 参数列表:
|
||||
* txMan:事务管理器指针,包含需要释放锁的行
|
||||
* numOfLocks:需要释放的锁的数量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于释放事务中行的锁,特别是在发生回滚或释放锁时。
|
||||
* 它遍历有序的访问集合,对每个需要释放锁的行执行释放操作。
|
||||
*/
|
||||
void OccTransactionManager::ReleaseRowsLocks(TxnManager *txMan, uint32_t numOfLocks)
|
||||
{
|
||||
if (numOfLocks == 0) {
|
||||
if (numOfLocks == 0) { // 如果需要释放的锁的数量为0,则直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
TxnAccess* tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t& orderedSet = tx->GetOrderedRowSet();
|
||||
|
||||
// use local counter to optimize
|
||||
for (const auto& raPair : orderedSet) {
|
||||
const Access* access = raPair.second;
|
||||
if (access->m_type == RD) {
|
||||
TxnAccess *tx = txMan->m_accessMgr.Get();
|
||||
TxnOrderedSet_t &orderedSet = tx->GetOrderedRowSet(); // 获取有序的访问集合
|
||||
// 使用本地计数器进行优化
|
||||
for (const auto &raPair : orderedSet) { // 遍历有序的访问集合
|
||||
const Access *access = raPair.second; // 获取访问对象
|
||||
if (access->m_type == RD) { // 如果操作类型是读取,则继续下一次循环
|
||||
continue;
|
||||
}
|
||||
|
||||
if (access->m_params.IsPrimarySentinel()) {
|
||||
if (access->m_params.IsPrimarySentinel()) { // 如果是主要的 Sentinel
|
||||
numOfLocks--;
|
||||
access->GetRowFromHeader()->m_rowHeader.Release();
|
||||
if (access->m_params.IsUpgradeInsert()) {
|
||||
// This is the global row that we switched!
|
||||
// Currently it's in the gc!
|
||||
access->m_localInsertRow->m_rowHeader.Release();
|
||||
access->GetRowFromHeader()->m_rowHeader.Release(); // 释放行的锁
|
||||
if (access->m_params.IsUpgradeInsert()) { // 如果是升级插入操作
|
||||
// 这是我们切换的全局行!
|
||||
// 目前它在垃圾回收中!
|
||||
access->m_localInsertRow->m_rowHeader.Release(); // 释放插入行的锁
|
||||
}
|
||||
}
|
||||
if (!numOfLocks) {
|
||||
|
|
@ -570,10 +805,19 @@ void OccTransactionManager::ReleaseRowsLocks(TxnManager* txMan, uint32_t numOfLo
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:清理事务管理器的状态
|
||||
*
|
||||
* 参数列表:
|
||||
* 无参数
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于清理事务管理器的状态,将相关计数器重置为零,以准备处理下一个事务。
|
||||
*/
|
||||
void OccTransactionManager::CleanUp()
|
||||
{
|
||||
m_writeSetSize = 0;
|
||||
m_insertSetSize = 0;
|
||||
m_rowsSetSize = 0;
|
||||
m_writeSetSize = 0; // 重置写集合大小为0
|
||||
m_insertSetSize = 0; // 重置插入集合大小为0
|
||||
m_rowsSetSize = 0; // 重置行集合大小为0
|
||||
}
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -34,9 +34,25 @@
|
|||
|
||||
namespace MOT {
|
||||
DECLARE_LOGGER(RowHeader, ConcurrenyControl);
|
||||
|
||||
RC RowHeader::GetLocalCopy(
|
||||
TxnAccess* txn, AccessType type, Row* localRow, const Row* origRow, TransactionId& lastTid) const
|
||||
/*
|
||||
* 功能:获取行的本地副本
|
||||
*
|
||||
* 参数列表:
|
||||
* txn:事务访问对象指针,包含当前事务的信息
|
||||
* type:访问类型,指示操作类型(例如:读取、写入、插入等)
|
||||
* localRow:用于存储本地副本的行对象指针
|
||||
* origRow:原始行对象的指针,从中获取要复制的内容
|
||||
* lastTid:用于存储最后一个事务标识符的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取本地副本,返回 RC_OK;否则返回 RC_ABORT。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取行的本地副本,以支持并发事务。它执行了多次尝试,直到成功获取行的本地副本。
|
||||
* 在获取本地副本时,会检查行的状态和锁,以确保安全地访问和修改行的内容。
|
||||
*/
|
||||
RC RowHeader::GetLocalCopy(TxnAccess *txn, AccessType type, Row *localRow, const Row *origRow,
|
||||
TransactionId &lastTid) const
|
||||
{
|
||||
uint64_t sleepTime = 1;
|
||||
uint64_t v = 0;
|
||||
|
|
@ -85,11 +101,37 @@ RC RowHeader::GetLocalCopy(
|
|||
return RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:验证写操作的事务标识符
|
||||
*
|
||||
* 参数列表:
|
||||
* tid:事务标识符,用于与行的当前事务标识符进行比较
|
||||
*
|
||||
* 返回值:
|
||||
* 如果给定的事务标识符与行的当前事务标识符匹配,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于验证写操作的事务标识符是否与行的当前事务标识符匹配。
|
||||
* 如果匹配,表示可以进行写操作;如果不匹配,表示其他事务已经修改了行。
|
||||
*/
|
||||
bool RowHeader::ValidateWrite(TransactionId tid) const
|
||||
{
|
||||
return (tid == GetCSN());
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:验证读操作的事务标识符
|
||||
*
|
||||
* 参数列表:
|
||||
* tid:事务标识符,用于与行的当前事务标识符进行比较
|
||||
*
|
||||
* 返回值:
|
||||
* 如果给定的事务标识符与行的当前事务标识符匹配且行未被锁定,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于验证读操作的事务标识符是否有效。它检查行是否已被锁定,如果锁定则返回false,
|
||||
* 否则检查给定的事务标识符是否与行的当前事务标识符匹配。
|
||||
*/
|
||||
bool RowHeader::ValidateRead(TransactionId tid) const
|
||||
{
|
||||
if (IsLocked() or (tid != GetCSN())) {
|
||||
|
|
@ -98,10 +140,19 @@ bool RowHeader::ValidateRead(TransactionId tid) const
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RowHeader::WriteChangesToRow(const Access* access, uint64_t csn)
|
||||
/*
|
||||
* 功能:将更改写入行
|
||||
*
|
||||
* 参数列表:
|
||||
* access:访问对象指针,包含要写入行的更改信息
|
||||
* csn:提交序列号,用于标识写入的版本
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将更改写入行。它根据访问类型执行不同的操作,包括复制数据、设置事务标识符等。
|
||||
*/
|
||||
void RowHeader::WriteChangesToRow(const Access *access, uint64_t csn)
|
||||
{
|
||||
Row* row = access->GetRowFromHeader();
|
||||
Row *row = access->GetRowFromHeader();
|
||||
AccessType type = access->m_type;
|
||||
|
||||
if (type == RD) {
|
||||
|
|
@ -112,8 +163,8 @@ void RowHeader::WriteChangesToRow(const Access* access, uint64_t csn)
|
|||
uint64_t v = m_csnWord;
|
||||
if (!MOTEngine::GetInstance()->IsRecovering()) {
|
||||
if (!(csn > GetCSN() && (v & LOCK_BIT))) {
|
||||
MOT_LOG_ERROR(
|
||||
"csn=%ld, v & LOCK_BIT=%ld, v & (~LOCK_BIT)=%ld\n", csn, (v & LOCK_BIT), (v & (~LOCK_BIT)));
|
||||
MOT_LOG_ERROR("csn=%ld, v & LOCK_BIT=%ld, v & (~LOCK_BIT)=%ld\n", csn, (v & LOCK_BIT),
|
||||
(v & (~LOCK_BIT)));
|
||||
MOT_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -152,7 +203,14 @@ void RowHeader::WriteChangesToRow(const Access* access, uint64_t csn)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:锁定行头信息
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于锁定行头信息,以确保其他事务无法同时修改该行。
|
||||
* 它使用原子操作来设置行头中的锁定位(LOCK_BIT)。
|
||||
* 如果行头已经被锁定,则会等待直到成功锁定。
|
||||
*/
|
||||
void RowHeader::Lock()
|
||||
{
|
||||
uint64_t v = m_csnWord;
|
||||
|
|
|
|||
|
|
@ -32,25 +32,45 @@ DECLARE_LOGGER(CmdLineConfigLoader, Configuration)
|
|||
|
||||
static const char CMDLINE_SEP = '-';
|
||||
|
||||
static bool ParseCmdLineSectionName(const mot_string& line, mot_string& sectionPath, mot_string& keyValuePart)
|
||||
/*
|
||||
* 功能:解析命令行中的部分名称
|
||||
*
|
||||
* 参数列表:
|
||||
* line:包含部分名称的输入字符串
|
||||
* sectionPath:用于存储部分路径的输出字符串
|
||||
* keyValuePart:用于存储键值部分的输出字符串
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析部分名称,则返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从输入字符串中解析部分路径和键值部分。
|
||||
* 输入字符串应包含一个分隔符(CMDLINE_SEP),用于分隔部分路径和键值部分。
|
||||
* 如果解析成功,结果存储在sectionPath和keyValuePart中,并返回true。
|
||||
* 如果解析失败,将记录错误信息并返回false。
|
||||
*/
|
||||
static bool ParseCmdLineSectionName(const mot_string &line, mot_string §ionPath, mot_string &keyValuePart)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 查找最后一个分隔符的位置
|
||||
uint32_t lastSlashPos = line.find_last_of(CMDLINE_SEP);
|
||||
|
||||
if (lastSlashPos != mot_string::npos) {
|
||||
// 将输入字符串的一部分复制到sectionPath中
|
||||
if (!line.substr(sectionPath, 0, lastSlashPos) || !line.substr(keyValuePart, lastSlashPos + 1)) {
|
||||
// 如果复制失败,记录内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name");
|
||||
} else {
|
||||
// 去除前后的空白字符
|
||||
sectionPath.trim();
|
||||
keyValuePart.trim();
|
||||
result = true;
|
||||
result = true; // 解析成功
|
||||
}
|
||||
} else {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG,
|
||||
"Load Configuration",
|
||||
"Malformed command line argument missing separator %c: %s",
|
||||
CMDLINE_SEP,
|
||||
line.c_str());
|
||||
// 如果找不到分隔符,记录配置无效的错误信息
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG, "Load Configuration",
|
||||
"Malformed command line argument missing separator %c: %s", CMDLINE_SEP, line.c_str());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -63,51 +83,100 @@ static bool ParseCmdLineSectionName(const mot_string& line, mot_string& sectionP
|
|||
break; \
|
||||
}
|
||||
|
||||
static ConfigSection* GetCmdLineConfigSection(
|
||||
const mot_string& sectionFullName, mot_list<ConfigSection*>& parsedSections, ConfigSectionMap& sectionMap)
|
||||
/*
|
||||
* 功能:根据部分全名获取命令行配置部分
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:要获取的部分的全名
|
||||
* parsedSections:已解析的部分列表,用于存储已解析的部分对象
|
||||
* sectionMap:部分映射,将部分全名映射到部分对象
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取或创建部分对象,则返回该部分对象的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据部分的全名获取相应的部分对象。如果部分已存在于sectionMap中,
|
||||
* 则直接返回该部分对象;否则,根据全名创建一个新的部分对象并将其插入sectionMap中。
|
||||
* 如果出现错误(如内存分配失败或无法解析部分名称),将记录错误信息并返回nullptr。
|
||||
*/
|
||||
static ConfigSection *GetCmdLineConfigSection(const mot_string §ionFullName,
|
||||
mot_list<ConfigSection *> &parsedSections, ConfigSectionMap §ionMap)
|
||||
{
|
||||
mot_string sectionPath;
|
||||
mot_string sectionName;
|
||||
|
||||
// 尝试查找部分全名是否已在sectionMap中
|
||||
ConfigSectionMap::iterator itr = sectionMap.find(sectionFullName);
|
||||
|
||||
if (itr == sectionMap.end()) {
|
||||
// 如果部分全名不存在于sectionMap中
|
||||
if (!ConfigFileParser::BreakSectionName(sectionFullName, sectionPath, sectionName, CMDLINE_SEP)) {
|
||||
// 无法解析部分名称,记录错误并返回nullptr
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse section name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ConfigSection* currentSection = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
// 创建新的配置部分对象
|
||||
ConfigSection *currentSection = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
if (currentSection == nullptr) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Load Configuration", "Failed to allocate memory for configuration section");
|
||||
// 内存分配失败,记录错误并返回nullptr
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to allocate memory for configuration section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 将新创建的部分对象添加到parsedSections列表中
|
||||
if (!parsedSections.push_back(currentSection)) {
|
||||
// 插入失败,记录错误并返回nullptr
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to insert parsed section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 将新创建的部分对象插入sectionMap中,以便下次可以直接获取
|
||||
itr = sectionMap.insert(ConfigSectionMap::value_type(sectionFullName, currentSection)).first;
|
||||
}
|
||||
|
||||
// 返回获取到的部分对象
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
static bool AddCmdLineArrayConfigItem(ConfigSection* currentSection, const mot_string& sectionFullName,
|
||||
const mot_string& key, const mot_string& value, uint64_t arrayIndex)
|
||||
/*
|
||||
* 功能:添加命令行数组配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* currentSection:当前部分对象,表示正在加载的部分
|
||||
* sectionFullName:部分的全名,用于唯一标识部分
|
||||
* key:配置项的键名称
|
||||
* value:配置项的值
|
||||
* arrayIndex:数组索引,用于指示配置项在数组中的位置
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置项,则返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于添加命令行中的数组配置项到配置部分中。
|
||||
* 如果指定的数组配置项不存在,将会创建一个新的数组。
|
||||
* 如果创建失败或添加配置项失败,将记录错误信息并返回false。
|
||||
* 此外,还会检查数组项的顺序是否正确,以确保它们按顺序添加到数组中。
|
||||
*/
|
||||
static bool AddCmdLineArrayConfigItem(ConfigSection *currentSection, const mot_string §ionFullName,
|
||||
const mot_string &key, const mot_string &value, uint64_t arrayIndex)
|
||||
{
|
||||
// create array if not created yet
|
||||
ConfigArray* configArray = currentSection->ModifyConfigArray(key.c_str());
|
||||
ConfigArray *configArray = currentSection->ModifyConfigArray(key.c_str());
|
||||
if (configArray == nullptr) {
|
||||
// 创建新的数组配置项
|
||||
configArray = ConfigArray::CreateConfigArray(sectionFullName.c_str(), key.c_str());
|
||||
if (configArray == nullptr) {
|
||||
// 内存分配失败,记录错误并返回false
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate memory for configuration array");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将数组配置项添加到当前部分
|
||||
if (!currentSection->AddConfigItem(configArray)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Load Configuration", "Failed to add configuration array to parent section");
|
||||
// 添加失败,记录错误并返回false
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to add configuration array to parent section");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -115,42 +184,56 @@ static bool AddCmdLineArrayConfigItem(ConfigSection* currentSection, const mot_s
|
|||
// create item and add it to array
|
||||
if (arrayIndex != configArray->GetConfigItemCount()) {
|
||||
// array items must be ordered
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG,
|
||||
"Load Configuration",
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INVALID_CFG, "Load Configuration",
|
||||
"Failed to parse command line arguments: array %s items not well-ordered, expecting %u, got %" PRIu64,
|
||||
configArray->GetName(),
|
||||
configArray->GetConfigItemCount(),
|
||||
arrayIndex);
|
||||
configArray->GetName(), configArray->GetConfigItemCount(), arrayIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigItem* configItem = ConfigFileParser::MakeArrayConfigValue(sectionFullName, arrayIndex, value);
|
||||
// 创建配置项并添加到数组中
|
||||
ConfigItem *configItem = ConfigFileParser::MakeArrayConfigValue(sectionFullName, arrayIndex, value);
|
||||
if (configItem == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to create array configuration value from raw value: %s",
|
||||
value.c_str());
|
||||
// 创建配置项失败,记录错误并返回false
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to create array configuration value from raw value: %s", value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将配置项添加到数组中
|
||||
if (!configArray->AddConfigItem(configItem)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to add %" PRIu64 "th item to configuration array %s",
|
||||
arrayIndex,
|
||||
configArray->GetName());
|
||||
// 添加配置项失败,记录错误并返回false
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to add %" PRIu64 "th item to configuration array %s", arrayIndex,
|
||||
configArray->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 配置项添加成功
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigTree* CmdLineConfigLoader::ParseCmdLine(char** argv, int argc)
|
||||
/*
|
||||
* 功能:解析命令行参数并构建配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* argv:命令行参数数组
|
||||
* argc:命令行参数数量
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析命令行参数并构建配置树,则返回配置树指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于解析命令行参数,并构建一个配置树来表示这些参数。
|
||||
* 命令行参数应遵循格式:--section1-section2-section3-key=value。
|
||||
* 如果解析过程中出现错误(如内存分配失败、解析失败等),将记录错误信息并返回nullptr。
|
||||
*/
|
||||
ConfigTree *CmdLineConfigLoader::ParseCmdLine(char **argv, int argc)
|
||||
{
|
||||
// section names are separated by dashes, it is expected to have the format:
|
||||
// --section1-section2-section3-key=value
|
||||
ConfigTree* cfgTree = ConfigTree::CreateConfigTree(GetPriority(), GetName(), true);
|
||||
// 创建配置树对象,表示命令行参数
|
||||
ConfigTree *cfgTree = ConfigTree::CreateConfigTree(GetPriority(), GetName(), true);
|
||||
if (cfgTree == nullptr) {
|
||||
// 创建配置树失败,记录错误并返回nullptr
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to create configuration tree");
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -158,8 +241,8 @@ ConfigTree* CmdLineConfigLoader::ParseCmdLine(char** argv, int argc)
|
|||
mot_string line;
|
||||
mot_string sectionFullName;
|
||||
mot_string keyValuePart;
|
||||
ConfigSection* currentSection = nullptr;
|
||||
mot_list<ConfigSection*> parsedSections;
|
||||
ConfigSection *currentSection = nullptr;
|
||||
mot_list<ConfigSection *> parsedSections;
|
||||
ConfigSectionMap sectionMap;
|
||||
mot_string key;
|
||||
mot_string value;
|
||||
|
|
@ -168,16 +251,20 @@ ConfigTree* CmdLineConfigLoader::ParseCmdLine(char** argv, int argc)
|
|||
bool hasArrayIndex = false;
|
||||
|
||||
for (int i = 0; i < argc && !parseError; ++i) {
|
||||
// 将命令行参数转换为字符串对象
|
||||
if (!line.assign(argv[i])) {
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_OOM, "Failed to allocate memory for next command line argument");
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_OOM,
|
||||
"Failed to allocate memory for next command line argument");
|
||||
}
|
||||
|
||||
// 检查命令行参数是否以"--"开头且长度不小于2
|
||||
if ((line.length() <= 2) || line[0] != '-' || line[1] != '-') {
|
||||
// 忽略格式不正确的命令行参数
|
||||
MOT_LOG_TRACE("Skipping ill-formed command line argument: %s", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析命令行参数的部分名称
|
||||
if (!ParseCmdLineSectionName(line, sectionFullName, keyValuePart)) {
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INTERNAL, "Failed to parse command line argument");
|
||||
}
|
||||
|
|
@ -196,31 +283,36 @@ ConfigTree* CmdLineConfigLoader::ParseCmdLine(char** argv, int argc)
|
|||
|
||||
// check for array item
|
||||
if (!hasArrayIndex) {
|
||||
ConfigItem* configItem = ConfigFileParser::MakeConfigValue(sectionFullName, key, value);
|
||||
// 创建配置值对象并添加到部分中
|
||||
ConfigItem *configItem = ConfigFileParser::MakeConfigValue(sectionFullName, key, value);
|
||||
if (configItem == nullptr) {
|
||||
// 创建配置项失败,记录错误并返回false
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INTERNAL,
|
||||
"Failed to create configuration value from raw key/value: %s/%s",
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
"Failed to create configuration value from raw key/value: %s/%s",
|
||||
key.c_str(), value.c_str());
|
||||
} else if (!currentSection->AddConfigItem(configItem)) {
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_OOM, "Failed to add configuration value to parent section");
|
||||
// 添加配置项失败,记录错误并返回false
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_OOM,
|
||||
"Failed to add configuration value to parent section");
|
||||
}
|
||||
} else {
|
||||
// 添加数组配置项
|
||||
if (!AddCmdLineArrayConfigItem(currentSection, sectionFullName, key, value, arrayIndex)) {
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INTERNAL,
|
||||
"Failed to add array item with arrayIndex %lu (command line argument: %s)",
|
||||
arrayIndex,
|
||||
line.c_str());
|
||||
// 添加数组项失败,记录错误并返回false
|
||||
CMDLINE_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_INTERNAL, "Failed to add array item with arrayIndex %lu (command line argument: %s)",
|
||||
arrayIndex, line.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parseError) {
|
||||
// 解析过程中出现错误,释放配置树并返回nullptr
|
||||
delete cfgTree;
|
||||
cfgTree = nullptr;
|
||||
} else {
|
||||
if (!cfgTree->Build(parsedSections)) {
|
||||
// 构建配置树失败,记录错误并释放配置树
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to build configuration tree");
|
||||
delete cfgTree;
|
||||
cfgTree = nullptr;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,18 @@ ConfigArray::ConfigArray() : ConfigItem(ConfigItemClass::CONFIG_ITEM_ARRAY)
|
|||
|
||||
ConfigArray::~ConfigArray()
|
||||
{}
|
||||
|
||||
/*
|
||||
* 功能:打印配置数组的内容
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:日志级别,指定打印日志的级别
|
||||
* fullPrint:是否完整打印,如果为false,则只打印部分名称,否则打印完整内容
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印配置数组的内容。可以选择是否打印完整的数组内容。
|
||||
* 如果fullPrint为false,将仅打印部分名称,否则将打印完整内容。
|
||||
* 打印的内容包括数组的部分名称和数组项的值。
|
||||
*/
|
||||
void ConfigArray::Print(LogLevel logLevel, bool fullPrint) const
|
||||
{
|
||||
// print indented section name
|
||||
|
|
@ -43,28 +54,39 @@ void ConfigArray::Print(LogLevel logLevel, bool fullPrint) const
|
|||
if (!fullPrint) {
|
||||
MOT_LOG(logLevel, "%*s%s[%u]", GetDepth(), "", GetName(), i);
|
||||
}
|
||||
// 打印数组项的值
|
||||
mItemArray[i]->Print(logLevel, fullPrint);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigArray::ForEach(ConfigItemVisitor& visitor) const
|
||||
/*
|
||||
* 功能:打印配置数组的内容
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:日志级别,指定打印日志的级别
|
||||
* fullPrint:是否完整打印,如果为false,则只打印部分名称,否则打印完整内容
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印配置数组的内容。可以选择是否打印完整的数组内容。
|
||||
* 如果fullPrint为false,将仅打印部分名称,否则将打印完整内容。
|
||||
* 打印的内容包括数组的部分名称和数组项的值。
|
||||
*/
|
||||
void ConfigArray::Print(LogLevel logLevel, bool fullPrint) const
|
||||
{
|
||||
visitor.OnConfigItem(this);
|
||||
// 打印缩进的部分名称
|
||||
if (!fullPrint) {
|
||||
MOT_LOG(logLevel, "%*s%s", GetDepth(), "", GetName());
|
||||
}
|
||||
|
||||
// 打印数组项的值
|
||||
for (uint32_t i = 0; i < mItemArray.size(); ++i) {
|
||||
const ConfigItem* configItem = mItemArray[i];
|
||||
switch (configItem->GetClass()) {
|
||||
case ConfigItemClass::CONFIG_ITEM_SECTION:
|
||||
static_cast<const ConfigSection*>(configItem)->ForEach(visitor);
|
||||
break;
|
||||
|
||||
case ConfigItemClass::CONFIG_ITEM_ARRAY:
|
||||
static_cast<const ConfigArray*>(configItem)->ForEach(visitor);
|
||||
break;
|
||||
|
||||
default:
|
||||
visitor.OnConfigItem(configItem);
|
||||
break;
|
||||
if (!fullPrint) {
|
||||
MOT_LOG(logLevel, "%*s%s[%u]", GetDepth(), "", GetName(), i);
|
||||
}
|
||||
|
||||
// 打印数组项的值
|
||||
mItemArray[i]->Print(logLevel, fullPrint);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -29,28 +29,57 @@
|
|||
namespace MOT {
|
||||
DECLARE_LOGGER(ConfigFileLoader, Configuration)
|
||||
|
||||
static void FormatTime(uint64_t timeVal, char* buf, uint32_t len)
|
||||
/*
|
||||
* 功能:将时间值格式化为字符串
|
||||
*
|
||||
* 参数列表:
|
||||
* timeVal:时间值,以纳秒为单位
|
||||
* buf:用于存储格式化后的时间字符串的缓冲区
|
||||
* len:缓冲区长度
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将时间值格式化为字符串,并存储在给定的缓冲区中。
|
||||
* 时间值以纳秒为单位,并转换为日期时间格式("%F %T.%09ld")。
|
||||
* 如果转换成功,结果存储在buf中;否则不进行任何操作。
|
||||
*/
|
||||
static void FormatTime(uint64_t timeVal, char *buf, uint32_t len)
|
||||
{
|
||||
// 将纳秒时间值转换为timespec结构
|
||||
timespec ts = {(long int)(timeVal / 1000000000ULL), (long int)(timeVal % 1000000000ULL)};
|
||||
struct tm t;
|
||||
|
||||
// 使用localtime_r函数将时间值转换为tm结构
|
||||
if (localtime_r(&(ts.tv_sec), &t) != NULL) {
|
||||
// 使用strftime函数将tm结构格式化为日期时间字符串
|
||||
size_t ret = strftime(buf, len, "%F %T", &t);
|
||||
if (ret != 0) {
|
||||
// 如果strftime成功,继续添加纳秒部分
|
||||
len -= ret;
|
||||
errno_t erc = snprintf_s(buf + ret, len, len - 1, ".%09ld", ts.tv_nsec);
|
||||
// 检查snprintf_s的返回值
|
||||
securec_check_ss(erc, "\0", "\0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigFileLoader::ConfigFileLoader(
|
||||
const char* typeName, const char* name, uint32_t priority, const char* configFilePath)
|
||||
ConfigFileLoader::ConfigFileLoader(const char *typeName, const char *name, uint32_t priority,
|
||||
const char *configFilePath)
|
||||
: ConfigLoader(ComposeFullName(typeName, name, configFilePath).c_str(), priority),
|
||||
m_configFilePath(configFilePath),
|
||||
m_lastModTime(GetFileModificationTime())
|
||||
{}
|
||||
|
||||
/*
|
||||
* 功能:检查配置文件是否已更改
|
||||
*
|
||||
* 返回值:
|
||||
* 如果配置文件的修改时间较上次检查发生了更改,则返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查配置文件的修改时间是否与上次检查不同。
|
||||
* 如果修改时间发生更改,将记录日志信息,更新上次检查的时间,并返回true;
|
||||
* 否则,返回false。
|
||||
*/
|
||||
bool ConfigFileLoader::HasChanged()
|
||||
{
|
||||
bool result = false;
|
||||
|
|
@ -58,35 +87,68 @@ bool ConfigFileLoader::HasChanged()
|
|||
if (modTime > m_lastModTime) {
|
||||
char lastDate[32];
|
||||
char newDate[32];
|
||||
|
||||
// 格式化上次检查的修改时间和新的修改时间
|
||||
FormatTime(m_lastModTime, lastDate, sizeof(lastDate));
|
||||
FormatTime(modTime, newDate, sizeof(newDate));
|
||||
|
||||
// 记录配置文件发生更改的日志信息
|
||||
MOT_LOG_INFO("Detected change in configuration file: %s (modification time changed: %" PRIu64 " --> %" PRIu64
|
||||
" [%s --> %s])",
|
||||
m_configFilePath.c_str(),
|
||||
m_lastModTime,
|
||||
modTime,
|
||||
lastDate,
|
||||
newDate);
|
||||
m_configFilePath.c_str(), m_lastModTime, modTime, lastDate, newDate);
|
||||
|
||||
// 更新上次检查的修改时间
|
||||
m_lastModTime = modTime;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
mot_string ConfigFileLoader::ComposeFullName(const char* typeName, const char* name, const char* configFilePath)
|
||||
/*
|
||||
* 功能:组合完整的配置项名称
|
||||
*
|
||||
* 参数列表:
|
||||
* typeName:配置项类型的名称
|
||||
* name:配置项的名称
|
||||
* configFilePath:配置文件的路径
|
||||
*
|
||||
* 返回值:
|
||||
* 返回组合后的完整配置项名称(mot_string对象)。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将配置项的类型名称、名称和配置文件路径组合成一个完整的配置项名称。
|
||||
* 配置项名称的格式为:"<typeName>[<name>]@<configFilePath>"。
|
||||
*/
|
||||
mot_string ConfigFileLoader::ComposeFullName(const char *typeName, const char *name, const char *configFilePath)
|
||||
{
|
||||
mot_string result;
|
||||
// 使用mot_string的format方法将类型名称、名称和配置文件路径组合成完整的配置项名称
|
||||
result.format("%s[%s]@%s", typeName, name, configFilePath);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取配置文件的修改时间
|
||||
*
|
||||
* 返回值:
|
||||
* 返回配置文件的修改时间,以纳秒为单位。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取配置文件的修改时间,并将其转换为纳秒为单位的时间值。
|
||||
* 如果成功获取配置文件的修改时间,将返回时间值;否则返回0。
|
||||
*/
|
||||
uint64_t ConfigFileLoader::GetFileModificationTime()
|
||||
{
|
||||
uint64_t filetime = 0;
|
||||
struct stat buf;
|
||||
|
||||
// 使用stat函数获取文件的信息,包括修改时间
|
||||
if (stat(m_configFilePath.c_str(), &buf) == 0) {
|
||||
// 计算纳秒级别的修改时间
|
||||
filetime = buf.st_mtim.tv_sec * 1000000000ULL + buf.st_mtim.tv_nsec;
|
||||
}
|
||||
|
||||
return filetime;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -37,38 +37,85 @@
|
|||
namespace MOT {
|
||||
IMPLEMENT_CLASS_LOGGER(ConfigFileParser, Configuration)
|
||||
|
||||
bool ConfigFileParser::BreakSectionName(const mot_string& sectionFullName, mot_string& sectionPath,
|
||||
mot_string& sectionName, char sep /* = ConfigItem::PATH_SEP */)
|
||||
/*
|
||||
* 功能:分解配置部分名称
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:完整的配置部分名称
|
||||
* sectionPath:用于存储配置部分路径的mot_string对象
|
||||
* sectionName:用于存储配置部分名称的mot_string对象
|
||||
* sep:分隔符,指示配置部分路径和名称之间的分隔符,默认为ConfigItem::PATH_SEP
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功分解配置部分名称,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于分解完整的配置部分名称,将其拆分为配置部分路径和名称。
|
||||
* 分隔符sep用于指示配置部分路径和名称之间的分隔符,默认为ConfigItem::PATH_SEP。
|
||||
* 如果成功分解名称,将结果存储在sectionPath和sectionName参数中,并返回true;
|
||||
* 否则,返回false,并记录错误信息。
|
||||
*/
|
||||
bool ConfigFileParser::BreakSectionName(const mot_string §ionFullName, mot_string §ionPath,
|
||||
mot_string §ionName, char sep /* = ConfigItem::PATH_SEP */)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// 查找最后一个分隔符的位置
|
||||
uint32_t lastSlashPos = sectionFullName.find_last_of(sep);
|
||||
|
||||
if (lastSlashPos != mot_string::npos) {
|
||||
// 如果找到分隔符,将字符串分割成路径和名称
|
||||
if (!sectionFullName.substr(sectionPath, 0, lastSlashPos) ||
|
||||
!sectionFullName.substr(sectionName, lastSlashPos + 1)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name");
|
||||
result = false;
|
||||
} else {
|
||||
// 去除空白字符
|
||||
sectionPath.trim();
|
||||
sectionName.trim();
|
||||
}
|
||||
} else if (!sectionPath.assign("") || !sectionName.assign(sectionFullName)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name");
|
||||
result = false;
|
||||
} else {
|
||||
// 如果没有找到分隔符,将整个字符串作为名称,路径为空
|
||||
if (!sectionPath.assign("") || !sectionName.assign(sectionFullName)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigFileParser::ParseKeyValue(const mot_string& keyValuePart, const mot_string& section, mot_string& key,
|
||||
mot_string& value, uint64_t& arrayIndex, bool& hasArrayIndex)
|
||||
/*
|
||||
* 功能:解析键值对字符串
|
||||
*
|
||||
* 参数列表:
|
||||
* keyValuePart:包含键值对的字符串
|
||||
* section:配置部分的名称
|
||||
* key:用于存储键的mot_string对象
|
||||
* value:用于存储值的mot_string对象
|
||||
* arrayIndex:用于存储数组索引的整数值
|
||||
* hasArrayIndex:指示是否包含数组索引的布尔值,如果包含则为true,否则为false
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析键值对字符串,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于解析包含键值对的字符串,并将键、值、数组索引信息提取出来。
|
||||
* 如果成功解析,将结果存储在key、value、arrayIndex和hasArrayIndex参数中,并返回true;
|
||||
* 否则,返回false,并记录错误信息。
|
||||
*/
|
||||
bool ConfigFileParser::ParseKeyValue(const mot_string &keyValuePart, const mot_string §ion, mot_string &key,
|
||||
mot_string &value, uint64_t &arrayIndex, bool &hasArrayIndex)
|
||||
{
|
||||
bool result = false;
|
||||
hasArrayIndex = false;
|
||||
|
||||
// 查找等号的位置,分割键值对
|
||||
uint32_t equalPos = keyValuePart.find('=');
|
||||
|
||||
if (equalPos == mot_string::npos) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to parse key/value: missing equal sign (%s)",
|
||||
keyValuePart.c_str());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse key/value: missing equal sign (%s)",
|
||||
keyValuePart.c_str());
|
||||
} else if (!keyValuePart.substr(key, 0, equalPos) || !keyValuePart.substr(value, equalPos + 1)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse key/value");
|
||||
} else {
|
||||
|
|
@ -76,69 +123,127 @@ bool ConfigFileParser::ParseKeyValue(const mot_string& keyValuePart, const mot_s
|
|||
value.trim();
|
||||
result = true;
|
||||
|
||||
// check for array item
|
||||
// 检查是否包含数组索引
|
||||
uint32_t poundPos = key.find('#');
|
||||
if (poundPos != mot_string::npos) {
|
||||
result = false;
|
||||
mot_string intPart;
|
||||
|
||||
// 提取数组索引的整数部分
|
||||
if (!key.substr(intPart, poundPos + 1)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse integer part from array item specifier");
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to parse integer part from array item specifier");
|
||||
} else {
|
||||
// 解析整数部分并存储到arrayIndex中
|
||||
if (ParseUIntValue(section, key, intPart, arrayIndex, true)) {
|
||||
hasArrayIndex = true;
|
||||
key.substr_inplace(0, poundPos);
|
||||
result = true;
|
||||
} else {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG,
|
||||
"Load Configuration",
|
||||
"Invalid array item specifier encountered: %s",
|
||||
key.c_str());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG, "Load Configuration",
|
||||
"Invalid array item specifier encountered: %s", key.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建配置项的通用配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:完整的配置部分名称
|
||||
* key:键的名称
|
||||
* value:值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建配置项的通用配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建通用配置值。
|
||||
* 首先,它检查键是否包含类型指示符,如果包含则调用MakeTypedConfigValue函数来创建类型化的配置值;
|
||||
* 否则,调用MakeUntypedConfigValue函数来创建非类型化的配置值。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
|
||||
// key may have type specifier (in the format type:key=value)
|
||||
// 检查键是否包含类型指示符(类型:key=value)
|
||||
uint32_t colonPos = key.find(':');
|
||||
if (colonPos != mot_string::npos) {
|
||||
mot_string typeName;
|
||||
mot_string keyName;
|
||||
|
||||
// 提取类型名称和键名称
|
||||
if (!key.substr(typeName, 0, colonPos) || !key.substr(keyName, colonPos + 1)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse typed key %s", key.c_str());
|
||||
} else {
|
||||
// 调用MakeTypedConfigValue函数创建类型化的配置值
|
||||
result = MakeTypedConfigValue(sectionFullName, typeName, keyName, value);
|
||||
}
|
||||
} else {
|
||||
// 调用MakeUntypedConfigValue函数创建非类型化的配置值
|
||||
result = MakeUntypedConfigValue(sectionFullName, key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeArrayConfigValue(const mot_string& path, uint64_t arrayIndex, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建数组配置项的配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* path:包含数组配置项的完整路径
|
||||
* arrayIndex:数组索引
|
||||
* value:值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建数组配置项的配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建数组配置项的配置值。
|
||||
* 首先,它将数组索引转换为字符串格式,并将其用作键。
|
||||
* 然后,调用MakeConfigValue函数来创建配置值,其中路径为包含数组配置项的完整路径,键为数组索引字符串。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeArrayConfigValue(const mot_string &path, uint64_t arrayIndex, const mot_string &value)
|
||||
{
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
mot_string key;
|
||||
|
||||
// 将数组索引转换为字符串格式,并作为键
|
||||
if (!key.format("%lu", arrayIndex)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to format array index %lu", arrayIndex);
|
||||
} else {
|
||||
// 调用MakeConfigValue函数创建配置值
|
||||
result = MakeConfigValue(path, key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeIntConfigValue(const mot_string& path, const mot_string& name, int64_t value)
|
||||
/*
|
||||
* 功能:创建整数类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* path:配置项所在的路径
|
||||
* name:配置项的名称
|
||||
* value:整数值
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建整数类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建整数类型的配置项配置值。
|
||||
* 首先,它尝试将整数值解析为不同的整数类型(从小到大),并根据解析结果创建相应类型的配置项。
|
||||
* 如果解析和创建成功,将返回相应的配置项指针;否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeIntConfigValue(const mot_string &path, const mot_string &name, int64_t value)
|
||||
{
|
||||
// try to parse value as integer starting from smallest type
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
|
||||
if ((value >= SCHAR_MIN) && (value <= SCHAR_MAX)) {
|
||||
result = CreateConfigValue<int8_t>(path.c_str(), name.c_str(), (int8_t)value);
|
||||
|
|
@ -164,11 +269,26 @@ ConfigItem* ConfigFileParser::MakeIntConfigValue(const mot_string& path, const m
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUIntConfigValue(const mot_string& path, const mot_string& name, uint64_t value)
|
||||
/*
|
||||
* 功能:创建无符号整数类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* path:配置项所在的路径
|
||||
* name:配置项的名称
|
||||
* value:无符号整数值
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建无符号整数类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建无符号整数类型的配置项配置值。
|
||||
* 首先,它尝试将无符号整数值解析为不同的无符号整数类型(从小到大),并根据解析结果创建相应类型的配置项。
|
||||
* 如果解析和创建成功,将返回相应的配置项指针;否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUIntConfigValue(const mot_string &path, const mot_string &name, uint64_t value)
|
||||
{
|
||||
// try to parse value as unsigned integer starting from smallest type
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
|
||||
if (value <= UCHAR_MAX) {
|
||||
result = CreateConfigValue<uint8_t>(path.c_str(), name.c_str(), (uint8_t)value);
|
||||
|
|
@ -195,21 +315,40 @@ ConfigItem* ConfigFileParser::MakeUIntConfigValue(const mot_string& path, const
|
|||
return result;
|
||||
}
|
||||
|
||||
bool ConfigFileParser::Split(const char* str, char sep, mot_string_list& tokens)
|
||||
/*
|
||||
* 功能:将字符串按指定分隔符分割为多个子字符串,并存储在字符串列表中
|
||||
*
|
||||
* 参数列表:
|
||||
* str:待分割的字符串
|
||||
* sep:分隔符
|
||||
* tokens:用于存储分割后的子字符串的字符串列表
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功分割并存储子字符串,返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将字符串按照指定的分隔符进行分割,并将分割后的子字符串存储在字符串列表中。
|
||||
* 它使用一个循环来处理字符串中的每个分隔符,并将分隔符之间的部分作为一个子字符串存储起来。
|
||||
* 在处理过程中,会跳过空字符串。
|
||||
*/
|
||||
bool ConfigFileParser::Split(const char *str, char sep, mot_string_list &tokens)
|
||||
{
|
||||
do {
|
||||
const char* begin = str;
|
||||
const char *begin = str;
|
||||
while (*str != sep && *str) {
|
||||
str++;
|
||||
}
|
||||
|
||||
// skip empty items
|
||||
// 跳过空项
|
||||
if (str != begin) {
|
||||
mot_string token;
|
||||
|
||||
// 将子字符串从begin到str的位置存储到token中
|
||||
if (!token.assign(begin, str - begin)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to push a token");
|
||||
return false;
|
||||
} else {
|
||||
// 将token添加到字符串列表中
|
||||
if (!tokens.push_back(token)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to push a token");
|
||||
return false;
|
||||
|
|
@ -217,14 +356,31 @@ bool ConfigFileParser::Split(const char* str, char sep, mot_string_list& tokens)
|
|||
}
|
||||
}
|
||||
} while (*str++ != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeIntConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建整数类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:整数值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建整数类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建整数类型的配置项配置值。
|
||||
* 它首先尝试将整数值的字符串表示解析为int64_t类型。
|
||||
* 如果解析成功,它会根据解析结果的大小选择适当的整数类型,并创建相应类型的配置项。
|
||||
* 如果创建成功,将返回相应的配置项指针;否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeIntConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as integer
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
int64_t intValue = 0;
|
||||
if (ParseIntValue(sectionFullName, key, value, intValue)) {
|
||||
if ((intValue >= SCHAR_MIN) && (intValue <= SCHAR_MAX)) {
|
||||
|
|
@ -251,12 +407,28 @@ ConfigItem* ConfigFileParser::MakeIntConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUIntConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建无符号整数类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:无符号整数值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建无符号整数类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建无符号整数类型的配置项配置值。
|
||||
* 它首先尝试将无符号整数值的字符串表示解析为uint64_t类型。
|
||||
* 如果解析成功,它会根据解析结果的大小选择适当的无符号整数类型,并创建相应类型的配置项。
|
||||
* 如果创建成功,将返回相应的配置项指针;否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUIntConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as unsigned integer
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
uint64_t intValue = 0;
|
||||
if (ParseUIntValue(sectionFullName, key, value, intValue)) {
|
||||
if (intValue <= UCHAR_MAX) {
|
||||
|
|
@ -284,44 +456,81 @@ ConfigItem* ConfigFileParser::MakeUIntConfigValue(
|
|||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeDoubleConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建双精度浮点数类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:双精度浮点数值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建双精度浮点数类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建双精度浮点数类型的配置项配置值。
|
||||
* 它首先尝试将双精度浮点数值的字符串表示解析为double类型。
|
||||
* 在解析过程中,会检查是否存在不合法的情况,如空输入、无有效数字、不合法的后续字符和溢出。
|
||||
* 如果解析成功且没有不合法情况,则创建相应类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeDoubleConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as double
|
||||
ConfigItem* result = nullptr;
|
||||
char* endptr = NULL;
|
||||
// 尝试解析字符串值为双精度浮点数
|
||||
ConfigItem *result = nullptr;
|
||||
char *endptr = nullptr;
|
||||
double doubleValue = strtod(value.c_str(), &endptr);
|
||||
if (*value.c_str() == 0) { // empty input
|
||||
|
||||
// 空输入
|
||||
if (*value.c_str() == 0) {
|
||||
MOT_LOG_TRACE("Configuration double at [section %s, key %s] is empty", sectionFullName.c_str(), key.c_str());
|
||||
} else if (endptr == value.c_str()) { // no valid digits
|
||||
}
|
||||
// 无有效数字
|
||||
else if (endptr == value.c_str()) {
|
||||
MOT_LOG_TRACE("Configuration double at [section %s, key %s] has no valid digits at all: %s",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if (*endptr != 0) { // some invalid trailing digits
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
}
|
||||
// 存在不合法的后续字符
|
||||
else if (*endptr != 0) {
|
||||
MOT_LOG_TRACE("Configuration double at [section %s, key %s] has invalid trailing characters: %s",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if (((doubleValue == HUGE_VALF) || (doubleValue == HUGE_VALL)) && (errno == ERANGE)) { // overflow
|
||||
MOT_LOG_TRACE("Configuration double at [section %s, key %s] overflows: %s",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
}
|
||||
// 溢出
|
||||
else if (((doubleValue == HUGE_VALF) || (doubleValue == HUGE_VALL)) && (errno == ERANGE)) {
|
||||
MOT_LOG_TRACE("Configuration double at [section %s, key %s] overflows: %s", sectionFullName.c_str(),
|
||||
key.c_str(), value.c_str());
|
||||
} else {
|
||||
result = CreateConfigValue<double>(sectionFullName.c_str(), key.c_str(), doubleValue);
|
||||
if (result == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate double configuration value");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeBoolConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建布尔类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:布尔值的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建布尔类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建布尔类型的配置项配置值。
|
||||
* 它首先尝试将布尔值的字符串表示解析为bool类型,支持"true"/"on"/"yes"和"false"/"off"/"no"。
|
||||
* 如果解析成功,则创建相应类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeBoolConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as Boolean
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
if ((strcasecmp(value.c_str(), "true") == 0) || (strcasecmp(value.c_str(), "on") == 0) ||
|
||||
(strcasecmp(value.c_str(), "yes") == 0)) {
|
||||
result = CreateConfigValue<bool>(sectionFullName.c_str(), key.c_str(), true);
|
||||
|
|
@ -337,12 +546,28 @@ ConfigItem* ConfigFileParser::MakeBoolConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeInt64ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建int64类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:int64类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建int64类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建int64类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为int64类型。
|
||||
* 如果解析成功,则创建int64类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeInt64ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as int64_t
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
int64_t intValue = 0;
|
||||
if (ParseIntValue(sectionFullName, key, value, intValue)) {
|
||||
result = CreateConfigValue<int64_t>(sectionFullName.c_str(), key.c_str(), intValue);
|
||||
|
|
@ -353,11 +578,28 @@ ConfigItem* ConfigFileParser::MakeInt64ConfigValue(
|
|||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeInt32ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建int32_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:int32_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建int32_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建int32_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为int64_t类型。
|
||||
* 如果解析成功并且值在int32_t的范围内,则创建int32_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeInt32ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as int32_t
|
||||
ConfigItem* result = nullptr;
|
||||
// 尝试解析字符串值为int64_t类型
|
||||
ConfigItem *result = nullptr;
|
||||
int64_t intValue = 0;
|
||||
if (ParseIntValue(sectionFullName, key, value, intValue)) {
|
||||
if ((intValue >= INT_MIN) && (intValue <= INT_MAX)) {
|
||||
|
|
@ -370,11 +612,28 @@ ConfigItem* ConfigFileParser::MakeInt32ConfigValue(
|
|||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeInt16ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建int16_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:int16_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建int16_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建int16_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为int64_t类型。
|
||||
* 如果解析成功并且值在int16_t的范围内,则创建int16_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeInt16ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as int16_t
|
||||
ConfigItem* result = nullptr;
|
||||
// 尝试解析字符串值为int64_t类型
|
||||
ConfigItem *result = nullptr;
|
||||
int64_t intValue = 0;
|
||||
if (ParseIntValue(sectionFullName, key, value, intValue)) {
|
||||
if ((intValue >= SHRT_MIN) && (intValue <= SHRT_MAX)) {
|
||||
|
|
@ -387,11 +646,28 @@ ConfigItem* ConfigFileParser::MakeInt16ConfigValue(
|
|||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeInt8ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建int8_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:int8_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建int8_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建int8_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为int64_t类型。
|
||||
* 如果解析成功并且值在int8_t的范围内,则创建int8_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeInt8ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as int8_t
|
||||
ConfigItem* result = nullptr;
|
||||
// 尝试解析字符串值为int64_t类型
|
||||
ConfigItem *result = nullptr;
|
||||
int64_t intValue = 0;
|
||||
if (ParseIntValue(sectionFullName, key, value, intValue)) {
|
||||
if ((intValue >= SCHAR_MIN) && (intValue <= SCHAR_MAX)) {
|
||||
|
|
@ -403,12 +679,28 @@ ConfigItem* ConfigFileParser::MakeInt8ConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUInt64ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建uint64_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:uint64_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建uint64_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建uint64_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为uint64_t类型。
|
||||
* 如果解析成功,创建uint64_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUInt64ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as uint64_t
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
uint64_t intValue = 0;
|
||||
if (ParseUIntValue(sectionFullName, key, value, intValue)) {
|
||||
result = CreateConfigValue<uint64_t>(sectionFullName.c_str(), key.c_str(), intValue);
|
||||
|
|
@ -418,12 +710,28 @@ ConfigItem* ConfigFileParser::MakeUInt64ConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUInt32ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建uint32_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:uint32_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建uint32_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建uint32_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为uint64_t类型。
|
||||
* 如果解析成功且值在uint32_t的范围内,创建uint32_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUInt32ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as uint32_t
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
uint64_t intValue = 0;
|
||||
if (ParseUIntValue(sectionFullName, key, value, intValue)) {
|
||||
if (intValue <= UINT_MAX) {
|
||||
|
|
@ -435,12 +743,28 @@ ConfigItem* ConfigFileParser::MakeUInt32ConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUInt16ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建uint16_t类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:uint16_t类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建uint16_t类型的配置项配置值,返回指向配置项的指针;否则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建uint16_t类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为uint64_t类型。
|
||||
* 如果解析成功且值在uint16_t的范围内,创建uint16_t类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUInt16ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as uint16_t
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
uint64_t intValue = 0;
|
||||
if (ParseUIntValue(sectionFullName, key, value, intValue)) {
|
||||
if (intValue <= USHRT_MAX) {
|
||||
|
|
@ -452,12 +776,28 @@ ConfigItem* ConfigFileParser::MakeUInt16ConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUInt8ConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建 uint8_t 类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:uint8_t 类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建 uint8_t 类型的配置项配置值,返回指向配置项的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建 uint8_t 类型的配置项配置值。
|
||||
* 它首先尝试将字符串值解析为 uint64_t 类型。
|
||||
* 如果解析成功且值在 uint8_t 的范围内,创建 uint8_t 类型的配置项,返回相应的配置项指针;
|
||||
* 否则返回 nullptr,并记录错误信息。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUInt8ConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to parse value as uint8_t
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
uint64_t intValue = 0;
|
||||
if (ParseUIntValue(sectionFullName, key, value, intValue)) {
|
||||
if (intValue <= UCHAR_MAX) {
|
||||
|
|
@ -469,13 +809,29 @@ ConfigItem* ConfigFileParser::MakeUInt8ConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeUntypedConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:创建未指定类型的配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:未指定类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建配置项配置值,返回指向配置项的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据提供的参数创建未指定类型的配置项配置值。
|
||||
* 它尝试根据字符串值的内容推断配置项的数据类型,然后创建相应类型的配置项。
|
||||
* 如果无法推断出数据类型或创建配置项失败,将默认创建一个 mot_string 类型的配置项。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeUntypedConfigValue(const mot_string §ionFullName, const mot_string &key,
|
||||
const mot_string &value)
|
||||
{
|
||||
// try to infer the configuration value type
|
||||
// 尝试推断配置值的数据类型
|
||||
MOT_LOG_TRACE("Attempting to parse signed integer value");
|
||||
ConfigItem* result = MakeIntConfigValue(sectionFullName, key, value);
|
||||
ConfigItem *result = MakeIntConfigValue(sectionFullName, key, value);
|
||||
if (result == nullptr) {
|
||||
MOT_LOG_TRACE("Attempting to parse unsigned integer value");
|
||||
result = MakeUIntConfigValue(sectionFullName, key, value);
|
||||
|
|
@ -490,6 +846,7 @@ ConfigItem* ConfigFileParser::MakeUntypedConfigValue(
|
|||
}
|
||||
if (result == nullptr) {
|
||||
MOT_LOG_TRACE("Defaulting to mot_string value");
|
||||
// 默认创建一个 mot_string 类型的配置项
|
||||
result = CreateConfigValue<mot_string, StringConfigValue>(sectionFullName.c_str(), key.c_str(), value);
|
||||
if (result == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate mot_string configuration value");
|
||||
|
|
@ -497,12 +854,27 @@ ConfigItem* ConfigFileParser::MakeUntypedConfigValue(
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigFileParser::MakeTypedConfigValue(
|
||||
const mot_string& sectionFullName, const mot_string& typeName, const mot_string& key, const mot_string& value)
|
||||
/*
|
||||
* 功能:根据类型名称创建配置项配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* typeName:配置项的类型名称
|
||||
* key:配置项的名称
|
||||
* value:配置项的值
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建配置项配置值,返回指向配置项的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数根据提供的类型名称,创建相应类型的配置项配置值。
|
||||
* 如果类型名称无效或无法创建配置项配置值,将生成相应的错误报告并返回 nullptr。
|
||||
*/
|
||||
ConfigItem *ConfigFileParser::MakeTypedConfigValue(const mot_string §ionFullName, const mot_string &typeName,
|
||||
const mot_string &key, const mot_string &value)
|
||||
{
|
||||
// build a configuration value by given type name
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
ConfigValueType valueType = ConfigValueTypeFromString(typeName.c_str());
|
||||
switch (valueType) {
|
||||
case ConfigValueType::CONFIG_VALUE_INT64:
|
||||
|
|
@ -548,98 +920,107 @@ ConfigItem* ConfigFileParser::MakeTypedConfigValue(
|
|||
case ConfigValueType::CONFIG_VALUE_STRING:
|
||||
result = CreateConfigValue<mot_string, StringConfigValue>(sectionFullName.c_str(), key.c_str(), value);
|
||||
if (result == nullptr) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Load Configuration", "Failed to allocate mot_string configuration value");
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to allocate mot_string configuration value");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG,
|
||||
"Load Configuration",
|
||||
"Invalid configuration value type name: %s",
|
||||
typeName.c_str());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG, "Load Configuration", "Invalid configuration value type name: %s",
|
||||
typeName.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigFileParser::ParseIntValue(
|
||||
const mot_string& sectionFullName, const mot_string& key, const mot_string& value, int64_t& intValue)
|
||||
/*
|
||||
* 功能:尝试将字符串值解析为 int64_t 类型
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:待解析的字符串值
|
||||
* intValue:成功解析后存储整数值的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析整数值,返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数尝试将字符串值解析为 int64_t 类型,如果解析失败,会记录错误信息并返回 false。
|
||||
*/
|
||||
bool ConfigFileParser::ParseIntValue(const mot_string §ionFullName, const mot_string &key, const mot_string &value,
|
||||
int64_t &intValue)
|
||||
{
|
||||
bool result = false;
|
||||
char* endptr = NULL;
|
||||
intValue = strtoll(value.c_str(), &endptr, 0);
|
||||
if (*value.c_str() == 0) { // empty input
|
||||
char *endptr = NULL;
|
||||
intValue = strtoll(value.c_str(), &endptr, 0); // 使用 strtoll 尝试解析字符串为 int64_t
|
||||
|
||||
if (*value.c_str() == 0) { // 空输入
|
||||
MOT_LOG_DIAG2("Configuration integer at [section %s, key %s] is empty", sectionFullName.c_str(), key.c_str());
|
||||
} else if (endptr == value.c_str()) { // no valid digits
|
||||
} else if (endptr == value.c_str()) { // 无有效数字
|
||||
MOT_LOG_DIAG2("Configuration integer at [section %s, key %s] has no valid digits at all: %s",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if (*endptr != 0) { // some invalid trailing digits
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
} else if (*endptr != 0) { // 存在无效的后续字符
|
||||
MOT_LOG_DIAG2("Configuration integer at [section %s, key %s] has invalid trailing characters: %s",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if (((intValue == LLONG_MIN) || (intValue == LLONG_MAX)) && (errno == ERANGE)) { // overflow
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
} else if (((intValue == LLONG_MIN) || (intValue == LLONG_MAX)) && (errno == ERANGE)) { // 溢出
|
||||
MOT_LOG_DIAG2("Configuration integer at [section %s, key %s] overflows: %s", value.c_str());
|
||||
} else {
|
||||
int err = errno;
|
||||
MOT_LOG_TRACE("Configuration integer at [section %s, key %s] is ok: %s --> %" PRId64
|
||||
" (value = %p, endptr = %p, *endptr = %u, errno: %d)",
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str(),
|
||||
intValue,
|
||||
value.c_str(),
|
||||
endptr,
|
||||
(unsigned)*endptr,
|
||||
err);
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str(), intValue, value.c_str(), endptr,
|
||||
(unsigned)*endptr, err);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigFileParser::ParseUIntValue(const mot_string& sectionFullName, const mot_string& key, const mot_string& value,
|
||||
uint64_t& intValue, bool arrayIndex /* = false */)
|
||||
/*
|
||||
* 功能:尝试将字符串值解析为 uint64_t 类型
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:配置项所在的完整路径
|
||||
* key:配置项的名称
|
||||
* value:待解析的字符串值
|
||||
* intValue:成功解析后存储无符号整数值的引用
|
||||
* arrayIndex:标志是否解析数组索引(可选,默认为 false)
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析无符号整数值,返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数尝试将字符串值解析为 uint64_t 类型,如果解析失败,会记录错误信息并返回 false。
|
||||
*/
|
||||
bool ConfigFileParser::ParseUIntValue(const mot_string §ionFullName, const mot_string &key, const mot_string &value,
|
||||
uint64_t &intValue, bool arrayIndex /* = false */)
|
||||
{
|
||||
bool result = false;
|
||||
char* endptr = NULL;
|
||||
intValue = strtoull(value.c_str(), &endptr, 0);
|
||||
const char* itemName = arrayIndex ? "arrayIndex" : "integer";
|
||||
if (*value.c_str() == 0) { // empty input
|
||||
MOT_LOG_DIAG2(
|
||||
"Configuration %s at [section %s, key %s] is empty", itemName, sectionFullName.c_str(), key.c_str());
|
||||
} else if (endptr == value.c_str()) { // no valid digits
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] has no valid digits at all: %s",
|
||||
itemName,
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if (*endptr != 0) { // some invalid trailing digits
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] has invalid trailing characters: %s",
|
||||
itemName,
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str());
|
||||
} else if ((intValue == ULLONG_MAX) && (errno == ERANGE)) { // overflow
|
||||
char *endptr = NULL;
|
||||
intValue = strtoull(value.c_str(), &endptr, 0); // 使用 strtoull 尝试解析字符串为 uint64_t
|
||||
const char *itemName = arrayIndex ? "arrayIndex" : "integer"; // 根据是否数组索引确定项目名称
|
||||
|
||||
if (*value.c_str() == 0) { // 空输入
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] is empty", itemName, sectionFullName.c_str(),
|
||||
key.c_str());
|
||||
} else if (endptr == value.c_str()) { // 无有效数字
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] has no valid digits at all: %s", itemName,
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
} else if (*endptr != 0) { // 存在无效的后续字符
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] has invalid trailing characters: %s", itemName,
|
||||
sectionFullName.c_str(), key.c_str(), value.c_str());
|
||||
} else if ((intValue == ULLONG_MAX) && (errno == ERANGE)) { // 溢出
|
||||
MOT_LOG_DIAG2("Configuration %s at [section %s, key %s] overflows: %s", itemName, value.c_str());
|
||||
} else {
|
||||
int err = errno;
|
||||
MOT_LOG_TRACE("Configuration %s at [section %s, key %s] is ok: %s --> %" PRIu64
|
||||
" (value = %p, endptr = %p, *endptr = %u, errno: %d)",
|
||||
itemName,
|
||||
sectionFullName.c_str(),
|
||||
key.c_str(),
|
||||
value.c_str(),
|
||||
intValue,
|
||||
value.c_str(),
|
||||
endptr,
|
||||
(unsigned)*endptr,
|
||||
err);
|
||||
itemName, sectionFullName.c_str(), key.c_str(), value.c_str(), intValue, value.c_str(), endptr,
|
||||
(unsigned)*endptr, err);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace MOT {
|
|||
IMPLEMENT_CLASS_LOGGER(ConfigItem, Configuration)
|
||||
|
||||
constexpr const char ConfigItem::PATH_SEP;
|
||||
constexpr const char* ConfigItem::PATH_SEP_STR;
|
||||
constexpr const char *ConfigItem::PATH_SEP_STR;
|
||||
constexpr size_t ConfigItem::CFG_MAX_PATH_LEN;
|
||||
constexpr size_t ConfigItem::CFG_MAX_NAME_LEN;
|
||||
constexpr size_t ConfigItem::CFG_MAX_FULL_PATH_LEN;
|
||||
|
|
@ -41,38 +41,47 @@ ConfigItem::ConfigItem(ConfigItemClass itemClass)
|
|||
m_depth(0)
|
||||
{}
|
||||
|
||||
bool ConfigItem::Initialize(const char* path, const char* name)
|
||||
/*
|
||||
* 功能:初始化配置项的路径和名称
|
||||
*
|
||||
* 参数列表:
|
||||
* path:配置项的路径
|
||||
* name:配置项的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 如果初始化成功,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化配置项的路径、名称和完整路径名称,以及计算配置项的深度。
|
||||
*/
|
||||
bool ConfigItem::Initialize(const char *path, const char *name)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if ((path[0] == 0) || (path[0] == PATH_SEP)) { // either empty or has leading slash
|
||||
result = m_path.assign(path);
|
||||
} else { // non-empty and missing leading slash
|
||||
result = m_path.format("%s%s", PATH_SEP_STR, path);
|
||||
if ((path[0] == 0) || (path[0] == PATH_SEP)) { // 检查路径是否为空或以斜杠开头
|
||||
result = m_path.assign(path); // 使用传入的路径值
|
||||
} else { // 路径非空且缺少斜杠开头
|
||||
result = m_path.format("%s%s", PATH_SEP_STR, path); // 在路径前加上斜杠
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Load Configuration", "Failed to initialize configuration path to: %s", path);
|
||||
if (!result) { // 检查路径初始化是否成功
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to initialize configuration path to: %s",
|
||||
path);
|
||||
} else {
|
||||
// safe copy without buffer overrun
|
||||
result = m_name.assign(name);
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Load Configuration", "Failed to initialize configuration name to: %s", name);
|
||||
// 安全复制配置项名称,避免缓冲区溢出
|
||||
result = m_name.assign(name); // 使用传入的名称值
|
||||
if (!result) { // 检查名称初始化是否成功
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to initialize configuration name to: %s",
|
||||
name);
|
||||
} else {
|
||||
// safe format without buffer overrun
|
||||
result = m_fullPathName.format("%s%s%s", m_path.c_str(), PATH_SEP_STR, m_name.c_str());
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to initialize full configuration path to: %s%s%s",
|
||||
name,
|
||||
m_path.c_str(),
|
||||
PATH_SEP_STR,
|
||||
m_name.c_str());
|
||||
// 安全格式化完整路径名称,避免缓冲区溢出
|
||||
result = m_fullPathName.format("%s%s%s", m_path.c_str(), PATH_SEP_STR, m_name.c_str()); // 合并路径和名称
|
||||
if (!result) { // 检查完整路径名称初始化是否成功
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to initialize full configuration path to: %s%s%s", name, m_path.c_str(),
|
||||
PATH_SEP_STR, m_name.c_str());
|
||||
} else {
|
||||
m_depth = ComputeDepth();
|
||||
m_depth = ComputeDepth(); // 计算配置项的深度
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,20 +89,25 @@ bool ConfigItem::Initialize(const char* path, const char* name)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:计算配置项的深度
|
||||
*
|
||||
* 返回值:
|
||||
* 返回配置项的深度作为无符号整数。
|
||||
*
|
||||
* 注意:
|
||||
* 该函数根据完整路径名称中的斜杠数来计算配置项的深度。如果完整路径为根目录,则深度为 0。
|
||||
*/
|
||||
uint32_t ConfigItem::ComputeDepth()
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if (m_fullPathName.compare(PATH_SEP_STR) != 0) {
|
||||
result = m_fullPathName.count(PATH_SEP);
|
||||
if (m_fullPathName.compare(PATH_SEP_STR) != 0) { // 检查完整路径是否为根目录
|
||||
result = m_fullPathName.count(PATH_SEP); // 计算完整路径中斜杠的数量,作为深度
|
||||
}
|
||||
MOT_LOG_DEBUG("Computed depth in full path %s (@%p), path %s (@%p) and name %s (@%p): %u",
|
||||
m_fullPathName.c_str(),
|
||||
m_fullPathName.c_str(),
|
||||
m_path.c_str(),
|
||||
m_path.c_str(),
|
||||
m_name.c_str(),
|
||||
m_name.c_str(),
|
||||
result);
|
||||
// 记录计算得到的深度信息
|
||||
MOT_LOG_DEBUG("Computed depth in full path %s (@%p), path %s (@%p) and name %s (@%p): %u", m_fullPathName.c_str(),
|
||||
m_fullPathName.c_str(), m_path.c_str(), m_path.c_str(), m_name.c_str(), m_name.c_str(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -26,34 +26,63 @@
|
|||
#include <string.h>
|
||||
|
||||
namespace MOT {
|
||||
static const char* CONFIG_ITEM_SECTION_STR = "section";
|
||||
static const char* CONFIG_ITEM_VALUE_STR = "value";
|
||||
static const char* CONFIG_ITEM_ARRAY_STR = "array";
|
||||
static const char* CONFIG_ITEM_UNDEFINED_STR = "N/A";
|
||||
static const char *CONFIG_ITEM_SECTION_STR = "section";
|
||||
static const char *CONFIG_ITEM_VALUE_STR = "value";
|
||||
static const char *CONFIG_ITEM_ARRAY_STR = "array";
|
||||
static const char *CONFIG_ITEM_UNDEFINED_STR = "N/A";
|
||||
|
||||
static const char* ITEM_CLASS_NAMES[] = {
|
||||
CONFIG_ITEM_SECTION_STR, CONFIG_ITEM_VALUE_STR, CONFIG_ITEM_ARRAY_STR, CONFIG_ITEM_UNDEFINED_STR};
|
||||
static const char *ITEM_CLASS_NAMES[] = {CONFIG_ITEM_SECTION_STR, CONFIG_ITEM_VALUE_STR, CONFIG_ITEM_ARRAY_STR,
|
||||
CONFIG_ITEM_UNDEFINED_STR};
|
||||
|
||||
extern ConfigItemClass ConfigItemClassFromString(const char* itemClassStr)
|
||||
/*
|
||||
* 功能:从字符串中解析配置项类别
|
||||
*
|
||||
* 参数列表:
|
||||
* itemClassStr:表示配置项类别的字符串
|
||||
*
|
||||
* 返回值:
|
||||
* 返回 `ConfigItemClass` 枚举值,表示解析得到的配置项类别。如果无法解析,返回
|
||||
* `ConfigItemClass::CONFIG_ITEM_UNDEFINED`。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数通过比较输入的字符串与已知的配置项类别字符串(如
|
||||
* CONFIG_ITEM_SECTION_STR、CONFIG_ITEM_VALUE_STR、CONFIG_ITEM_ARRAY_STR),
|
||||
* 来确定输入字符串表示的配置项类别。如果匹配成功,将返回相应的枚举值,否则返回 CONFIG_ITEM_UNDEFINED。
|
||||
*/
|
||||
extern ConfigItemClass ConfigItemClassFromString(const char *itemClassStr)
|
||||
{
|
||||
ConfigItemClass result = ConfigItemClass::CONFIG_ITEM_UNDEFINED;
|
||||
ConfigItemClass result = ConfigItemClass::CONFIG_ITEM_UNDEFINED; // 初始化结果为 CONFIG_ITEM_UNDEFINED
|
||||
|
||||
if (strcmp(itemClassStr, CONFIG_ITEM_SECTION_STR) == 0) {
|
||||
result = ConfigItemClass::CONFIG_ITEM_SECTION;
|
||||
} else if (strcmp(itemClassStr, CONFIG_ITEM_VALUE_STR) == 0) {
|
||||
result = ConfigItemClass::CONFIG_ITEM_VALUE;
|
||||
} else if (strcmp(itemClassStr, CONFIG_ITEM_ARRAY_STR) == 0) {
|
||||
result = ConfigItemClass::CONFIG_ITEM_ARRAY;
|
||||
if (strcmp(itemClassStr, CONFIG_ITEM_SECTION_STR) == 0) { // 检查是否匹配 CONFIG_ITEM_SECTION_STR
|
||||
result = ConfigItemClass::CONFIG_ITEM_SECTION; // 匹配成功,设置结果为 CONFIG_ITEM_SECTION
|
||||
} else if (strcmp(itemClassStr, CONFIG_ITEM_VALUE_STR) == 0) { // 检查是否匹配 CONFIG_ITEM_VALUE_STR
|
||||
result = ConfigItemClass::CONFIG_ITEM_VALUE; // 匹配成功,设置结果为 CONFIG_ITEM_VALUE
|
||||
} else if (strcmp(itemClassStr, CONFIG_ITEM_ARRAY_STR) == 0) { // 检查是否匹配 CONFIG_ITEM_ARRAY_STR
|
||||
result = ConfigItemClass::CONFIG_ITEM_ARRAY; // 匹配成功,设置结果为 CONFIG_ITEM_ARRAY
|
||||
}
|
||||
|
||||
return result;
|
||||
return result; // 返回解析得到的配置项类别
|
||||
}
|
||||
|
||||
extern const char* ConfigItemClassToString(ConfigItemClass configItemClass)
|
||||
/*
|
||||
* 功能:将配置项类别枚举值转换为字符串表示
|
||||
*
|
||||
* 参数列表:
|
||||
* configItemClass:表示配置项类别的枚举值
|
||||
*
|
||||
* 返回值:
|
||||
* 返回一个指向表示配置项类别的字符串的指针。如果枚举值无效,则返回 CONFIG_ITEM_UNDEFINED_STR。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数通过将枚举值转换为对应的整数索引,然后使用该索引查找预定义的字符串数组 ITEM_CLASS_NAMES,
|
||||
* 来获取配置项类别的字符串表示。如果枚举值有效,将返回相应的字符串,否则返回 CONFIG_ITEM_UNDEFINED_STR。
|
||||
*/
|
||||
extern const char *ConfigItemClassToString(ConfigItemClass configItemClass)
|
||||
{
|
||||
if (configItemClass < ConfigItemClass::CONFIG_ITEM_UNDEFINED) {
|
||||
return ITEM_CLASS_NAMES[(uint32_t)configItemClass];
|
||||
return ITEM_CLASS_NAMES[(uint32_t)configItemClass]; // 使用整数索引查找预定义的字符串数组
|
||||
}
|
||||
return CONFIG_ITEM_UNDEFINED_STR;
|
||||
return CONFIG_ITEM_UNDEFINED_STR; // 枚举值无效,返回 CONFIG_ITEM_UNDEFINED_STR
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -29,10 +29,26 @@
|
|||
|
||||
namespace MOT {
|
||||
|
||||
ConfigLoader::ConfigLoader(const char* name, uint32_t priority) : m_priority(priority), m_configTree(nullptr)
|
||||
/*
|
||||
* 构造函数:ConfigLoader
|
||||
*
|
||||
* 参数列表:
|
||||
* name: 配置加载器的名称,用于标识加载器。
|
||||
* priority: 配置加载器的优先级,用于确定加载顺序。
|
||||
*
|
||||
* 功能:
|
||||
* 初始化 ConfigLoader 类的新实例。
|
||||
* 设置配置加载器的名称和优先级,并分配内部配置树指针。
|
||||
*
|
||||
* 注意:
|
||||
* 该构造函数使用了 strncpy_s 函数来安全地复制名称,以确保不会发生缓冲区溢出。
|
||||
*/
|
||||
ConfigLoader::ConfigLoader(const char *name, uint32_t priority) : m_priority(priority), m_configTree(nullptr)
|
||||
{
|
||||
// 使用 strncpy_s 复制名称以确保安全性
|
||||
errno_t erc = strncpy_s(m_name, MAX_CONFIG_LOADER_NAME, name, strlen(name));
|
||||
securec_check(erc, "\0", "\0");
|
||||
// 确保名称以 null 终止
|
||||
m_name[MAX_CONFIG_LOADER_NAME - 1] = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,22 +37,45 @@
|
|||
namespace MOT {
|
||||
DECLARE_LOGGER(ConfigManager, Configuration)
|
||||
|
||||
ConfigManager* ConfigManager::m_manager = nullptr;
|
||||
ConfigManager *ConfigManager::m_manager = nullptr;
|
||||
|
||||
/*
|
||||
* 功能:初始化 ConfigManager 类的新实例,设置配置加载器和监听器的初始计数,使用 memset_s
|
||||
函数将加载器和监听器数组初始化为零。
|
||||
*
|
||||
* 注意:
|
||||
* 使用 memset_s 函数可以确保初始化过程是安全的,以防止潜在的内存访问问题。
|
||||
*
|
||||
* 它执行以下操作:
|
||||
* 1、将配置加载器计数 m_configLoaderCount 初始化为零。
|
||||
* 2、将监听器计数 m_listenerCount 初始化为零。
|
||||
* 3、使用 memset_s 函数将配置加载器数组 m_configLoaders 初始化为零。
|
||||
* 4、使用 memset_s 函数将监听器数组 m_listeners 初始化为零。
|
||||
*/
|
||||
ConfigManager::ConfigManager() : m_configLoaderCount(0), m_listenerCount(0)
|
||||
{
|
||||
// 使用 memset_s 函数将配置加载器数组初始化为零
|
||||
errno_t erc = memset_s(m_configLoaders, sizeof(m_configLoaders), 0, sizeof(m_configLoaders));
|
||||
securec_check(erc, "\0", "\0");
|
||||
// 使用 memset_s 函数将监听器数组初始化为零
|
||||
erc = memset_s(m_listeners, sizeof(m_listeners), 0, sizeof(m_listeners));
|
||||
securec_check(erc, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:ConfigManager 类的析构函数,用于清理所有配置加载器的资源
|
||||
*
|
||||
* 注意:
|
||||
* 析构函数负责释放所有配置加载器的资源。它首先检查每个加载器是否为空指针,
|
||||
* 如果不为空,则尝试从配置加载器中获取配置,并谨慎地将其从分层配置树中移除。
|
||||
* 最后,删除配置加载器并将其设置为 nullptr。
|
||||
*/
|
||||
ConfigManager::~ConfigManager()
|
||||
{
|
||||
// cleanup all configuration loaders
|
||||
// 清理所有配置加载器
|
||||
for (uint32_t i = 0; i < m_configLoaderCount; ++i) {
|
||||
if (m_configLoaders[i]) {
|
||||
// remove configuration tree carefully (some might have failed to load)
|
||||
// 谨慎地移除配置树(某些加载器可能加载失败)
|
||||
if (m_configLoaders[i]->GetConfig() != nullptr) {
|
||||
m_layeredConfigTree.RemoveConfigTree(m_configLoaders[i]->GetConfig());
|
||||
}
|
||||
|
|
@ -62,46 +85,108 @@ ConfigManager::~ConfigManager()
|
|||
}
|
||||
}
|
||||
|
||||
bool ConfigManager::CreateInstance(char** argv /* = nullptr */, int argc /* = 0 */)
|
||||
/*
|
||||
* 功能:创建 ConfigManager 的单例实例
|
||||
*
|
||||
* 参数列表:
|
||||
* argv:命令行参数数组的指针,默认值为 nullptr
|
||||
* argc:命令行参数数组的大小,默认值为 0
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建了 ConfigManager 实例,则返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数负责创建 ConfigManager 的单例实例,首先检查 m_manager 是否已分配。
|
||||
* 如果尚未分配,则尝试为 m_manager 分配内存。如果内存分配失败,函数会报告内存分配错误并返回 false。
|
||||
* 如果成功分配内存,将调用 m_manager 的 Initialize 方法进行初始化。
|
||||
* 如果初始化失败,将释放分配的内存,并将 m_manager 设置为 nullptr,并返回 false。
|
||||
* 如果初始化成功,函数将返回 true,表示成功创建了 ConfigManager 实例。
|
||||
*/
|
||||
bool ConfigManager::CreateInstance(char **argv /* = nullptr */, int argc /* = 0 */)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 检查 m_manager 是否已分配
|
||||
MOT_ASSERT(m_manager == nullptr);
|
||||
|
||||
if (m_manager == nullptr) {
|
||||
// 尝试为 m_manager 分配内存
|
||||
m_manager = new (std::nothrow) ConfigManager();
|
||||
|
||||
if (m_manager == nullptr) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Load Configuration", "Failed to allocate memory for configuration manager, aborting");
|
||||
// 内存分配失败,报告错误并返回 false
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to allocate memory for configuration manager, aborting");
|
||||
} else {
|
||||
// 调用初始化方法进行初始化
|
||||
result = m_manager->Initialize(argv, argc);
|
||||
|
||||
if (!result) {
|
||||
// 初始化失败,释放分配的内存,并将 m_manager 设置为 nullptr
|
||||
delete m_manager;
|
||||
m_manager = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 ConfigManager 的单例实例
|
||||
*
|
||||
* 注意:
|
||||
* 此函数负责销毁 ConfigManager 的单例实例。
|
||||
* 首先,它会检查 m_manager 是否已分配,确保单例实例存在。
|
||||
* 然后,它会删除 m_manager,释放其分配的内存,并将 m_manager 设置为 nullptr,以确保单例实例不再存在。
|
||||
*/
|
||||
void ConfigManager::DestroyInstance()
|
||||
{
|
||||
MOT_ASSERT(m_manager != nullptr);
|
||||
|
||||
if (m_manager != nullptr) {
|
||||
// 删除 m_manager,释放分配的内存,并将 m_manager 设置为 nullptr
|
||||
delete m_manager;
|
||||
m_manager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager& ConfigManager::GetInstance()
|
||||
/*
|
||||
* 功能:获取 ConfigManager 的单例实例
|
||||
*
|
||||
* 参数列表:无
|
||||
*
|
||||
* 返回值:返回 ConfigManager 的单例实例的引用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取 ConfigManager 的单例实例。
|
||||
* 首先,它会检查 m_manager 是否已分配,确保单例实例存在。
|
||||
* 然后,它返回该单例实例的引用,允许客户端代码使用 ConfigManager 的功能。
|
||||
*/
|
||||
ConfigManager &ConfigManager::GetInstance()
|
||||
{
|
||||
MOT_ASSERT(m_manager != nullptr);
|
||||
return *m_manager;
|
||||
}
|
||||
|
||||
bool ConfigManager::AddConfigFile(const char* configFilePath, const char* name /* = "Main" */,
|
||||
ConfigFileFormat configFileFormat /* = ConfigFileFormat::CONFIG_FILE_FORMAT_NONE */)
|
||||
/*
|
||||
* 功能:向配置管理器添加配置文件加载器
|
||||
*
|
||||
* 参数列表:
|
||||
* configFilePath:配置文件的路径
|
||||
* name:配置文件加载器的名称,默认为 "Main"
|
||||
* configFileFormat:配置文件的格式,默认为 ConfigFileFormat::CONFIG_FILE_FORMAT_NONE
|
||||
*
|
||||
* 返回值:如果成功添加配置文件加载器,则返回 true,否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向配置管理器添加配置文件加载器,以便加载配置文件并解析其中的配置项。
|
||||
* 它根据配置文件的格式创建相应的配置文件加载器。
|
||||
* 如果成功创建和添加加载器,返回 true,否则返回 false 并记录错误。
|
||||
*/
|
||||
bool ConfigManager::AddConfigFile(const char *configFilePath, const char *name /* = "Main" */,
|
||||
ConfigFileFormat configFileFormat /* = ConfigFileFormat::CONFIG_FILE_FORMAT_NONE */)
|
||||
{
|
||||
bool result = false;
|
||||
ConfigFileLoader* cfgFileLoader = nullptr;
|
||||
ConfigFileLoader *cfgFileLoader = nullptr;
|
||||
|
||||
switch (configFileFormat) {
|
||||
case ConfigFileFormat::CONFIG_FILE_PROPS:
|
||||
|
|
@ -113,20 +198,16 @@ bool ConfigManager::AddConfigFile(const char* configFilePath, const char* name /
|
|||
break;
|
||||
|
||||
default:
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG,
|
||||
"Load Configuration",
|
||||
"Invalid configuration file format specification %d",
|
||||
(int)configFileFormat);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG, "Load Configuration",
|
||||
"Invalid configuration file format specification %d", (int)configFileFormat);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfgFileLoader != nullptr) {
|
||||
result = AddConfigLoader(cfgFileLoader);
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG,
|
||||
"Load Configuration",
|
||||
"Failed to add configuration file loader by name %s (duplicate?)",
|
||||
name);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG, "Load Configuration",
|
||||
"Failed to add configuration file loader by name %s (duplicate?)", name);
|
||||
delete cfgFileLoader;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -136,8 +217,21 @@ bool ConfigManager::AddConfigFile(const char* configFilePath, const char* name /
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool CompareConfgLoaders(ConfigLoader* lhs, ConfigLoader* rhs)
|
||||
/*
|
||||
* 功能:比较两个配置加载器的优先级
|
||||
*
|
||||
* 参数列表:
|
||||
* lhs:要比较的第一个配置加载器
|
||||
* rhs:要比较的第二个配置加载器
|
||||
*
|
||||
* 返回值:如果第一个配置加载器的优先级较低(数字较高),则返回 true,否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于比较两个配置加载器的优先级,以确定它们在加载顺序中的位置。
|
||||
* 优先级数字较低的配置加载器(即数字较高)应该在加载顺序中排在前面。
|
||||
* 如果第一个配置加载器的优先级较低,则返回 true,否则返回 false。
|
||||
*/
|
||||
static bool CompareConfgLoaders(ConfigLoader *lhs, ConfigLoader *rhs)
|
||||
{
|
||||
int lhsPriority = lhs->GetPriority();
|
||||
int rhsPriority = rhs->GetPriority();
|
||||
|
|
@ -148,18 +242,29 @@ static bool CompareConfgLoaders(ConfigLoader* lhs, ConfigLoader* rhs)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManager::AddConfigLoader(ConfigLoader* configLoader)
|
||||
/*
|
||||
* 功能:添加配置加载器到配置管理器
|
||||
*
|
||||
* 参数列表:
|
||||
* configLoader:要添加的配置加载器对象的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置加载器,则返回 true;如果配置加载器已达到最大数量限制或添加失败,则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将配置加载器添加到配置管理器中。配置加载器会根据其优先级在加载配置时进行排序。
|
||||
* 较低优先级的加载器将首先加载配置,以便较高优先级的加载器可以基于已加载的配置设置其他限制。
|
||||
* 如果已达到配置加载器的最大数量限制,将返回 false。
|
||||
*/
|
||||
bool ConfigManager::AddConfigLoader(ConfigLoader *configLoader)
|
||||
{
|
||||
bool result = false;
|
||||
MOT_LOG_TRACE("Adding configuration loader %s", configLoader->GetName());
|
||||
|
||||
if (m_configLoaderCount == MAX_CONFIG_LOADER_COUNT) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_RESOURCE_LIMIT,
|
||||
"Load Configuration",
|
||||
"Cannot add configuration loader %s: Reached limit %u",
|
||||
configLoader->GetName(),
|
||||
(unsigned)MAX_CONFIG_LOADER_COUNT);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_RESOURCE_LIMIT, "Load Configuration",
|
||||
"Cannot add configuration loader %s: Reached limit %u", configLoader->GetName(),
|
||||
(unsigned)MAX_CONFIG_LOADER_COUNT);
|
||||
} else {
|
||||
// insert sorted in descending order - we want lower priority loaders to load configuration first, so higher
|
||||
// priority loaders can impose other limits based on configuration loaded already (as in external loaders that
|
||||
|
|
@ -168,15 +273,27 @@ bool ConfigManager::AddConfigLoader(ConfigLoader* configLoader)
|
|||
std::sort(m_configLoaders, m_configLoaders + m_configLoaderCount, CompareConfgLoaders);
|
||||
uint32_t configLoaderPos =
|
||||
std::find(m_configLoaders, m_configLoaders + m_configLoaderCount, configLoader) - m_configLoaders;
|
||||
MOT_LOG_TRACE(
|
||||
"Configuration loader %s added successfully at slot %u", configLoader->GetName(), configLoaderPos);
|
||||
MOT_LOG_TRACE("Configuration loader %s added successfully at slot %u", configLoader->GetName(),
|
||||
configLoaderPos);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigManager::RemoveConfigLoader(const char* configName)
|
||||
/*
|
||||
* 功能:从配置管理器中删除指定名称的配置加载器
|
||||
*
|
||||
* 参数列表:
|
||||
* configName:要删除的配置加载器的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功删除配置加载器,则返回 true;如果配置加载器未找到或删除失败,则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置管理器中删除指定名称的配置加载器。如果配置加载器被成功删除,它将从内部列表中移除,
|
||||
* 同时也会从配置树中移除与之相关联的配置。如果配置加载器未找到或删除失败,将返回 false。
|
||||
*/
|
||||
bool ConfigManager::RemoveConfigLoader(const char *configName)
|
||||
{
|
||||
bool result = false;
|
||||
MOT_LOG_TRACE("Removing configuration loader %s [%u registered]", configName, m_configLoaderCount);
|
||||
|
|
@ -205,16 +322,26 @@ bool ConfigManager::RemoveConfigLoader(const char* configName)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigManager::AddConfigChangeListener(IConfigChangeListener* listener)
|
||||
/*
|
||||
* 功能:向配置管理器中添加配置更改监听器
|
||||
*
|
||||
* 参数列表:
|
||||
* listener:要添加的配置更改监听器的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加监听器,则返回 true;如果已达到监听器数量上限或添加失败,则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向配置管理器中添加配置更改监听器。监听器会在配置更改时被通知,以便执行相应的操作。
|
||||
* 如果成功添加监听器,将返回 true;如果已达到监听器数量上限或添加失败,将返回 false。
|
||||
*/
|
||||
bool ConfigManager::AddConfigChangeListener(IConfigChangeListener *listener)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (m_listenerCount == MAX_CONFIG_LISTENER_COUNT) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_RESOURCE_LIMIT,
|
||||
"Load Configuration",
|
||||
"Cannot add configuration listener: Reached limit %u",
|
||||
(unsigned)MAX_CONFIG_LISTENER_COUNT);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_RESOURCE_LIMIT, "Load Configuration",
|
||||
"Cannot add configuration listener: Reached limit %u", (unsigned)MAX_CONFIG_LISTENER_COUNT);
|
||||
} else {
|
||||
m_listeners[m_listenerCount++] = listener;
|
||||
result = true;
|
||||
|
|
@ -223,7 +350,21 @@ bool ConfigManager::AddConfigChangeListener(IConfigChangeListener* listener)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool ConfigManager::RemoveConfigChangeListener(IConfigChangeListener* listener)
|
||||
/*
|
||||
* 功能:从配置管理器中移除配置更改监听器
|
||||
*
|
||||
* 参数列表:
|
||||
* listener:要移除的配置更改监听器的指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功移除监听器,则返回 true;如果监听器未找到或移除失败,则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置管理器中移除配置更改监听器。首先,它会遍历监听器数组,寻找与给定 listener 指针匹配的监听器。
|
||||
* 如果找到匹配的监听器,将返回 true,并将数组中的监听器往前移动一个位置,以填补删除的位置。
|
||||
* 如果监听器未找到或移除失败,将返回 false,并记录错误消息。
|
||||
*/
|
||||
bool ConfigManager::RemoveConfigChangeListener(IConfigChangeListener *listener)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
|
|
@ -246,7 +387,17 @@ bool ConfigManager::RemoveConfigChangeListener(IConfigChangeListener* listener)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化加载配置
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功初始化加载配置,则返回 true;如果加载配置失败,则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化加载配置。它首先尝试从所有配置加载器中加载配置,然后触发从刚刚加载的配置更新(仅对特定监听器)。
|
||||
* 如果加载配置失败,函数将记录错误信息,但会继续使用默认配置并返回 true。
|
||||
* 如果加载配置成功,函数会验证主配置是否有效,如果有效则返回 true。
|
||||
*/
|
||||
bool ConfigManager::InitLoad()
|
||||
{
|
||||
bool result = false;
|
||||
|
|
@ -262,7 +413,7 @@ bool ConfigManager::InitLoad()
|
|||
} else {
|
||||
// trigger update from just-loaded configuration (only to this specific listener).
|
||||
MOT_LOG_DEBUG("Reloaded MOTConfiguration from main configuration");
|
||||
MOTConfiguration& motCfg = GetGlobalConfiguration();
|
||||
MOTConfiguration &motCfg = GetGlobalConfiguration();
|
||||
motCfg.OnConfigChange();
|
||||
|
||||
// now the main configuration is fully loaded, so let's validate it
|
||||
|
|
@ -273,9 +424,22 @@ bool ConfigManager::InitLoad()
|
|||
return result;
|
||||
}
|
||||
|
||||
const ConfigTree* ConfigManager::GetConfigTree(const char* configName) const
|
||||
/*
|
||||
* 功能:获取指定配置名称的配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* configName:配置名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回一个指向配置树的指针,如果找不到指定名称的配置则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取指定配置名称的配置树。它会遍历所有配置加载器,查找名称匹配的配置树。
|
||||
* 如果找到匹配的配置树,将返回指向该配置树的指针,否则返回 nullptr。
|
||||
*/
|
||||
const ConfigTree *ConfigManager::GetConfigTree(const char *configName) const
|
||||
{
|
||||
const ConfigTree* result = nullptr;
|
||||
const ConfigTree *result = nullptr;
|
||||
for (uint32_t i = 0; i < m_configLoaderCount; ++i) {
|
||||
if (strcmp(m_configLoaders[i]->GetName(), configName) == 0) {
|
||||
result = m_configLoaders[i]->GetConfig();
|
||||
|
|
@ -284,12 +448,27 @@ const ConfigTree* ConfigManager::GetConfigTree(const char* configName) const
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigManager::Initialize(char** argv, int argc)
|
||||
/*
|
||||
* 功能:初始化配置管理器
|
||||
*
|
||||
* 参数列表:
|
||||
* argv:指向命令行参数的指针数组
|
||||
* argc:命令行参数的数量
|
||||
*
|
||||
* 返回值:
|
||||
* 如果初始化成功,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化配置管理器。它可以设置日志级别,添加命令行配置加载器等。
|
||||
* 首先,它检查环境变量 "MOT_DEBUG_CFG_LOAD" 是否设置为 "TRUE",如果是则将日志组件 "Configuration" 的日志级别设置为
|
||||
* DEBUG。 然后,如果传入了命令行参数,它会尝试添加命令行配置加载器,以解析这些参数。 如果初始化成功,返回
|
||||
* true;否则返回 false。
|
||||
*/
|
||||
bool ConfigManager::Initialize(char **argv, int argc)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
char* envvar = getenv("MOT_DEBUG_CFG_LOAD");
|
||||
char *envvar = getenv("MOT_DEBUG_CFG_LOAD");
|
||||
if (envvar && (strcmp(envvar, "TRUE") == 0)) {
|
||||
SetLogComponentLogLevel("Configuration", LogLevel::LL_DEBUG);
|
||||
}
|
||||
|
|
@ -298,42 +477,71 @@ bool ConfigManager::Initialize(char** argv, int argc)
|
|||
if (argv != nullptr && argc != 0) {
|
||||
result = AddCmdLineConfigLoader(argv, argc);
|
||||
if (!result) {
|
||||
MOT_REPORT_PANIC(MOT_ERROR_INTERNAL,
|
||||
"Configuration Manager Initialization",
|
||||
"Failed to create command line configuration loader");
|
||||
MOT_REPORT_PANIC(MOT_ERROR_INTERNAL, "Configuration Manager Initialization",
|
||||
"Failed to create command line configuration loader");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigManager::AddCmdLineConfigLoader(char** argv, int argc)
|
||||
/*
|
||||
* 功能:添加命令行配置加载器
|
||||
*
|
||||
* 参数列表:
|
||||
* argv:指向命令行参数的指针数组
|
||||
* argc:命令行参数的数量
|
||||
*
|
||||
* 返回值:
|
||||
* 如果添加成功,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于创建并添加一个命令行配置加载器。命令行配置加载器将解析给定的命令行参数以加载配置。
|
||||
* 首先,它尝试分配内存以创建 `CmdLineConfigLoader` 的实例。
|
||||
* 如果分配内存失败,将返回 false 并报告内存分配错误。
|
||||
* 否则,它尝试通过调用 `AddConfigLoader` 函数将加载器添加到配置管理器中。
|
||||
* 如果添加成功,返回 true;否则返回 false。
|
||||
*/
|
||||
bool ConfigManager::AddCmdLineConfigLoader(char **argv, int argc)
|
||||
{
|
||||
bool result = true;
|
||||
ConfigLoader* cmdLineCfgLoader = new (std::nothrow) CmdLineConfigLoader(argv, argc);
|
||||
ConfigLoader *cmdLineCfgLoader = new (std::nothrow) CmdLineConfigLoader(argv, argc);
|
||||
if (cmdLineCfgLoader == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate command line configuration loader");
|
||||
result = false;
|
||||
} else {
|
||||
result = AddConfigLoader(cmdLineCfgLoader);
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add command line configuration loader (duplicate?)");
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add command line configuration loader (duplicate?)");
|
||||
delete cmdLineCfgLoader;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigFileLoader* ConfigManager::CreateConfigFileLoader(const char* configFilePath, const char* name)
|
||||
/*
|
||||
* 功能:创建配置文件加载器
|
||||
*
|
||||
* 参数列表:
|
||||
* configFilePath:配置文件的路径
|
||||
* name:配置文件加载器的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建配置文件加载器,返回该加载器的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据配置文件的路径和名称创建一个配置文件加载器。首先,它检查文件路径中的文件后缀。
|
||||
* 如果后缀为 "conf",则创建一个 `PropsConfigFileLoader` 实例,该加载器用于解析 "conf" 格式的配置文件。
|
||||
* 否则,函数会报告无效的参数错误并返回 nullptr。
|
||||
* 如果成功创建加载器,它将返回加载器的指针;否则返回 nullptr。
|
||||
*/
|
||||
ConfigFileLoader *ConfigManager::CreateConfigFileLoader(const char *configFilePath, const char *name)
|
||||
{
|
||||
ConfigFileLoader* cfgFileLoader = nullptr;
|
||||
ConfigFileLoader *cfgFileLoader = nullptr;
|
||||
std::string cfgPath(configFilePath);
|
||||
std::string::size_type dotPos = cfgPath.find_last_of('.');
|
||||
if (dotPos == std::string::npos) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG,
|
||||
"Load Configuration",
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INVALID_ARG, "Load Configuration",
|
||||
"Unable to load configuration file. Format not specified and configuration file does not contain suffix: "
|
||||
"%s",
|
||||
cfgPath.c_str());
|
||||
|
|
@ -343,8 +551,8 @@ ConfigFileLoader* ConfigManager::CreateConfigFileLoader(const char* configFilePa
|
|||
if (suffix.compare("conf") == 0) {
|
||||
cfgFileLoader = new (std::nothrow) PropsConfigFileLoader(name, CFG_FILE_CONFIG_PRIORITY, configFilePath);
|
||||
} else {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_ARG,
|
||||
"Load Configuration",
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INVALID_ARG, "Load Configuration",
|
||||
"Unable to load configuration file: Format not specified and cannot be inferred from file suffix: %s",
|
||||
suffix.c_str());
|
||||
return nullptr;
|
||||
|
|
@ -358,24 +566,36 @@ ConfigFileLoader* ConfigManager::CreateConfigFileLoader(const char* configFilePa
|
|||
return cfgFileLoader;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重新加载配置
|
||||
*
|
||||
* 参数列表:
|
||||
* ignoreErrors:是否忽略配置加载过程中的错误,默认为 true
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功重新加载配置,返回 true;如果在加载过程中出现错误且不忽略错误,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重新加载配置。它首先清除配置树,然后循环遍历所有配置加载器,尝试从每个加载器中加载配置树。
|
||||
* 如果加载成功,将该配置树添加到层次化配置树中。如果加载失败且不忽略错误,函数将返回 false。
|
||||
* 最后,函数根据配置中的日志级别打印已加载的配置。
|
||||
*/
|
||||
bool ConfigManager::ReloadConfig(bool ignoreErrors /* = true */)
|
||||
{
|
||||
bool result = true;
|
||||
MOT_LOG_DEBUG("Reloading configuration");
|
||||
m_layeredConfigTree.Clear();
|
||||
for (uint32_t i = 0; i < m_configLoaderCount; ++i) {
|
||||
ConfigLoader* configLoader = m_configLoaders[i];
|
||||
ConfigTree* configTree = configLoader->Load();
|
||||
ConfigLoader *configLoader = m_configLoaders[i];
|
||||
ConfigTree *configTree = configLoader->Load();
|
||||
if (configTree != nullptr) {
|
||||
MOT_LOG_DEBUG("Adding configuration tree %p with priority %u from configuration loader %s",
|
||||
configTree,
|
||||
configTree->GetPriority(),
|
||||
configLoader->GetName());
|
||||
MOT_LOG_DEBUG("Adding configuration tree %p with priority %u from configuration loader %s", configTree,
|
||||
configTree->GetPriority(), configLoader->GetName());
|
||||
m_layeredConfigTree.AddConfigTree(configTree);
|
||||
} else {
|
||||
MOT_LOG_WARN("Failed to load configuration tree from configuration loader %s, configuration from this "
|
||||
"loader will be ignored. Please fix configuration and trigger reload.",
|
||||
configLoader->GetName());
|
||||
configLoader->GetName());
|
||||
if (!ignoreErrors) {
|
||||
result = false;
|
||||
}
|
||||
|
|
@ -392,6 +612,13 @@ bool ConfigManager::ReloadConfig(bool ignoreErrors /* = true */)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:通知配置更改
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于通知配置更改,它遍历所有配置更改监听器并调用它们的 OnConfigChange 函数。
|
||||
* 这个函数通常在配置发生更改时被调用,以通知它们配置已更改,可以执行相关的操作。
|
||||
*/
|
||||
void ConfigManager::NotifyConfigChange()
|
||||
{
|
||||
MOT_LOG_INFO("Propagating configuration changes");
|
||||
|
|
@ -399,4 +626,5 @@ void ConfigManager::NotifyConfigChange()
|
|||
m_listeners[i]->OnConfigChange();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -31,28 +31,39 @@ ConfigSection::ConfigSection() : ConfigItem(ConfigItemClass::CONFIG_ITEM_SECTION
|
|||
|
||||
ConfigSection::~ConfigSection()
|
||||
{
|
||||
// delete all direct values
|
||||
// 删除所有直接值(ConfigValue 对象)
|
||||
ConfigValueMap::iterator vitr = m_valueMap.begin();
|
||||
while (vitr != m_valueMap.end()) {
|
||||
delete vitr->second;
|
||||
delete vitr->second; // 释放 ConfigValue 对象的内存
|
||||
++vitr;
|
||||
}
|
||||
|
||||
// delete all direct arrays
|
||||
// 删除所有直接数组(ConfigArray 对象)
|
||||
ConfigArrayMap::iterator aitr = m_arrayMap.begin();
|
||||
while (aitr != m_arrayMap.end()) {
|
||||
delete aitr->second;
|
||||
delete aitr->second; // 释放 ConfigArray 对象的内存
|
||||
++aitr;
|
||||
}
|
||||
|
||||
// delete all sub-sections
|
||||
// 删除所有子配置部分(ConfigSection 对象)
|
||||
ConfigSectionMap::iterator sitr = m_sectionMap.begin();
|
||||
while (sitr != m_sectionMap.end()) {
|
||||
delete sitr->second;
|
||||
delete sitr->second; // 释放 ConfigSection 对象的内存
|
||||
++sitr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:要用于打印的日志级别
|
||||
* fullPrint:是否进行完整打印,默认为 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印配置节及其包含的值、数组和子节。
|
||||
* 首先,它打印缩进的节名称。如果 fullPrint 参数为 false,它将只打印节名称,否则还会打印节名称的地址。
|
||||
* 然后,它循环遍历配置节中的值、数组和子节,并分别打印它们。
|
||||
*/
|
||||
void ConfigSection::Print(LogLevel logLevel, bool fullPrint) const
|
||||
{
|
||||
// print indented section name
|
||||
|
|
@ -83,91 +94,165 @@ void ConfigSection::Print(LogLevel logLevel, bool fullPrint) const
|
|||
++itr3;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 功能:获取当前配置节中所有子节的名称
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionNames:输出参数,包含子节名称的字符串列表
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取子节名称,返回 true;如果在获取过程中出现错误,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取当前配置节中所有子节的名称,并将它们存储在 sectionNames 列表中。
|
||||
* 如果成功获取所有子节名称,函数返回 true。如果在添加子节名称到列表时遇到内存分配错误,
|
||||
* 函数将报告错误并返回 false。
|
||||
*/
|
||||
|
||||
bool ConfigSection::GetConfigSectionNames(mot_string_list& sectionNames) const
|
||||
bool ConfigSection::GetConfigSectionNames(mot_string_list §ionNames) const
|
||||
{
|
||||
bool result = true;
|
||||
ConfigSectionMap::const_iterator itr = m_sectionMap.begin();
|
||||
while (itr != m_sectionMap.end()) {
|
||||
result = sectionNames.push_back(itr->first);
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to get section names (%u retrieved up to failed point)",
|
||||
sectionNames.size());
|
||||
break;
|
||||
bool result = true; // 初始化返回值为 true
|
||||
ConfigSectionMap::const_iterator itr = m_sectionMap.begin(); // 获取子节的迭代器,并初始化为开始位置
|
||||
|
||||
while (itr != m_sectionMap.end()) { // 遍历子节映射
|
||||
result = sectionNames.push_back(itr->first); // 将子节名称添加到结果列表中
|
||||
if (!result) { // 如果添加失败,说明内存不足
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to get section names (%u retrieved up to failed point)",
|
||||
sectionNames.size()); // 报告内存分配失败
|
||||
break; // 退出循环
|
||||
}
|
||||
++itr;
|
||||
++itr; // 移动到下一个子节
|
||||
}
|
||||
return result;
|
||||
|
||||
return result; // 返回是否成功
|
||||
}
|
||||
|
||||
bool ConfigSection::GetConfigValueNames(mot_string_list& valueNames) const
|
||||
/*
|
||||
* 功能:获取当前配置节中所有值和数组的名称
|
||||
*
|
||||
* 参数列表:
|
||||
* valueNames:输出参数,包含值和数组名称的字符串列表
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取值和数组名称,返回 true;如果在获取过程中出现错误,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取当前配置节中所有值和数组的名称,并将它们存储在 valueNames 列表中。
|
||||
* 如果成功获取所有名称,函数返回 true。如果在添加名称到列表时遇到内存分配错误,
|
||||
* 函数将报告错误并返回 false。
|
||||
*/
|
||||
bool ConfigSection::GetConfigValueNames(mot_string_list &valueNames) const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// 遍历值的映射并添加名称到列表
|
||||
ConfigValueMap::const_iterator itr = m_valueMap.begin();
|
||||
while (itr != m_valueMap.end()) {
|
||||
if (!valueNames.push_back(itr->first)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to get value names (%u retrieved up to failed point)",
|
||||
valueNames.size());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to get value names (%u retrieved up to failed point)", valueNames.size());
|
||||
return false;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
|
||||
// 遍历数组的映射并添加名称到列表
|
||||
ConfigArrayMap::const_iterator itr2 = m_arrayMap.begin();
|
||||
while (itr2 != m_arrayMap.end()) {
|
||||
if (!valueNames.push_back(itr2->first)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to get value names (%u retrieved up to failed point)",
|
||||
valueNames.size());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to get value names (%u retrieved up to failed point)", valueNames.size());
|
||||
return false;
|
||||
}
|
||||
++itr2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigSection::AddConfigItem(ConfigItem* configItem, bool replaceIfExists /* = false */)
|
||||
/*
|
||||
* 功能:向当前配置节添加配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* configItem:要添加的配置项
|
||||
* replaceIfExists:如果为 true,则如果已存在同名的配置项,将替换它;如果为 false,则不替换,默认为 false。
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置项,返回 true;如果在添加过程中出现错误或配置项类别无效,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向当前配置节添加配置项,可以是配置节、配置值或配置数组。
|
||||
* 根据配置项的类别,函数会分派给不同的添加函数来处理。
|
||||
* 如果 replaceIfExists 为 true,如果已存在同名的配置项,将替换它;
|
||||
* 如果为 false,如果同名配置项已存在,函数将返回 false。
|
||||
*/
|
||||
bool ConfigSection::AddConfigItem(ConfigItem *configItem, bool replaceIfExists /* = false */)
|
||||
{
|
||||
bool result = false;
|
||||
// 根据配置项的类别分派到不同的添加函数
|
||||
switch (configItem->GetClass()) {
|
||||
case ConfigItemClass::CONFIG_ITEM_SECTION:
|
||||
result = AddConfigSection(static_cast<ConfigSection*>(configItem));
|
||||
result = AddConfigSection(static_cast<ConfigSection *>(configItem));
|
||||
break;
|
||||
|
||||
case ConfigItemClass::CONFIG_ITEM_VALUE:
|
||||
result = AddConfigValue(static_cast<ConfigValue*>(configItem), replaceIfExists);
|
||||
result = AddConfigValue(static_cast<ConfigValue *>(configItem), replaceIfExists);
|
||||
break;
|
||||
|
||||
case ConfigItemClass::CONFIG_ITEM_ARRAY:
|
||||
result = AddConfigArray(static_cast<ConfigArray*>(configItem));
|
||||
result = AddConfigArray(static_cast<ConfigArray *>(configItem));
|
||||
break;
|
||||
|
||||
default:
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Invalid configuration item class %d",
|
||||
(int)configItem->GetClass());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Invalid configuration item class %d",
|
||||
(int)configItem->GetClass());
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigSection::Merge(ConfigSection* configSection)
|
||||
/*
|
||||
* 功能:合并配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* configSection:要合并的配置节
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功合并配置节中的配置项,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于合并两个配置节的配置项,包括配置值和配置数组。
|
||||
* 它首先尝试合并配置值,然后合并配置数组。
|
||||
*/
|
||||
bool ConfigSection::Merge(ConfigSection *configSection)
|
||||
{
|
||||
// 调用合并配置值和配置数组的函数,两者都成功才返回 true
|
||||
return MergeValues(configSection) && MergeArrays(configSection);
|
||||
}
|
||||
|
||||
const ConfigItem* ConfigSection::GetConfigItem(const char* name) const
|
||||
/*
|
||||
* 功能:获取指定名称的配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要获取的配置项的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置项的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置节中检索具有指定名称的配置项。
|
||||
* 它首先在子节中查找,然后在配置值和配置数组中查找匹配项。
|
||||
*/
|
||||
const ConfigItem *ConfigSection::GetConfigItem(const char *name) const
|
||||
{
|
||||
const ConfigItem* result = nullptr;
|
||||
const ConfigItem *result = nullptr;
|
||||
|
||||
// 首先在子节中查找
|
||||
ConfigSectionMap::const_iterator itr = m_sectionMap.find(name);
|
||||
if (itr != m_sectionMap.end()) {
|
||||
result = itr->second;
|
||||
} else {
|
||||
// 如果在子节中没有找到匹配项,再在配置值和配置数组中查找
|
||||
ConfigValueMap::const_iterator itr2 = m_valueMap.find(name);
|
||||
if (itr2 != m_valueMap.end()) {
|
||||
result = itr2->second;
|
||||
|
|
@ -178,33 +263,80 @@ const ConfigItem* ConfigSection::GetConfigItem(const char* name) const
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ConfigSection* ConfigSection::GetConfigSection(const char* name) const
|
||||
/*
|
||||
* 功能:获取指定名称的配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要获取的配置节的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置节的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置节中检索具有指定名称的子配置节。
|
||||
* 它在当前配置节的子节映射中查找匹配项,如果找到则返回相应的指针,否则返回 nullptr。
|
||||
*/
|
||||
const ConfigSection *ConfigSection::GetConfigSection(const char *name) const
|
||||
{
|
||||
const ConfigSection* result = nullptr;
|
||||
const ConfigSection *result = nullptr;
|
||||
|
||||
// 在子节映射中查找匹配项
|
||||
ConfigSectionMap::const_iterator itr = m_sectionMap.find(name);
|
||||
if (itr != m_sectionMap.end()) {
|
||||
result = itr->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ConfigArray* ConfigSection::GetConfigArray(const char* name) const
|
||||
/*
|
||||
* 功能:获取指定名称的配置数组
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要获取的配置数组的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置数组的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置节中检索具有指定名称的子配置数组。
|
||||
* 它在当前配置节的子数组映射中查找匹配项,如果找到则返回相应的指针,否则返回 nullptr。
|
||||
*/
|
||||
const ConfigArray *ConfigSection::GetConfigArray(const char *name) const
|
||||
{
|
||||
const ConfigArray* result = nullptr;
|
||||
const ConfigArray *result = nullptr;
|
||||
|
||||
// 在子数组映射中查找匹配项
|
||||
ConfigArrayMap::const_iterator itr = m_arrayMap.find(name);
|
||||
if (itr != m_arrayMap.end()) {
|
||||
result = itr->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigItem* ConfigSection::ModifyConfigItem(const char* name)
|
||||
/*
|
||||
* 功能:修改指定名称的配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要修改的配置项的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置项的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于修改配置节中具有指定名称的子配置项。
|
||||
* 它在当前配置节的子配置项映射中查找匹配项,如果找到则返回相应的指针,否则返回 nullptr。
|
||||
*/
|
||||
ConfigItem *ConfigSection::ModifyConfigItem(const char *name)
|
||||
{
|
||||
ConfigItem* result = nullptr;
|
||||
ConfigItem *result = nullptr;
|
||||
|
||||
// 在子配置项映射中查找匹配项
|
||||
ConfigSectionMap::iterator itr = m_sectionMap.find(name);
|
||||
if (itr != m_sectionMap.end()) {
|
||||
result = itr->second;
|
||||
|
|
@ -223,166 +355,311 @@ ConfigItem* ConfigSection::ModifyConfigItem(const char* name)
|
|||
return result;
|
||||
}
|
||||
|
||||
ConfigSection* ConfigSection::ModifyConfigSection(const char* name)
|
||||
/*
|
||||
* 功能:修改指定名称的配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要修改的配置节的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置节的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于修改当前配置节中具有指定名称的子配置节。
|
||||
* 它在当前配置节的子配置节映射中查找匹配项,如果找到则返回相应的指针,否则返回 nullptr。
|
||||
*/
|
||||
ConfigSection *ConfigSection::ModifyConfigSection(const char *name)
|
||||
{
|
||||
ConfigSection* result = nullptr;
|
||||
ConfigSection *result = nullptr;
|
||||
|
||||
// 在子配置节映射中查找匹配项
|
||||
ConfigSectionMap::iterator itr = m_sectionMap.find(name);
|
||||
if (itr != m_sectionMap.end()) {
|
||||
result = itr->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigArray* ConfigSection::ModifyConfigArray(const char* name)
|
||||
/*
|
||||
* 功能:修改指定名称的配置数组
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要修改的配置数组的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回指向匹配名称的配置数组的指针,如果没有找到匹配项则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于修改当前配置节中具有指定名称的子配置数组。
|
||||
* 它在当前配置节的子配置数组映射中查找匹配项,如果找到则返回相应的指针,否则返回 nullptr。
|
||||
*/
|
||||
ConfigArray *ConfigSection::ModifyConfigArray(const char *name)
|
||||
{
|
||||
ConfigArray* result = nullptr;
|
||||
ConfigArray *result = nullptr;
|
||||
|
||||
// 在子配置数组映射中查找匹配项
|
||||
ConfigArrayMap::iterator itr = m_arrayMap.find(name);
|
||||
if (itr != m_arrayMap.end()) {
|
||||
result = itr->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConfigSection::ForEach(ConfigItemVisitor& visitor) const
|
||||
/*
|
||||
* 功能:对当前配置节及其所有子项应用访问者对象的操作
|
||||
*
|
||||
* 参数列表:
|
||||
* visitor:用于访问配置项的访问者对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于递归地遍历当前配置节及其所有子项,并对每个配置项应用访问者对象的操作。
|
||||
* 访问者对象必须实现 `OnConfigItem` 方法以处理配置项。
|
||||
* 对每个值、数组和子配置节,此函数还会分别调用适当的 `ForEachValue`、`ForEachArray` 和 `ForEachSection` 方法。
|
||||
*/
|
||||
void ConfigSection::ForEach(ConfigItemVisitor &visitor) const
|
||||
{
|
||||
// 对当前配置节应用访问者对象的操作
|
||||
visitor.OnConfigItem(this);
|
||||
|
||||
// 对当前配置节的值应用访问者对象的操作
|
||||
ForEachValue(visitor);
|
||||
|
||||
// 对当前配置节的数组应用访问者对象的操作
|
||||
ForEachArray(visitor);
|
||||
|
||||
// 对当前配置节的子配置节应用访问者对象的操作
|
||||
ForEachSection(visitor);
|
||||
}
|
||||
|
||||
bool ConfigSection::AddConfigSection(ConfigSection* configSection)
|
||||
/*
|
||||
* 功能:向当前配置节添加子配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* configSection:要添加的子配置节对象
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加子配置节,返回 true;如果子配置节已存在或达到资源限制,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向当前配置节添加子配置节。如果成功添加,将返回 true;如果子配置节已存在,将记录错误并返回 false。
|
||||
* 如果达到了资源限制,也会记录错误并返回 false。
|
||||
*/
|
||||
bool ConfigSection::AddConfigSection(ConfigSection *configSection)
|
||||
{
|
||||
// 尝试将子配置节添加到当前配置节的映射中
|
||||
ConfigSectionMap::pairis pairis =
|
||||
m_sectionMap.insert(ConfigSectionMap::value_type(configSection->GetName(), configSection));
|
||||
|
||||
// 处理插入结果
|
||||
if (pairis.second == INSERT_EXISTS) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Cannot add configuration section %s to section %s: section already exists",
|
||||
configSection->GetName(),
|
||||
GetName());
|
||||
// 如果子配置节已经存在,记录错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Cannot add configuration section %s to section %s: section already exists",
|
||||
configSection->GetName(), GetName());
|
||||
} else if (pairis.second == INSERT_FAILED) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add configuration section %s to section %s: reached resource limit",
|
||||
configSection->GetName(),
|
||||
GetName());
|
||||
// 如果插入失败,记录错误并表示已达到资源限制
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add configuration section %s to section %s: reached resource limit",
|
||||
configSection->GetName(), GetName());
|
||||
}
|
||||
|
||||
// 返回是否成功添加子配置节
|
||||
return (pairis.second == INSERT_SUCCESS);
|
||||
}
|
||||
|
||||
bool ConfigSection::AddConfigValue(ConfigValue* configValue, bool replaceIfExists)
|
||||
/*
|
||||
* 功能:向当前配置节添加配置值
|
||||
*
|
||||
* 参数列表:
|
||||
* configValue:要添加的配置值对象
|
||||
* replaceIfExists:如果配置值已经存在,是否替换它(默认为 false)
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置值,返回 true;如果配置值已存在但未替换,或者达到资源限制,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向当前配置节添加配置值。如果成功添加,将返回 true;如果配置值已存在但未替换且 `replaceIfExists`
|
||||
* 参数为 false,将记录错误并返回 false。如果达到了资源限制,也会记录错误并返回 false。
|
||||
* 如果 `replaceIfExists` 参数为 true,则会替换已存在的配置值。
|
||||
*/
|
||||
bool ConfigSection::AddConfigValue(ConfigValue *configValue, bool replaceIfExists)
|
||||
{
|
||||
// 尝试将配置值添加到当前配置节的映射中
|
||||
ConfigValueMap::pairis pairis = m_valueMap.insert(ConfigValueMap::value_type(configValue->GetName(), configValue));
|
||||
|
||||
// 处理插入结果
|
||||
if (pairis.second == INSERT_EXISTS) {
|
||||
if (replaceIfExists) {
|
||||
ConfigValue* prevValue = pairis.first->second;
|
||||
// 如果配置值已经存在,并且允许替换,则替换它
|
||||
ConfigValue *prevValue = pairis.first->second;
|
||||
pairis.first->second = configValue;
|
||||
delete prevValue;
|
||||
pairis.second = INSERT_SUCCESS;
|
||||
} else {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Cannot add configuration value %s to section %s: value already exists",
|
||||
configValue->GetName(),
|
||||
GetName());
|
||||
// 如果配置值已经存在但不替换,记录错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Cannot add configuration value %s to section %s: value already exists",
|
||||
configValue->GetName(), GetName());
|
||||
}
|
||||
} else if (pairis.second == INSERT_FAILED) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add configuration value %s to section %s: reached resource limit",
|
||||
configValue->GetName(),
|
||||
GetName());
|
||||
// 如果插入失败,记录错误并表示已达到资源限制
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add configuration value %s to section %s: reached resource limit",
|
||||
configValue->GetName(), GetName());
|
||||
}
|
||||
|
||||
// 返回是否成功添加配置值
|
||||
return (pairis.second == INSERT_SUCCESS);
|
||||
}
|
||||
|
||||
bool ConfigSection::AddConfigArray(ConfigArray* configArray)
|
||||
/*
|
||||
* 功能:向当前配置节添加配置数组
|
||||
*
|
||||
* 参数列表:
|
||||
* configArray:要添加的配置数组对象
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置数组,返回 true;如果配置数组已经存在或者达到资源限制,返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于向当前配置节添加配置数组。如果成功添加,将返回 true;如果配置数组已经存在,将记录错误并返回 false。
|
||||
* 如果达到了资源限制,也会记录错误并返回 false。
|
||||
*/
|
||||
bool ConfigSection::AddConfigArray(ConfigArray *configArray)
|
||||
{
|
||||
// 尝试将配置数组添加到当前配置节的映射中
|
||||
ConfigArrayMap::pairis pairis = m_arrayMap.insert(ConfigArrayMap::value_type(configArray->GetName(), configArray));
|
||||
|
||||
// 处理插入结果
|
||||
if (pairis.second == INSERT_EXISTS) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Cannot add configuration array %s to section %s: array already exists",
|
||||
configArray->GetName(),
|
||||
GetName());
|
||||
// 如果配置数组已经存在,记录错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Cannot add configuration array %s to section %s: array already exists",
|
||||
configArray->GetName(), GetName());
|
||||
} else if (pairis.second == INSERT_FAILED) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add configuration array %s to section %s: reached resource limit",
|
||||
configArray->GetName(),
|
||||
GetName());
|
||||
// 如果插入失败,记录错误并表示已达到资源限制
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add configuration array %s to section %s: reached resource limit",
|
||||
configArray->GetName(), GetName());
|
||||
}
|
||||
|
||||
// 返回是否成功添加配置数组
|
||||
return (pairis.second == INSERT_SUCCESS);
|
||||
}
|
||||
|
||||
void ConfigSection::ForEachValue(ConfigItemVisitor& visitor) const
|
||||
/*
|
||||
* 功能:遍历当前配置节中的配置值并对其应用访问者
|
||||
*
|
||||
* 参数列表:
|
||||
* visitor:一个配置项访问者对象,用于对配置值应用访问者操作
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于遍历当前配置节中的配置值,并对每个配置值应用配置项访问者对象中定义的操作。
|
||||
*/
|
||||
void ConfigSection::ForEachValue(ConfigItemVisitor &visitor) const
|
||||
{
|
||||
// 遍历配置值映射
|
||||
ConfigValueMap::const_iterator itr = m_valueMap.begin();
|
||||
while (itr != m_valueMap.end()) {
|
||||
// 对每个配置值应用访问者操作
|
||||
visitor.OnConfigItem(itr->second);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigSection::ForEachArray(ConfigItemVisitor& visitor) const
|
||||
/*
|
||||
* 功能:遍历当前配置节中的配置数组并对其应用访问者
|
||||
*
|
||||
* 参数列表:
|
||||
* visitor:一个配置项访问者对象,用于对配置数组应用访问者操作
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于遍历当前配置节中的配置数组,并对每个配置数组应用配置项访问者对象中定义的操作。
|
||||
*/
|
||||
void ConfigSection::ForEachArray(ConfigItemVisitor &visitor) const
|
||||
{
|
||||
// 遍历配置数组映射
|
||||
ConfigArrayMap::const_iterator itr = m_arrayMap.begin();
|
||||
while (itr != m_arrayMap.end()) {
|
||||
static_cast<const ConfigArray*>(itr->second)->ForEach(visitor);
|
||||
// 对每个配置数组应用访问者操作
|
||||
static_cast<const ConfigArray *>(itr->second)->ForEach(visitor);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigSection::ForEachSection(ConfigItemVisitor& visitor) const
|
||||
/*
|
||||
* 功能:遍历当前配置节中的子配置节并对其应用访问者
|
||||
*
|
||||
* 参数列表:
|
||||
* visitor:一个配置项访问者对象,用于对子配置节应用访问者操作
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于遍历当前配置节中的子配置节,并对每个子配置节应用配置项访问者对象中定义的操作。
|
||||
*/
|
||||
void ConfigSection::ForEachSection(ConfigItemVisitor &visitor) const
|
||||
{
|
||||
// 遍历子配置节映射
|
||||
ConfigSectionMap::const_iterator itr = m_sectionMap.begin();
|
||||
while (itr != m_sectionMap.end()) {
|
||||
static_cast<const ConfigSection*>(itr->second)->ForEach(visitor);
|
||||
// 对每个子配置节应用访问者操作
|
||||
static_cast<const ConfigSection *>(itr->second)->ForEach(visitor);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigSection::MergeValues(ConfigSection* configSection)
|
||||
/*
|
||||
* 功能:将另一个配置节的值合并到当前配置节中
|
||||
*
|
||||
* 参数列表:
|
||||
* configSection:包含要合并的值的另一个配置节
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功合并值,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个配置节的值合并到当前配置节中。如果合并的值成功,将返回 true;否则,将返回 false。
|
||||
* 如果发生错误,函数会尝试报告错误并回滚操作。
|
||||
*/
|
||||
bool ConfigSection::MergeValues(ConfigSection *configSection)
|
||||
{
|
||||
// 遍历要合并的配置节中的值映射
|
||||
ConfigValueMap::iterator itr = configSection->m_valueMap.begin();
|
||||
while (itr != configSection->m_valueMap.end()) {
|
||||
ConfigValue* configValue = itr->second;
|
||||
ConfigValue *configValue = itr->second;
|
||||
// 将值插入到当前配置节的值映射中
|
||||
ConfigValueMap::pairis pairis =
|
||||
m_valueMap.insert(ConfigValueMap::value_type(configValue->GetName(), configValue));
|
||||
if (pairis.second == INSERT_SUCCESS) {
|
||||
MOT_LOG_DEBUG("Inserted new value: %s", configValue->GetName());
|
||||
} else {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge value %s into section %s",
|
||||
configValue->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并值失败的严重错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to merge value %s into section %s",
|
||||
configValue->GetName(), GetFullPathName());
|
||||
return false;
|
||||
}
|
||||
// not inserted, so replace existing value
|
||||
ConfigValueMap::iterator& itr2 = pairis.first;
|
||||
ConfigValue* oldConfigValue = itr2->second;
|
||||
// 未插入,因此替换现有值
|
||||
ConfigValueMap::iterator &itr2 = pairis.first;
|
||||
ConfigValue *oldConfigValue = itr2->second;
|
||||
MOT_LOG_DEBUG(" *** --> Deleting merged section value %s", oldConfigValue->GetFullPathName());
|
||||
oldConfigValue->Print(LogLevel::LL_DEBUG, true);
|
||||
m_valueMap.erase(itr2);
|
||||
m_valueMap.erase(itr2); // 删除旧值
|
||||
MOT_LOG_DEBUG(" *** --> Delete merged section value done");
|
||||
delete oldConfigValue;
|
||||
delete oldConfigValue; // 删除旧值的内存
|
||||
if (!m_valueMap.insert(ConfigValueMap::value_type(configValue->GetName(), configValue)).second) {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge value %s into section %s (severe error)",
|
||||
configValue->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并值失败的严重错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to merge value %s into section %s (severe error)", configValue->GetName(),
|
||||
GetFullPathName());
|
||||
return false;
|
||||
}
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge value %s into section %s (unexpected duplicate)",
|
||||
configValue->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并值失败的错误,这是一个不应该发生的意外情况
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to merge value %s into section %s (unexpected duplicate)",
|
||||
configValue->GetName(), GetFullPathName());
|
||||
MOT_ASSERT(false);
|
||||
return false;
|
||||
} else {
|
||||
|
|
@ -392,51 +669,63 @@ bool ConfigSection::MergeValues(ConfigSection* configSection)
|
|||
}
|
||||
++itr;
|
||||
}
|
||||
configSection->m_valueMap.clear(); // required to make sure items are not deleted in destructor
|
||||
configSection->m_valueMap.clear(); // 清空要合并的配置节中的值映射,确保不会在析构函数中删除这些项
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigSection::MergeArrays(ConfigSection* configSection)
|
||||
/*
|
||||
* 功能:将另一个配置节的数组合并到当前配置节中
|
||||
*
|
||||
* 参数列表:
|
||||
* configSection:包含要合并的数组的另一个配置节
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功合并数组,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个配置节的数组合并到当前配置节中。如果合并的数组成功,将返回 true;否则,将返回 false。
|
||||
* 如果发生错误,函数会尝试报告错误并回滚操作。
|
||||
*/
|
||||
bool ConfigSection::MergeArrays(ConfigSection *configSection)
|
||||
{
|
||||
// 遍历要合并的配置节中的数组映射
|
||||
ConfigArrayMap::iterator itr = configSection->m_arrayMap.begin();
|
||||
while (itr != configSection->m_arrayMap.end()) {
|
||||
ConfigArray* configArray = itr->second;
|
||||
ConfigArray *configArray = itr->second;
|
||||
// 将数组插入到当前配置节的数组映射中
|
||||
ConfigArrayMap::pairis pairis =
|
||||
m_arrayMap.insert(ConfigArrayMap::value_type(configArray->GetName(), configArray));
|
||||
if (pairis.second != INSERT_SUCCESS) {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge array %s into section %s",
|
||||
configArray->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并数组失败的严重错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to merge array %s into section %s",
|
||||
configArray->GetName(), GetFullPathName());
|
||||
return false;
|
||||
}
|
||||
// not inserted, so replace existing value
|
||||
ConfigArrayMap::iterator& itr2 = pairis.first;
|
||||
// 未插入,因此替换现有数组
|
||||
ConfigArrayMap::iterator &itr2 = pairis.first;
|
||||
delete itr2->second;
|
||||
m_arrayMap.erase(itr2);
|
||||
if (!m_arrayMap.insert(ConfigArrayMap::value_type(configArray->GetName(), configArray)).second) {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge array %s into section %s (severe error)",
|
||||
configArray->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并数组失败的严重错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to merge array %s into section %s (severe error)", configArray->GetName(),
|
||||
GetFullPathName());
|
||||
return false;
|
||||
}
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge array %s into section %s (unexpected duplicate)",
|
||||
configArray->GetName(),
|
||||
GetFullPathName());
|
||||
// 报告合并数组失败的错误,这是一个不应该发生的意外情况
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to merge array %s into section %s (unexpected duplicate)",
|
||||
configArray->GetName(), GetFullPathName());
|
||||
MOT_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
configSection->m_arrayMap.clear(); // required to make sure items are not deleted in destructor
|
||||
configSection->m_arrayMap.clear(); // 清空要合并的配置节中的数组映射,确保不会在析构函数中删除这些项
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -32,53 +32,67 @@ IMPLEMENT_CLASS_LOGGER(ConfigTree, Configuration)
|
|||
|
||||
/** @define Limit the depth of a section. */
|
||||
#define MAX_SECTION_DEPTH 10
|
||||
|
||||
bool ConfigTree::ConsolidateParsedSections(mot_list<ConfigSection*>& parsedSections, ConfigSectionMap& sectionMap)
|
||||
/*
|
||||
* 功能:将解析的配置节合并到配置树中
|
||||
*
|
||||
* 参数列表:
|
||||
* parsedSections:包含解析的配置节的链表
|
||||
* sectionMap:包含配置树中已有配置节的映射
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功合并配置节,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将解析的配置节合并到配置树中。解析的配置节包含在链表 `parsedSections` 中,而已有配置节映射存储在
|
||||
* `sectionMap` 中。 函数首先将根配置节与解析的配置节合并,然后将其它配置节添加到映射或者与已有配置节合并。
|
||||
* 如果合并失败或者在合并过程中发生错误,函数将尝试报告错误并清理资源。
|
||||
*/
|
||||
bool ConfigTree::ConsolidateParsedSections(mot_list<ConfigSection *> &parsedSections, ConfigSectionMap §ionMap)
|
||||
{
|
||||
// consolidate all parsed section into a map:
|
||||
// 1. new sections are added to map and removed from the list (need to delete section from map in case of error)
|
||||
// 2. duplicate sections are merged and deleted (no need to delete merged section in case of error)
|
||||
// 3. iteration stops on first error, and all sections in section list and map are deleted
|
||||
// 4. on successful execution the section list should be empty
|
||||
// 初始化结果标志为 true
|
||||
bool result = true;
|
||||
mot_list<ConfigSection*>::iterator listItr = parsedSections.begin();
|
||||
// 遍历解析的配置节链表
|
||||
mot_list<ConfigSection *>::iterator listItr = parsedSections.begin();
|
||||
while (listItr != parsedSections.end()) {
|
||||
ConfigSection* section = *listItr;
|
||||
|
||||
ConfigSection *section = *listItr;
|
||||
// special case: root section
|
||||
if (section->GetDepth() == 0) {
|
||||
MOT_LOG_DEBUG("Merging root section");
|
||||
// 合并根配置节
|
||||
if (!m_rootSection.Merge(section)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Malformed configuration file: failed to merge root section");
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Malformed configuration file: failed to merge root section");
|
||||
result = false;
|
||||
break; // go to cleanup after error (currently iterated section will be cleaned up from list)
|
||||
}
|
||||
// cleanup merged section now, even if we fail later (because if we don't fail, this is a memory leak)
|
||||
delete section;
|
||||
} else {
|
||||
// 查找在映射中是否存在当前配置节
|
||||
ConfigSectionMap::iterator mapItr = sectionMap.find(section->GetFullPathName());
|
||||
if (mapItr == sectionMap.end()) {
|
||||
// 插入新配置节到映射
|
||||
// insert new section
|
||||
MOT_LOG_DEBUG("Adding section: %s (name: %s)", section->GetFullPathName(), section->GetName());
|
||||
if (!sectionMap.insert(ConfigSectionMap::value_type(section->GetFullPathName(), section)).second) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to build configuration tree, cannot add section %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to build configuration tree, cannot add section %s",
|
||||
section->GetFullPathName());
|
||||
result = false;
|
||||
break; // go to cleanup after error (currently iterated section will be cleaned up from list)
|
||||
}
|
||||
} else {
|
||||
// merge into existing section
|
||||
ConfigSection* existingSection = mapItr->second;
|
||||
ConfigSection *existingSection = mapItr->second;
|
||||
MOT_LOG_DEBUG("Merging section: %s (name: %s)", section->GetFullPathName(), section->GetName());
|
||||
if (!existingSection->Merge(section)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge section %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to merge section %s",
|
||||
section->GetFullPathName());
|
||||
result = false;
|
||||
break; // go to cleanup after error (currently iterated section will be cleaned up from list)
|
||||
}
|
||||
|
|
@ -98,14 +112,14 @@ bool ConfigTree::ConsolidateParsedSections(mot_list<ConfigSection*>& parsedSecti
|
|||
// cleanup parsed sections and section map
|
||||
listItr = parsedSections.begin();
|
||||
while (listItr != parsedSections.end()) {
|
||||
ConfigSection* section = *listItr;
|
||||
ConfigSection *section = *listItr;
|
||||
delete section;
|
||||
++listItr;
|
||||
}
|
||||
parsedSections.clear();
|
||||
ConfigSectionMap::iterator mapItr = sectionMap.begin();
|
||||
while (mapItr != sectionMap.end()) {
|
||||
ConfigSection* section = mapItr->second;
|
||||
ConfigSection *section = mapItr->second;
|
||||
delete section;
|
||||
++mapItr;
|
||||
}
|
||||
|
|
@ -113,8 +127,22 @@ bool ConfigTree::ConsolidateParsedSections(mot_list<ConfigSection*>& parsedSecti
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap& sectionMap)
|
||||
/*
|
||||
* 功能:链接合并后的配置节映射
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionMap:包含合并后的配置节映射
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功链接配置节,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于链接合并后的配置节映射。合并后的配置节映射包含在 `sectionMap` 中。
|
||||
* 函数遍历映射中的每个配置节,找到或创建其父配置节,然后将子配置节添加到父配置节中或与已有的子配置节合并。
|
||||
* 如果链接失败或在链接过程中发生错误,函数将尝试报告错误并清理资源。
|
||||
* 在成功执行时,配置节映射应为空。
|
||||
*/
|
||||
bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap §ionMap)
|
||||
{
|
||||
// link consolidated section map. for each iterated section:
|
||||
// 1. find or create parent section
|
||||
|
|
@ -122,21 +150,21 @@ bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap& sectionMap)
|
|||
// 3. duplicate sub-sections are merged and deleted (no need to delete merged section in case of error)
|
||||
// 4. iteration stops on first error, and all sections in section map are deleted
|
||||
// 5. on successful execution the section map should be empty
|
||||
// 初始化结果标志为 true
|
||||
bool result = true;
|
||||
MOT_LOG_DEBUG("Linking sections");
|
||||
// 遍历配置节映射
|
||||
ConfigSectionMap::iterator mapItr = sectionMap.begin();
|
||||
while (mapItr != sectionMap.end()) {
|
||||
ConfigSection* section = mapItr->second;
|
||||
ConfigSection *section = mapItr->second;
|
||||
MOT_LOG_DEBUG("Linking section %s with depth %u", section->GetFullPathName(), section->GetDepth());
|
||||
bool created = false;
|
||||
|
||||
// get the parent or create a new one (recursively)
|
||||
ConfigSection* parent = GetOrCreateParent(section, 0, created);
|
||||
ConfigSection *parent = GetOrCreateParent(section, 0, created);
|
||||
if (parent == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to get or create parent of section %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to get or create parent of section %s",
|
||||
section->GetFullPathName());
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -144,21 +172,16 @@ bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap& sectionMap)
|
|||
// add or merge the section into its parent
|
||||
if (!parent->ContainsConfigSection(section->GetName())) {
|
||||
if (!parent->AddConfigItem(section)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add section %s to new parent %s",
|
||||
section->GetFullPathName(),
|
||||
parent->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to add section %s to new parent %s",
|
||||
section->GetFullPathName(), parent->GetFullPathName());
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ConfigSection* existingSection = (ConfigSection*)parent->GetConfigSection(section->GetName());
|
||||
ConfigSection *existingSection = (ConfigSection *)parent->GetConfigSection(section->GetName());
|
||||
if (!existingSection->Merge(section)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to merge existing section %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to merge existing section %s",
|
||||
section->GetFullPathName());
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -176,7 +199,7 @@ bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap& sectionMap)
|
|||
if (!result) {
|
||||
mapItr = sectionMap.begin();
|
||||
while (mapItr != sectionMap.end()) {
|
||||
ConfigSection* section = mapItr->second;
|
||||
ConfigSection *section = mapItr->second;
|
||||
delete section;
|
||||
++mapItr;
|
||||
}
|
||||
|
|
@ -184,18 +207,33 @@ bool ConfigTree::LinkConsolidatedSectionMap(ConfigSectionMap& sectionMap)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigTree::Build(mot_list<ConfigSection*>& parsedSections)
|
||||
/*
|
||||
* 功能:构建配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* parsedSections:包含解析后的配置节的链表
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功构建配置树,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于构建配置树。它首先将所有配置节按照完整路径名插入映射中。
|
||||
* 然后,它尝试链接合并后的配置节映射,将子配置节添加到父配置节中或与已有的子配置节合并。
|
||||
* 如果构建失败,函数将尝试报告错误,并返回 false。
|
||||
*/
|
||||
bool ConfigTree::Build(mot_list<ConfigSection *> &parsedSections)
|
||||
{
|
||||
bool result = true;
|
||||
MOT_LOG_DEBUG("Building configuration tree");
|
||||
|
||||
// insert all section to map according to full path name
|
||||
// 将所有配置节按照完整路径名插入映射中
|
||||
ConfigSectionMap sectionMap;
|
||||
if (!ConsolidateParsedSections(parsedSections, sectionMap)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to consolidate parsed section list");
|
||||
result = false;
|
||||
} else {
|
||||
// 尝试链接合并后的配置节映射
|
||||
if (!LinkConsolidatedSectionMap(sectionMap)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to build configuration tree");
|
||||
result = false;
|
||||
|
|
@ -211,25 +249,36 @@ bool ConfigTree::Build(mot_list<ConfigSection*>& parsedSections)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const ConfigItem* ConfigTree::GetConfigItem(const char* fullPathName) const
|
||||
/*
|
||||
* 功能:根据完整路径名获取配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* fullPathName:配置项的完整路径名
|
||||
*
|
||||
* 返回值:
|
||||
* 如果找到指定路径的配置项,返回指向该配置项的指针;如果未找到,返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从配置树中获取具有给定完整路径名的配置项。它首先将路径名分割为各个组件,
|
||||
* 然后逐级遍历配置树以查找指定的配置项。如果找到配置项,函数将返回指向该配置项的指针;
|
||||
* 如果未找到,将返回 nullptr。
|
||||
*/
|
||||
const ConfigItem *ConfigTree::GetConfigItem(const char *fullPathName) const
|
||||
{
|
||||
MOT_LOG_DEBUG("Getting from tree %s config item %s: starting", GetSource(), fullPathName);
|
||||
const ConfigItem* result = nullptr;
|
||||
const ConfigItem *result = nullptr;
|
||||
|
||||
// break path name to components
|
||||
mot_string_list pathComponents;
|
||||
if (!ConfigFileParser::Split(fullPathName, ConfigItem::PATH_SEP, pathComponents)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to split configuration path %s into components",
|
||||
fullPathName);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to split configuration path %s into components", fullPathName);
|
||||
return nullptr;
|
||||
}
|
||||
MOT_LOG_DEBUG("Getting from tree %s config item %s: finished split", GetSource(), fullPathName);
|
||||
|
||||
// dig into the tree
|
||||
const ConfigSection* currentSection = &m_rootSection;
|
||||
const ConfigSection *currentSection = &m_rootSection;
|
||||
mot_string_list::iterator itr = pathComponents.begin();
|
||||
while (itr != pathComponents.end() && currentSection) {
|
||||
MOT_LOG_DEBUG("Getting from section %s config item %s", currentSection->GetFullPathName(), itr->c_str());
|
||||
|
|
@ -238,7 +287,7 @@ const ConfigItem* ConfigTree::GetConfigItem(const char* fullPathName) const
|
|||
break;
|
||||
}
|
||||
if (result->GetClass() == ConfigItemClass::CONFIG_ITEM_SECTION) {
|
||||
currentSection = static_cast<const ConfigSection*>(result);
|
||||
currentSection = static_cast<const ConfigSection *>(result);
|
||||
} else {
|
||||
currentSection = nullptr;
|
||||
}
|
||||
|
|
@ -251,38 +300,50 @@ const ConfigItem* ConfigTree::GetConfigItem(const char* fullPathName) const
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigSection* ConfigTree::GetOrCreateParent(ConfigSection* section, int depth, bool& created)
|
||||
/*
|
||||
* 功能:获取或创建配置项的父级部分
|
||||
*
|
||||
* 参数列表:
|
||||
* section:要获取或创建父级部分的配置项
|
||||
* depth:递归深度,用于防止无限递归调用
|
||||
* created:用于指示是否已创建新的父级部分
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取或创建父级部分,则返回指向父级部分的指针;如果出现错误,则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取或创建配置项的父级部分。它可以递归地创建父级部分,如果父级部分不存在的话。
|
||||
* 如果递归深度超过了最大允许值,函数将返回 nullptr 并报告错误。
|
||||
* 如果成功获取或创建父级部分,created 参数将指示是否已创建了新的父级部分。
|
||||
*/
|
||||
ConfigSection *ConfigTree::GetOrCreateParent(ConfigSection *section, int depth, bool &created)
|
||||
{
|
||||
MOT_LOG_DEBUG("Building recursive parent for section: %s", section->GetFullPathName());
|
||||
|
||||
// guard against endless recurring calls
|
||||
if (depth >= MAX_SECTION_DEPTH) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_STATE,
|
||||
"Load Configuration"
|
||||
"Failed to get or create parent of section %s: section depth exceeds maximum allowed (%u)",
|
||||
section->GetFullPathName(),
|
||||
(unsigned)MAX_SECTION_DEPTH);
|
||||
"Load Configuration"
|
||||
"Failed to get or create parent of section %s: section depth exceeds maximum allowed (%u)",
|
||||
section->GetFullPathName(), (unsigned)MAX_SECTION_DEPTH);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// get parent or create it (recursively)
|
||||
created = false;
|
||||
ConfigSection* parent = nullptr;
|
||||
ConfigSection *parent = nullptr;
|
||||
if (section->GetDepth() == 1) {
|
||||
MOT_LOG_DEBUG("Found root parent of section %s", section->GetFullPathName());
|
||||
parent = &m_rootSection;
|
||||
} else {
|
||||
parent = (ConfigSection*)GetConfigSection(section->GetPath());
|
||||
parent = (ConfigSection *)GetConfigSection(section->GetPath());
|
||||
}
|
||||
if (parent == nullptr) {
|
||||
MOT_LOG_DEBUG("Parent of section %s not found, creating", section->GetFullPathName());
|
||||
parent = CreateParent(section, depth);
|
||||
if (parent == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to create section path: %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to create section path: %s",
|
||||
section->GetFullPathName());
|
||||
return nullptr;
|
||||
}
|
||||
created = true;
|
||||
|
|
@ -293,12 +354,9 @@ ConfigSection* ConfigTree::GetOrCreateParent(ConfigSection* section, int depth,
|
|||
if (depth > 0) {
|
||||
MOT_LOG_TRACE("Linking section %s to its parent %s", section->GetFullPathName(), parent->GetFullPathName());
|
||||
if (!parent->AddConfigItem(section)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add section %s to parent %s (call depth: %d)",
|
||||
section->GetFullPathName(),
|
||||
parent->GetFullPathName(),
|
||||
depth);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add section %s to parent %s (call depth: %d)", section->GetFullPathName(),
|
||||
parent->GetFullPathName(), depth);
|
||||
// if the parent was created, then we should not clean it up, since it is already linked to the tree
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -306,10 +364,23 @@ ConfigSection* ConfigTree::GetOrCreateParent(ConfigSection* section, int depth,
|
|||
|
||||
return parent;
|
||||
}
|
||||
|
||||
ConfigSection* ConfigTree::CreateParent(ConfigSection* section, int depth)
|
||||
/*
|
||||
* 功能:创建配置项的父级部分
|
||||
*
|
||||
* 参数列表:
|
||||
* section:要创建父级部分的配置项
|
||||
* depth:递归深度,用于防止无限递归调用
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建父级部分,则返回指向父级部分的指针;如果出现错误,则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于创建配置项的父级部分。它可以递归地创建父级部分,如果父级部分不存在的话。
|
||||
* 如果递归深度超过了最大允许值,函数将返回 nullptr 并报告错误。
|
||||
*/
|
||||
ConfigSection *ConfigTree::CreateParent(ConfigSection *section, int depth)
|
||||
{
|
||||
ConfigSection* parent = nullptr;
|
||||
ConfigSection *parent = nullptr;
|
||||
mot_string sectionPath;
|
||||
mot_string sectionName;
|
||||
|
||||
|
|
@ -318,17 +389,16 @@ ConfigSection* ConfigTree::CreateParent(ConfigSection* section, int depth)
|
|||
// guard against endless recurring calls
|
||||
if (depth >= MAX_SECTION_DEPTH) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_STATE,
|
||||
"Load Configuration"
|
||||
"Failed to create parent of section %s: section depth exceeds maximum allowed (%u)",
|
||||
section->GetFullPathName(),
|
||||
(unsigned)MAX_SECTION_DEPTH);
|
||||
"Load Configuration"
|
||||
"Failed to create parent of section %s: section depth exceeds maximum allowed (%u)",
|
||||
section->GetFullPathName(), (unsigned)MAX_SECTION_DEPTH);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// parse full section name of parent and create the parent
|
||||
if (!ConfigFileParser::BreakSectionName(section->GetPath(), sectionPath, sectionName)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse section name: %s", section->GetPath());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse section name: %s",
|
||||
section->GetPath());
|
||||
return nullptr;
|
||||
}
|
||||
parent = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
|
|
@ -339,12 +409,10 @@ ConfigSection* ConfigTree::CreateParent(ConfigSection* section, int depth)
|
|||
|
||||
// get the parent of the new parent section (make sure it exists and linked to the parent we just created)
|
||||
bool parentCreated = false;
|
||||
ConfigSection* grandParent = GetOrCreateParent(parent, depth + 1, parentCreated);
|
||||
ConfigSection *grandParent = GetOrCreateParent(parent, depth + 1, parentCreated);
|
||||
if (grandParent == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to get or create parent of parent section %s",
|
||||
parent->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to get or create parent of parent section %s", parent->GetFullPathName());
|
||||
delete parent;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,33 +26,38 @@
|
|||
#include "string.h"
|
||||
|
||||
namespace MOT {
|
||||
static const char* CONFIG_VALUE_INT64_STR = "int64";
|
||||
static const char* CONFIG_VALUE_INT32_STR = "int32";
|
||||
static const char* CONFIG_VALUE_INT16_STR = "int16";
|
||||
static const char* CONFIG_VALUE_INT8_STR = "int8";
|
||||
static const char* CONFIG_VALUE_UINT64_STR = "uint64";
|
||||
static const char* CONFIG_VALUE_UINT32_STR = "uint32";
|
||||
static const char* CONFIG_VALUE_UINT16_STR = "uint16";
|
||||
static const char* CONFIG_VALUE_UINT8_STR = "uint8";
|
||||
static const char* CONFIG_VALUE_DOUBLE_STR = "double";
|
||||
static const char* CONFIG_VALUE_BOOL_STR = "bool";
|
||||
static const char* CONFIG_VALUE_STRING_STR = "string";
|
||||
static const char* CONFIG_VALUE_UNDEFINED_STR = "N/A";
|
||||
static const char *CONFIG_VALUE_INT64_STR = "int64";
|
||||
static const char *CONFIG_VALUE_INT32_STR = "int32";
|
||||
static const char *CONFIG_VALUE_INT16_STR = "int16";
|
||||
static const char *CONFIG_VALUE_INT8_STR = "int8";
|
||||
static const char *CONFIG_VALUE_UINT64_STR = "uint64";
|
||||
static const char *CONFIG_VALUE_UINT32_STR = "uint32";
|
||||
static const char *CONFIG_VALUE_UINT16_STR = "uint16";
|
||||
static const char *CONFIG_VALUE_UINT8_STR = "uint8";
|
||||
static const char *CONFIG_VALUE_DOUBLE_STR = "double";
|
||||
static const char *CONFIG_VALUE_BOOL_STR = "bool";
|
||||
static const char *CONFIG_VALUE_STRING_STR = "string";
|
||||
static const char *CONFIG_VALUE_UNDEFINED_STR = "N/A";
|
||||
|
||||
static const char* VALUE_TYPE_NAMES[] = {CONFIG_VALUE_INT64_STR,
|
||||
CONFIG_VALUE_INT32_STR,
|
||||
CONFIG_VALUE_INT16_STR,
|
||||
CONFIG_VALUE_INT8_STR,
|
||||
CONFIG_VALUE_UINT64_STR,
|
||||
CONFIG_VALUE_UINT32_STR,
|
||||
CONFIG_VALUE_UINT16_STR,
|
||||
CONFIG_VALUE_UINT8_STR,
|
||||
CONFIG_VALUE_DOUBLE_STR,
|
||||
CONFIG_VALUE_BOOL_STR,
|
||||
CONFIG_VALUE_STRING_STR,
|
||||
CONFIG_VALUE_UNDEFINED_STR};
|
||||
|
||||
extern ConfigValueType ConfigValueTypeFromString(const char* valueTypeStr)
|
||||
static const char *VALUE_TYPE_NAMES[] = {CONFIG_VALUE_INT64_STR, CONFIG_VALUE_INT32_STR, CONFIG_VALUE_INT16_STR,
|
||||
CONFIG_VALUE_INT8_STR, CONFIG_VALUE_UINT64_STR, CONFIG_VALUE_UINT32_STR,
|
||||
CONFIG_VALUE_UINT16_STR, CONFIG_VALUE_UINT8_STR, CONFIG_VALUE_DOUBLE_STR,
|
||||
CONFIG_VALUE_BOOL_STR, CONFIG_VALUE_STRING_STR, CONFIG_VALUE_UNDEFINED_STR};
|
||||
/*
|
||||
* 功能:从字符串中解析配置值的类型
|
||||
*
|
||||
* 参数列表:
|
||||
* valueTypeStr:要解析的配置值类型的字符串表示
|
||||
*
|
||||
* 返回值:
|
||||
* 返回从字符串解析的配置值类型(`ConfigValueType` 枚举值)。
|
||||
* 如果字符串无法识别,则返回 `CONFIG_VALUE_UNDEFINED`。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数根据输入的字符串值返回相应的配置值类型。
|
||||
* 如果输入字符串无法识别,则返回 `CONFIG_VALUE_UNDEFINED`。
|
||||
*/
|
||||
extern ConfigValueType ConfigValueTypeFromString(const char *valueTypeStr)
|
||||
{
|
||||
ConfigValueType result = ConfigValueType::CONFIG_VALUE_UNDEFINED;
|
||||
|
||||
|
|
@ -82,15 +87,40 @@ extern ConfigValueType ConfigValueTypeFromString(const char* valueTypeStr)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern const char* ConfigValueTypeToString(ConfigValueType valueType)
|
||||
/*
|
||||
* 功能:将配置值类型转换为字符串表示
|
||||
*
|
||||
* 参数列表:
|
||||
* valueType:要转换为字符串的配置值类型(`ConfigValueType` 枚举值)
|
||||
*
|
||||
* 返回值:
|
||||
* 返回配置值类型的字符串表示。
|
||||
* 如果配置值类型无效,则返回表示未定义配置值类型的字符串。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数将给定的配置值类型转换为相应的字符串表示。
|
||||
* 如果配置值类型无效,则返回表示未定义配置值类型的字符串。
|
||||
*/
|
||||
extern const char *ConfigValueTypeToString(ConfigValueType valueType)
|
||||
{
|
||||
if (valueType < ConfigValueType::CONFIG_VALUE_UNDEFINED) {
|
||||
return VALUE_TYPE_NAMES[(uint32_t)valueType];
|
||||
}
|
||||
return CONFIG_VALUE_UNDEFINED_STR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查配置值类型是否为整数类型
|
||||
*
|
||||
* 参数列表:
|
||||
* valueType:要检查的配置值类型(`ConfigValueType` 枚举值)
|
||||
*
|
||||
* 返回值:
|
||||
* 如果配置值类型是整数类型,则返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查给定的配置值类型是否表示整数类型。
|
||||
* 如果配置值类型为整数类型之一,则返回 true,否则返回 false。
|
||||
*/
|
||||
extern bool IsConfigValueIntegral(ConfigValueType valueType)
|
||||
{
|
||||
bool result = false;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,17 @@
|
|||
|
||||
namespace MOT {
|
||||
IMPLEMENT_CLASS_LOGGER(ExtConfigLoader, Configuration)
|
||||
|
||||
/*
|
||||
* 功能:加载外部配置
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功加载外部配置,则返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于加载外部配置。它包含了加载、处理和最终化外部配置的步骤。
|
||||
* 如果在任何步骤中出现错误,函数将返回 false,并记录适当的错误消息。
|
||||
* 如果成功加载外部配置,则最终配置信息将打印到日志(如果日志级别为 `LogLevel::LL_TRACE`)。
|
||||
*/
|
||||
bool ExtConfigLoader::LoadExtConfig()
|
||||
{
|
||||
bool result = false;
|
||||
|
|
@ -41,11 +51,13 @@ bool ExtConfigLoader::LoadExtConfig()
|
|||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to load external configuration");
|
||||
} else {
|
||||
// wrap up
|
||||
// 完成加载外部配置
|
||||
result = EndExtConfigLoad();
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Load Configuration", "Failed to finalize external configuration loading");
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to finalize external configuration loading");
|
||||
} else {
|
||||
// 如果日志级别为 LogLevel::LL_TRACE,则打印已加载的外部配置信息
|
||||
if (MOT_CHECK_LOG_LEVEL(LogLevel::LL_TRACE)) {
|
||||
MOT_LOG_INFO("Loaded external configuration:");
|
||||
m_extConfigTree->Print(LogLevel::LL_TRACE);
|
||||
|
|
@ -56,73 +68,93 @@ bool ExtConfigLoader::LoadExtConfig()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtStringConfigItem(const char* path, const char* key, const char* value)
|
||||
bool ExtConfigLoader::AddExtStringConfigItem(const char *path, const char *key, const char *value)
|
||||
{
|
||||
return AddTypedConfigItem<MOT::mot_string, StringConfigValue>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtUInt64ConfigItem(const char* path, const char* key, uint64_t value)
|
||||
bool ExtConfigLoader::AddExtUInt64ConfigItem(const char *path, const char *key, uint64_t value)
|
||||
{
|
||||
return AddTypedConfigItem<uint64_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtUInt32ConfigItem(const char* path, const char* key, uint32_t value)
|
||||
bool ExtConfigLoader::AddExtUInt32ConfigItem(const char *path, const char *key, uint32_t value)
|
||||
{
|
||||
return AddTypedConfigItem<uint32_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtUInt16ConfigItem(const char* path, const char* key, uint16_t value)
|
||||
bool ExtConfigLoader::AddExtUInt16ConfigItem(const char *path, const char *key, uint16_t value)
|
||||
{
|
||||
return AddTypedConfigItem<uint16_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtUInt8ConfigItem(const char* path, const char* key, uint8_t value)
|
||||
bool ExtConfigLoader::AddExtUInt8ConfigItem(const char *path, const char *key, uint8_t value)
|
||||
{
|
||||
return AddTypedConfigItem<uint8_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtInt64ConfigItem(const char* path, const char* key, int64_t value)
|
||||
bool ExtConfigLoader::AddExtInt64ConfigItem(const char *path, const char *key, int64_t value)
|
||||
{
|
||||
return AddTypedConfigItem<int64_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtInt32ConfigItem(const char* path, const char* key, int32_t value)
|
||||
bool ExtConfigLoader::AddExtInt32ConfigItem(const char *path, const char *key, int32_t value)
|
||||
{
|
||||
return AddTypedConfigItem<int32_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtInt16ConfigItem(const char* path, const char* key, int16_t value)
|
||||
bool ExtConfigLoader::AddExtInt16ConfigItem(const char *path, const char *key, int16_t value)
|
||||
{
|
||||
return AddTypedConfigItem<int16_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtInt8ConfigItem(const char* path, const char* key, int8_t value)
|
||||
bool ExtConfigLoader::AddExtInt8ConfigItem(const char *path, const char *key, int8_t value)
|
||||
{
|
||||
return AddTypedConfigItem<int8_t>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtDoubleConfigItem(const char* path, const char* key, double value)
|
||||
bool ExtConfigLoader::AddExtDoubleConfigItem(const char *path, const char *key, double value)
|
||||
{
|
||||
return AddTypedConfigItem<double>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtBoolConfigItem(const char* path, const char* key, bool value)
|
||||
bool ExtConfigLoader::AddExtBoolConfigItem(const char *path, const char *key, bool value)
|
||||
{
|
||||
return AddTypedConfigItem<bool>(path, key, value);
|
||||
}
|
||||
|
||||
bool ExtConfigLoader::AddExtConfigItem(ConfigItem* configItem)
|
||||
/*
|
||||
* 功能:添加外部配置项
|
||||
*
|
||||
* 参数:
|
||||
* configItem - 要添加的外部配置项
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置项,则返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于添加外部配置项。它首先查找配置项的父部分,如果不存在,则创建新的父部分。
|
||||
* 然后将配置项添加到父部分中。如果父部分不存在,或者添加失败,则函数返回 false。
|
||||
* 如果配置项已存在于父部分中,它可能会记录警告消息,但不会导致函数失败。
|
||||
*/
|
||||
bool ExtConfigLoader::AddExtConfigItem(ConfigItem *configItem)
|
||||
{
|
||||
ConfigSection* section = nullptr;
|
||||
ConfigSection *section = nullptr;
|
||||
ConfigSectionMap::iterator itr = m_sectionMap.find(configItem->GetPath());
|
||||
|
||||
// 如果配置项的父部分不存在,创建新的父部分
|
||||
if (itr == m_sectionMap.end()) {
|
||||
mot_string sectionPath;
|
||||
mot_string sectionName;
|
||||
if (!ConfigFileParser::BreakSectionName(
|
||||
configItem->GetPath(), sectionPath, sectionName, ConfigItem::PATH_SEP)) {
|
||||
|
||||
// 解析配置项的路径,以获取父部分的路径和名称
|
||||
if (!ConfigFileParser::BreakSectionName(configItem->GetPath(), sectionPath, sectionName,
|
||||
ConfigItem::PATH_SEP)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse external section name");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建新的配置部分
|
||||
section = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
if (section == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to create new external section");
|
||||
|
|
@ -135,10 +167,8 @@ bool ExtConfigLoader::AddExtConfigItem(ConfigItem* configItem)
|
|||
}
|
||||
if (!m_sectionMap.insert(ConfigSectionMap::value_type(section->GetFullPathName(), section)).second) {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to insert external section %s",
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to insert external section %s",
|
||||
section->GetFullPathName());
|
||||
delete section;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -149,27 +179,39 @@ bool ExtConfigLoader::AddExtConfigItem(ConfigItem* configItem)
|
|||
section = itr->second;
|
||||
}
|
||||
|
||||
// 将配置项添加到父部分中
|
||||
if (!section->AddConfigItem(configItem)) {
|
||||
if (MOT_IS_SEVERE()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to add external configuration item %s to parent section %s",
|
||||
configItem->GetPath(),
|
||||
section->GetFullPathName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to add external configuration item %s to parent section %s", configItem->GetPath(),
|
||||
section->GetFullPathName());
|
||||
return false;
|
||||
}
|
||||
MOT_LOG_WARN("Failed to add duplicate external configuration item %s to parent section %s",
|
||||
configItem->GetPath(),
|
||||
section->GetFullPathName());
|
||||
configItem->GetPath(), section->GetFullPathName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:结束外部配置加载
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功结束外部配置加载并构建配置树,则返回 true,否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于结束外部配置加载过程。它创建外部配置树对象,并使用已解析的部分构建配置树。
|
||||
* 如果成功构建配置树,则返回 true,否则返回 false。如果构建失败,它会记录错误消息并清理已创建的树对象。
|
||||
*/
|
||||
bool ExtConfigLoader::EndExtConfigLoad()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 创建外部配置树对象
|
||||
m_extConfigTree = ConfigTree::CreateConfigTree(GetPriority(), GetName(), false);
|
||||
if (m_extConfigTree != nullptr) {
|
||||
|
||||
// 使用已解析的部分构建配置树
|
||||
result = m_extConfigTree->Build(m_parsedSections);
|
||||
if (!result) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to build external configuration tree");
|
||||
|
|
@ -182,13 +224,24 @@ bool ExtConfigLoader::EndExtConfigLoad()
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:清理外部配置加载器的状态
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于清理外部配置加载器的状态。它清空了 `m_sectionMap` 和 `m_parsedSections`,
|
||||
* 并在需要时删除外部配置树对象 `m_extConfigTree`。
|
||||
*/
|
||||
void ExtConfigLoader::Cleanup() noexcept
|
||||
{
|
||||
m_sectionMap.clear();
|
||||
m_parsedSections.clear();
|
||||
// 清空外部配置加载器的状态
|
||||
m_sectionMap.clear(); // 清空配置节映射
|
||||
m_parsedSections.clear(); // 清空已解析的配置节列表
|
||||
|
||||
// 删除外部配置树对象
|
||||
if (m_extConfigTree != nullptr) {
|
||||
delete m_extConfigTree;
|
||||
m_extConfigTree = nullptr;
|
||||
delete m_extConfigTree; // 删除配置树
|
||||
m_extConfigTree = nullptr; // 将配置树指针置为空指针
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -25,26 +25,41 @@
|
|||
#include "file_line_reader.h"
|
||||
|
||||
namespace MOT {
|
||||
FileLineReader::FileLineReader(const char* configFilePath)
|
||||
: m_configFilePath(configFilePath), m_configFile(configFilePath), m_lineItr(NULL), m_lineNumber(0)
|
||||
/*
|
||||
* 构造函数:创建 FileLineReader 对象并解析配置文件
|
||||
*
|
||||
* @param configFilePath 配置文件的路径
|
||||
*/
|
||||
FileLineReader::FileLineReader(const char *configFilePath)
|
||||
: m_configFilePath(configFilePath), // 存储配置文件路径
|
||||
m_configFile(configFilePath), // 通过文件路径创建配置文件对象
|
||||
m_lineItr(NULL), // 将行迭代器初始化为 NULL
|
||||
m_lineNumber(0) // 初始化行号为 0
|
||||
{
|
||||
// 解析配置文件内容
|
||||
ParseFile();
|
||||
}
|
||||
|
||||
FileLineReader::~FileLineReader()
|
||||
{}
|
||||
|
||||
/*
|
||||
* 解析配置文件内容,将每一行存储到容器中
|
||||
*/
|
||||
void FileLineReader::ParseFile()
|
||||
{
|
||||
if (m_configFile) {
|
||||
std::string line;
|
||||
while (std::getline(m_configFile, line)) {
|
||||
// 读取配置文件中的每一行并存储到 m_lines 容器中
|
||||
m_lines.push_back(line.c_str());
|
||||
}
|
||||
|
||||
// 初始化行迭代器为 m_lines 的开头,行号为 1
|
||||
m_lineItr = m_lines.cbegin();
|
||||
m_lineNumber = 1;
|
||||
} else {
|
||||
// 如果配置文件无法打开,则初始化行迭代器为 m_lines 的结尾
|
||||
m_lineItr = m_lines.cend();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ IMPLEMENT_CLASS_LOGGER(LayeredConfigTree, Configuration)
|
|||
class PrintVisitor : public ConfigItemVisitor {
|
||||
public:
|
||||
/** @var The configuration list to build. */
|
||||
ConfigItemList* m_printList;
|
||||
ConfigItemList *m_printList;
|
||||
|
||||
/** @brief The set of all configuration items that were added. */
|
||||
mot_set<mot_string> m_itemsAdded;
|
||||
|
|
@ -51,7 +51,7 @@ public:
|
|||
* @brief Constructor.
|
||||
* @param printList The configuration list to build.
|
||||
*/
|
||||
explicit PrintVisitor(ConfigItemList* printList) : m_printList(printList), m_status(true)
|
||||
explicit PrintVisitor(ConfigItemList *printList) : m_printList(printList), m_status(true)
|
||||
{}
|
||||
|
||||
~PrintVisitor() override
|
||||
|
|
@ -67,7 +67,7 @@ public:
|
|||
* @brief Adds configuration item to print list.
|
||||
* @param configItem The configuration item.
|
||||
*/
|
||||
void OnConfigItem(const ConfigItem* configItem) override
|
||||
void OnConfigItem(const ConfigItem *configItem) override
|
||||
{
|
||||
if (!m_status) {
|
||||
return;
|
||||
|
|
@ -75,7 +75,7 @@ public:
|
|||
|
||||
if (configItem->GetClass() == ConfigItemClass::CONFIG_ITEM_VALUE) {
|
||||
if (m_itemsAdded.find(configItem->GetFullPathName()) == m_itemsAdded.end()) {
|
||||
if (!m_printList->push_back(const_cast<ConfigItem*>(configItem))) {
|
||||
if (!m_printList->push_back(const_cast<ConfigItem *>(configItem))) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to add item to print list");
|
||||
m_status = false;
|
||||
return;
|
||||
|
|
@ -103,22 +103,31 @@ LayeredConfigTree::~LayeredConfigTree()
|
|||
{
|
||||
ClearConfigTrees();
|
||||
}
|
||||
|
||||
bool LayeredConfigTree::AddConfigTree(ConfigTree* configTree)
|
||||
/*
|
||||
* 功能:向层次化配置树中添加配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* configTree:要添加的配置树指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置树,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将配置树添加到层次化配置树中。它将配置树根据其优先级插入到适当的位置,以确保按照优先级排序。
|
||||
*/
|
||||
bool LayeredConfigTree::AddConfigTree(ConfigTree *configTree)
|
||||
{
|
||||
bool configTreeAdded = false;
|
||||
MOT_LOG_DEBUG("Adding configuration tree %p %s with priority %d",
|
||||
configTree,
|
||||
configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
MOT_LOG_DEBUG("Adding configuration tree %p %s with priority %d", configTree, configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
|
||||
// insert sorted
|
||||
LayeredConfigTrees::iterator itr = m_configTrees.begin();
|
||||
while (itr != m_configTrees.end()) {
|
||||
// check next list (get priority of first tree in the list (all trees in the list have same priority)
|
||||
ConfigTreeList& configTreeList = *itr;
|
||||
ConfigTreeList &configTreeList = *itr;
|
||||
MOT_ASSERT(!configTreeList.empty());
|
||||
ConfigTree* currConfigTree = *configTreeList.begin();
|
||||
ConfigTree *currConfigTree = *configTreeList.begin();
|
||||
if (currConfigTree->GetPriority() == configTree->GetPriority()) {
|
||||
// matching list found, we are done
|
||||
configTreeAdded = AddConfigTreeToList(configTree, configTreeList);
|
||||
|
|
@ -141,73 +150,121 @@ bool LayeredConfigTree::AddConfigTree(ConfigTree* configTree)
|
|||
return configTreeAdded;
|
||||
}
|
||||
|
||||
void LayeredConfigTree::RemoveConfigTree(ConfigTree* configTree)
|
||||
/*
|
||||
* 功能:从层次化配置树中移除配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* configTree:要移除的配置树指针
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从层次化配置树中移除指定的配置树。它会根据配置树的优先级在相应的列表中查找并移除。
|
||||
*/
|
||||
void LayeredConfigTree::RemoveConfigTree(ConfigTree *configTree)
|
||||
{
|
||||
int priority = 0;
|
||||
bool found = false;
|
||||
MOT_LOG_DEBUG("Removing configuration tree %p %s with priority %d",
|
||||
configTree,
|
||||
configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
|
||||
// 记录调试信息,表示正在移除配置树
|
||||
MOT_LOG_DEBUG("Removing configuration tree %p %s with priority %d", configTree, configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
|
||||
// 逐个检查配置树列表,根据配置树的优先级移除
|
||||
LayeredConfigTrees::iterator itr = m_configTrees.begin();
|
||||
while (itr != m_configTrees.end()) {
|
||||
ConfigTreeList& configTreeList = *itr;
|
||||
ConfigTreeList &configTreeList = *itr;
|
||||
MOT_ASSERT(!configTreeList.empty());
|
||||
ConfigTreeList::iterator itr2 = std::find(configTreeList.begin(), configTreeList.end(), configTree);
|
||||
|
||||
if (itr2 == configTreeList.end()) {
|
||||
// 配置树不在当前列表中,继续查找
|
||||
MOT_LOG_TRACE(
|
||||
"Configuration tree %s not found in list priority %d in layered configuration tree (search continues)",
|
||||
configTree->GetSource(),
|
||||
priority);
|
||||
++itr; // keep searching
|
||||
++priority; // for debug printing
|
||||
configTree->GetSource(), priority);
|
||||
++itr; // 继续搜索
|
||||
++priority; // 用于调试打印
|
||||
} else {
|
||||
// 找到配置树,从列表中移除
|
||||
MOT_LOG_TRACE("Removing configuration tree %s from layered configuration tree", configTree->GetSource());
|
||||
configTreeList.erase(itr2);
|
||||
|
||||
// 如果列表为空,则从层次化配置树中移除整个列表
|
||||
if (configTreeList.empty()) {
|
||||
m_configTrees.erase(itr);
|
||||
}
|
||||
|
||||
found = true;
|
||||
break; // stop searching
|
||||
break; // 停止搜索
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// 未找到指定的配置树
|
||||
MOT_LOG_WARN("Failed to remove configuration tree %s from layered configuration tree: not found",
|
||||
configTree->GetSource());
|
||||
configTree->GetSource());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印层次化配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:打印的日志级别
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于以指定的日志级别打印整个层次化配置树。它会调用 `BuildPrintList` 函数构建打印列表,
|
||||
* 然后逐个打印配置项。如果构建打印列表失败,函数将记录错误信息并清除错误堆栈。
|
||||
*/
|
||||
void LayeredConfigTree::Print(LogLevel logLevel) const
|
||||
{
|
||||
MOT_LOG(logLevel, "Loaded configuration:");
|
||||
|
||||
// 如果打印列表为空,尝试构建它
|
||||
if (m_printList.empty()) {
|
||||
if (!const_cast<LayeredConfigTree*>(this)->BuildPrintList()) {
|
||||
if (!const_cast<LayeredConfigTree *>(this)->BuildPrintList()) {
|
||||
// 构建打印列表失败时记录错误信息并清除错误堆栈
|
||||
MOT_LOG_ERROR_STACK("Failed to build print list for layered configuration tree");
|
||||
ClearErrorStack();
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历打印列表并打印每个配置项
|
||||
ConfigItemList::const_iterator itr = m_printList.begin();
|
||||
while (itr != m_printList.end()) {
|
||||
const ConfigItem* configItem = *itr;
|
||||
const ConfigItem *configItem = *itr;
|
||||
configItem->Print(logLevel, true);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
const ConfigItem* LayeredConfigTree::GetConfigItem(const char* fullPathName) const
|
||||
/*
|
||||
* 功能:根据完整路径名获取配置项
|
||||
*
|
||||
* 参数列表:
|
||||
* fullPathName:要获取的配置项的完整路径名
|
||||
*
|
||||
* 返回值:
|
||||
* 如果找到匹配的配置项,则返回该配置项;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于在层次化配置树中查找指定完整路径名的配置项。它会从每个配置树中逐层查找,
|
||||
* 直到找到匹配的配置项或搜索完整个树。如果找到匹配的配置项,会在调试日志中打印信息。
|
||||
*/
|
||||
const ConfigItem *LayeredConfigTree::GetConfigItem(const char *fullPathName) const
|
||||
{
|
||||
const ConfigItem* result = nullptr;
|
||||
const ConfigItem *result = nullptr;
|
||||
|
||||
// search item layer by layer
|
||||
// 逐层搜索配置项
|
||||
LayeredConfigTrees::const_iterator itr = m_configTrees.begin();
|
||||
while (result == nullptr && itr != m_configTrees.end()) {
|
||||
const ConfigTreeList& configTreeList = *itr;
|
||||
const ConfigTreeList &configTreeList = *itr;
|
||||
ConfigTreeList::const_iterator itr2 = configTreeList.begin();
|
||||
while (result == nullptr && itr2 != configTreeList.end()) {
|
||||
const ConfigTree* configTree = *itr2;
|
||||
const ConfigTree *configTree = *itr2;
|
||||
result = configTree->GetConfigItem(fullPathName);
|
||||
if (result != nullptr) {
|
||||
MOT_LOG_DEBUG("*** --> Found %s", fullPathName);
|
||||
|
||||
// 如果日志级别为 LL_DEBUG,则打印配置项信息
|
||||
if (MOT_CHECK_LOG_LEVEL(LogLevel::LL_DEBUG)) {
|
||||
result->Print(LogLevel::LL_DEBUG, true);
|
||||
}
|
||||
|
|
@ -220,7 +277,16 @@ const ConfigItem* LayeredConfigTree::GetConfigItem(const char* fullPathName) con
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:构建层次化配置树的打印列表
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功构建打印列表,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于构建层次化配置树的打印列表。它会逐层遍历每个配置树,将配置项添加到打印列表中。
|
||||
* 在构建过程中,使用配置项访问者 PrintVisitor。
|
||||
*/
|
||||
bool LayeredConfigTree::BuildPrintList()
|
||||
{
|
||||
// add configuration items layer by layer, only if not added already
|
||||
|
|
@ -230,20 +296,16 @@ bool LayeredConfigTree::BuildPrintList()
|
|||
PrintVisitor pv(&m_printList);
|
||||
LayeredConfigTrees::const_iterator itr = m_configTrees.cbegin();
|
||||
while (itr != m_configTrees.cend()) {
|
||||
const ConfigTreeList& configTreeList = *itr;
|
||||
const ConfigTreeList &configTreeList = *itr;
|
||||
ConfigTreeList::const_iterator itr2 = configTreeList.begin();
|
||||
while (itr2 != configTreeList.end()) {
|
||||
const ConfigTree* configTree = *itr2;
|
||||
MOT_LOG_DEBUG("Scanning config tree %p %s with priority %d",
|
||||
configTree,
|
||||
configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
const ConfigTree *configTree = *itr2;
|
||||
MOT_LOG_DEBUG("Scanning config tree %p %s with priority %d", configTree, configTree->GetSource(),
|
||||
configTree->GetPriority());
|
||||
configTree->ForEach(pv);
|
||||
if (!pv.GetStatus()) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Load Configuration",
|
||||
"Failed to build print list for configuration tree %s",
|
||||
configTree->GetSource());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration",
|
||||
"Failed to build print list for configuration tree %s", configTree->GetSource());
|
||||
return false;
|
||||
}
|
||||
++itr2;
|
||||
|
|
@ -252,16 +314,21 @@ bool LayeredConfigTree::BuildPrintList()
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:清除层次化配置树中的所有配置树
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于清除层次化配置树中的所有配置树。它首先遍历所有配置树,删除非静态配置树,然后清空配置树列表和打印列表。
|
||||
*/
|
||||
void LayeredConfigTree::ClearConfigTrees()
|
||||
{
|
||||
MOT_LOG_DEBUG("Clearing all configuration trees from layered configuration tree");
|
||||
LayeredConfigTrees::iterator itr = m_configTrees.begin();
|
||||
while (itr != m_configTrees.end()) {
|
||||
ConfigTreeList& configTreeList = *itr;
|
||||
ConfigTreeList &configTreeList = *itr;
|
||||
ConfigTreeList::iterator litr = configTreeList.begin();
|
||||
while (litr != configTreeList.end()) {
|
||||
ConfigTree* configTree = *litr;
|
||||
ConfigTree *configTree = *litr;
|
||||
if (!configTree->IsStatic()) {
|
||||
MOT_LOG_DEBUG("Destroying configuration tree %p %s", configTree, configTree->GetSource());
|
||||
delete configTree;
|
||||
|
|
@ -274,26 +341,59 @@ void LayeredConfigTree::ClearConfigTrees()
|
|||
m_configTrees.clear();
|
||||
m_printList.clear();
|
||||
}
|
||||
|
||||
bool LayeredConfigTree::AddNewConfigTreeAt(LayeredConfigTrees::iterator itr, ConfigTree* configTree)
|
||||
/*
|
||||
* 功能:在指定位置添加新的配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* itr:指向插入位置的迭代器
|
||||
* configTree:要添加的配置树指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置树,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于在指定位置添加新的配置树。它首先尝试将新的配置树列表插入到层次化配置树中,
|
||||
* 然后在配置树列表中添加配置树。如果插入和添加成功,则返回 true。
|
||||
*/
|
||||
bool LayeredConfigTree::AddNewConfigTreeAt(LayeredConfigTrees::iterator itr, ConfigTree *configTree)
|
||||
{
|
||||
bool configTreeAdded = false;
|
||||
LayeredConfigTrees::pairib pb = m_configTrees.insert(itr, ConfigTreeList());
|
||||
if (!pb.second) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to insert configuration tree list");
|
||||
} else {
|
||||
ConfigTreeList& configTreeList = *pb.first;
|
||||
ConfigTreeList &configTreeList = *pb.first;
|
||||
configTreeAdded = AddConfigTreeToList(configTree, configTreeList);
|
||||
}
|
||||
return configTreeAdded;
|
||||
}
|
||||
|
||||
bool LayeredConfigTree::AddConfigTreeToList(ConfigTree* configTree, ConfigTreeList& configTreeList)
|
||||
/*
|
||||
* 功能:将配置树添加到配置树列表中
|
||||
*
|
||||
* 参数列表:
|
||||
* configTree:要添加的配置树指针
|
||||
* configTreeList:配置树列表,将在其中添加配置树
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加配置树,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将配置树添加到配置树列表中。它首先尝试将配置树添加到列表的末尾,
|
||||
* 如果添加成功,则返回 true,否则返回 false。
|
||||
*/
|
||||
bool LayeredConfigTree::AddConfigTreeToList(ConfigTree *configTree, ConfigTreeList &configTreeList)
|
||||
{
|
||||
// 将配置树添加到配置树列表的末尾
|
||||
bool configTreeAdded = configTreeList.push_back(configTree);
|
||||
|
||||
// 检查是否成功添加配置树
|
||||
if (!configTreeAdded) {
|
||||
// 如果添加失败,则报告内部错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to add configuration tree to new list");
|
||||
}
|
||||
|
||||
return configTreeAdded;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -29,28 +29,50 @@
|
|||
|
||||
namespace MOT {
|
||||
DECLARE_LOGGER(PropsConfigFileLoader, Configuration)
|
||||
|
||||
static bool ParsePropsSectionName(const mot_string& line, mot_string& sectionPath, mot_string& keyValuePart)
|
||||
/*
|
||||
* 功能:解析配置项的节名称和键值部分
|
||||
*
|
||||
* 参数列表:
|
||||
* line:包含配置项的完整行
|
||||
* sectionPath:用于存储解析后的节名称的字符串
|
||||
* keyValuePart:用于存储解析后的键值部分的字符串
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功解析配置项,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于解析配置项的节名称和键值部分。配置项的格式应为“节名称=键值”,其中节名称可能包含路径信息。
|
||||
* 函数首先查找等号(=),确保它出现在路径分隔符(如果有的话)之前。然后,它将行分割为节名称和键值部分,
|
||||
* 并对它们进行修剪(去除前导和尾随空格)。
|
||||
*/
|
||||
static bool ParsePropsSectionName(const mot_string &line, mot_string §ionPath, mot_string &keyValuePart)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// since configuration value might have path names to disk, we need to make sure that section separator appears
|
||||
// BEFORE the equals sign
|
||||
// 检查等号的位置,确保等号出现在路径分隔符之前
|
||||
uint32_t equalsPos = line.find('=');
|
||||
if (equalsPos == mot_string::npos) { // this is an illegal format, there must be an equals sign
|
||||
// 报告内部错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name: missing equals sign");
|
||||
result = false;
|
||||
} else {
|
||||
// 查找等号前的最后一个路径分隔符的位置
|
||||
uint32_t lastSlashPos = line.find_last_of(ConfigItem::PATH_SEP, equalsPos);
|
||||
if (lastSlashPos != mot_string::npos) {
|
||||
// 提取节名称和键值部分
|
||||
if (!line.substr(sectionPath, 0, lastSlashPos) || !line.substr(keyValuePart, lastSlashPos + 1)) {
|
||||
// 报告内部错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to parse section name");
|
||||
result = false;
|
||||
} else {
|
||||
// 修剪节名称和键值部分,去除前导和尾随空格
|
||||
sectionPath.trim();
|
||||
keyValuePart.trim();
|
||||
}
|
||||
} else {
|
||||
// 行中没有路径分隔符,将节名称设置为空字符串,并提取键值部分
|
||||
sectionPath.assign("");
|
||||
keyValuePart.assign(line);
|
||||
keyValuePart.trim();
|
||||
|
|
@ -67,101 +89,169 @@ static bool ParsePropsSectionName(const mot_string& line, mot_string& sectionPat
|
|||
break; \
|
||||
}
|
||||
|
||||
static ConfigSection* GetPropsConfigSection(
|
||||
const mot_string& sectionFullName, mot_list<ConfigSection*>& parsedSections, ConfigSectionMap& sectionMap)
|
||||
/*
|
||||
* 功能:根据完整的节名称获取或创建配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* sectionFullName:完整的节名称,包括可能的路径
|
||||
* parsedSections:存储已解析的配置节的列表
|
||||
* sectionMap:配置节的映射,用于快速查找已创建的配置节
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功获取或创建配置节,返回指向该配置节的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数根据完整的节名称获取或创建配置节。它首先检查给定的节名称是否已存在于配置节映射(sectionMap)中,
|
||||
* 如果已存在,则直接返回已创建的配置节。如果节名称不存在,则解析节的路径和名称,创建新的配置节,
|
||||
* 并将其添加到已解析的配置节列表(parsedSections)和配置节映射中。最后,返回指向新创建或已存在的配置节的指针。
|
||||
*/
|
||||
static ConfigSection *GetPropsConfigSection(const mot_string §ionFullName,
|
||||
mot_list<ConfigSection *> &parsedSections, ConfigSectionMap §ionMap)
|
||||
{
|
||||
mot_string sectionPath;
|
||||
mot_string sectionName;
|
||||
mot_map<mot_string, ConfigSection*>::iterator itr = sectionMap.find(sectionFullName);
|
||||
|
||||
// 检查节名称是否已存在于配置节映射中
|
||||
mot_map<mot_string, ConfigSection *>::iterator itr = sectionMap.find(sectionFullName);
|
||||
if (itr == sectionMap.end()) {
|
||||
// 节名称尚未在映射中找到,需要创建新的配置节
|
||||
if (!ConfigFileParser::BreakSectionName(sectionFullName, sectionPath, sectionName)) {
|
||||
// 解析节名称失败,报告内部错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to parse section name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ConfigSection* currentSection = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
// 创建新的配置节
|
||||
ConfigSection *currentSection = ConfigSection::CreateConfigSection(sectionPath.c_str(), sectionName.c_str());
|
||||
if (currentSection == nullptr) {
|
||||
// 分配配置节内存失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate configuration section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 将新的配置节添加到已解析的配置节列表中
|
||||
if (!parsedSections.push_back(currentSection)) {
|
||||
// 添加到已解析的配置节列表失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to add parsed section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 将新的配置节添加到配置节映射中
|
||||
itr = sectionMap.insert(ConfigSectionMap::value_type(sectionFullName, currentSection)).first;
|
||||
}
|
||||
|
||||
// 返回指向新创建或已存在的配置节的指针
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
static bool AddPropsArrayConfigItem(const char* configFilePath, const FileLineReader& reader, const mot_string& line,
|
||||
ConfigSection* currentSection, const mot_string& sectionFullName, const mot_string& key, const mot_string& value,
|
||||
uint64_t arrayIndex)
|
||||
/*
|
||||
* 功能:将属性数组配置项添加到配置节
|
||||
*
|
||||
* 参数列表:
|
||||
* configFilePath:配置文件的路径
|
||||
* reader:文件行读取器,用于获取当前行号
|
||||
* line:包含配置项的文本行
|
||||
* currentSection:当前配置节
|
||||
* sectionFullName:完整的节名称,包括可能的路径
|
||||
* key:配置项的键
|
||||
* value:配置项的值
|
||||
* arrayIndex:配置项在数组中的索引
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功添加属性数组配置项,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将属性数组配置项添加到配置节中。首先,它尝试获取或创建名为 "key" 的配置数组(ConfigArray),
|
||||
* 如果该数组不存在,则创建一个新的配置数组,并将其添加到当前配置节中。接下来,它检查数组的索引是否与
|
||||
* 预期的索引(arrayIndex)相匹配,以确保数组项按顺序添加。然后,它使用提供的值创建配置项(ConfigItem),
|
||||
* 并将其添加到配置数组中。如果任何步骤失败,函数将报告错误并返回 false。
|
||||
*/
|
||||
static bool AddPropsArrayConfigItem(const char *configFilePath, const FileLineReader &reader, const mot_string &line,
|
||||
ConfigSection *currentSection, const mot_string §ionFullName,
|
||||
const mot_string &key, const mot_string &value, uint64_t arrayIndex)
|
||||
{
|
||||
ConfigArray* configArray = currentSection->ModifyConfigArray(key.c_str());
|
||||
// 尝试获取或创建名为 "key" 的配置数组
|
||||
ConfigArray *configArray = currentSection->ModifyConfigArray(key.c_str());
|
||||
if (configArray == nullptr) {
|
||||
// 配置数组不存在,创建一个新的配置数组
|
||||
configArray = ConfigArray::CreateConfigArray(sectionFullName.c_str(), key.c_str());
|
||||
if (configArray == nullptr) {
|
||||
// 分配配置数组内存失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to allocate configuration array");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将新的配置数组添加到当前配置节中
|
||||
if (!currentSection->AddConfigItem(configArray)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Load Configuration", "Failed to add configuration array to parent section");
|
||||
// 添加配置数组到父配置节失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to add configuration array to parent section");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查数组项的索引是否按顺序添加
|
||||
if (arrayIndex != configArray->GetConfigItemCount()) {
|
||||
// array items must be ordered
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG,
|
||||
"Load Configuration",
|
||||
"Failed to parse configuration file %s at line %u: %s (array %s items not "
|
||||
"well-ordered, expecting %u, got %" PRIu64 ")",
|
||||
configFilePath,
|
||||
reader.GetLineNumber(),
|
||||
line.c_str(),
|
||||
configArray->GetName(),
|
||||
configArray->GetConfigItemCount(),
|
||||
arrayIndex);
|
||||
// 数组项的索引与预期的索引不匹配,报告配置文件解析错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INVALID_CFG, "Load Configuration",
|
||||
"Failed to parse configuration file %s at line %u: %s (array %s items not "
|
||||
"well-ordered, expecting %u, got %" PRIu64 ")",
|
||||
configFilePath, reader.GetLineNumber(), line.c_str(), configArray->GetName(),
|
||||
configArray->GetConfigItemCount(), arrayIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigItem* configItem = ConfigFileParser::MakeArrayConfigValue(sectionFullName, arrayIndex, value);
|
||||
// 使用提供的值创建配置项并添加到配置数组中
|
||||
ConfigItem *configItem = ConfigFileParser::MakeArrayConfigValue(sectionFullName, arrayIndex, value);
|
||||
if (configItem == nullptr) {
|
||||
// 创建配置数组值失败,报告内部错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to create configuration array value");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!configArray->AddConfigItem(configItem)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Configuration",
|
||||
"Failed to add %" PRIu64 "th item to configuration array %s",
|
||||
arrayIndex,
|
||||
configArray->GetName());
|
||||
// 将数组项添加到配置数组中失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration",
|
||||
"Failed to add %" PRIu64 "th item to configuration array %s", arrayIndex,
|
||||
configArray->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigTree* PropsConfigFileLoader::LoadConfigFile(const char* configFilePath)
|
||||
/*
|
||||
* 功能:从PROPS配置文件加载配置并构建配置树
|
||||
*
|
||||
* 参数列表:
|
||||
* configFilePath:要加载的配置文件的路径
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功加载配置文件并构建配置树,返回一个指向配置树的指针;如果出现错误,则返回nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数负责从指定的PROPS配置文件中加载配置并构建配置树。它首先创建一个配置树对象,然后逐行解析配置文件,
|
||||
* 将配置项添加到相应的配置节中。如果配置文件的格式不正确或解析过程中发生错误,将返回nullptr。否则,
|
||||
* 将返回指向已加载配置的配置树指针。
|
||||
*/
|
||||
ConfigTree *PropsConfigFileLoader::LoadConfigFile(const char *configFilePath)
|
||||
{
|
||||
// 记录加载 PROPS 配置文件的跟踪日志
|
||||
MOT_LOG_TRACE("Loading PROPS configuration file from: %s", configFilePath);
|
||||
ConfigTree* configTree = ConfigTree::CreateConfigTree(GetPriority(), GetName(), false);
|
||||
|
||||
// 创建配置树,用于存储从配置文件加载的配置项
|
||||
ConfigTree *configTree = ConfigTree::CreateConfigTree(GetPriority(), GetName(), false);
|
||||
if (configTree == nullptr) {
|
||||
// 内存分配失败,报告内存分配错误
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Configuration", "Failed to create configuration tree");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 声明一些变量以供后续使用
|
||||
mot_string line;
|
||||
mot_string sectionFullName;
|
||||
mot_string keyValuePart;
|
||||
ConfigSection* currentSection = nullptr;
|
||||
mot_list<ConfigSection*> parsedSections;
|
||||
ConfigSection *currentSection = nullptr;
|
||||
mot_list<ConfigSection *> parsedSections;
|
||||
ConfigSectionMap sectionMap;
|
||||
mot_string key;
|
||||
mot_string value;
|
||||
|
|
@ -169,86 +259,102 @@ ConfigTree* PropsConfigFileLoader::LoadConfigFile(const char* configFilePath)
|
|||
uint64_t arrayIndex = 0;
|
||||
bool hasArrayIndex = false;
|
||||
|
||||
// unsupported yet
|
||||
// 初始化文件行读取器以读取配置文件
|
||||
// 如果无法打开文件,将发出警告并返回空的配置树
|
||||
FileLineReader reader(configFilePath);
|
||||
if (!reader.IsValid()) {
|
||||
MOT_LOG_WARN("Failed to load configuration file %s: unable to open file", configFilePath);
|
||||
// we return an empty tree to avoid errors during startup, but a warning is still issued
|
||||
// 为了避免在启动过程中出现错误,我们返回一个空的配置树,但仍然发出警告
|
||||
return configTree;
|
||||
}
|
||||
|
||||
while (!reader.Eof() && !parseError) {
|
||||
// parse next non-empty line
|
||||
// 解析下一行非空行
|
||||
if (!line.assign(reader.GetLine().c_str())) {
|
||||
// 内存分配失败,报告内存分配错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_OOM, "Failed to allocate memory for next line");
|
||||
}
|
||||
line.trim();
|
||||
if (line.length() && line[0] != '#') {
|
||||
// break tokens to section and key-value by last separator
|
||||
// 检查行是否为空并且不是注释行(以 '#' 开头的行)
|
||||
|
||||
// 分解行以获取配置节名称和键值部分
|
||||
MOT_LOG_DEBUG("Parsing config line: %s", line.c_str());
|
||||
if (!ParsePropsSectionName(line, sectionFullName, keyValuePart)) {
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INVALID_CFG,
|
||||
// 配置文件的节名称行格式错误,报告配置文件解析错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_INVALID_CFG,
|
||||
"Failed to parse configuration file %s at line %u: %s (section name line malformed)",
|
||||
configFilePath,
|
||||
reader.GetLineNumber(),
|
||||
line.c_str());
|
||||
configFilePath, reader.GetLineNumber(), line.c_str());
|
||||
}
|
||||
|
||||
// get the configuration section
|
||||
// 获取或创建配置节(ConfigSection)
|
||||
currentSection = GetPropsConfigSection(sectionFullName, parsedSections, sectionMap);
|
||||
if (currentSection == nullptr) {
|
||||
// 获取或创建配置节失败,报告内部错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INTERNAL, "Failed to get configuration section");
|
||||
}
|
||||
|
||||
// parse the key-value part
|
||||
if (!ConfigFileParser::ParseKeyValue(
|
||||
keyValuePart, sectionFullName, key, value, arrayIndex, hasArrayIndex)) {
|
||||
// key-value line malformed
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INVALID_CFG,
|
||||
"Failed to parse configuration file %s at line %u: %s (key/value malformed)",
|
||||
configFilePath,
|
||||
reader.GetLineNumber(),
|
||||
line.c_str());
|
||||
// 解析键值部分
|
||||
if (!ConfigFileParser::ParseKeyValue(keyValuePart, sectionFullName, key, value, arrayIndex,
|
||||
hasArrayIndex)) {
|
||||
// 键值行格式错误,报告配置文件解析错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_INVALID_CFG, "Failed to parse configuration file %s at line %u: %s (key/value malformed)",
|
||||
configFilePath, reader.GetLineNumber(), line.c_str());
|
||||
}
|
||||
|
||||
// check for array item
|
||||
// 检查是否为数组项
|
||||
if (!hasArrayIndex) {
|
||||
ConfigItem* configItem = ConfigFileParser::MakeConfigValue(sectionFullName, key, value);
|
||||
// 非数组项
|
||||
|
||||
// 创建配置值项(ConfigItem)并将其添加到当前配置节
|
||||
ConfigItem *configItem = ConfigFileParser::MakeConfigValue(sectionFullName, key, value);
|
||||
if (configItem == nullptr) {
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_INTERNAL,
|
||||
"Failed to parse configuration file %s at line %u: %s (invalid type specification?)",
|
||||
configFilePath,
|
||||
reader.GetLineNumber(),
|
||||
line.c_str());
|
||||
} else if (!currentSection->AddConfigItem(configItem, true)) {
|
||||
// 创建配置值项失败,报告内部错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_OOM, "Failed to add configuration item to parent section");
|
||||
MOT_ERROR_INTERNAL,
|
||||
"Failed to parse configuration file %s at line %u: %s (invalid type specification?)",
|
||||
configFilePath, reader.GetLineNumber(), line.c_str());
|
||||
} else if (!currentSection->AddConfigItem(configItem, true)) {
|
||||
// 将配置值项添加到父配置节失败,报告内存分配错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_OOM,
|
||||
"Failed to add configuration item to parent section");
|
||||
}
|
||||
} else {
|
||||
if (!AddPropsArrayConfigItem(
|
||||
configFilePath, reader, line, currentSection, sectionFullName, key, value, arrayIndex)) {
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(MOT_ERROR_OOM,
|
||||
// 数组项
|
||||
|
||||
// 添加属性数组配置项
|
||||
if (!AddPropsArrayConfigItem(configFilePath, reader, line, currentSection, sectionFullName, key, value,
|
||||
arrayIndex)) {
|
||||
// 添加属性数组配置项失败,报告内存分配错误并中断解析
|
||||
PROPS_REPORT_PARSE_ERROR_AND_BREAK(
|
||||
MOT_ERROR_OOM,
|
||||
"Failed to add array item with arrayIndex %lu in configuration file %s at line %u: %s",
|
||||
arrayIndex,
|
||||
configFilePath,
|
||||
reader.GetLineNumber(),
|
||||
line.c_str());
|
||||
arrayIndex, configFilePath, reader.GetLineNumber(), line.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 读取下一行
|
||||
reader.NextLine();
|
||||
}
|
||||
|
||||
// 检查是否发生了解析错误
|
||||
if (parseError) {
|
||||
// 解析错误发生,释放配置树的内存并返回空指针
|
||||
delete configTree;
|
||||
configTree = nullptr;
|
||||
} else {
|
||||
// 解析成功,构建配置树
|
||||
if (!configTree->Build(parsedSections)) {
|
||||
// 构建配置树失败,报告内部错误,释放配置树的内存并返回空指针
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Load Configuration", "Failed to build configuration tree");
|
||||
delete configTree;
|
||||
configTree = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return configTree;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -36,16 +36,32 @@ BitmapSet::BitmapSet()
|
|||
Reset();
|
||||
}
|
||||
|
||||
BitmapSet::BitmapSet(uint8_t* data, uint16_t size) : m_data(data), m_size(size), m_init(true)
|
||||
BitmapSet::BitmapSet(uint8_t *data, uint16_t size) : m_data(data), m_size(size), m_init(true)
|
||||
{}
|
||||
|
||||
BitmapSet::~BitmapSet()
|
||||
{}
|
||||
|
||||
void BitmapSet::Init(uint8_t* data, uint16_t size)
|
||||
/*
|
||||
* 功能:初始化位图集合对象
|
||||
*
|
||||
* 参数列表:
|
||||
* data:指向位图数据的指针,用于初始化位图集合
|
||||
* size:位图数据的大小,以字节数表示
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化位图集合对象,设置位图数据和大小。
|
||||
* 它将输入的数据和大小分配给位图集合对象的成员变量。
|
||||
*/
|
||||
void BitmapSet::Init(uint8_t *data, uint16_t size)
|
||||
{
|
||||
// 设置位图集合对象的大小成员变量
|
||||
m_size = size;
|
||||
|
||||
// 设置位图集合对象的数据成员变量
|
||||
m_data = data;
|
||||
|
||||
// 将初始化标志设置为true,表示对象已成功初始化
|
||||
m_init = true;
|
||||
}
|
||||
|
||||
|
|
@ -56,70 +72,196 @@ void BitmapSet::Reset()
|
|||
m_init = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置位图集合对象
|
||||
*
|
||||
* 参数列表:
|
||||
* size:位图数据的新大小,以字节数表示
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重置位图集合对象,将其大小更改为新值,并将位图数据清零。
|
||||
* 重置前需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
*/
|
||||
void BitmapSet::Reset(uint16_t size)
|
||||
{
|
||||
// 断言确保对象已成功初始化
|
||||
MOT_ASSERT(m_init);
|
||||
|
||||
// 设置位图集合对象的大小成员变量为新值
|
||||
m_size = size;
|
||||
|
||||
// 使用 memset_s 函数将位图数据清零
|
||||
errno_t erc = memset_s(m_data, GetLength(), 0, GetLength());
|
||||
securec_check(erc, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:清除位图集合对象的数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将位图集合对象的数据清零。
|
||||
* 在调用此函数之前,需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
*/
|
||||
void BitmapSet::Clear()
|
||||
{
|
||||
// 使用 memset_s 函数将位图数据清零
|
||||
errno_t erc = memset_s(m_data, GetLength(), 0, GetLength());
|
||||
securec_check(erc, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查位图集合对象是否已清零
|
||||
*
|
||||
* 返回值:
|
||||
* 如果位图集合对象已清零,则返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查位图集合对象的数据是否已完全清零。
|
||||
* 在调用此函数之前,需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
*/
|
||||
bool BitmapSet::IsClear()
|
||||
{
|
||||
// 获取位图数据的字节数
|
||||
uint16_t numBytes = GetLength();
|
||||
for (uint16_t i = 0; i < numBytes; i++)
|
||||
|
||||
// 遍历位图数据,检查是否有非零字节
|
||||
for (uint16_t i = 0; i < numBytes; i++) {
|
||||
if (m_data[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有字节都为零,返回true,表示已清零
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:设置位图集合对象中指定位的值为1
|
||||
*
|
||||
* 参数列表:
|
||||
* bit:要设置的位的索引
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将位图集合对象中指定位的值设置为1。
|
||||
* 在调用此函数之前,需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
* 还需要确保传递给函数的位索引(bit)小于位图集合对象的大小。
|
||||
*/
|
||||
void BitmapSet::SetBit(uint16_t bit)
|
||||
{
|
||||
// 使用断言确保位索引不超出位图集合对象的大小
|
||||
MOT_ASSERT(bit < m_size);
|
||||
|
||||
// 计算字节索引并在相应字节上设置指定位
|
||||
m_data[GetByteIndex(bit)] |= (1 << (bit & 0x07));
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将位图集合对象中指定位的值设置为0
|
||||
*
|
||||
* 参数列表:
|
||||
* bit:要取消设置的位的索引
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将位图集合对象中指定位的值设置为0。
|
||||
* 在调用此函数之前,需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
* 还需要确保传递给函数的位索引(bit)小于位图集合对象的大小。
|
||||
*/
|
||||
void BitmapSet::UnsetBit(uint16_t bit)
|
||||
{
|
||||
// 使用断言确保位索引不超出位图集合对象的大小
|
||||
MOT_ASSERT(bit < m_size);
|
||||
|
||||
// 计算字节索引并在相应字节上取消设置指定位
|
||||
m_data[GetByteIndex(bit)] &= ~(1 << (bit & 0x07));
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取位图集合对象中指定位的值
|
||||
*
|
||||
* 参数列表:
|
||||
* bit:要获取的位的索引
|
||||
*
|
||||
* 返回值:
|
||||
* 如果位的值为1,则返回1;否则返回0。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从位图集合对象中获取指定位的值。
|
||||
* 在调用此函数之前,需要确保对象已成功初始化(通过检查初始化标志)。
|
||||
* 还需要确保传递给函数的位索引(bit)小于位图集合对象的大小。
|
||||
*/
|
||||
uint8_t BitmapSet::GetBit(uint16_t bit)
|
||||
{
|
||||
// 使用断言确保位索引不超出位图集合对象的大小
|
||||
MOT_ASSERT(bit < m_size);
|
||||
|
||||
// 获取指定位的值并返回(如果位的值为1,则返回1;否则返回0)
|
||||
return (m_data[GetByteIndex(bit)] & (1 << (bit & 0x07))) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:计算指定位的字节索引
|
||||
*
|
||||
* 参数列表:
|
||||
* bit:要计算字节索引的位的索引
|
||||
*
|
||||
* 返回值:
|
||||
* 返回位索引对应的字节索引。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于计算位图集合对象中指定位的字节索引。
|
||||
* 位索引(bit)通常用于表示在位图中的某一位,而字节索引表示这一位在字节数组中的位置。
|
||||
*/
|
||||
uint16_t BitmapSet::GetByteIndex(uint16_t bit)
|
||||
{
|
||||
// 计算位索引对应的字节索引,并返回
|
||||
return (bit >> 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:按位或运算符(|=)重载函数
|
||||
*
|
||||
* 参数列表:
|
||||
* bitmapSet:要与当前位图集合对象执行按位或运算的另一个位图集合对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重载按位或运算符(|=),用于执行两个位图集合对象之间的按位或运算。
|
||||
* 它将当前位图集合对象的每个字节与另一个位图集合对象的相应字节执行按位或运算,并将结果存储在当前位图集合对象中。
|
||||
* 在执行此操作之前,需要确保两个位图集合对象的大小相同。
|
||||
*/
|
||||
void BitmapSet::operator|=(BitmapSet bitmapSet)
|
||||
{
|
||||
// 获取位图集合对象的大小
|
||||
uint16_t length = GetLength();
|
||||
|
||||
// 遍历每个字节并执行按位或运算
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
m_data[i] |= bitmapSet.m_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:按位与运算符(&=)重载函数
|
||||
*
|
||||
* 参数列表:
|
||||
* bitmapSet:要与当前位图集合对象执行按位与运算的另一个位图集合对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重载按位与运算符(&=),用于执行两个位图集合对象之间的按位与运算。
|
||||
* 它将当前位图集合对象的每个字节与另一个位图集合对象的相应字节执行按位与运算,并将结果存储在当前位图集合对象中。
|
||||
* 在执行此操作之前,需要确保两个位图集合对象的大小相同。
|
||||
*/
|
||||
void BitmapSet::operator&=(BitmapSet bitmapSet)
|
||||
{
|
||||
// 获取位图集合对象的大小
|
||||
uint16_t length = GetLength();
|
||||
|
||||
// 遍历每个字节并执行按位与运算
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
m_data[i] &= bitmapSet.m_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
BitmapSet::BitmapSetIterator::BitmapSetIterator(const BitmapSet& bitmapSet)
|
||||
BitmapSet::BitmapSetIterator::BitmapSetIterator(const BitmapSet &bitmapSet)
|
||||
: m_bms(&bitmapSet), m_data(m_bms->m_data), m_bitIndex(-1), m_byteCache(0), m_isSetCache(false)
|
||||
{
|
||||
Next();
|
||||
|
|
@ -139,20 +281,40 @@ bool BitmapSet::BitmapSetIterator::End() const
|
|||
return m_bitIndex >= m_bms->m_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取下一个设置位的迭代器
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在下一个设置位,则返回true;否则返回false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于 BitmapSet 对象的迭代器,用于查找下一个设置位。
|
||||
* 迭代器会递增位索引,并在位图中查找下一个设置位的位置。
|
||||
* 如果找到下一个设置位,将其状态存储在迭代器的缓存中,以便后续获取。
|
||||
*/
|
||||
bool BitmapSet::BitmapSetIterator::Next()
|
||||
{
|
||||
// 递增位索引
|
||||
m_bitIndex++;
|
||||
|
||||
// 如果位索引超出位图集合对象的大小,则返回false
|
||||
if (m_bitIndex >= m_bms->m_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果当前位索引是字节边界,则更新字节缓存
|
||||
if (m_bitIndex % SIZE_OF_BYTE == 0) {
|
||||
m_byteCache = m_data[GetByteIndex(m_bitIndex)];
|
||||
} else {
|
||||
// 否则右移字节缓存一位
|
||||
m_byteCache = m_byteCache >> 1;
|
||||
}
|
||||
|
||||
// 更新设置位缓存
|
||||
m_isSetCache = (m_byteCache & 1) != 0;
|
||||
|
||||
// 返回true表示找到下一个设置位
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -28,42 +28,127 @@
|
|||
#include <iomanip>
|
||||
|
||||
namespace MOT {
|
||||
/*
|
||||
* 功能:对布尔统计变量进行总结
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:一个布尔值,指示是否应该更新时间戳(可选)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于对布尔统计变量进行总结。它会计算百分比,并可选地更新时间戳信息。
|
||||
* 百分比计算方式为 m_sum 除以 m_countSaved,并将结果保存在 m_percentage 中。
|
||||
* 此代码不全,缺少与参数相关的操作。
|
||||
*/
|
||||
void BooleanStatisticVariable::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 将当前的 m_count 值保存到 m_countSaved 中
|
||||
m_countSaved = m_count;
|
||||
|
||||
// 计算 m_percentage(百分比):将 m_sum 转换为 double 类型并除以 m_countSaved
|
||||
m_percentage = ((double)m_sum) / ((double)m_countSaved);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:以指定的日志级别打印布尔统计变量的摘要信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:要使用的日志级别(例如:INFO、DEBUG、ERROR 等)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印布尔统计变量的摘要信息到日志中。摘要包括变量名称、样本数、百分比。
|
||||
* 打印的信息格式为 "变量名={ samples: 样本数, percent: 百分比%}"。
|
||||
*/
|
||||
void BooleanStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
// 使用MOT_LOG宏以指定的日志级别打印布尔统计变量的摘要信息
|
||||
MOT_LOG(logLevel, "%s={ samples: %" PRIu64 ", percent: %0.4f%%}", m_name, m_countSaved, m_percentage * 100.0f);
|
||||
}
|
||||
|
||||
void BooleanStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将布尔统计变量的值从另一个统计变量复制到当前变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要复制数据的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将布尔统计变量的值从另一个统计变量对象复制到当前变量对象。
|
||||
* 该函数在进行复制操作之前,将源对象转换为布尔统计变量类型。
|
||||
*/
|
||||
void BooleanStatisticVariable::Assign(const StatisticVariable &rhs)
|
||||
{
|
||||
const BooleanStatisticVariable& boolRhs = static_cast<const BooleanStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为布尔统计变量类型
|
||||
const BooleanStatisticVariable &boolRhs = static_cast<const BooleanStatisticVariable &>(rhs);
|
||||
|
||||
// 将源统计变量对象的 m_sum 复制到当前对象的 m_sum
|
||||
m_sum = boolRhs.m_sum;
|
||||
|
||||
// 将源统计变量对象的 m_count 复制到当前对象的 m_count
|
||||
m_count = boolRhs.m_count;
|
||||
}
|
||||
|
||||
void BooleanStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个布尔统计变量的值添加到当前统计变量的值上
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个布尔统计变量的值(包括 m_sum 和 m_count)添加到当前统计变量的值上。
|
||||
* 该函数在进行添加操作之前,将源对象转换为布尔统计变量类型。
|
||||
*/
|
||||
void BooleanStatisticVariable::Add(const StatisticVariable &rhs)
|
||||
{
|
||||
const BooleanStatisticVariable& boolRhs = static_cast<const BooleanStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为布尔统计变量类型
|
||||
const BooleanStatisticVariable &boolRhs = static_cast<const BooleanStatisticVariable &>(rhs);
|
||||
|
||||
// 将源统计变量对象的 m_sum 添加到当前对象的 m_sum
|
||||
m_sum += boolRhs.m_sum;
|
||||
|
||||
// 将源统计变量对象的 m_count 添加到当前对象的 m_count
|
||||
m_count += boolRhs.m_count;
|
||||
}
|
||||
|
||||
void BooleanStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:从当前布尔统计变量的值中减去另一个布尔统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中减去的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前布尔统计变量的值中减去另一个布尔统计变量的值(包括 m_sum 和 m_count)。
|
||||
* 该函数在进行减法操作之前,将源对象转换为布尔统计变量类型。
|
||||
*/
|
||||
void BooleanStatisticVariable::Subtract(const StatisticVariable &rhs)
|
||||
{
|
||||
const BooleanStatisticVariable& boolRhs = static_cast<const BooleanStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为布尔统计变量类型
|
||||
const BooleanStatisticVariable &boolRhs = static_cast<const BooleanStatisticVariable &>(rhs);
|
||||
|
||||
// 从当前对象的 m_sum 中减去源统计变量对象的 m_sum
|
||||
m_sum -= boolRhs.m_sum;
|
||||
|
||||
// 从当前对象的 m_count 中减去源统计变量对象的 m_count
|
||||
m_count -= boolRhs.m_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将当前布尔统计变量的值除以给定的因子
|
||||
*
|
||||
* 参数列表:
|
||||
* factor:除数,用于将当前统计变量的值除以它
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前布尔统计变量的值除以给定的因子(如果因子大于0)。
|
||||
* 它将 m_count 和 m_sum 成员变量都除以因子,以更新统计值。
|
||||
* 如果因子为0或负数,函数不执行任何操作。
|
||||
*/
|
||||
void BooleanStatisticVariable::Divide(uint32_t factor)
|
||||
{
|
||||
// 检查因子是否大于0
|
||||
if (factor > 0) {
|
||||
// 将当前对象的 m_count 除以因子
|
||||
m_count /= factor;
|
||||
|
||||
// 将当前对象的 m_sum 除以因子
|
||||
m_sum /= factor;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,52 +29,133 @@
|
|||
#include "utilities.h"
|
||||
|
||||
namespace MOT {
|
||||
/*
|
||||
* 功能:计算频率统计变量的摘要信息
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:一个布尔值,指示是否应该更新时间戳(可选)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于计算频率统计变量的摘要信息,包括计算频率值和时间间隔。
|
||||
* 如果 updateTstamp 参数为 true,则获取当前CPU周期计数作为时间戳。
|
||||
* 频率计算方式为将样本数 m_countSaved 除以时间间隔 m_intervalSeconds。
|
||||
*/
|
||||
void FrequencyStatisticVariable::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 如果需要更新时间戳,则获取当前CPU周期计数
|
||||
if (updateTstamp) {
|
||||
m_tstamp = CpuCyclesLevelTime::Rdtscp();
|
||||
}
|
||||
// 将当前的 m_count 值保存到 m_countSaved 中
|
||||
m_countSaved = m_count;
|
||||
// 计算时间间隔(秒):将当前时间戳减去初始时间戳,并将CPU周期转换为秒
|
||||
m_intervalSeconds = CpuCyclesLevelTime::CyclesToSeconds(m_tstamp - m_initTstamp);
|
||||
// 计算频率(Hz):将 m_countSaved 除以时间间隔
|
||||
m_frequency = ((double)(m_countSaved)) / m_intervalSeconds;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:以指定的日志级别打印频率统计变量的摘要信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:要使用的日志级别(例如:INFO、DEBUG、ERROR 等)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于以指定的日志级别打印频率统计变量的摘要信息到日志中。
|
||||
* 摘要包括变量名称、样本数、时间间隔、频率值。
|
||||
* 打印的信息格式为 "变量名={ samples: 样本数, interval: 时间间隔 seconds, frequency: 频率 (evt/sec) }"。
|
||||
*/
|
||||
void FrequencyStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
MOT_LOG(logLevel,
|
||||
"%s={ samples: %" PRIu64 ", interval: %0.2f seconds, frequency: %0.4f (evt/sec) }",
|
||||
m_name,
|
||||
m_countSaved,
|
||||
m_intervalSeconds,
|
||||
m_frequency);
|
||||
// 使用MOT_LOG宏以指定的日志级别打印频率统计变量的摘要信息
|
||||
MOT_LOG(logLevel, "%s={ samples: %" PRIu64 ", interval: %0.2f seconds, frequency: %0.4f (evt/sec) }",
|
||||
m_name, // 变量名
|
||||
m_countSaved, // 样本数
|
||||
m_intervalSeconds, // 时间间隔(秒)
|
||||
m_frequency // 频率(事件/秒)
|
||||
);
|
||||
}
|
||||
|
||||
void FrequencyStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将频率统计变量的值从另一个统计变量复制到当前变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要复制数据的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将频率统计变量的值从另一个统计变量对象复制到当前变量对象。
|
||||
* 该函数在进行复制操作之前,将源对象转换为频率统计变量类型。
|
||||
*/
|
||||
void FrequencyStatisticVariable::Assign(const StatisticVariable &rhs)
|
||||
{
|
||||
const FrequencyStatisticVariable& freqRhs = static_cast<const FrequencyStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为频率统计变量类型
|
||||
const FrequencyStatisticVariable &freqRhs = static_cast<const FrequencyStatisticVariable &>(rhs);
|
||||
|
||||
// 将源统计变量对象的 m_count 复制到当前对象的 m_count
|
||||
m_count = freqRhs.m_count;
|
||||
|
||||
// 将源统计变量对象的 m_initTstamp 复制到当前对象的 m_initTstamp
|
||||
m_initTstamp = freqRhs.m_initTstamp;
|
||||
|
||||
// 将源统计变量对象的 m_tstamp 复制到当前对象的 m_tstamp
|
||||
m_tstamp = freqRhs.m_tstamp;
|
||||
|
||||
// 将源统计变量对象的 m_frequency 复制到当前对象的 m_frequency
|
||||
m_frequency = freqRhs.m_frequency;
|
||||
}
|
||||
|
||||
void FrequencyStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个频率统计变量的值添加到当前统计变量的值上
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个频率统计变量的值(包括 m_count 和 m_frequency)添加到当前统计变量的值上。
|
||||
* 如果源对象的 m_initTstamp 更早,它会更新当前对象的 m_initTstamp。
|
||||
* 如果源对象的 m_tstamp 更晚,它会更新当前对象的 m_tstamp。
|
||||
*/
|
||||
void FrequencyStatisticVariable::Add(const StatisticVariable &rhs)
|
||||
{
|
||||
const FrequencyStatisticVariable& freqRhs = static_cast<const FrequencyStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为频率统计变量类型
|
||||
const FrequencyStatisticVariable &freqRhs = static_cast<const FrequencyStatisticVariable &>(rhs);
|
||||
|
||||
// 将源统计变量对象的 m_count 添加到当前对象的 m_count
|
||||
m_count += freqRhs.m_count;
|
||||
|
||||
// 将源统计变量对象的 m_frequency 添加到当前对象的 m_frequency
|
||||
m_frequency += freqRhs.m_frequency;
|
||||
|
||||
// 如果源统计变量对象的 m_initTstamp 更早,更新当前对象的 m_initTstamp
|
||||
if ((m_initTstamp == 0) || ((freqRhs.m_initTstamp > 0) && (freqRhs.m_initTstamp < m_initTstamp))) {
|
||||
m_initTstamp = freqRhs.m_initTstamp;
|
||||
}
|
||||
|
||||
// 如果源统计变量对象的 m_tstamp 更晚,更新当前对象的 m_tstamp
|
||||
if (freqRhs.m_tstamp > m_tstamp) {
|
||||
m_tstamp = freqRhs.m_tstamp;
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:从当前频率统计变量的值中减去另一个频率统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中减去的源统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前频率统计变量的值中减去另一个频率统计变量的值(包括 m_count)。
|
||||
* 如果源对象的 m_tstamp 不为零,则将其值赋给当前对象的 m_initTstamp。
|
||||
* 请注意,此操作后需要调用 Summarize 函数以重新计算频率。
|
||||
*/
|
||||
void FrequencyStatisticVariable::Subtract(const StatisticVariable &rhs)
|
||||
{
|
||||
const FrequencyStatisticVariable& freqRhs = static_cast<const FrequencyStatisticVariable&>(rhs);
|
||||
// 将源统计变量对象转换为频率统计变量类型
|
||||
const FrequencyStatisticVariable &freqRhs = static_cast<const FrequencyStatisticVariable &>(rhs);
|
||||
|
||||
// 从当前对象的 m_count 减去源统计变量对象的 m_count
|
||||
m_count -= freqRhs.m_count;
|
||||
|
||||
// 如果源统计变量对象的 m_tstamp 不为零,将其值赋给当前对象的 m_initTstamp
|
||||
if (freqRhs.m_tstamp != 0) {
|
||||
m_initTstamp = freqRhs.m_tstamp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,22 +27,51 @@
|
|||
namespace MOT {
|
||||
DECLARE_LOGGER(GlobalStatistics, Statistics)
|
||||
|
||||
/*
|
||||
* 功能:汇总全局统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:一个布尔值,指示是否应该更新时间戳(可选)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于汇总全局统计数据,遍历所有统计变量对象并调用它们的 Summarize 函数。
|
||||
* 如果 updateTstamp 参数为 true,则会将更新时间戳的请求传递给各个统计变量。
|
||||
*/
|
||||
void GlobalStatistics::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量并调用其 Summarize 函数
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Summarize(updateTstamp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印全局统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* statId:要打印的特定统计变量的索引(可选)
|
||||
* logLevel:要使用的日志级别(例如:INFO、DEBUG、ERROR 等)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印全局统计数据,可以选择打印特定索引的统计变量或打印所有统计变量。
|
||||
* 如果提供 statId 参数,则仅打印指定索引的统计变量(如果其样本数大于0)。
|
||||
* 否则,将遍历所有统计变量并打印样本数大于0的统计变量。
|
||||
*/
|
||||
void GlobalStatistics::Print(uint32_t statId, LogLevel logLevel) const
|
||||
{
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 如果提供 statId 参数,则仅打印指定索引的统计变量(如果其样本数大于0)
|
||||
if (statId < statCount) {
|
||||
if (m_statVars[statId]->GetSampleCount() > 0) {
|
||||
m_statVars[statId]->Print(logLevel);
|
||||
}
|
||||
} else {
|
||||
// 否则,遍历所有统计变量并打印样本数大于0的统计变量
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
if (m_statVars[i]->GetSampleCount() > 0) {
|
||||
m_statVars[i]->Print(logLevel);
|
||||
|
|
@ -51,47 +80,117 @@ void GlobalStatistics::Print(uint32_t statId, LogLevel logLevel) const
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置全局统计数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重置全局统计数据,它会遍历所有统计变量对象并调用它们的 Reset 函数。
|
||||
* 调用 Reset 函数将清除所有统计变量的计数和摘要信息,将它们重置为初始状态。
|
||||
*/
|
||||
void GlobalStatistics::Reset()
|
||||
{
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量并调用其 Reset 函数
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalStatistics::Assign(const GlobalStatistics& rhs)
|
||||
/*
|
||||
* 功能:将另一个全局统计对象的值分配给当前全局统计对象
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中复制数据的源全局统计对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个全局统计对象的值分配给当前全局统计对象。
|
||||
* 它会遍历所有统计变量对象,并调用它们的 Assign 函数以完成分配操作。
|
||||
*/
|
||||
void GlobalStatistics::Assign(const GlobalStatistics &rhs)
|
||||
{
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量并调用其 Assign 函数以完成分配操作
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Assign(*rhs.m_statVars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalStatistics::Subtract(const GlobalStatistics& rhs)
|
||||
/*
|
||||
* 功能:从当前全局统计对象的值中减去另一个全局统计对象的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中减去的源全局统计对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前全局统计对象的值中减去另一个全局统计对象的值。
|
||||
* 它会遍历所有统计变量对象,并调用它们的 Subtract 函数以完成减法操作。
|
||||
*/
|
||||
void GlobalStatistics::Subtract(const GlobalStatistics &rhs)
|
||||
{
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量并调用其 Subtract 函数以完成减法操作
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Subtract(*rhs.m_statVars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否存在有效的全局统计样本
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在至少一个具有有效样本的全局统计变量,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查是否存在至少一个具有有效样本的全局统计变量。
|
||||
* 它会遍历所有统计变量对象,并检查其样本数是否大于0。
|
||||
*/
|
||||
bool GlobalStatistics::HasValidSamples() const
|
||||
{
|
||||
// 初始化结果为 false
|
||||
bool result = false;
|
||||
|
||||
// 获取当前统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量并检查其样本数是否大于0
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
if (m_statVars[i]->GetSampleCount() > 0) {
|
||||
// 如果找到至少一个具有有效样本的统计变量,将结果设为 true 并退出循环
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return result;
|
||||
}
|
||||
|
||||
mot_string GlobalStatistics::MakeName(const char* baseName, NamingScheme namingScheme)
|
||||
/*
|
||||
* 功能:生成全局统计名称
|
||||
*
|
||||
* 参数列表:
|
||||
* baseName:基础名称,用作生成全局统计名称的前缀
|
||||
* namingScheme:命名方案,指定生成全局统计名称的方式
|
||||
*
|
||||
* 返回值:
|
||||
* 返回生成的全局统计名称。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于生成全局统计名称,根据指定的命名方案生成名称的后缀。
|
||||
* 基础名称 baseName 用作生成全局统计名称的前缀,根据不同的命名方案添加不同的后缀。
|
||||
*/
|
||||
mot_string GlobalStatistics::MakeName(const char *baseName, NamingScheme namingScheme)
|
||||
{
|
||||
// 初始化结果字符串
|
||||
mot_string result;
|
||||
|
||||
// 根据命名方案生成全局统计名称
|
||||
if ((namingScheme == NamingScheme::NAMING_SCHEME_TOTAL) ||
|
||||
(namingScheme == NamingScheme::NAMING_SCHEME_TOTAL_PREV)) {
|
||||
result.format("%s[TOTAL]", baseName);
|
||||
|
|
@ -99,14 +198,28 @@ mot_string GlobalStatistics::MakeName(const char* baseName, NamingScheme namingS
|
|||
result.format("%s[DIFF]", baseName);
|
||||
}
|
||||
|
||||
// 返回生成的全局统计名称
|
||||
return result;
|
||||
}
|
||||
|
||||
void GlobalStatistics::RegisterStatistics(StatisticVariable* statVar)
|
||||
/*
|
||||
* 功能:注册全局统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* statVar:要注册的全局统计变量对象的指针
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于注册全局统计变量。它将给定的统计变量对象指针添加到统计变量列表中。
|
||||
* 如果无法添加统计变量(由于内存不足),将生成错误日志。
|
||||
*/
|
||||
void GlobalStatistics::RegisterStatistics(StatisticVariable *statVar)
|
||||
{
|
||||
// 将给定的统计变量对象指针添加到统计变量列表中
|
||||
if (!m_statVars.push_back(statVar)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Register Statistics", "Failed to register statistics variable %s", statVar->GetName());
|
||||
// 如果无法添加统计变量(由于内存不足),生成错误日志
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Register Statistics", "Failed to register statistics variable %s",
|
||||
statVar->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -27,33 +27,69 @@
|
|||
#include "level_statistic_variable.h"
|
||||
|
||||
namespace MOT {
|
||||
/*
|
||||
* 功能:汇总级别统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:一个布尔值,指示是否应该更新时间戳(可选)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于汇总级别统计数据,它会根据指定的命名方案生成名称的后缀。
|
||||
* 如果提供 updateTstamp 参数并设置为 true,则会更新时间戳。
|
||||
* 汇总过程包括保存计数、级别和峰值,计算平均值和计算时间间隔。
|
||||
*/
|
||||
void LevelStatisticVariable::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 如果提供 updateTstamp 参数并设置为 true,则更新时间戳
|
||||
if (updateTstamp) {
|
||||
m_tstamp = CpuCyclesLevelTime::Rdtscp();
|
||||
}
|
||||
|
||||
// 保存计数、级别和峰值
|
||||
m_countSaved = m_count;
|
||||
m_levelSaved = m_level;
|
||||
m_peakSaved = m_peak;
|
||||
|
||||
// 计算平均值
|
||||
m_avg = ((double)m_integral) / ((double)(m_tstamp - m_initTstamp));
|
||||
|
||||
// 计算时间间隔
|
||||
m_intervalSeconds = CpuCyclesLevelTime::CyclesToSeconds(m_tstamp - m_initTstamp);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印级别统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:要使用的日志级别(例如:INFO、DEBUG、ERROR 等)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印级别统计数据,包括当前级别、峰值和样本数量。
|
||||
* 打印的信息包括统计变量的名称、当前级别、峰值、单位和样本数量。
|
||||
*/
|
||||
void LevelStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
MOT_LOG(logLevel,
|
||||
"%s={ current level: %" PRId64 " %s, peak: %" PRIu64 " %s [%" PRIu64 " samples] }",
|
||||
m_name,
|
||||
m_levelSaved / m_factor,
|
||||
m_units,
|
||||
m_peakSaved / m_factor,
|
||||
m_units,
|
||||
m_countSaved);
|
||||
// 使用指定的日志级别打印级别统计数据
|
||||
MOT_LOG(logLevel, "%s={ current level: %" PRId64 " %s, peak: %" PRIu64 " %s [%" PRIu64 " samples] }", m_name,
|
||||
m_levelSaved / m_factor, m_units, m_peakSaved / m_factor, m_units, m_countSaved);
|
||||
}
|
||||
|
||||
void LevelStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个级别统计变量的值分配给当前级别统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中复制数据的源级别统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个级别统计变量的值分配给当前级别统计变量。
|
||||
* 它将源级别统计变量的各个属性值复制到当前级别统计变量。
|
||||
*/
|
||||
void LevelStatisticVariable::Assign(const StatisticVariable &rhs)
|
||||
{
|
||||
const LevelStatisticVariable& levelRhs = static_cast<const LevelStatisticVariable&>(rhs);
|
||||
// 将源级别统计变量强制转换为 LevelStatisticVariable 类型
|
||||
const LevelStatisticVariable &levelRhs = static_cast<const LevelStatisticVariable &>(rhs);
|
||||
|
||||
// 复制源级别统计变量的各个属性值到当前级别统计变量
|
||||
m_level = levelRhs.m_level;
|
||||
m_peak = levelRhs.m_peak;
|
||||
m_tstamp = levelRhs.m_tstamp;
|
||||
|
|
@ -62,33 +98,80 @@ void LevelStatisticVariable::Assign(const StatisticVariable& rhs)
|
|||
m_count = levelRhs.m_count;
|
||||
}
|
||||
|
||||
void LevelStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个级别统计变量的值添加到当前级别统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加值的源级别统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个级别统计变量的值添加到当前级别统计变量。
|
||||
* 它会逐个属性进行相加,并在必要时更新时间戳。
|
||||
*/
|
||||
void LevelStatisticVariable::Add(const StatisticVariable &rhs)
|
||||
{
|
||||
const LevelStatisticVariable& levelRhs = static_cast<const LevelStatisticVariable&>(rhs);
|
||||
// 将源级别统计变量强制转换为 LevelStatisticVariable 类型
|
||||
const LevelStatisticVariable &levelRhs = static_cast<const LevelStatisticVariable &>(rhs);
|
||||
|
||||
// 逐个属性进行相加
|
||||
m_level += levelRhs.m_level;
|
||||
m_peak += levelRhs.m_peak;
|
||||
|
||||
// 如果源级别统计变量的时间戳大于当前级别统计变量的时间戳,则更新时间戳
|
||||
if (levelRhs.m_tstamp > m_tstamp) {
|
||||
m_tstamp = levelRhs.m_tstamp;
|
||||
}
|
||||
|
||||
// 如果源级别统计变量的初始时间戳小于当前级别统计变量的初始时间戳,则更新初始时间戳
|
||||
if (levelRhs.m_initTstamp < m_initTstamp) {
|
||||
m_initTstamp = levelRhs.m_initTstamp;
|
||||
}
|
||||
|
||||
// 逐个属性进行相加
|
||||
m_integral += levelRhs.m_integral;
|
||||
m_count += levelRhs.m_count;
|
||||
}
|
||||
|
||||
void LevelStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:从当前级别统计变量中减去另一个级别统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中减去值的源级别统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前级别统计变量中减去另一个级别统计变量的值。
|
||||
* 它会从当前级别统计变量的属性中减去源级别统计变量的属性值,
|
||||
* 但不会减去级别属性,因为即使在周期性差异报告中,我们仍然希望显示当前级别。
|
||||
* 此外,峰值无法在时间间隔内推断,除非我们主动维护它。
|
||||
*/
|
||||
void LevelStatisticVariable::Subtract(const StatisticVariable &rhs)
|
||||
{
|
||||
const LevelStatisticVariable& levelRhs = static_cast<const LevelStatisticVariable&>(rhs);
|
||||
// 将源级别统计变量强制转换为 LevelStatisticVariable 类型
|
||||
const LevelStatisticVariable &levelRhs = static_cast<const LevelStatisticVariable &>(rhs);
|
||||
|
||||
// 更新当前级别统计变量的初始时间戳
|
||||
m_initTstamp = levelRhs.m_tstamp;
|
||||
|
||||
// 从当前级别统计变量的属性中减去源级别统计变量的属性值
|
||||
m_integral -= levelRhs.m_integral;
|
||||
m_count -= levelRhs.m_count;
|
||||
// we do not subtract level, since even in periodic diff report we would like to show current level
|
||||
// in addition, peak cannot be inferred within interval, unless we actively maintain it
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将当前级别统计变量的值除以指定因子
|
||||
*
|
||||
* 参数列表:
|
||||
* factor:要除以的因子值
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前级别统计变量的属性值除以指定的因子。
|
||||
* 它会对计数、级别和积分属性进行除法运算,前提是因子大于零。
|
||||
*/
|
||||
void LevelStatisticVariable::Divide(uint32_t factor)
|
||||
{
|
||||
// 如果因子大于零,则将当前级别统计变量的属性值除以指定的因子
|
||||
if (factor > 0) {
|
||||
m_count /= factor;
|
||||
m_level /= factor;
|
||||
|
|
|
|||
|
|
@ -25,39 +25,117 @@
|
|||
#include "memory_statistic_variable.h"
|
||||
|
||||
namespace MOT {
|
||||
/*
|
||||
* 功能:打印内存统计变量的信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:要使用的日志级别(例如:INFO、DEBUG、ERROR 等)
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印内存统计变量的信息,包括内存级别和内存速率。
|
||||
* 它会分别打印内存级别和内存速率的信息,使用指定的日志级别。
|
||||
*/
|
||||
void MemoryStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
// 打印内存级别的信息
|
||||
m_level.Print(logLevel);
|
||||
|
||||
// 打印内存速率的信息
|
||||
m_rate.Print(logLevel);
|
||||
}
|
||||
|
||||
void MemoryStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个内存统计变量的值分配给当前内存统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中复制数据的源内存统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个内存统计变量的值分配给当前内存统计变量。
|
||||
* 它会分别复制计数、内存级别和内存速率的属性值。
|
||||
*/
|
||||
void MemoryStatisticVariable::Assign(const StatisticVariable &rhs)
|
||||
{
|
||||
const MemoryStatisticVariable& memRhs = (const MemoryStatisticVariable&)rhs;
|
||||
// 将源内存统计变量强制转换为 MemoryStatisticVariable 类型
|
||||
const MemoryStatisticVariable &memRhs = (const MemoryStatisticVariable &)rhs;
|
||||
|
||||
// 复制计数属性值
|
||||
m_count = memRhs.m_count;
|
||||
|
||||
// 调用 MemoryLevelStatisticVariable 对象的 Assign 函数,复制内存级别属性值
|
||||
m_level.Assign(memRhs.m_level);
|
||||
|
||||
// 调用 MemoryRateStatisticVariable 对象的 Assign 函数,复制内存速率属性值
|
||||
m_rate.Assign(memRhs.m_rate);
|
||||
}
|
||||
|
||||
void MemoryStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个内存统计变量的值添加到当前内存统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加值的源内存统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个内存统计变量的值添加到当前内存统计变量。
|
||||
* 它会分别对计数、内存级别和内存速率属性进行相加操作。
|
||||
*/
|
||||
void MemoryStatisticVariable::Add(const StatisticVariable &rhs)
|
||||
{
|
||||
const MemoryStatisticVariable& memRhs = (const MemoryStatisticVariable&)rhs;
|
||||
// 将源内存统计变量强制转换为 MemoryStatisticVariable 类型
|
||||
const MemoryStatisticVariable &memRhs = (const MemoryStatisticVariable &)rhs;
|
||||
|
||||
// 对计数属性进行相加操作
|
||||
m_count += memRhs.m_count;
|
||||
|
||||
// 调用 MemoryLevelStatisticVariable 对象的 Add 函数,对内存级别属性进行相加操作
|
||||
m_level.Add(memRhs.m_level);
|
||||
|
||||
// 调用 MemoryRateStatisticVariable 对象的 Add 函数,对内存速率属性进行相加操作
|
||||
m_rate.Add(memRhs.m_rate);
|
||||
}
|
||||
|
||||
void MemoryStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:从当前内存统计变量中减去另一个内存统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要从中减去值的源内存统计变量
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前内存统计变量中减去另一个内存统计变量的值。
|
||||
* 它会分别对计数、内存级别和内存速率属性进行减法运算。
|
||||
*/
|
||||
void MemoryStatisticVariable::Subtract(const StatisticVariable &rhs)
|
||||
{
|
||||
const MemoryStatisticVariable& memRhs = (const MemoryStatisticVariable&)rhs;
|
||||
// 将源内存统计变量强制转换为 MemoryStatisticVariable 类型
|
||||
const MemoryStatisticVariable &memRhs = (const MemoryStatisticVariable &)rhs;
|
||||
|
||||
// 对计数属性进行减法运算
|
||||
m_count -= memRhs.m_count;
|
||||
|
||||
// 调用 MemoryLevelStatisticVariable 对象的 Subtract 函数,对内存级别属性进行减法运算
|
||||
m_level.Subtract(memRhs.m_level);
|
||||
|
||||
// 调用 MemoryRateStatisticVariable 对象的 Subtract 函数,对内存速率属性进行减法运算
|
||||
m_rate.Subtract(memRhs.m_rate);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将当前内存统计变量的值除以指定因子
|
||||
*
|
||||
* 参数列表:
|
||||
* factor:要除以的因子值
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前内存统计变量的属性值除以指定的因子。
|
||||
* 它会调用 MemoryLevelStatisticVariable 和 MemoryRateStatisticVariable 对象的
|
||||
* Divide 函数,分别对内存级别和内存速率属性进行除法运算。
|
||||
*/
|
||||
void MemoryStatisticVariable::Divide(uint32_t factor)
|
||||
{
|
||||
// 调用 MemoryLevelStatisticVariable 对象的 Divide 函数,对内存级别属性进行除法运算
|
||||
m_level.Divide(factor);
|
||||
|
||||
// 调用 MemoryRateStatisticVariable 对象的 Divide 函数,对内存速率属性进行除法运算
|
||||
m_rate.Divide(factor);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,79 +29,206 @@
|
|||
|
||||
namespace MOT {
|
||||
NumericStatisticVariable::NumericStatisticVariable(
|
||||
const char* name, uint64_t factor /* = 1 */, const char* units /* = "" */, uint64_t limit /* = ULONG_LONG_MAX */)
|
||||
: StatisticVariable(name),
|
||||
m_factor(factor),
|
||||
m_limit(limit),
|
||||
m_sum(0),
|
||||
m_squareSum(0),
|
||||
m_avg(0.0f),
|
||||
m_var(0.0f),
|
||||
m_std(0.0f),
|
||||
m_countSaved(0)
|
||||
/*
|
||||
* 功能:构造一个内存级别统计变量对象
|
||||
*
|
||||
* 参数列表:
|
||||
* name:统计变量的名称
|
||||
* factor:缩放因子,默认为 1
|
||||
* units:单位字符串,默认为空字符串
|
||||
* limit:统计变量的上限,默认为 ULONG_LONG_MAX
|
||||
*
|
||||
* 注意:
|
||||
* 此构造函数用于初始化内存级别统计变量对象的属性。
|
||||
* 它接受统计变量的名称、缩放因子、单位字符串和上限值,并将其设置为对象的属性。
|
||||
*/
|
||||
MemoryLevelStatisticVariable::MemoryLevelStatisticVariable(const char* name, uint64_t factor /* = 1 */, const char* units /* = "" */, uint64_t limit /* = ULONG_LONG_MAX */)
|
||||
: StatisticVariable(name), // 调用基类 StatisticVariable 的构造函数来初始化名称属性
|
||||
m_factor(factor), // 初始化缩放因子属性
|
||||
m_limit(limit), // 初始化上限属性
|
||||
m_sum(0), // 初始化总和属性
|
||||
m_squareSum(0), // 初始化平方总和属性
|
||||
m_avg(0.0f), // 初始化平均值属性
|
||||
m_var(0.0f), // 初始化方差属性
|
||||
m_std(0.0f), // 初始化标准差属性
|
||||
m_countSaved(0) // 初始化保存计数属性
|
||||
{
|
||||
// 使用 snprintf_s 函数将单位字符串复制到 m_units 属性中,确保不超出最大长度 STAT_VAR_MAX_NAME_LEN
|
||||
errno_t erc = snprintf_s(m_units, STAT_VAR_MAX_NAME_LEN, STAT_VAR_MAX_NAME_LEN - 1, "%s", units);
|
||||
securec_check_ss(erc, "\0", "\0");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:汇总数值统计变量的信息
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:是否更新时间戳,此参数未使用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于汇总数值统计变量的信息,包括计数、平均值、方差和标准差。
|
||||
* 它会根据当前的总和和计数属性计算平均值,然后计算方差和标准差。
|
||||
* 参数 `updateTstamp` 未使用。
|
||||
*/
|
||||
void NumericStatisticVariable::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 参数 `updateTstamp` 未使用,避免编译器警告
|
||||
(void)updateTstamp;
|
||||
|
||||
// 将当前计数值保存到 m_countSaved 属性
|
||||
m_countSaved = m_count;
|
||||
|
||||
// 计算平均值,使用总和属性和计数属性
|
||||
m_avg = ((double)m_sum) / ((double)m_countSaved);
|
||||
|
||||
// 计算方差,使用平方总和属性和平均值
|
||||
m_var = ((double)m_squareSum) - m_avg * m_avg;
|
||||
|
||||
// 计算标准差,使用方差并调用 sqrt 函数
|
||||
m_std = sqrt(m_var);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:打印数值统计变量的信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:日志级别,指定打印信息的详细程度
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将数值统计变量的信息以日志形式打印出来,包括样本数、平均值和标准差。
|
||||
* 它将打印的信息格式化并使用指定的日志级别输出。
|
||||
*/
|
||||
void NumericStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
MOT_LOG(logLevel,
|
||||
"%s={ samples: %" PRIu64 ", avg: %0.4f %s, std: %0.4f %s }",
|
||||
m_name,
|
||||
m_countSaved,
|
||||
m_avg / m_factor,
|
||||
m_units,
|
||||
m_std / m_factor,
|
||||
m_units);
|
||||
// 使用 MOT_LOG 函数将数值统计变量的信息以指定格式和日志级别打印出来
|
||||
MOT_LOG(logLevel, "%s={ samples: %" PRIu64 ", avg: %0.4f %s, std: %0.4f %s }", m_name, m_countSaved,
|
||||
m_avg / m_factor, m_units, m_std / m_factor, m_units);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:将数值统计变量的值赋给当前数值统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要赋值的源数值统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将源数值统计变量对象的值赋给当前数值统计变量对象。
|
||||
* 它将总和、平方总和和计数属性分别赋值为源对象的对应属性值。
|
||||
*/
|
||||
void NumericStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
{
|
||||
auto numRhs = static_cast<const NumericStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 NumericStatisticVariable 类型
|
||||
auto numRhs = static_cast<const NumericStatisticVariable &>(rhs);
|
||||
|
||||
// 将总和属性赋值为源对象的总和属性值
|
||||
m_sum = numRhs.m_sum;
|
||||
|
||||
// 将平方总和属性赋值为源对象的平方总和属性值
|
||||
m_squareSum = numRhs.m_squareSum;
|
||||
|
||||
// 将计数属性赋值为源对象的计数属性值
|
||||
m_count = numRhs.m_count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:将另一个数值统计变量的值添加到当前数值统计变量的值中
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加的源数值统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个数值统计变量对象的值添加到当前数值统计变量对象的值中。
|
||||
* 它将总和、平方总和和计数属性分别相加。
|
||||
*/
|
||||
void NumericStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
{
|
||||
auto numRhs = static_cast<const NumericStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 NumericStatisticVariable 类型
|
||||
auto numRhs = static_cast<const NumericStatisticVariable &>(rhs);
|
||||
|
||||
// 将总和属性相加
|
||||
m_sum += numRhs.m_sum;
|
||||
|
||||
// 将平方总和属性相加
|
||||
m_squareSum += numRhs.m_squareSum;
|
||||
|
||||
// 将计数属性相加
|
||||
m_count += numRhs.m_count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:从当前数值统计变量的值中减去另一个数值统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要减去的源数值统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前数值统计变量对象的值中减去另一个数值统计变量对象的值。
|
||||
* 它将总和、平方总和和计数属性分别减去。
|
||||
*/
|
||||
void NumericStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
{
|
||||
auto numRhs = static_cast<const NumericStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 NumericStatisticVariable 类型
|
||||
auto numRhs = static_cast<const NumericStatisticVariable &>(rhs);
|
||||
|
||||
// 从总和属性中减去源对象的总和属性值
|
||||
m_sum -= numRhs.m_sum;
|
||||
|
||||
// 从平方总和属性中减去源对象的平方总和属性值
|
||||
m_squareSum -= numRhs.m_squareSum;
|
||||
|
||||
// 从计数属性中减去源对象的计数属性值
|
||||
m_count -= numRhs.m_count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:将当前数值统计变量的值除以指定因子
|
||||
*
|
||||
* 参数列表:
|
||||
* factor:除以的因子,必须大于零
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前数值统计变量对象的值除以指定因子。
|
||||
* 它将计数属性、总和属性和平方总和属性分别除以因子值。
|
||||
*/
|
||||
void NumericStatisticVariable::Divide(uint32_t factor)
|
||||
{
|
||||
// 检查因子是否大于零
|
||||
if (factor > 0) {
|
||||
// 将计数属性除以因子
|
||||
m_count /= factor;
|
||||
|
||||
// 将总和属性除以因子
|
||||
m_sum /= factor;
|
||||
|
||||
// 将平方总和属性除以因子
|
||||
m_squareSum /= factor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 功能:重置数值统计变量的值
|
||||
*
|
||||
* 参数列表:无
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将数值统计变量的值重置为零。它将总和属性、平方总和属性和计数属性都设置为零。
|
||||
*/
|
||||
void NumericStatisticVariable::Reset()
|
||||
{
|
||||
// 将总和属性设置为零
|
||||
m_sum = 0;
|
||||
|
||||
// 将平方总和属性设置为零
|
||||
m_squareSum = 0;
|
||||
|
||||
// 将计数属性设置为零
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -28,85 +28,191 @@
|
|||
#include "utilities.h"
|
||||
|
||||
namespace MOT {
|
||||
/*
|
||||
* 功能:计算速率统计变量的摘要信息
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:指示是否需要更新时间戳
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于计算速率统计变量的摘要信息,包括速率、频率等信息。
|
||||
* 它可以选择是否更新时间戳,并根据时间戳计算间隔秒数、速率和频率。
|
||||
*/
|
||||
void RateStatisticVariable::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 如果需要更新时间戳
|
||||
if (updateTstamp) {
|
||||
// 获取当前时间戳
|
||||
m_tstamp = CpuCyclesLevelTime::Rdtscp();
|
||||
}
|
||||
// 保存当前计数
|
||||
m_countSaved = m_count;
|
||||
// 保存当前总和
|
||||
m_sumSaved = m_sum;
|
||||
// 计算间隔秒数
|
||||
m_intervalSeconds = CpuCyclesLevelTime::CyclesToSeconds(m_tstamp - m_initTstamp);
|
||||
// 计算速率
|
||||
m_rate = ((double)(m_sumSaved)) / m_intervalSeconds / m_factor;
|
||||
// 计算频率
|
||||
m_frequency = ((double)(m_countSaved)) / m_intervalSeconds;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印速率统计变量的信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:日志级别,指示打印信息的详细程度
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印速率统计变量的信息,包括速率、频率和样本数等信息。
|
||||
*/
|
||||
void RateStatisticVariable::Print(LogLevel logLevel) const
|
||||
{
|
||||
MOT_LOG(logLevel,
|
||||
"%s={ rate: %0.4f (%s/sec), frequency: %0.4f (evt/sec) [%" PRIu64 " samples] }",
|
||||
m_name,
|
||||
m_rate,
|
||||
m_units,
|
||||
m_frequency,
|
||||
m_countSaved);
|
||||
// 使用指定的日志级别打印信息
|
||||
MOT_LOG(logLevel, "%s={ rate: %0.4f (%s/sec), frequency: %0.4f (evt/sec) [%" PRIu64 " samples] }", m_name, m_rate,
|
||||
m_units, m_frequency, m_countSaved);
|
||||
}
|
||||
|
||||
void RateStatisticVariable::Assign(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个速率统计变量的值分配给当前速率统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要分配的源速率统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个速率统计变量对象的值分配给当前速率统计变量对象。
|
||||
* 它将计数、总和、时间戳、速率、时间间隔、频率等属性都分配给当前对象。
|
||||
*/
|
||||
void RateStatisticVariable::Assign(const StatisticVariable &rhs)
|
||||
{
|
||||
auto rateRhs = static_cast<const RateStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 RateStatisticVariable 类型
|
||||
auto rateRhs = static_cast<const RateStatisticVariable &>(rhs);
|
||||
// 分配计数属性
|
||||
m_count = rateRhs.m_count;
|
||||
// 分配总和属性
|
||||
m_sum = rateRhs.m_sum;
|
||||
// 分配初始时间戳属性
|
||||
m_initTstamp = rateRhs.m_initTstamp;
|
||||
// 分配时间戳属性
|
||||
m_tstamp = rateRhs.m_tstamp;
|
||||
// 分配速率属性
|
||||
m_rate = rateRhs.m_rate;
|
||||
// 分配时间间隔属性
|
||||
m_intervalSeconds = rateRhs.m_intervalSeconds;
|
||||
// 分配频率属性
|
||||
m_frequency = rateRhs.m_frequency;
|
||||
// 分配保存的计数属性
|
||||
m_countSaved = rateRhs.m_count;
|
||||
// 分配保存的总和属性
|
||||
m_sumSaved = rateRhs.m_sumSaved;
|
||||
}
|
||||
|
||||
void RateStatisticVariable::Add(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:将另一个速率统计变量的值添加到当前速率统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要添加的源速率统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将另一个速率统计变量对象的值添加到当前速率统计变量对象。
|
||||
* 它将计数、总和、速率、频率、时间戳等属性相应地添加到当前对象,并更新初始时间戳。
|
||||
*/
|
||||
void RateStatisticVariable::Add(const StatisticVariable &rhs)
|
||||
{
|
||||
auto rateRhs = static_cast<const RateStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 RateStatisticVariable 类型
|
||||
auto rateRhs = static_cast<const RateStatisticVariable &>(rhs);
|
||||
// 添加计数属性
|
||||
m_count += rateRhs.m_countSaved;
|
||||
// 添加总和属性
|
||||
m_sum += rateRhs.m_sumSaved;
|
||||
// 添加速率属性
|
||||
m_rate += rateRhs.m_rate;
|
||||
// 添加频率属性
|
||||
m_frequency += rateRhs.m_frequency;
|
||||
// 如果当前对象的初始时间戳为零,或者源对象的初始时间戳大于零且小于当前对象的初始时间戳
|
||||
if ((m_initTstamp == 0) || ((rateRhs.m_initTstamp > 0) && (rateRhs.m_initTstamp < m_initTstamp))) {
|
||||
// 更新当前对象的初始时间戳
|
||||
m_initTstamp = rateRhs.m_initTstamp;
|
||||
}
|
||||
// 如果源对象的时间戳大于当前对象的时间戳
|
||||
if (rateRhs.m_tstamp > m_tstamp) {
|
||||
// 更新当前对象的时间戳
|
||||
m_tstamp = rateRhs.m_tstamp;
|
||||
}
|
||||
}
|
||||
|
||||
void RateStatisticVariable::Subtract(const StatisticVariable& rhs)
|
||||
/*
|
||||
* 功能:从当前速率统计变量中减去另一个速率统计变量的值
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要减去的源速率统计变量对象
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前速率统计变量对象中减去另一个速率统计变量对象的值。
|
||||
* 它减去计数、总和属性,并在需要时更新初始时间戳。
|
||||
*/
|
||||
void RateStatisticVariable::Subtract(const StatisticVariable &rhs)
|
||||
{
|
||||
auto rateRhs = static_cast<const RateStatisticVariable&>(rhs);
|
||||
// 将源对象强制转换为 RateStatisticVariable 类型
|
||||
auto rateRhs = static_cast<const RateStatisticVariable &>(rhs);
|
||||
// 减去计数属性
|
||||
m_count -= rateRhs.m_count;
|
||||
// 减去总和属性
|
||||
m_sum -= rateRhs.m_sum;
|
||||
// 如果源对象的时间戳不为零
|
||||
if (rateRhs.m_tstamp != 0) {
|
||||
// 更新当前对象的初始时间戳为源对象的时间戳
|
||||
m_initTstamp = rateRhs.m_tstamp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将当前速率统计变量的属性值除以指定的因子
|
||||
*
|
||||
* 参数列表:
|
||||
* factor:除数因子,用于除以当前速率统计变量的属性值
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前速率统计变量的计数、速率、频率属性值除以指定的因子。
|
||||
* 如果因子大于零,则会执行除法操作,否则不执行操作。
|
||||
*/
|
||||
void RateStatisticVariable::Divide(uint32_t factor)
|
||||
{
|
||||
// 如果因子大于零
|
||||
if (factor > 0) {
|
||||
// 除以指定的因子,更新计数属性
|
||||
m_count /= factor;
|
||||
// 除以指定的因子,更新速率属性
|
||||
m_rate /= factor;
|
||||
// 除以指定的因子,更新频率属性
|
||||
m_frequency /= factor;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置当前速率统计变量的属性值
|
||||
*
|
||||
* 参数列表:无
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将当前速率统计变量的属性值重置为初始状态,包括计数、总和、速率、频率、时间戳等属性。
|
||||
*/
|
||||
void RateStatisticVariable::Reset()
|
||||
{
|
||||
// 重置计数属性为零
|
||||
m_count = 0;
|
||||
// 重置总和属性为零
|
||||
m_sum = 0;
|
||||
// 重置速率属性为零
|
||||
m_rate = 0.0f;
|
||||
// 重置频率属性为零
|
||||
m_frequency = 0.0f;
|
||||
// 重置初始时间戳属性为零
|
||||
m_initTstamp = 0;
|
||||
// 重置时间戳属性为零
|
||||
m_tstamp = 0;
|
||||
// 重置时间间隔属性为零
|
||||
m_intervalSeconds = 0.0f;
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
namespace MOT {
|
||||
DECLARE_LOGGER(StatisticsManager, Statistics)
|
||||
|
||||
StatisticsManager* StatisticsManager::m_manager = nullptr;
|
||||
StatisticsManager *StatisticsManager::m_manager = nullptr;
|
||||
|
||||
#define STAT_PRINT_CHECK_PERIOD_SECONDS 1
|
||||
|
||||
|
|
@ -51,41 +51,66 @@ StatisticsManager* StatisticsManager::m_manager = nullptr;
|
|||
break; \
|
||||
}
|
||||
|
||||
static bool CreateRecursiveMutex(pthread_mutex_t* mutex)
|
||||
/*
|
||||
* 功能:创建递归互斥锁
|
||||
*
|
||||
* 参数列表:
|
||||
* mutex:pthread_mutex_t 类型的指针,用于存储创建的互斥锁对象
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功创建互斥锁,则返回 true;否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于创建一个递归互斥锁,并将其存储在提供的 mutex 指针中。
|
||||
* 如果创建成功,函数返回 true,否则返回 false。
|
||||
* 互斥锁属性配置为递归类型,以支持递归锁定。
|
||||
*/
|
||||
static bool CreateRecursiveMutex(pthread_mutex_t *mutex)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 初始化互斥锁属性
|
||||
pthread_mutexattr_t lockattr;
|
||||
int rc = pthread_mutexattr_init(&lockattr);
|
||||
if (rc != 0) {
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc,
|
||||
pthread_mutexattr_init,
|
||||
"Statistics Manager Initialization",
|
||||
"Failed to initialize recursive mutex attribute for the statistics manager");
|
||||
// 报告初始化互斥锁属性失败的系统错误
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc, pthread_mutexattr_init, "Statistics Manager Initialization",
|
||||
"Failed to initialize recursive mutex attribute for the statistics manager");
|
||||
} else {
|
||||
// 配置互斥锁属性为递归类型
|
||||
rc = pthread_mutexattr_settype(&lockattr, PTHREAD_MUTEX_RECURSIVE);
|
||||
if (rc != 0) {
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc,
|
||||
pthread_mutexattr_settype,
|
||||
"Statistics Manager Initialization",
|
||||
"Failed to configure recursive mutex type for the statistics manager");
|
||||
// 报告配置互斥锁类型失败的系统错误
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc, pthread_mutexattr_settype, "Statistics Manager Initialization",
|
||||
"Failed to configure recursive mutex type for the statistics manager");
|
||||
} else {
|
||||
// 创建递归互斥锁
|
||||
rc = pthread_mutex_init(mutex, &lockattr);
|
||||
if (rc != 0) {
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc,
|
||||
pthread_mutex_init,
|
||||
"Statistics Manager Initialization",
|
||||
"Failed to create recursive mutex for the statistics manager");
|
||||
// 报告创建互斥锁失败的系统错误
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc, pthread_mutex_init, "Statistics Manager Initialization",
|
||||
"Failed to create recursive mutex for the statistics manager");
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
// 销毁互斥锁属性
|
||||
pthread_mutexattr_destroy(&lockattr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:初始化统计管理器
|
||||
*
|
||||
* 返回值:
|
||||
* 如果初始化成功,返回 true;否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化统计管理器,包括创建互斥锁、条件变量和注册配置更改通知。
|
||||
* 在初始化的过程中,函数会检查每个步骤的结果,如果出现错误,将返回 false。
|
||||
*/
|
||||
bool StatisticsManager::Initialize()
|
||||
{
|
||||
bool result = true;
|
||||
|
|
@ -102,6 +127,7 @@ bool StatisticsManager::Initialize()
|
|||
CHECK_INIT_STATUS(result, "Failed to create statistics printing lock");
|
||||
m_initPhase = INIT_STAT_PRINT_LOCK;
|
||||
|
||||
// 初始化统计打印条件变量
|
||||
int rc = pthread_cond_init(&m_statsPrintCond, nullptr);
|
||||
result = (rc == 0);
|
||||
CHECK_SYS_INIT_STATUS(rc, pthread_cond_init, "Failed to initialize statistics printing condition variable");
|
||||
|
|
@ -126,7 +152,13 @@ StatisticsManager::StatisticsManager()
|
|||
{
|
||||
// the statistics thread should be started explicitly
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:统计管理器的析构函数
|
||||
*
|
||||
* 注意:
|
||||
* 此析构函数用于释放统计管理器的资源,包括停止统计打印线程、取消注册配置更改通知、
|
||||
* 销毁条件变量和互斥锁。在析构的过程中,根据初始化阶段执行相应的资源释放操作。
|
||||
*/
|
||||
StatisticsManager::~StatisticsManager()
|
||||
{
|
||||
switch (m_initPhase) {
|
||||
|
|
@ -151,53 +183,103 @@ StatisticsManager::~StatisticsManager()
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:创建 StatisticsManager 实例
|
||||
*
|
||||
* 返回值:
|
||||
* 如果创建成功,返回 true;否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于创建 StatisticsManager 实例,并在创建后进行初始化。它确保只有一个实例被创建。
|
||||
* 如果内存分配或初始化失败,将会记录错误并返回 false。
|
||||
*/
|
||||
bool StatisticsManager::CreateInstance()
|
||||
{
|
||||
bool result = false;
|
||||
MOT_ASSERT(m_manager == nullptr);
|
||||
|
||||
// 检查是否已经存在 StatisticsManager 实例
|
||||
if (m_manager == nullptr) {
|
||||
// 尝试分配 StatisticsManager 实例内存
|
||||
m_manager = new (std::nothrow) StatisticsManager();
|
||||
if (!m_manager) {
|
||||
MOT_LOG_ERROR("Failed to allocate memory for statistics manager, aborting");
|
||||
SetLastError(MOT_ERROR_OOM, MOT_SEVERITY_FATAL);
|
||||
} else {
|
||||
// 初始化 StatisticsManager 实例
|
||||
result = m_manager->Initialize();
|
||||
if (!result) {
|
||||
// 初始化失败,释放已分配的内存
|
||||
delete m_manager;
|
||||
m_manager = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:销毁 StatisticsManager 实例
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于销毁 StatisticsManager 实例。它会释放实例的内存,并将实例指针设为 nullptr。
|
||||
* 在销毁前,会检查是否存在有效的实例,以确保安全销毁。
|
||||
*/
|
||||
void StatisticsManager::DestroyInstance()
|
||||
{
|
||||
MOT_ASSERT(m_manager != nullptr);
|
||||
// 检查是否存在有效的实例
|
||||
if (m_manager != nullptr) {
|
||||
// 释放实例内存
|
||||
delete m_manager;
|
||||
m_manager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
StatisticsManager& StatisticsManager::GetInstance()
|
||||
/*
|
||||
* 功能:获取 StatisticsManager 实例的引用
|
||||
*
|
||||
* 返回值:
|
||||
* 返回 StatisticsManager 实例的引用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于获取 StatisticsManager 实例的引用,以便在应用程序中使用单一实例进行统计管理。
|
||||
* 在获取引用前,会检查是否存在有效的实例,以确保安全使用。
|
||||
*/
|
||||
StatisticsManager &StatisticsManager::GetInstance()
|
||||
{
|
||||
MOT_ASSERT(m_manager != nullptr);
|
||||
|
||||
// 检查是否存在有效的实例
|
||||
return *m_manager;
|
||||
}
|
||||
|
||||
bool StatisticsManager::RegisterStatisticsProvider(StatisticsProvider* statisticsProvider)
|
||||
/*
|
||||
* 功能:注册统计信息提供者
|
||||
*
|
||||
* 参数列表:
|
||||
* statisticsProvider:要注册的统计信息提供者对象指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果注册成功,返回 true;否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于注册统计信息提供者,以便统计管理器可以收集其提供的统计数据。
|
||||
* 在注册提供者前,会检查是否已经注册,以确保不会重复注册。
|
||||
*/
|
||||
bool StatisticsManager::RegisterStatisticsProvider(StatisticsProvider *statisticsProvider)
|
||||
{
|
||||
bool result = false;
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
mot_list<StatisticsProvider*>::iterator itr = find(m_providers.begin(), m_providers.end(), statisticsProvider);
|
||||
// 查找提供者是否已经注册
|
||||
mot_list<StatisticsProvider *>::iterator itr = find(m_providers.begin(), m_providers.end(), statisticsProvider);
|
||||
if (itr == m_providers.end()) {
|
||||
// 如果提供者尚未注册,则尝试注册
|
||||
if (!m_providers.push_back(statisticsProvider)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Register Statistics",
|
||||
"Failed to register statistics provider %s",
|
||||
statisticsProvider->GetName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Register Statistics", "Failed to register statistics provider %s",
|
||||
statisticsProvider->GetName());
|
||||
} else {
|
||||
result = true;
|
||||
MOT_LOG_TRACE("Registered statistics provider: %s", statisticsProvider->GetName());
|
||||
|
|
@ -208,13 +290,28 @@ bool StatisticsManager::RegisterStatisticsProvider(StatisticsProvider* statistic
|
|||
return result;
|
||||
}
|
||||
|
||||
bool StatisticsManager::UnregisterStatisticsProvider(StatisticsProvider* statisticsProvider)
|
||||
/*
|
||||
* 功能:取消注册统计信息提供者
|
||||
*
|
||||
* 参数列表:
|
||||
* statisticsProvider:要取消注册的统计信息提供者对象指针
|
||||
*
|
||||
* 返回值:
|
||||
* 如果取消注册成功,返回 true;否则返回 false
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于取消注册统计信息提供者,以停止统计管理器收集其提供的统计数据。
|
||||
* 在取消注册提供者前,会检查是否已经注册,以确保不会重复取消注册。
|
||||
*/
|
||||
bool StatisticsManager::UnregisterStatisticsProvider(StatisticsProvider *statisticsProvider)
|
||||
{
|
||||
bool result = false;
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
mot_list<StatisticsProvider*>::iterator itr = find(m_providers.begin(), m_providers.end(), statisticsProvider);
|
||||
// 查找提供者是否已经注册
|
||||
mot_list<StatisticsProvider *>::iterator itr = find(m_providers.begin(), m_providers.end(), statisticsProvider);
|
||||
if (itr != m_providers.end()) {
|
||||
// 如果提供者已经注册,则取消注册
|
||||
m_providers.erase(itr);
|
||||
MOT_LOG_TRACE("Unregistered statistics provider: %s", statisticsProvider->GetName());
|
||||
result = true;
|
||||
|
|
@ -224,26 +321,53 @@ bool StatisticsManager::UnregisterStatisticsProvider(StatisticsProvider* statist
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:处理配置更改通知
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于响应配置更改通知,更新统计信息打印的周期参数。
|
||||
* 它会根据全局配置文件中的配置值,更新统计信息的打印周期和全量统计信息的打印周期。
|
||||
*/
|
||||
void StatisticsManager::OnConfigChange()
|
||||
{
|
||||
// 从全局配置中获取统计信息打印周期参数
|
||||
m_statsPrintPeriodSeconds = GetGlobalConfiguration().m_statPrintPeriodSeconds;
|
||||
// 从全局配置中获取全量统计信息打印周期参数
|
||||
m_fullStatsPrintPeriodSeconds = GetGlobalConfiguration().m_statPrintFullPeriodSeconds;
|
||||
}
|
||||
|
||||
// functor for std::find_if()
|
||||
struct StatisticsProviderFinder {
|
||||
const char* m_name;
|
||||
const char *m_name;
|
||||
|
||||
explicit StatisticsProviderFinder(const char* name) : m_name(name)
|
||||
explicit StatisticsProviderFinder(const char *name) : m_name(name)
|
||||
{}
|
||||
|
||||
inline bool operator()(StatisticsProvider* const& provider) const
|
||||
inline bool operator()(StatisticsProvider *const &provider) const
|
||||
{
|
||||
return strcmp(m_name, provider->GetName()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// functor for std::for_each()
|
||||
/*
|
||||
* 结构体:StatisticsProviderPrinter
|
||||
*
|
||||
* 功能:用于打印统计信息提供者的统计数据
|
||||
*
|
||||
* 成员变量:
|
||||
* m_logLevel:指定打印日志的级别
|
||||
* m_statOpts:指定统计选项,用于过滤要打印的统计数据
|
||||
*
|
||||
* 构造函数:
|
||||
* StatisticsProviderPrinter(LogLevel logLevel, uint32_t statOpts):
|
||||
* 初始化结构体的成员变量,包括打印日志级别和统计选项。
|
||||
*
|
||||
* 操作符重载:
|
||||
* inline void operator()(StatisticsProvider* const& provider):
|
||||
* 用于调用统计信息提供者的打印统计数据函数,前提是提供者已启用。
|
||||
* 如果提供者已启用,将使用指定的日志级别和统计选项来打印其统计数据。
|
||||
*/
|
||||
struct StatisticsProviderPrinter {
|
||||
LogLevel m_logLevel;
|
||||
|
||||
|
|
@ -253,7 +377,7 @@ struct StatisticsProviderPrinter {
|
|||
: m_logLevel(logLevel), m_statOpts(statOpts)
|
||||
{}
|
||||
|
||||
inline void operator()(StatisticsProvider* const& provider)
|
||||
inline void operator()(StatisticsProvider *const &provider)
|
||||
{
|
||||
if (provider->IsEnabled()) {
|
||||
provider->PrintStatistics(m_logLevel, m_statOpts);
|
||||
|
|
@ -261,13 +385,28 @@ struct StatisticsProviderPrinter {
|
|||
}
|
||||
};
|
||||
|
||||
StatisticsProvider* StatisticsManager::GetStatisticsProvider(const char* name)
|
||||
/*
|
||||
* 功能:根据名称获取统计信息提供者
|
||||
*
|
||||
* 参数列表:
|
||||
* name:要查找的统计信息提供者的名称
|
||||
*
|
||||
* 返回值:
|
||||
* 如果找到匹配名称的统计信息提供者,则返回该提供者的指针;否则返回 nullptr。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据名称查找并获取统计信息提供者的指针。
|
||||
*/
|
||||
StatisticsProvider *StatisticsManager::GetStatisticsProvider(const char *name)
|
||||
{
|
||||
StatisticsProvider* result = nullptr;
|
||||
StatisticsProvider *result = nullptr;
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
mot_list<StatisticsProvider*>::iterator itr =
|
||||
// 使用 find_if 函数查找匹配名称的统计信息提供者
|
||||
mot_list<StatisticsProvider *>::iterator itr =
|
||||
find_if(m_providers.begin(), m_providers.end(), StatisticsProviderFinder(name));
|
||||
|
||||
// 如果找到匹配名称的提供者,将其赋值给 result
|
||||
if (itr != m_providers.end()) {
|
||||
result = *itr;
|
||||
}
|
||||
|
|
@ -276,73 +415,110 @@ StatisticsProvider* StatisticsManager::GetStatisticsProvider(const char* name)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:为当前线程保留统计信息线程槽位
|
||||
*
|
||||
* 返回值:
|
||||
* 如果成功保留线程槽位,返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于为当前线程保留统计信息线程槽位,以便进行统计信息的收集和记录。
|
||||
*/
|
||||
bool StatisticsManager::ReserveThreadSlot()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 获取当前线程的标识符
|
||||
MOTThreadId tid = MOTCurrThreadId;
|
||||
if (tid == INVALID_THREAD_ID) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Reserve Thread Slot for Statistics",
|
||||
"Invalid attempt to reserve statistics thread slot without current thread identifier denied");
|
||||
// 如果当前线程标识符无效,报告错误并拒绝保留线程槽位
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL, "Reserve Thread Slot for Statistics",
|
||||
"Invalid attempt to reserve statistics thread slot without current thread identifier denied");
|
||||
} else {
|
||||
// 获取统计信息提供者锁,确保线程安全
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
// 默认情况下,假设成功保留线程槽位
|
||||
result = true;
|
||||
MOT_LOG_TRACE("Reserving statistics thread slot for thread id %" PRIu16, tid);
|
||||
mot_list<StatisticsProvider*>::iterator itr = m_providers.begin();
|
||||
|
||||
// 遍历统计信息提供者列表,为每个提供者保留线程槽位
|
||||
mot_list<StatisticsProvider *>::iterator itr = m_providers.begin();
|
||||
while (itr != m_providers.end()) {
|
||||
StatisticsProvider* provider = *itr;
|
||||
StatisticsProvider *provider = *itr;
|
||||
if (!provider->ReserveThreadSlot()) {
|
||||
// 如果某个提供者无法保留线程槽位,则标记结果为失败并中止遍历
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
|
||||
// 释放统计信息提供者锁,结束函数
|
||||
pthread_mutex_unlock(&m_providersLock);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:取消为当前线程保留的统计信息线程槽位
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于取消当前线程保留的统计信息线程槽位,以便在线程退出时释放相应的资源。
|
||||
*/
|
||||
void StatisticsManager::UnreserveThreadSlot()
|
||||
{
|
||||
MOTThreadId tid = MOTCurrThreadId;
|
||||
if (tid == INVALID_THREAD_ID) {
|
||||
// 如果当前线程标识符无效,报告错误
|
||||
MOT_LOG_ERROR("Invalid attempt to un-reserve statistics thread slot without current thread identifier denied");
|
||||
} else {
|
||||
// 获取统计信息提供者锁,确保线程安全
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
MOT_LOG_TRACE("Un-reserving statistics thread slot for thread id %" PRIu16, tid);
|
||||
mot_list<StatisticsProvider*>::iterator itr = m_providers.begin();
|
||||
// 遍历统计信息提供者列表,并取消为当前线程保留的线程槽位
|
||||
mot_list<StatisticsProvider *>::iterator itr = m_providers.begin();
|
||||
while (itr != m_providers.end()) {
|
||||
StatisticsProvider* provider = *itr;
|
||||
StatisticsProvider *provider = *itr;
|
||||
provider->UnreserveThreadSlot();
|
||||
++itr;
|
||||
}
|
||||
|
||||
// 释放统计信息提供者锁,结束函数
|
||||
pthread_mutex_unlock(&m_providersLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印统计信息
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:日志级别,用于确定要输出的日志级别
|
||||
* statOpts:统计选项,指示要打印的统计信息类型
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印统计信息,根据不同的统计选项选择不同的输出方式。
|
||||
*/
|
||||
void StatisticsManager::PrintStatistics(LogLevel logLevel, uint32_t statOpts /* = STAT_OPT_DEFAULT */)
|
||||
{
|
||||
pthread_mutex_lock(&m_providersLock);
|
||||
|
||||
// make sure there is at least one enabled provider with some statistics
|
||||
// 确保至少有一个启用的提供者具有一些统计信息
|
||||
if (!m_providers.empty()) {
|
||||
bool hasEnabled = false;
|
||||
bool hasStats = false;
|
||||
mot_list<StatisticsProvider*>::iterator itr = m_providers.begin();
|
||||
mot_list<StatisticsProvider *>::iterator itr = m_providers.begin();
|
||||
while (itr != m_providers.end()) {
|
||||
StatisticsProvider* provider = *itr;
|
||||
StatisticsProvider *provider = *itr;
|
||||
if (provider->IsEnabled()) {
|
||||
hasEnabled = true;
|
||||
provider->Summarize();
|
||||
if (provider->HasStatisticsFor(statOpts)) {
|
||||
hasStats = true;
|
||||
}
|
||||
// 继续对其他统计提供者进行统计总结
|
||||
// continue summarizing statistics for other statistics providers
|
||||
}
|
||||
++itr;
|
||||
|
|
@ -365,21 +541,31 @@ void StatisticsManager::PrintStatistics(LogLevel logLevel, uint32_t statOpts /*
|
|||
|
||||
pthread_mutex_unlock(&m_providersLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:启动统计信息打印线程
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于创建和启动一个新的线程,该线程负责定期打印统计信息。
|
||||
*/
|
||||
bool StatisticsManager::StartStatsPrintThread()
|
||||
{
|
||||
if (!m_running) {
|
||||
int rc = pthread_create(&m_statsThread, nullptr, StatsPrintThreadStatic, this);
|
||||
if (rc != 0) {
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(
|
||||
rc, pthread_create, "Statistics Manager Initialization", "Failed to create statistics printing thread");
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc, pthread_create, "Statistics Manager Initialization",
|
||||
"Failed to create statistics printing thread");
|
||||
} else {
|
||||
m_running = true;
|
||||
}
|
||||
}
|
||||
return m_running;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:停止统计信息打印线程
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于停止正在运行的统计信息打印线程。它会发送信号以通知线程停止,并等待线程结束。
|
||||
*/
|
||||
void StatisticsManager::StopStatsPrintThread()
|
||||
{
|
||||
// signal done flag and wake up statistics printing thread
|
||||
|
|
@ -394,14 +580,28 @@ void StatisticsManager::StopStatsPrintThread()
|
|||
}
|
||||
}
|
||||
|
||||
void* StatisticsManager::StatsPrintThreadStatic(void* param)
|
||||
/*
|
||||
* 功能:统计信息打印线程的静态回调函数
|
||||
*
|
||||
* 参数:
|
||||
* param:指向 StatisticsManager 实例的指针
|
||||
*
|
||||
* 注意:
|
||||
* 此函数是统计信息打印线程的静态回调函数,用于创建线程并调用 StatisticsManager 实例的 StatsPrintThread 函数。
|
||||
*/
|
||||
void *StatisticsManager::StatsPrintThreadStatic(void *param)
|
||||
{
|
||||
auto pThis = reinterpret_cast<StatisticsManager*>(param);
|
||||
auto pThis = reinterpret_cast<StatisticsManager *>(param);
|
||||
knl_thread_mot_init();
|
||||
pThis->StatsPrintThread();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:执行统计信息打印的线程函数
|
||||
*
|
||||
* 注意:
|
||||
* 此函数执行统计信息打印的线程功能。它定期打印统计信息报告,包括周期性报告和完整报告,并在退出前最后打印一次完整报告。
|
||||
*/
|
||||
void StatisticsManager::StatsPrintThread()
|
||||
{
|
||||
MOT_LOG_INFO("Statistics thread started");
|
||||
|
|
@ -439,19 +639,39 @@ void StatisticsManager::StatsPrintThread()
|
|||
MOT_LOG_INFO("Statistics thread stopped");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:等待下一次打印统计信息的时间点
|
||||
*
|
||||
* 注意:
|
||||
* 此函数计算并等待下一次打印统计信息的时间点。它使用当前时间和预定义的统计信息检查周期来计算等待时间。
|
||||
* 等待时间到达后,线程将被唤醒以执行统计信息打印。
|
||||
*/
|
||||
void StatisticsManager::WaitNextPrint()
|
||||
{
|
||||
// 获取当前时间
|
||||
struct timeval now;
|
||||
gettimeofday(&now, nullptr);
|
||||
// 计算等待时间,使用当前时间和预定义的统计信息检查周期
|
||||
struct timespec ts = {(time_t)(now.tv_sec + STAT_PRINT_CHECK_PERIOD_SECONDS), now.tv_usec * 1000L};
|
||||
|
||||
// 获取打印统计信息的锁,以确保线程安全
|
||||
pthread_mutex_lock(&m_statsPrintLock);
|
||||
// 等待下一次打印时间到达,或者等待被其他线程唤醒
|
||||
pthread_cond_timedwait(&m_statsPrintCond, &m_statsPrintLock, &ts);
|
||||
// 释放打印统计信息的锁
|
||||
pthread_mutex_unlock(&m_statsPrintLock);
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
||||
/*
|
||||
* 功能:打印所有统计信息
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从 MOT::StatisticsManager 获取所有统计信息,并以指定的日志级别(LL_INFO)打印出来。
|
||||
* 它是一个简便的函数,可用于在代码中调用以打印当前的统计信息。
|
||||
*/
|
||||
void dumpStats()
|
||||
{
|
||||
// 获取 StatisticsManager 实例并调用 PrintAllStatistics 方法打印所有统计信息
|
||||
MOT::StatisticsManager::GetInstance().PrintAllStatistics(MOT::LogLevel::LL_INFO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,21 @@
|
|||
|
||||
namespace MOT {
|
||||
DECLARE_LOGGER(StatisticsProvider, Statistics)
|
||||
|
||||
StatisticsProvider::StatisticsProvider(
|
||||
const char* name, StatisticsGenerator* generator, bool enable, bool extended /* = false */)
|
||||
/*
|
||||
* 功能:StatisticsProvider 类的构造函数
|
||||
*
|
||||
* 参数列表:
|
||||
* name:提供程序的名称
|
||||
* generator:与此提供程序关联的统计生成器
|
||||
* enable:是否启用此提供程序
|
||||
* extended:是否启用扩展统计信息(默认为 false)
|
||||
*
|
||||
* 注意:
|
||||
* 此构造函数用于创建 StatisticsProvider 的实例,它接受提供程序的名称、与之关联的统计生成器、
|
||||
* 启用状态以及是否启用扩展统计信息。它还初始化了提供程序的各种统计信息对象。
|
||||
*/
|
||||
StatisticsProvider::StatisticsProvider(const char *name, StatisticsGenerator *generator, bool enable,
|
||||
bool extended /* = false */)
|
||||
: m_enable(enable),
|
||||
m_generator(generator),
|
||||
m_threadStats(nullptr),
|
||||
|
|
@ -51,116 +63,158 @@ StatisticsProvider::StatisticsProvider(
|
|||
m_prevGlobalStats(nullptr),
|
||||
m_diffGlobalStats(nullptr)
|
||||
{
|
||||
// 使用 snprintf_s 函数将提供程序的名称复制到成员变量 m_name 中
|
||||
errno_t erc = snprintf_s(m_name, MAX_PROVIDER_NAME, MAX_PROVIDER_NAME - 1, "%s", name);
|
||||
securec_check_ss(erc, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:StatisticsProvider 类的析构函数
|
||||
*
|
||||
* 注意:
|
||||
* 此析构函数用于销毁 StatisticsProvider 的实例,释放与提供程序相关的统计信息对象的内存。
|
||||
*/
|
||||
StatisticsProvider::~StatisticsProvider()
|
||||
{
|
||||
// 释放全局统计信息对象的内存
|
||||
if (m_globalStats) {
|
||||
delete m_globalStats;
|
||||
}
|
||||
|
||||
// 释放先前的全局统计信息对象的内存
|
||||
if (m_prevGlobalStats) {
|
||||
delete m_prevGlobalStats;
|
||||
}
|
||||
|
||||
// 释放差异全局统计信息对象的内存
|
||||
if (m_diffGlobalStats) {
|
||||
delete m_diffGlobalStats;
|
||||
}
|
||||
|
||||
// 释放线程统计信息对象数组的内存
|
||||
if (m_threadStats) {
|
||||
for (uint32_t i = 0; i < m_threadStatCount; ++i) {
|
||||
if (m_threadStats[i] != nullptr) {
|
||||
// 调用 FreeThreadStats 函数释放线程统计信息对象的内存
|
||||
FreeThreadStats(i, m_threadStats[i]);
|
||||
}
|
||||
}
|
||||
free(m_threadStats);
|
||||
}
|
||||
|
||||
// 释放聚合统计信息对象的内存
|
||||
if (m_aggregateStats) {
|
||||
delete m_aggregateStats;
|
||||
}
|
||||
// 释放先前的聚合统计信息对象的内存
|
||||
if (m_prevAggregateStats) {
|
||||
delete m_prevAggregateStats;
|
||||
}
|
||||
// 释放平均统计信息对象的内存
|
||||
if (m_averageStats) {
|
||||
delete m_averageStats;
|
||||
}
|
||||
// 释放差异统计信息对象的内存
|
||||
if (m_diffStats) {
|
||||
delete m_diffStats;
|
||||
}
|
||||
// 释放差异平均统计信息对象的内存
|
||||
if (m_diffAverageStats) {
|
||||
delete m_diffAverageStats;
|
||||
}
|
||||
|
||||
// 释放死线程统计信息对象的内存
|
||||
if (m_deadThreadStats) {
|
||||
delete m_deadThreadStats;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:StatisticsProvider 类的初始化函数
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于初始化 StatisticsProvider 的实例,包括创建各种统计信息对象以及初始化互斥锁。
|
||||
*
|
||||
* 返回值:
|
||||
* - 如果初始化成功,返回 true。
|
||||
* - 如果发生错误(例如内存分配失败或互斥锁创建失败),则返回 false。
|
||||
*/
|
||||
bool StatisticsProvider::Initialize()
|
||||
{
|
||||
// 创建线程聚合统计信息对象
|
||||
m_aggregateStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_TOTAL);
|
||||
// 创建先前线程聚合统计信息对象
|
||||
m_prevAggregateStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_TOTAL);
|
||||
// 创建线程平均统计信息对象
|
||||
m_averageStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_AVG);
|
||||
// 创建线程差异统计信息对象
|
||||
m_diffStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_DIFF);
|
||||
// 创建线程差异平均统计信息对象
|
||||
m_diffAverageStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_DIFF_AVG);
|
||||
// 创建死线程统计信息对象
|
||||
m_deadThreadStats = m_generator->CreateThreadStatistics(ThreadStatistics::THREAD_ID_TOTAL);
|
||||
// 检查是否成功创建所有线程统计信息对象
|
||||
if (!m_aggregateStats || !m_prevAggregateStats || !m_averageStats || !m_diffStats || !m_diffAverageStats ||
|
||||
!m_deadThreadStats) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Statistics", "Failed to create thread statistics object(s)");
|
||||
return false; // safe cleanup in object destruction
|
||||
return false; // 在对象销毁时进行安全清理
|
||||
}
|
||||
|
||||
// 获取最大线程数并创建线程统计信息对象数组
|
||||
m_threadStatCount = GetGlobalConfiguration().m_maxThreads;
|
||||
m_threadStats = (ThreadStatistics**)calloc(m_threadStatCount, sizeof(ThreadStatistics*));
|
||||
m_threadStats = (ThreadStatistics **)calloc(m_threadStatCount, sizeof(ThreadStatistics *));
|
||||
if (!m_threadStats) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Load Statistics",
|
||||
"Failed to create thread statistics array in size of %u slots",
|
||||
m_threadStatCount);
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Statistics",
|
||||
"Failed to create thread statistics array in size of %u slots", m_threadStatCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建全局统计信息对象
|
||||
m_globalStats = m_generator->CreateGlobalStatistics(GlobalStatistics::NamingScheme::NAMING_SCHEME_TOTAL);
|
||||
// 创建先前的全局统计信息对象
|
||||
m_prevGlobalStats = m_generator->CreateGlobalStatistics(GlobalStatistics::NamingScheme::NAMING_SCHEME_TOTAL);
|
||||
// 创建差异全局统计信息对象
|
||||
m_diffGlobalStats = m_generator->CreateGlobalStatistics(GlobalStatistics::NamingScheme::NAMING_SCHEME_DIFF);
|
||||
// 检查是否成功创建所有全局统计信息对象
|
||||
if (!m_globalStats || !m_prevGlobalStats || !m_diffGlobalStats) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Load Statistics", "Failed to create global statistics object(s)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建并初始化互斥锁
|
||||
int rc = pthread_spin_init(&m_statLock, 0);
|
||||
if (rc != 0) {
|
||||
MOT_REPORT_SYSTEM_ERROR_CODE(rc, pthread_spin_init, "Load Statistics", "Failed to create statistics lock");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:为统计提供者保留线程槽
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于为统计提供者的线程保留槽位,以存储线程的统计信息。
|
||||
*/
|
||||
bool StatisticsProvider::ReserveThreadSlot()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// 获取当前线程的标识符和节点标识符
|
||||
MOTThreadId threadId = MOTCurrThreadId;
|
||||
int node = MOTCurrentNumaNodeId;
|
||||
|
||||
// 检查线程和节点标识符是否有效
|
||||
if ((threadId == INVALID_THREAD_ID) || (node == MEM_INVALID_NODE)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_INTERNAL,
|
||||
"Reserve Thread Slot for Statistics",
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_INTERNAL, "Reserve Thread Slot for Statistics",
|
||||
"Invalid attempt to reserve statistics thread slot without current thread/node identifier denied (thread "
|
||||
"id: %u, node id: %d)",
|
||||
(unsigned)threadId,
|
||||
node);
|
||||
(unsigned)threadId, node);
|
||||
} else {
|
||||
// there is no race here because only the current thread modifies its own slot
|
||||
if (m_threadStats[threadId] != nullptr) {
|
||||
MOT_LOG_TRACE("Double attempt to reserve statistics thread slot for thread %" PRIu16
|
||||
" silently ignored: thread slot already reserved",
|
||||
threadId);
|
||||
threadId);
|
||||
result = true;
|
||||
} else {
|
||||
MOT_LOG_TRACE("Reserving %s statistics thread slot for thread id %" PRIu16, GetName(), threadId);
|
||||
void* buffer = nullptr;
|
||||
void *buffer = nullptr;
|
||||
// since statistics provider is created before MemInit(), it is preferred to keep it clean from MM API calls
|
||||
if (GetGlobalConfiguration().m_numaNodes > 1) {
|
||||
buffer = MemNumaAllocLocal(m_generator->GetObjectSize(), node);
|
||||
|
|
@ -168,10 +222,8 @@ bool StatisticsProvider::ReserveThreadSlot()
|
|||
buffer = malloc(m_generator->GetObjectSize());
|
||||
}
|
||||
if (buffer == nullptr) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Reserve Thread Slot for Statistics",
|
||||
"Failed to allocate buffer in size of %u bytes",
|
||||
m_generator->GetObjectSize());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Reserve Thread Slot for Statistics",
|
||||
"Failed to allocate buffer in size of %u bytes", m_generator->GetObjectSize());
|
||||
} else {
|
||||
pthread_spin_lock(&m_statLock);
|
||||
m_threadStats[threadId] = m_generator->CreateThreadStatistics(threadId, buffer);
|
||||
|
|
@ -184,7 +236,13 @@ bool StatisticsProvider::ReserveThreadSlot()
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:取消保留线程槽
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于取消保留线程槽,将线程的统计信息移动到死线程列表中。
|
||||
* 统计信息将保留在那里,直到下一次摘要打印,然后再次回收。
|
||||
*/
|
||||
void StatisticsProvider::UnreserveThreadSlot()
|
||||
{
|
||||
// move the statistics to the dead thread list
|
||||
|
|
@ -196,11 +254,11 @@ void StatisticsProvider::UnreserveThreadSlot()
|
|||
if (m_threadStats[threadId] == nullptr) {
|
||||
MOT_LOG_TRACE("Attempt to unreserve statistics thread slot for thread %" PRIu16
|
||||
" silently ignored: thread slot already unreserved",
|
||||
threadId);
|
||||
threadId);
|
||||
} else {
|
||||
// aggregate dead thread statistics and cleanup
|
||||
pthread_spin_lock(&m_statLock);
|
||||
ThreadStatistics* threadStats = m_threadStats[threadId];
|
||||
ThreadStatistics *threadStats = m_threadStats[threadId];
|
||||
m_deadThreadStats->Add(*threadStats);
|
||||
m_threadStats[threadId] = nullptr; // this must be guarded with a lock due to race with Summarize()
|
||||
pthread_spin_unlock(&m_statLock);
|
||||
|
|
@ -210,10 +268,17 @@ void StatisticsProvider::UnreserveThreadSlot()
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:总结统计信息
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于总结统计信息,包括线程级别、全局级别和差异级别的统计信息。
|
||||
*/
|
||||
void StatisticsProvider::Summarize()
|
||||
{
|
||||
pthread_spin_lock(&m_statLock);
|
||||
|
||||
// 备份并重置聚合统计信息
|
||||
m_prevAggregateStats->Assign(*m_aggregateStats);
|
||||
m_aggregateStats->Reset();
|
||||
m_averageStats->Reset();
|
||||
|
|
@ -256,20 +321,39 @@ void StatisticsProvider::Summarize()
|
|||
m_prevGlobalStats->Assign(*m_globalStats);
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否存在满足给定统计选项的有效统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* statOpts:指定要检查的统计选项,可以是多个选项的按位组合
|
||||
*
|
||||
* 返回值:
|
||||
* 如果存在满足给定统计选项的有效统计数据,则返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查是否存在满足给定统计选项的有效统计数据。根据统计选项的不同,会检查不同的统计数据对象。
|
||||
*/
|
||||
bool StatisticsProvider::HasStatisticsFor(uint32_t statOpts)
|
||||
{
|
||||
bool result = m_hasExtendedStats;
|
||||
|
||||
// 如果需要线程级别的统计数据
|
||||
if (!result && (statOpts & STAT_OPT_SCOPE_THREAD)) {
|
||||
// 检查摘要级别的差异统计数据是否有有效样本
|
||||
if (statOpts & STAT_OPT_LEVEL_SUMMARY) {
|
||||
result = m_diffStats->HasValidSamples();
|
||||
}
|
||||
// 如果没有找到满足摘要级别要求的统计数据,检查详细级别的差异平均统计数据
|
||||
if (!result && (statOpts & STAT_OPT_LEVEL_DETAIL)) {
|
||||
result = m_diffAverageStats->HasValidSamples();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果需要全局级别的统计数据
|
||||
if (!result && (statOpts & STAT_OPT_SCOPE_GLOBAL)) {
|
||||
// 如果需要详细级别的统计数据
|
||||
if (statOpts & STAT_OPT_LEVEL_DETAIL) {
|
||||
// 首先获取锁以访问线程级别的统计数据
|
||||
pthread_spin_lock(&m_statLock);
|
||||
for (uint32_t i = 0; i < m_threadStatCount; ++i) {
|
||||
if (m_threadStats[i] != nullptr) {
|
||||
|
|
@ -279,11 +363,16 @@ bool StatisticsProvider::HasStatisticsFor(uint32_t statOpts)
|
|||
}
|
||||
}
|
||||
}
|
||||
// 释放锁
|
||||
pthread_spin_unlock(&m_statLock);
|
||||
|
||||
// 如果没有找到满足详细级别要求的线程级别的统计数据,检查平均统计数据
|
||||
if (!result) {
|
||||
result = m_averageStats->HasValidSamples();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到满足摘要级别要求的统计数据,检查聚合统计数据
|
||||
if (!result && (statOpts & STAT_OPT_LEVEL_SUMMARY)) {
|
||||
result = m_aggregateStats->HasValidSamples();
|
||||
}
|
||||
|
|
@ -292,71 +381,135 @@ bool StatisticsProvider::HasStatisticsFor(uint32_t statOpts)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* logLevel:指定要使用的日志级别,用于打印统计数据
|
||||
* statOpts:指定要打印的统计选项,可以是多个选项的按位组合,默认为 STAT_OPT_DEFAULT
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于根据给定的统计选项打印统计数据。根据统计选项的不同,会打印线程级别和全局级别的统计数据。
|
||||
*/
|
||||
void StatisticsProvider::PrintStatistics(LogLevel logLevel, uint32_t statOpts /* = STAT_OPT_DEFAULT */)
|
||||
{
|
||||
// 如果需要线程级别的统计数据
|
||||
if (statOpts & STAT_OPT_SCOPE_THREAD) {
|
||||
for (uint32_t statId = 0; statId < m_aggregateStats->GetStatCount(); ++statId) {
|
||||
// 打印线程级别的统计数据
|
||||
PrintThreadStats(statOpts, statId, logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果需要全局级别的统计数据
|
||||
if (statOpts & STAT_OPT_SCOPE_GLOBAL) {
|
||||
for (uint32_t statId = 0; statId < m_globalStats->GetStatCount(); ++statId) {
|
||||
// 打印全局级别的统计数据
|
||||
PrintGlobalStats(statOpts, statId, logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// 打印额外的统计数据(未提供具体实现,可能在后续代码中定义)
|
||||
PrintStatisticsEx();
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印线程级别的统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* statOpts:指定要打印的统计选项,可以是多个选项的按位组合
|
||||
* statId:要打印的统计数据标识符
|
||||
* logLevel:指定要使用的日志级别,用于打印统计数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印线程级别的统计数据,根据给定的统计选项和统计数据标识符来确定要打印哪些数据以及使用哪个日志级别。
|
||||
*/
|
||||
void StatisticsProvider::PrintThreadStats(uint32_t statOpts, uint32_t statId, LogLevel logLevel)
|
||||
{
|
||||
// print diff stats
|
||||
// 如果需要打印周期差异统计数据
|
||||
if (statOpts & STAT_OPT_PERIOD_DIFF) {
|
||||
// 如果需要打印摘要级别的统计数据
|
||||
if (statOpts & STAT_OPT_LEVEL_SUMMARY) {
|
||||
// 打印差异统计数据
|
||||
m_diffStats->Print(statId, logLevel);
|
||||
}
|
||||
|
||||
// 如果需要打印详细级别的统计数据
|
||||
if (statOpts & STAT_OPT_LEVEL_DETAIL) {
|
||||
// 打印差异平均统计数据
|
||||
m_diffAverageStats->Print(statId, logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// print total stats
|
||||
// 如果需要打印周期总计统计数据
|
||||
if (statOpts & STAT_OPT_PERIOD_TOTAL) {
|
||||
// 如果需要打印详细级别的统计数据
|
||||
if (statOpts & STAT_OPT_LEVEL_DETAIL) {
|
||||
pthread_spin_lock(&m_statLock);
|
||||
for (uint32_t i = 0; i < m_threadStatCount; ++i) {
|
||||
if (m_threadStats[i] != nullptr) {
|
||||
// 打印线程级别的统计数据
|
||||
m_threadStats[i]->Print(statId, logLevel);
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&m_statLock);
|
||||
|
||||
// 打印平均统计数据
|
||||
m_averageStats->Print(statId, logLevel);
|
||||
}
|
||||
|
||||
// 如果需要打印摘要级别的统计数据
|
||||
if (statOpts & STAT_OPT_LEVEL_SUMMARY) {
|
||||
// 打印聚合统计数据
|
||||
m_aggregateStats->Print(statId, logLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印全局级别的统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* statOpts:指定要打印的统计选项,可以是多个选项的按位组合
|
||||
* statId:要打印的统计数据标识符
|
||||
* logLevel:指定要使用的日志级别,用于打印统计数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印全局级别的统计数据,根据给定的统计选项和统计数据标识符来确定要打印哪些数据以及使用哪个日志级别。
|
||||
*/
|
||||
void StatisticsProvider::PrintGlobalStats(uint32_t statOpts, uint32_t statId, LogLevel logLevel)
|
||||
{
|
||||
// print diff stats
|
||||
if (statOpts & STAT_OPT_PERIOD_DIFF) {
|
||||
// 打印差异统计数据
|
||||
m_diffGlobalStats->Print(statId, logLevel);
|
||||
}
|
||||
|
||||
// print total stats
|
||||
// 如果需要打印周期总计统计数据,并且不需要打印详细级别的统计数据
|
||||
if ((statOpts & STAT_OPT_PERIOD_TOTAL) && !(statOpts & STAT_OPT_LEVEL_DETAIL)) {
|
||||
m_globalStats->Print(statId, logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void StatisticsProvider::FreeThreadStats(MOTThreadId threadId, ThreadStatistics* threadStats)
|
||||
/*
|
||||
* 功能:释放线程统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* threadId:要释放统计数据的线程标识符
|
||||
* threadStats:指向要释放的线程统计数据对象的指针
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于释放线程级别的统计数据,包括内存的回收。它根据线程标识符获取要释放的统计数据,然后回收相关的内存。
|
||||
*/
|
||||
void StatisticsProvider::FreeThreadStats(MOTThreadId threadId, ThreadStatistics *threadStats)
|
||||
{
|
||||
MOT_LOG_TRACE("Reclaiming %s statistics thread slot for thread id %" PRIu16, GetName(), threadId);
|
||||
void* buffer = (void*)threadStats->GetInPlaceBuffer();
|
||||
// 获取要回收的内存缓冲区
|
||||
void *buffer = (void *)threadStats->GetInPlaceBuffer();
|
||||
int node = threadStats->GetNodeId();
|
||||
// 销毁线程统计数据对象
|
||||
threadStats->~ThreadStatistics();
|
||||
// 根据配置的NUMA节点数进行内存回收
|
||||
if (GetGlobalConfiguration().m_numaNodes > 1) {
|
||||
MemNumaFreeLocal(buffer, m_generator->GetObjectSize(), node);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,23 +27,53 @@
|
|||
namespace MOT {
|
||||
DECLARE_LOGGER(ThreadStatistics, Statistics)
|
||||
|
||||
/*
|
||||
* 功能:汇总线程统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* updateTstamp:一个布尔值,指示是否需要更新时间戳
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于汇总线程级别的统计数据。它迭代处理每个统计变量,并要求它们执行汇总操作,可选择是否更新时间戳。
|
||||
*/
|
||||
void ThreadStatistics::Summarize(bool updateTstamp)
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 遍历所有统计变量,执行汇总操作
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Summarize(updateTstamp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:打印线程统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* statId:要打印的统计变量的ID
|
||||
* logLevel:日志级别,指示打印的日志级别
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于打印线程级别的统计数据。它根据给定的统计变量ID,打印特定统计变量的数据,
|
||||
* 或者如果未提供ID,则打印所有统计变量的数据。
|
||||
*/
|
||||
void ThreadStatistics::Print(uint32_t statId, LogLevel logLevel) const
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 如果提供了statId,只打印指定的统计变量
|
||||
if (statId < statCount) {
|
||||
// 检查该统计变量是否有样本数据,如果有则打印
|
||||
if (m_statVars[statId]->GetSampleCount() > 0) {
|
||||
m_statVars[statId]->Print(logLevel);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
// 如果未提供statId,打印所有统计变量的数据
|
||||
else {
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
// 检查每个统计变量是否有样本数据,如果有则打印
|
||||
if (m_statVars[i]->GetSampleCount() > 0) {
|
||||
m_statVars[i]->Print(logLevel);
|
||||
}
|
||||
|
|
@ -51,57 +81,123 @@ void ThreadStatistics::Print(uint32_t statId, LogLevel logLevel) const
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:重置线程统计数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于重置线程级别的统计数据。它将所有统计变量的数据重置为初始状态,并清除有效线程计数。
|
||||
*/
|
||||
void ThreadStatistics::Reset()
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 重置每个统计变量的数据,并清除有效线程计数
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Reset();
|
||||
m_validThreads[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadStatistics::Assign(const ThreadStatistics& rhs)
|
||||
/*
|
||||
* 功能:将线程统计数据赋值为另一个线程统计数据的副本
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要赋值的线程统计数据对象的引用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将线程统计数据赋值为另一个线程统计数据对象的副本。它逐个统计变量地赋值,并保留有效线程计数。
|
||||
*/
|
||||
void ThreadStatistics::Assign(const ThreadStatistics &rhs)
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 逐个统计变量地赋值,并保留有效线程计数
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Assign(*rhs.m_statVars[i]);
|
||||
m_validThreads[i] = rhs.m_validThreads[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadStatistics::Add(const ThreadStatistics& rhs)
|
||||
/*
|
||||
* 功能:将线程统计数据累加到当前线程统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要累加的线程统计数据对象的引用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于将线程统计数据累加到当前线程统计数据对象中。它逐个统计变量地累加,并更新有效线程计数。
|
||||
*/
|
||||
void ThreadStatistics::Add(const ThreadStatistics &rhs)
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 逐个统计变量地累加,并更新有效线程计数
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Add(*rhs.m_statVars[i]);
|
||||
if (rhs.m_statVars[i]->GetSampleCount() > 0)
|
||||
if (rhs.m_statVars[i]->GetSampleCount() > 0) {
|
||||
++m_validThreads[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadStatistics::Subtract(const ThreadStatistics& rhs)
|
||||
/*
|
||||
* 功能:从当前线程统计数据中减去另一个线程统计数据
|
||||
*
|
||||
* 参数列表:
|
||||
* rhs:要减去的线程统计数据对象的引用
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于从当前线程统计数据中减去另一个线程统计数据对象。它逐个统计变量地进行减法操作。
|
||||
*/
|
||||
void ThreadStatistics::Subtract(const ThreadStatistics &rhs)
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 逐个统计变量地进行减法操作
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
m_statVars[i]->Subtract(*rhs.m_statVars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:规范化当前线程统计数据
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于规范化当前线程的统计数据。它逐个统计变量地进行规范化操作,将每个变量的值除以有效线程数。
|
||||
*/
|
||||
void ThreadStatistics::Normalize()
|
||||
{
|
||||
// 获取统计变量的数量
|
||||
uint32_t statCount = m_statVars.size();
|
||||
|
||||
// 逐个统计变量地进行规范化操作
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
// 如果有有效线程,将统计变量的值除以有效线程数
|
||||
if (m_validThreads[i] > 0) {
|
||||
m_statVars[i]->Divide(m_validThreads[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:检查是否存在有效的统计样本
|
||||
*
|
||||
* 返回值:
|
||||
* 如果至少一个统计变量存在有效的统计样本,则返回 true;否则返回 false。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于检查当前线程的统计数据是否至少有一个统计变量包含有效的统计样本。
|
||||
*/
|
||||
bool ThreadStatistics::HasValidSamples() const
|
||||
{
|
||||
bool result = false;
|
||||
uint32_t statCount = m_statVars.size();
|
||||
for (uint32_t i = 0; i < statCount; ++i) {
|
||||
// 如果统计变量的统计样本数量大于 0,则将 result 设置为 true 并退出循环
|
||||
if (m_statVars[i]->GetSampleCount() > 0) {
|
||||
result = true;
|
||||
break;
|
||||
|
|
@ -110,34 +206,64 @@ bool ThreadStatistics::HasValidSamples() const
|
|||
return result;
|
||||
}
|
||||
|
||||
mot_string ThreadStatistics::MakeName(const char* baseName, uint64_t threadId)
|
||||
/*
|
||||
* 功能:生成统计变量名称
|
||||
*
|
||||
* 参数列表:
|
||||
* baseName:基本名称,用于构建统计变量名称
|
||||
* threadId:线程标识符,用于区分不同线程的统计变量名称
|
||||
*
|
||||
* 返回值:
|
||||
* 返回一个包含构建的统计变量名称的 mot_string 对象。
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于生成统计变量的名称,根据给定的基本名称和线程标识符来构建不同的名称。
|
||||
*/
|
||||
mot_string ThreadStatistics::MakeName(const char *baseName, uint64_t threadId)
|
||||
{
|
||||
mot_string result;
|
||||
if (threadId == THREAD_ID_TOTAL) {
|
||||
// 如果线程标识符为 THREAD_ID_TOTAL,则将 [TOTAL] 添加到基本名称中
|
||||
result.format("%s[TOTAL]", baseName);
|
||||
} else if (threadId == THREAD_ID_AVG) {
|
||||
// 如果线程标识符为 THREAD_ID_AVG,则将 [AVG] 添加到基本名称中
|
||||
result.format("%s[AVG]", baseName);
|
||||
} else if (threadId == THREAD_ID_DIFF) {
|
||||
// 如果线程标识符为 THREAD_ID_DIFF,则将 [DIFF] 添加到基本名称中
|
||||
result.format("%s[DIFF]", baseName);
|
||||
} else if (threadId == THREAD_ID_DIFF_AVG) {
|
||||
// 如果线程标识符为 THREAD_ID_DIFF_AVG,则将 [DIFF-AVG] 添加到基本名称中
|
||||
result.format("%s[DIFF-AVG]", baseName);
|
||||
} else {
|
||||
// 否则,将线程标识符添加到基本名称中
|
||||
result.format("%s[%u]", baseName, (unsigned)threadId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ThreadStatistics::RegisterStatistics(StatisticVariable* statVar)
|
||||
/*
|
||||
* 功能:注册统计变量
|
||||
*
|
||||
* 参数列表:
|
||||
* statVar:要注册的统计变量指针
|
||||
*
|
||||
* 注意:
|
||||
* 此函数用于注册统计变量,将统计变量添加到统计变量列表中,并分配相应的有效线程槽。
|
||||
* 如果添加统计变量或有效线程槽失败,会报告内存不足错误。
|
||||
*/
|
||||
void ThreadStatistics::RegisterStatistics(StatisticVariable *statVar)
|
||||
{
|
||||
// 将统计变量添加到统计变量列表中
|
||||
if (!m_statVars.push_back(statVar)) {
|
||||
MOT_REPORT_ERROR(
|
||||
MOT_ERROR_OOM, "Register Statistics", "Failed to add statistic variable %s", statVar->GetName());
|
||||
} else if (!m_validThreads.push_back(0)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM,
|
||||
"Register Statistics",
|
||||
"Failed to add valid thread slot for statistic variable %s",
|
||||
statVar->GetName());
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Register Statistics", "Failed to add statistic variable %s",
|
||||
statVar->GetName());
|
||||
}
|
||||
// 为统计变量分配相应的有效线程槽
|
||||
else if (!m_validThreads.push_back(0)) {
|
||||
MOT_REPORT_ERROR(MOT_ERROR_OOM, "Register Statistics",
|
||||
"Failed to add valid thread slot for statistic variable %s", statVar->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOT
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue