openGauss-server/src/gausskernel/process/threadpool/threadpool_sessctl.cpp

896 lines
38 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* threadpool_sessctl.cpp
* Class ThreadPoolSessControl is used to control all session info in thread pool.
*
* IDENTIFICATION
* src/gausskernel/process/threadpool/threadpool_sessctl.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "threadpool/threadpool.h"
#include "access/xact.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "commands/user.h"
#include "gssignal/gs_signal.h"
#include "lib/dllist.h"
#include "libpq/ip.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "pgstat.h"
#include "pgxc/pgxc.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "tcop/dest.h"
#include "utils/atomic.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/acl.h"
#include "executor/executor.h"
#include "communication/commproxy_interface.h"
#ifdef MEMORY_CONTEXT_TRACK
#include "memory_func.h"
#endif
// 线程池会话控制类的构造函数(参数:内存上下文)
ThreadPoolSessControl::ThreadPoolSessControl(MemoryContext context)
{
// 自动上下文切换到指定的内存上下文
AutoContextSwitch acontext(context);
m_context = context;
pthread_mutex_init(&m_sessCtrlock, NULL); // 初始化线程互斥锁
m_sessionId = 0; // 会话 ID 初始化为 0
m_activeSessionCount = 0; // 活跃会话计数器初始化为 0
m_maxActiveSessionCount = GLOBAL_MAX_SESSION_NUM; // 最大活跃会话数初始化为全局最大会话数
m_maxReserveSessionCount = GLOBAL_RESERVE_SESSION_NUM; // 最大保留会话数初始化为全局保留会话数
DLInitList(&m_activelist); // 初始化活跃会话列表
DLInitList(&m_freelist); // 初始化空闲会话列表
// 分配存储空间并初始化基础会话控制结构
m_base = (knl_sess_control*)palloc0(sizeof(knl_sess_control) * m_maxActiveSessionCount);
for (int i = 0; i < m_maxActiveSessionCount; i++) {
m_base[i].idx = (m_maxReserveSessionCount + i); // 设置会话索引
m_base[i].lock = 0; // 会话锁定标志初始化为 0
DLInitElem(&m_base[i].elem, &m_base[i]); // 初始化双向链表元素
DLAddHead(&m_freelist, &m_base[i].elem); // 将当前会话添加到空闲会话列表头部
}
}
// 线程池会话控制类的析构函数
ThreadPoolSessControl::~ThreadPoolSessControl()
{
pfree_ext(m_base); // 释放基础会话控制结构的内存
m_context = NULL; // 将内存上下文指针置为空
}
// 创建新的线程池会话(参数:端口指针)
knl_session_context* ThreadPoolSessControl::CreateSession(Port* port)
{
knl_session_context* sc = NULL;
/* 使用 u_sess->session_id 标记内存上下文。 */
m_sessionId++;
// 创建会话上下文
sc = create_session_context(m_context, m_sessionId);
if (sc == NULL) {
ereport(WARNING, (errmsg("can't allocate memory for session"))); // 内存分配失败报警
return NULL;
}
// 切换内存上下文到默认内存上下文
MemoryContext old_cxt = MemoryContextSwitchTo(sc->mcxt_group->GetMemCxtGroup(MEMORY_CONTEXT_DEFAULT));
// 分配并复制端口信息到新的会话上下文
sc->proc_cxt.MyProcPort = (Port*)palloc0(sizeof(Port));
MemoryContextSwitchTo(old_cxt);
// 复制端口信息
int rc = memcpy_s(sc->proc_cxt.MyProcPort, sizeof(Port), port, sizeof(Port));
securec_check(rc, "\0", "\0");
ereport(DEBUG4, (errmsg("CreateSession fd:[%d] to dispatch.", port->sock))); // 创建会话并分派报告日志
// 分配会话槽位,如果成功则返回会话,否则清理资源返回 NULL
if (AllocateSlot(sc)) {
sc->stat_cxt.trackedBytes += u_sess->stat_cxt.trackedBytes;
sc->stat_cxt.trackedMemChunks += u_sess->stat_cxt.trackedMemChunks;
u_sess->stat_cxt.trackedBytes = 0;
u_sess->stat_cxt.trackedMemChunks = 0;
return sc;
} else {
u_sess->stat_cxt.trackedBytes += sc->stat_cxt.trackedBytes;
u_sess->stat_cxt.trackedMemChunks += sc->stat_cxt.trackedMemChunks;
// sc->top_mem_cxt 在 create_session_context 中已经被封存
MemoryContextUnSeal(sc->top_mem_cxt);
MemoryContextDeleteChildren(sc->top_mem_cxt);
MemoryContextDelete(sc->top_mem_cxt);
pfree(sc);
return NULL;
}
}
// 分配一个会话槽位并返回会话控制结构指针(参数:会话上下文)
knl_sess_control* ThreadPoolSessControl::AllocateSlot(knl_session_context* sc)
{
AutoMutexLock alock(&m_sessCtrlock); // 自动锁定互斥锁
alock.lock(); // 锁定互斥锁
// 检查是否可以接受新连接
if (canAcceptConnections(true) != CAC_OK) {
alock.unLock(); // 解锁互斥锁
/* 数据库正在执行恢复过程,此时连接错误是合理的 */
ereport(WARNING, (errmsg("ThreadPool cannot start new session due to PM state, PM state is %s", GetPMState(pmState))));
return NULL;
}
// 检查活跃会话数是否已达上限
if (m_activeSessionCount == m_maxActiveSessionCount) {
alock.unLock(); // 解锁互斥锁
ereport(WARNING, (errmsg("ThreadPool cannot start new session due to to many sessions, current upper bound is %d",
m_maxActiveSessionCount))); // 活跃会话数已达上限报警
return NULL;
}
Assert(DLListLength(&m_freelist) != 0); // 确保空闲会话列表不为空
// 从空闲会话列表中移除一个会话控制结构
knl_sess_control* ctrl = (knl_sess_control*)DLE_VAL(DLRemHead(&m_freelist));
ctrl->sess = sc;
sc->session_ctr_index = ctrl->idx; // 设置会话槽位索引
DLAddHead(&m_activelist, &ctrl->elem); // 将会话控制结构添加到活跃会话列表头部
m_activeSessionCount++; // 活跃会话计数器加一
alock.unLock(); // 解锁互斥锁
return ctrl;
}
// 释放一个会话槽位(参数:会话槽位索引)
void ThreadPoolSessControl::FreeSlot(int ctrl_index)
{
if (!IsValidCtrlIndex(ctrl_index)) {
return;
}
AutoMutexLock alock(&m_sessCtrlock); // 自动锁定互斥锁
alock.lock(); // 锁定互斥锁
knl_sess_control *ctrl = &m_base[ctrl_index - m_maxReserveSessionCount]; // 获取会话控制结构
Assert(ctrl->elem.dle_list == &m_activelist); // 确保会话控制结构在活跃会话列表中
m_activeSessionCount--; // 活跃会话计数器减一
volatile sig_atomic_t* plock = &ctrl->lock;
sig_atomic_t val;
do {
if (*plock == 0) {
/* 进行原子比较和交换操作 */
val = __sync_val_compare_and_swap(plock, 0, 1);
if (val == 0) {
ctrl->sess = NULL;
/* 恢复值 */
ctrl->lock = 0;
DLRemove(&ctrl->elem); // 从活跃会话列表中移除
DLAddHead(&m_freelist, &ctrl->elem); // 将会话控制结构添加到空闲会话列表头部
break;
}
}
pg_usleep(100); // 休眠 100 微秒
} while (true);
alock.unLock(); // 解锁互斥锁
}
// 标记所有会话为关闭状态
void ThreadPoolSessControl::MarkAllSessionClose()
{
/* 标记所有会话为关闭状态。*/
AutoMutexLock alock(&m_sessCtrlock); // 自动锁定互斥锁
alock.lock(); // 锁定互斥锁
knl_sess_control* ctrl = NULL;
Dlelem* elem = DLGetHead(&m_activelist); // 获取活跃会话列表的头部
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem);
ctrl->sess->status = KNL_SESS_CLOSE; // 设置会话状态为关闭
CloseClientSocket(ctrl->sess, false); // 关闭客户端套接字
elem = DLGetSucc(elem); // 获取下一个元素
}
alock.unLock(); // 解锁互斥锁
ereport(LOG, (errmodule(MOD_THREAD_POOL),
errmsg("pmState:%d, mark all threadpool sessions closed.", pmState))); // 记录日志
}
// 检查发送信号的权限(参数:会话上下文,锁指针)
void ThreadPoolSessControl::CheckPermissionForSendSignal(knl_session_context* sess, sig_atomic_t* lock)
{
/* 当处理取消信号时,用户 ID 无效。因为取消信号的权限是通过随机取消密钥保证的,所以不需要再次检查权限。*/
if (!OidIsValid(u_sess->misc_cxt.CurrentUserId)) {
return;
}
/* 只有具有 sysadmin 特权、gs_role_signal_backend 角色的成员、数据库的所有者或用户本身才具有发送信号的权限。 */
bool role_signal_backend_permission = is_member_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID) &&
(sess->proc_cxt.MyRoleId != BOOTSTRAP_SUPERUSERID && !is_role_persistence(sess->proc_cxt.MyRoleId));
if (!superuser() && !pg_database_ownercheck(sess->proc_cxt.MyDatabaseId, u_sess->misc_cxt.CurrentUserId) &&
!role_signal_backend_permission) {
if (sess->proc_cxt.MyRoleId != GetUserId()) {
*lock = 0; // 解锁
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must have sysadmin privilege or a member of the gs_role_signal_backend role or the "
"owner of the database or the same user to terminate other backend")))); // 发送错误报告
}
}
}
// 如果必要,释放锁(通常用于处理异常情况)
void ThreadPoolSessControl::releaseLockIfNecessary()
{
if (unlikely(t_thrd.sig_cxt.cur_ctrl_index != 0)) {
knl_sess_control* ctrl = &m_base[t_thrd.sig_cxt.cur_ctrl_index - m_maxReserveSessionCount]; // 获取会话控制结构
volatile sig_atomic_t plock = ctrl->lock;
if (plock != 0) {
plock = 0;
}
t_thrd.sig_cxt.cur_ctrl_index = 0;
}
}
// 向指定会话发送信号(参数:会话槽位索引,信号编号)
int ThreadPoolSessControl::SendSignal(int ctrl_index, int signal)
{
Assert(signal != SIGHUP); // 断言:信号不能为 SIGHUP
int status = ESRCH; // 初始状态为 ESRCH没有这样的进程
// 检查会话槽位索引是否有效
if (!IsValidCtrlIndex(ctrl_index)) {
return ESRCH; // 返回 ESRCH没有这样的进程
}
// 获取指定会话的会话控制结构
knl_sess_control* ctrl = &m_base[ctrl_index - m_maxReserveSessionCount];
t_thrd.sig_cxt.cur_ctrl_index = ctrl_index; // 设置当前控制结构的索引
volatile sig_atomic_t* plock = &ctrl->lock; // 获取控制结构中的锁
sig_atomic_t val;
do {
if (*plock == 0) {
/* 执行原子比较和交换操作 */
val = __sync_val_compare_and_swap(plock, 0, 1);
if (val == 0) {
knl_session_context* sess = ctrl->sess; // 获取会话上下文
/* 当会话在清理连接过程中退出时,会话可能为 NULL。此时我们不执行任何操作。 */
if (sess == NULL) {
/* 恢复值。 */
ctrl->lock = 0;
status = ESRCH; // 设置状态为 ESRCH没有这样的进程
break;
}
/* 检查用户权限,对于取消请求,我们没有用户 ID。 */
CheckPermissionForSendSignal(sess, (sig_atomic_t*)plock);
// 如果会话状态为已连接KNL_SESS_ATTACH
if (sess->status == KNL_SESS_ATTACH) {
t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_SESS_KEY; // 设置信号检查类型
t_thrd.sig_cxt.session_id = sess->session_id; // 设置会话 ID
status = gs_signal_send(sess->attachPid, signal); // 向进程发送信号
t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_NONE; // 恢复信号检查类型
t_thrd.sig_cxt.session_id = 0; // 恢复会话 ID
}
// 如果会话状态为已分离KNL_SESS_DETACH
else if (sess->status == KNL_SESS_DETACH) {
switch (signal) {
case SIGTERM: // 如果信号是 SIGTERM终止信号
sess->status = KNL_SESS_CLOSE; // 设置会话状态为关闭
CloseClientSocket(sess, false); // 关闭客户端套接字
status = 0; // 设置状态为 0成功
break;
default:
break;
}
} else {
status = ESRCH; // 设置状态为 ESRCH没有这样的进程
}
/* 恢复值。 */
ctrl->lock = 0;
break;
}
}
pg_usleep(100); // 休眠 100 微秒
} while (true);
t_thrd.sig_cxt.cur_ctrl_index = 0; // 恢复当前控制结构的索引为 0
return status; // 返回状态
}
// 向指定的会话发送信号(参数:会话槽位索引,信号编号)
int ThreadPoolSessControl::SendSignal(int ctrl_index, int signal)
{
Assert(signal != SIGHUP); // 断言:信号不能为 SIGHUP
int status = ESRCH; // 初始状态为 ESRCH没有这样的进程
// 检查会话槽位索引是否有效
if (!IsValidCtrlIndex(ctrl_index)) {
return ESRCH; // 返回 ESRCH没有这样的进程
}
// 获取指定会话的会话控制结构
knl_sess_control* ctrl = &m_base[ctrl_index - m_maxReserveSessionCount];
t_thrd.sig_cxt.cur_ctrl_index = ctrl_index; // 设置当前控制结构的索引
volatile sig_atomic_t* plock = &ctrl->lock; // 获取控制结构中的锁
sig_atomic_t val;
do {
if (*plock == 0) {
/* 执行原子比较和交换操作 */
val = __sync_val_compare_and_swap(plock, 0, 1);
if (val == 0) {
knl_session_context* sess = ctrl->sess; // 获取会话上下文
/* 当会话在清理连接过程中退出时,会话可能为 NULL。此时我们不执行任何操作。 */
if (sess == NULL) {
/* 恢复锁的值。 */
ctrl->lock = 0;
status = ESRCH; // 设置状态为 ESRCH没有这样的进程
break;
}
/* 检查用户权限。对于取消请求,我们没有用户 ID。 */
CheckPermissionForSendSignal(sess, (sig_atomic_t*)plock);
// 如果会话状态为已连接KNL_SESS_ATTACH
if (sess->status == KNL_SESS_ATTACH) {
t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_SESS_KEY; // 设置信号检查类型
t_thrd.sig_cxt.session_id = sess->session_id; // 设置会话 ID
status = gs_signal_send(sess->attachPid, signal); // 向进程发送信号
t_thrd.sig_cxt.gs_sigale_check_type = SIGNAL_CHECK_NONE; // 恢复信号检查类型
t_thrd.sig_cxt.session_id = 0; // 恢复会话 ID
}
// 如果会话状态为已分离KNL_SESS_DETACH
else if (sess->status == KNL_SESS_DETACH) {
switch (signal) {
case SIGTERM: // 如果信号是 SIGTERM终止信号
sess->status = KNL_SESS_CLOSE; // 设置会话状态为关闭
CloseClientSocket(sess, false); // 关闭客户端套接字
status = 0; // 设置状态为 0成功
break;
default:
break;
}
} else {
status = ESRCH; // 设置状态为 ESRCH没有这样的进程
}
/* 恢复锁的值。 */
ctrl->lock = 0;
break;
}
}
pg_usleep(100); // 休眠 100 微秒
} while (true);
t_thrd.sig_cxt.cur_ctrl_index = 0; // 恢复当前控制结构的索引为 0
return status; // 返回状态
}
// 计算指定数据库ID的会话数量
int ThreadPoolSessControl::CountDBSessions(Oid dbId)
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
int count = 0; // 会话数量计数器
// 遍历活动会话链表
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
// 如果会话的数据库ID匹配指定的数据库ID
if (ctrl->sess->proc_cxt.MyDatabaseId == dbId) {
count++; // 增加会话数量计数器
// 如果会话的应用程序名称是 "WDRXdb"
if (strcmp(ctrl->sess->attr.attr_common.application_name, "WDRXdb") == 0) {
ThreadPoolSessControl::SendSignal(ctrl->idx, SIGTERM); // 发送SIGTERM信号给会话
ThreadPoolSessControl::SendSignal(ctrl->idx, SIGUSR2); // 发送SIGUSR2信号给会话
}
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
return count; // 返回会话数量
}
// 验证指定的数据库OID和用户OID是否有效
bool ThreadPoolSessControl::ValidDBoidAndUseroid(Oid dbOid, Oid userOid, knl_sess_control* ctrl)
{
/*
* 在清理连接过程中线程有3种情况
* 1. 只有数据库例如CLEAN CONNECTION TO ALL FORCE FOR DATABASE xxx;
* 2. 只有用户例如CLEAN CONNECTION TO ALL FORCE TO USER xxx;
* 3. 既有数据库又有用户例如CLEAN CONNECTION TO ALL FORCE FOR DATABASE xxx TO USER xxx;
*/
if (((dbOid != InvalidOid) && (userOid == InvalidOid) && (ctrl->sess->proc_cxt.MyDatabaseId == dbOid))
|| ((dbOid == InvalidOid) && (userOid != InvalidOid) && (ctrl->sess->proc_cxt.MyRoleId == userOid))
|| ((ctrl->sess->proc_cxt.MyDatabaseId == dbOid) && (ctrl->sess->proc_cxt.MyRoleId == userOid))) {
return true; // 验证通过
}
return false; // 验证未通过
}
// 计算未清理的指定数据库OID和用户OID的会话数量
int ThreadPoolSessControl::CountDBSessionsNotCleaned(Oid dbOid, Oid userOid)
{
// 如果数据库OID和用户OID都是无效的可能为NULL
if ((dbOid == InvalidOid) && (userOid == InvalidOid)) {
ereport(WARNING,
(errmsg("DB oid and user oid are all Invalid (may be NULL). Shut down clean activite sessions.")));
return 0; // 返回0表示没有会话被清理
}
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
int count = 0; // 会话数量计数器
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
// 遍历活动会话链表
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
// 如果数据库OID和用户OID与会话的数据库ID和用户ID匹配
if (ValidDBoidAndUseroid(dbOid, userOid, ctrl)) {
int status = ThreadPoolSessControl::SendSignal(ctrl->idx, 0); // 向会话发送信号0表示查询是否已经终止
// 如果终止操作尚未完成
if (status == 0) {
count++; // 增加会话数量计数器
}
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
return count; // 返回会话数量
}
// 清理指定数据库OID和用户OID的会话
int ThreadPoolSessControl::CleanDBSessions(Oid dbOid, Oid userOid)
{
// 如果数据库OID和用户OID都是无效的可能为NULL
if ((dbOid == InvalidOid) && (userOid == InvalidOid)) {
ereport(WARNING,
(errmsg("DB oid and user oid are all Invalid (may be NULL). Shut down clean activite sessions.")));
return 0; // 返回0表示没有会话被清理
}
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
int count = 0; // 会话数量计数器
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
// 遍历活动会话链表
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
// 如果数据库OID和用户OID与会话的数据库ID和用户ID匹配
if (ValidDBoidAndUseroid(dbOid, userOid, ctrl)) {
count++; // 增加会话数量计数器
ThreadPoolSessControl::SendSignal(ctrl->idx, SIGTERM); // 向会话发送SIGTERM信号
ThreadPoolSessControl::SendSignal(ctrl->idx, SIGUSR2); // 向会话发送SIGUSR2信号
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
return count; // 返回会话数量
}
// 处理SIGHUP信号
void ThreadPoolSessControl::SigHupHandler()
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
// 遍历活动会话链表
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
ctrl->sess->sig_cxt.got_SIGHUP = true; // 标记接收到SIGHUP信号
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
}
// 处理连接池重新加载事件
void ThreadPoolSessControl::HandlePoolerReload()
{
// 如果是PGXC数据节点直接返回不处理连接池重新加载事件
if (IS_PGXC_DATANODE) {
return;
}
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
// 遍历活动会话链表
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
/* 已经向线程发送了got_pool_reload信号 */
ctrl->sess->sig_cxt.got_pool_reload = true; // 标记接收到连接池重新加载事件
ctrl->sess->sig_cxt.cp_PoolReload = true; // 标记连接池重新加载事件
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
}
// 计算会话的内存上下文统计信息
void ThreadPoolSessControl::calculateSessMemCxtStats(
knl_session_context* sess, const MemoryContext context, Tuplestorestate* tupStore, TupleDesc tupDesc)
{
AllocSetContext* set = (AllocSetContext*)context; // 获取内存上下文的分配集上下文
char sessId[SESSION_ID_LEN] = {0}; // 会话ID
ThreadId threadId = 0; // 线程ID
pg_time_t sessStartTime = 0; // 会话开始时间
uint64 sessionId = 0; // 会话ID
if (sess != NULL) {
sessionId = sess->session_id; // 获取会话ID
threadId = sess->attachPid; // 获取线程ID
sessStartTime = timestamptz_to_time_t(sess->proc_cxt.MyProcPort->SessionStartTime); // 获取会话开始时间
}
getSessionID(sessId, sessStartTime, sessionId); // 生成会话ID
/* 构建一个元组并将其保存在元组存储器中。*/
Datum values[NUM_SESSION_MEMORY_DETAIL_ELEM] = {0}; // 元组数据
bool nulls[NUM_SESSION_MEMORY_DETAIL_ELEM] = {false}; // 元组的空标志位
values[0] = CStringGetTextDatum(sessId); // 会话ID
values[1] = Int64GetDatum(threadId); // 线程ID
values[2] = CStringGetTextDatum(pstrdup(context->name)); // 内存上下文名称
values[3] = Int16GetDatum(context->level); // 内存上下文层级
if (context->level > 0 && context->parent != NULL)
values[4] = CStringGetTextDatum(pstrdup(context->parent->name)); // 父级内存上下文名称
else
nulls[4] = true; // 如果没有父级内存上下文将空标志位设置为true
values[5] = Int64GetDatum(set->totalSpace); // 总共分配的内存空间
values[6] = Int64GetDatum(set->freeSpace); // 剩余的内存空间
values[7] = Int64GetDatum(set->totalSpace - set->freeSpace); // 已使用的内存空间
tuplestore_putvalues(tupStore, tupDesc, values, nulls); // 将元组数据插入到元组存储器中
}
// 递归计算会话的内存上下文统计信息
void ThreadPoolSessControl::recursiveSessMemCxt(
knl_session_context* sess, const MemoryContext context, Tuplestorestate* tupStore, TupleDesc tupDesc)
{
/* 计算内存上下文统计信息 */
calculateSessMemCxtStats(sess, context, tupStore, tupDesc);
/* 递归处理内存上下文的子级 */
for (MemoryContext child = context->firstchild; child != NULL; child = child->nextchild) {
recursiveSessMemCxt(sess, child, tupStore, tupDesc);
}
}
// 获取会话的内存详细信息
void ThreadPoolSessControl::getSessionMemoryDetail(Tuplestorestate* tupStore,
TupleDesc tupDesc, knl_sess_control** sess)
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = NULL; // 双向链表元素指针
PG_TRY();
{
HOLD_INTERRUPTS(); // 暂时屏蔽中断
alock.lock(); // 获取锁
/* 收集所有内存上下文的状态信息,将其放入数据中 */
elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
*sess = ctrl; // 更新会话控制结构指针
if (ctrl->sess) {
(void)syscalllockAcquire(&ctrl->sess->utils_cxt.deleMemContextMutex); // 获取内存上下文互斥锁
recursiveSessMemCxt(ctrl->sess, ctrl->sess->top_mem_cxt, tupStore, tupDesc); // 递归计算内存上下文统计信息
(void)syscalllockRelease(&ctrl->sess->utils_cxt.deleMemContextMutex); // 释放内存上下文互斥锁
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
RESUME_INTERRUPTS(); // 恢复中断
}
PG_CATCH();
{
if (*sess != NULL) {
ctrl = *sess;
(void)syscalllockRelease(&ctrl->sess->utils_cxt.deleMemContextMutex); // 释放内存上下文互斥锁
}
alock.unLock(); // 释放锁
PG_RE_THROW(); // 重新抛出异常
}
PG_END_TRY(); // 结束异常处理
}
// 计算客户端信息
void ThreadPoolSessControl::calculateClientInfo(
knl_session_context* sess, Tuplestorestate* tupStore, TupleDesc tupDesc)
{
/* 构建一个元组并将其保存在元组存储器中。*/
const int COLUMN_NUM = 2; // 元组的列数
Datum values[COLUMN_NUM] = {0}; // 元组数据
bool nulls[COLUMN_NUM] = {false}; // 元组的空标志位
values[0] = Int64GetDatum(sess->session_id); // 会话ID
if (sess->plsql_cxt.client_info != NULL) {
values[1] = CStringGetTextDatum(sess->plsql_cxt.client_info); // 客户端信息
} else {
nulls[1] = true; // 如果客户端信息为空将空标志位设置为true
}
tuplestore_putvalues(tupStore, tupDesc, values, nulls); // 将元组数据插入到元组存储器中
}
// 获取会话的客户端信息
void ThreadPoolSessControl::getSessionClientInfo(Tuplestorestate* tupStore, TupleDesc tupDesc)
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = NULL; // 双向链表元素指针
knl_sess_control* sess = NULL; // 会话控制结构指针
PG_TRY();
{
HOLD_INTERRUPTS(); // 暂时屏蔽中断
alock.lock(); // 获取锁
/* 收集所有内存上下文的状态信息,将其放入数据中 */
elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
sess = ctrl; // 更新会话控制结构指针
if (ctrl->sess) {
(void)syscalllockAcquire(&ctrl->sess->plsql_cxt.client_info_lock); // 获取客户端信息互斥锁
calculateClientInfo(ctrl->sess, tupStore, tupDesc); // 计算客户端信息并将其保存在元组存储器中
(void)syscalllockRelease(&ctrl->sess->plsql_cxt.client_info_lock); // 释放客户端信息互斥锁
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
sess = NULL; // 重置会话控制结构指针
RESUME_INTERRUPTS(); // 恢复中断
}
PG_CATCH();
{
if (sess != NULL) {
ctrl = sess;
(void)syscalllockRelease(&ctrl->sess->plsql_cxt.client_info_lock); // 释放客户端信息互斥锁
}
alock.unLock(); // 释放锁
PG_RE_THROW(); // 重新抛出异常
}
PG_END_TRY(); // 结束异常处理
}
// 获取指定内存上下文的详细信息,并将结果保存在给定的缓冲区中
void ThreadPoolSessControl::getSessionMemoryContextInfo(const char* ctx_name, StringInfoData* buf, knl_sess_control** sess)
{
#ifdef MEMORY_CONTEXT_TRACK // 如果启用内存上下文追踪
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
knl_sess_control* ctrl = NULL; // 会话控制结构指针
Dlelem* elem = NULL; // 双向链表元素指针
PG_TRY();
{
HOLD_INTERRUPTS(); // 暂时屏蔽中断
alock.lock(); // 获取锁
/* 收集所有内存上下文的状态信息,将其放入数据中 */
elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
while (elem != NULL) {
ctrl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
*sess = ctrl; // 更新会话控制结构指针
if (ctrl->sess) {
(void)syscalllockAcquire(&ctrl->sess->utils_cxt.deleMemContextMutex); // 获取内存上下文互斥锁
gs_recursive_unshared_memory_context(ctrl->sess->top_mem_cxt, ctx_name, buf); // 递归获取内存上下文信息
(void)syscalllockRelease(&ctrl->sess->utils_cxt.deleMemContextMutex); // 释放内存上下文互斥锁
}
elem = DLGetSucc(elem); // 获取下一个会话元素
}
alock.unLock(); // 释放锁
RESUME_INTERRUPTS(); // 恢复中断
}
PG_CATCH();
{
if (*sess != NULL) {
ctrl = *sess;
(void)syscalllockRelease(&ctrl->sess->utils_cxt.deleMemContextMutex); // 释放内存上下文互斥锁
}
alock.unLock(); // 释放锁
PG_RE_THROW(); // 重新抛出异常
}
PG_END_TRY(); // 结束异常处理
#endif
}
// 根据会话槽位索引获取会话上下文
knl_session_context* ThreadPoolSessControl::GetSessionByIdx(int idx)
{
if (IsValidCtrlIndex(idx)) {
return m_base[idx - m_maxReserveSessionCount].sess; // 返回指定槽位索引的会话上下文
} else {
return NULL; // 如果索引无效,则返回 NULL
}
}
// 根据会话ID查找会话槽位索引
int ThreadPoolSessControl::FindCtrlIdxBySessId(uint64 id)
{
int cidx = 0; // 控制结构索引
for (cidx = 0; cidx < m_maxActiveSessionCount; cidx++) {
if (m_base[cidx].sess != NULL && m_base[cidx].sess->session_id == id) {
return cidx + m_maxReserveSessionCount; // 返回找到的会话槽位索引
}
}
ereport(LOG, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid session id"))); // 记录日志表示会话ID无效
return -1; // 返回-1表示未找到
}
// 检查会话超时
void ThreadPoolSessControl::CheckSessionTimeout()
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
int cidx; // 控制结构索引
TimestampTz now = GetCurrentTimestamp(); // 获取当前时间戳
// 遍历所有活动会话的控制结构
for (cidx = 0; cidx < m_maxActiveSessionCount; cidx++) {
knl_sess_control* ctrl = &m_base[cidx]; // 获取会话控制结构指针
knl_session_context* sess = ctrl->sess; // 获取会话上下文指针
// 如果会话不为NULL且设置了会话超时
if (sess != NULL && sess->attr.attr_common.SessionTimeout != 0) {
// 如果会话的存储上下文中的会话超时活动标志为真
if (sess->storage_cxt.session_timeout_active) {
// 如果当前时间超过了会话的结束时间并且会话的超时计数小于10
if (now >= sess->storage_cxt.session_fin_time && sess->attr.attr_common.SessionTimeoutCount < 10) {
#ifdef HAVE_INT64_TIMESTAMP
elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %ld. But now is:%ld",
sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1,
sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now);
#else
elog(LOG, "close session : %lu for %d times due to session timeout : %d, max finish time is %lf. But now is:%lf",
sess->session_id, sess->attr.attr_common.SessionTimeoutCount + 1,
sess->attr.attr_common.SessionTimeout, sess->storage_cxt.session_fin_time, now);
#endif
sess->attr.attr_common.SessionTimeoutCount++; // 增加会话的超时计数
CloseClientSocket(sess, false); // 关闭客户端套接字
}
}
}
}
alock.unLock(); // 释放锁
}
// 列出所有会话的全局临时表GTT的冻结事务ID
TransactionId ThreadPoolSessControl::ListAllSessionGttFrozenxids(int maxSize,
ThreadId *pids, TransactionId *xids, int *n)
{
TransactionId result = InvalidTransactionId; // 初始化冻结事务ID为无效事务ID
int i = 0; // 计数器
// 如果未启用全局临时表,直接返回
if (u_sess->attr.attr_storage.max_active_gtt <= 0) {
return 0;
}
// 如果传入的参数合法,初始化相关变量
if (maxSize > 0) {
Assert(pids);
Assert(xids);
Assert(n);
*n = 0;
}
// 如果数据库中未启用全局临时表返回无效事务ID
if (u_sess->attr.attr_storage.max_active_gtt <= 0) {
return InvalidTransactionId;
}
// 如果处于恢复状态返回无效事务ID
if (RecoveryInProgress()) {
return InvalidTransactionId;
}
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
knl_sess_control *ctl = nullptr; // 会话控制结构指针
knl_session_context *session = nullptr; // 会话上下文指针
Dlelem* elem = DLGetHead(&m_activelist); // 获取活动会话链表的头指针
while (elem != nullptr) {
ctl = (knl_sess_control*)DLE_VAL(elem); // 获取会话控制结构指针
session = ctl->sess; // 获取会话上下文指针
// 如果会话的数据库ID与当前会话的数据库ID相同并且会话的GTT冻结事务ID有效
if (session->proc_cxt.MyDatabaseId == u_sess->proc_cxt.MyDatabaseId &&
TransactionIdIsNormal(session->gtt_ctx.gtt_session_frozenxid)) {
// 如果result为无效事务ID或者当前会话的GTT冻结事务ID较小则更新result
if (result == InvalidTransactionId) {
result = session->gtt_ctx.gtt_session_frozenxid;
} else if (TransactionIdPrecedes(session->gtt_ctx.gtt_session_frozenxid, result)) {
result = session->gtt_ctx.gtt_session_frozenxid;
}
// 如果传入的参数允许保存信息
if (maxSize > 0) {
pids[i] = session->attachPid; // 保存会话的进程ID
xids[i] = session->gtt_ctx.gtt_session_frozenxid; // 保存会话的GTT冻结事务ID
i++; // 计数器加1
}
}
elem = DLGetSucc(elem); // 获取下一个会话
}
alock.unLock(); // 释放锁
// 如果传入的参数允许保存信息,更新保存信息的数量
if (maxSize > 0) {
*n = i;
}
return result; // 返回最大的GTT冻结事务ID
}
// 检查活动会话链表是否为空
bool ThreadPoolSessControl::IsActiveListEmpty()
{
AutoMutexLock alock(&m_sessCtrlock); // 自动互斥锁,确保线程安全
alock.lock(); // 获取锁
bool res = (m_activelist.dll_len == 0); // 检查活动会话链表的长度是否为0
alock.unLock(); // 释放锁
return res; // 返回结果true表示链表为空false表示链表不为空
}