opengauss项目代码注释 #26

Open
nuoya wants to merge 55 commits from nuoya/openGauss-server:master into master
130 changed files with 40605 additions and 15649 deletions

View File

@ -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

View File

@ -0,0 +1 @@
DIR *.* /B >LIST.TXT

View File

@ -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));
}

View File

@ -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
transtypeAggregateCreate
使 "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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
* initdbPostmasterGucInfoMain等
* 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);
}

View File

@ -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

View File

@ -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

View File

@ -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.")));
}

View File

@ -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); // 允许随时处理SIGQUITquickdie信号
}
// 从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);
}

View File

@ -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_IDinfo为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进程
}
}
}

View File

@ -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; // 返回实际注册的后台工作者数量
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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
* relidparentid 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 modeSHAREDEXCLUSIVE
*
*
* 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 保持为 NULLfound 保持为 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 保持为 NULLfound 保持为 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

View File

@ -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); // 强制退出
}
/*
*
*
*
*
*
*
* codearg使
*
*
*/
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类型truefalse
*/
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

View File

@ -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; // 恢复之前保存的错误码
}

View File

@ -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; // 恢复之前保存的错误码
}

View File

@ -0,0 +1 @@
DIR *.* /B >LIST.TXT

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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为NULLResourceOwnerEnlargePthreadMutex中将发生核心转储
* 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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1 @@
DIR *.* /B >LIST.TXT

View File

@ -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);
}
//用于记录最大页面刷新LSNLog 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 bit1
*/
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();
//初始化 fifoFirst-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

View File

@ -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)));
}

View File

@ -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);
}
}

View File

@ -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.
* sockidx 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);
}
}

View File

@ -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

View File

@ -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;
// 重置SMPSymmetric Multi-Processing ID为0
beentry->st_smpid = 0;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,5 @@
{
"files.associations": {
"condition_variable": "cpp"
}
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -20,6 +20,13 @@
*
* -------------------------------------------------------------------------
*/
/*
*BufferTags映射到缓冲区索引的相关功能
*
*
*
*
*/
#include "postgres.h"
#include "knl/knl_variable.h"

View File

@ -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表示没有可用的缓冲区
}

View File

@ -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

View File

@ -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(&gt_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缓冲区中
*
*
* bufStringInfo缓冲区
*
*
* 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对象
*
*
* bufStringInfo缓冲区
*
*
* 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, "", "");
}
/*
* MaxNumOfValueStringInfo缓冲区中
*
*
* bufStringInfo缓冲区
*
*
* MaxNumOfValueStringInfo缓冲区中
* 便使
*/
void ImportError::serializeMaxNumOfValue(StringInfo buf)
{
// 使用 pq_sendint16 函数将 MaxNumOfValue 序列化为一个16位整数并写入缓冲区
pq_sendint16(buf, MaxNumOfValue);
}
/*
* MaxNumOfValueStringInfo缓冲区中
*
*
* bufStringInfo缓冲区
*
*
* MaxNumOfValueStringInfo缓冲区中
* 便使
*/
void CopyError::serializeMaxNumOfValue(StringInfo buf)
{
// 使用 pq_sendint 函数将 MaxNumOfValue 序列化为一个2字节整数并写入缓冲区
pq_sendint(buf, MaxNumOfValue, 2);
}
/*
* BaseError对象中
*
*
* edataBaseError对象
*
*
* 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对象的错误信息保存到文件中
*
*
* edataBaseError对象
*
*
* 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
*
*
* edataBaseError对象
*
*
* 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
* edataBaseError对象
*
*
* 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
* ierrorImportError对象
*
*
* 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

View File

@ -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);

View File

@ -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访
*
*
* truefalse
*
*
* 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
*
*
* truefalse
*
*
* 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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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
* obsOptionsOBS 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
* recursive1 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
* flagO_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
* recursive1 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;
}
/*
*
*
* key1Oid的指针
* key2Oid的指针
* keySizeOid的大小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
*
*
* serverOidOID
*
*
* 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

View File

@ -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
*
*
* filePathXlog
* fileSizeXlog
*/
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)));

View File

@ -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是否与访问对象中的事务IDTID匹配
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
*
*
* truefalse
*
*
*
* 访
* truefalse
*/
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
*
*
* truefalse
*
*
*
* 访
* truefalse
*/
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
*
*
* truefalse
*
*
*
* 访
* 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
*
*
* truefalse
*
*
*
* 访
* 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
*
*
* truefalse
*
*
*
* 访访
*
*/
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)
/*
* OCCOptimistic 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
*
*
*
* 访
* Sentinelaccess->GetRowFromHeader()
* 便 CSNrowid
*/
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

View File

@ -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
*
*
* truefalse
*
*
*
*
*/
bool RowHeader::ValidateWrite(TransactionId tid) const
{
return (tid == GetCSN());
}
/*
*
*
*
* tid
*
*
* truefalse
*
*
* 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;

View File

@ -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
*
*
* truefalse
*
*
*
* CMDLINE_SEP
* sectionPath和keyValuePart中true
* false
*/
static bool ParseCmdLineSectionName(const mot_string &line, mot_string &sectionPath, 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 &sectionFullName,
mot_list<ConfigSection *> &parsedSections, ConfigSectionMap &sectionMap)
{
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
*
*
* truefalse
*
*
*
*
* false
*
*/
static bool AddCmdLineArrayConfigItem(ConfigSection *currentSection, const mot_string &sectionFullName,
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;

View File

@ -30,7 +30,18 @@ ConfigArray::ConfigArray() : ConfigItem(ConfigItemClass::CONFIG_ITEM_ARRAY)
ConfigArray::~ConfigArray()
{}
/*
*
*
*
* logLevel
* fullPrintfalse
*
*
*
* 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
* fullPrintfalse
*
*
*
* 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

View File

@ -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())
{}
/*
*
*
*
* truefalse
*
*
*
* 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

View File

@ -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
* sectionPathmot_string对象
* sectionNamemot_string对象
* sepConfigItem::PATH_SEP
*
*
* truefalse
*
*
*
* sep用于指示配置部分路径和名称之间的分隔符ConfigItem::PATH_SEP
* sectionPath和sectionName参数中true
* false
*/
bool ConfigFileParser::BreakSectionName(const mot_string &sectionFullName, mot_string &sectionPath,
mot_string &sectionName, 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
* keymot_string对象
* valuemot_string对象
* arrayIndex
* hasArrayIndextruefalse
*
*
* truefalse
*
*
*
* keyvaluearrayIndex和hasArrayIndex参数中true
* false
*/
bool ConfigFileParser::ParseKeyValue(const mot_string &keyValuePart, const mot_string &section, 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 &sectionFullName, 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
*
*
* truefalse
*
*
*
* 使
*
*/
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 &sectionFullName, 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 &sectionFullName, 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 &sectionFullName, 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 &sectionFullName, 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
* valueint64类型的字符串表示
*
*
* int64类型的配置项配置值nullptr
*
*
* int64类型的配置项配置值
* int64类型
* int64类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeInt64ConfigValue(const mot_string &sectionFullName, 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
* valueint32_t类型的字符串表示
*
*
* int32_t类型的配置项配置值nullptr
*
*
* int32_t类型的配置项配置值
* int64_t类型
* int32_t的范围内int32_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeInt32ConfigValue(const mot_string &sectionFullName, 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
* valueint16_t类型的字符串表示
*
*
* int16_t类型的配置项配置值nullptr
*
*
* int16_t类型的配置项配置值
* int64_t类型
* int16_t的范围内int16_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeInt16ConfigValue(const mot_string &sectionFullName, 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
* valueint8_t类型的字符串表示
*
*
* int8_t类型的配置项配置值nullptr
*
*
* int8_t类型的配置项配置值
* int64_t类型
* int8_t的范围内int8_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeInt8ConfigValue(const mot_string &sectionFullName, 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
* valueuint64_t类型的字符串表示
*
*
* uint64_t类型的配置项配置值nullptr
*
*
* uint64_t类型的配置项配置值
* uint64_t类型
* uint64_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeUInt64ConfigValue(const mot_string &sectionFullName, 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
* valueuint32_t类型的字符串表示
*
*
* uint32_t类型的配置项配置值nullptr
*
*
* uint32_t类型的配置项配置值
* uint64_t类型
* uint32_t的范围内uint32_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeUInt32ConfigValue(const mot_string &sectionFullName, 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
* valueuint16_t类型的字符串表示
*
*
* uint16_t类型的配置项配置值nullptr
*
*
* uint16_t类型的配置项配置值
* uint64_t类型
* uint16_t的范围内uint16_t类型的配置项
* nullptr
*/
ConfigItem *ConfigFileParser::MakeUInt16ConfigValue(const mot_string &sectionFullName, 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
* valueuint8_t
*
*
* uint8_t nullptr
*
*
* uint8_t
* uint64_t
* uint8_t uint8_t
* nullptr
*/
ConfigItem *ConfigFileParser::MakeUInt8ConfigValue(const mot_string &sectionFullName, 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 &sectionFullName, 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 &sectionFullName, 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 &sectionFullName, 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 &sectionFullName, 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

View File

@ -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

View File

@ -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_STRCONFIG_ITEM_VALUE_STRCONFIG_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

View File

@ -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;
}

View File

@ -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

View File

@ -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 &sectionNames) 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

View File

@ -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 &sectionMap)
{
// 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 &sectionMap)
{
// 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;
}

View File

@ -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;

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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 &sectionPath, 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 &sectionFullName,
mot_list<ConfigSection *> &parsedSections, ConfigSectionMap &sectionMap)
{
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 &sectionFullName,
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

View File

@ -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");
}
/*
*
*
*
* truefalse
*
*
*
*
*/
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
*
*
* 110
*
*
*
*
* 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;
}
/*
*
*
*
* truefalse
*
*
* 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

View File

@ -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使INFODEBUGERROR
*
*
*
* "变量名={ 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;
}
}

View File

@ -29,52 +29,133 @@
#include "utilities.h"
namespace MOT {
/*
*
*
*
* updateTstamp
*
*
*
* updateTstamp trueCPU周期计数作为时间戳
* 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使INFODEBUGERROR
*
*
*
*
* "变量名={ 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;
}

View File

@ -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使INFODEBUGERROR
*
*
*
* 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

View File

@ -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使INFODEBUGERROR
*
*
*
*
*/
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;

View File

@ -25,39 +25,117 @@
#include "memory_statistic_variable.h"
namespace MOT {
/*
*
*
*
* logLevel使INFODEBUGERROR
*
*
*
* 使
*/
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);
}

View File

@ -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

View File

@ -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

View File

@ -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)
/*
*
*
*
* mutexpthread_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);
}

View File

@ -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 {

View File

@ -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);
}
}
/*
* 线
*
*
* statIdID
* 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