PostgreSQL的学习心得和知识总结(一百四十三)|深入理解PostgreSQL数据库之Support event trigger for logoff


注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL master源码开发而成


深入理解PostgreSQL数据库之Support event trigger for logoff

  • 文章快速说明索引
  • 功能使用背景说明
  • 补丁实现原理分析



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、深入理解PostgreSQL数据库之Support event trigger for logoff


学习时间:

2024年05月10日 23:32:16


学习产出:

1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0

postgres=# select version();
                                                  version                                                   
------------------------------------------------------------------------------------------------------------
 PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
(1 row)

postgres=#

#-----------------------------------------------------------------------------#

SQL> select * from v$version;          

BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
CON_ID 0


#-----------------------------------------------------------------------------#

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.06 sec)

mysql>

功能使用背景说明

前段时间PostgreSQL合入了Add support event triggers on authenticated login功能,可以看一下本人之前的博客:

  • PostgreSQL的学习心得和知识总结(一百三十六)|深入理解PostgreSQL数据库之Add support event triggers on authenticated login,点击前往

于是我跟建平决定实现一版 logoff 的事件触发器,不过因为考虑的不是那么周全 外加社区的态度比较冷淡,该patch属于放弃项。

在这里插入图片描述

接下来本着开源分享的目的,我给大家详细介绍一下其使用和实现原理。对此感兴趣的小伙伴可以自行查看邮件列表:

  • Support event trigger for logoff,点击前往

使用案例,如下:

-- src/test/regress/expected/event_trigger_logoff.out

-- Logoff event triggers
CREATE TABLE user_logoffs(id serial, who text);
GRANT SELECT ON user_logoffs TO public;
CREATE FUNCTION on_logoff_proc() RETURNS event_trigger AS $$
BEGIN
  INSERT INTO user_logoffs (who) VALUES (SESSION_USER);
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER on_logoff_trigger ON logoff EXECUTE FUNCTION on_logoff_proc();
ALTER EVENT TRIGGER on_logoff_trigger ENABLE ALWAYS;
\c
-- Is it enough to wait 100ms to let the logoff event trigger execute?
SELECT pg_sleep(0.1);
 pg_sleep 
----------
 
(1 row)

SELECT COUNT(*) FROM user_logoffs;
 count 
-------
     1
(1 row)

\c
SELECT pg_sleep(0.1);
 pg_sleep 
----------
 
(1 row)

SELECT COUNT(*) FROM user_logoffs;
 count 
-------
     2
(1 row)

-- Check dathaslogoffevt in system catalog
SELECT dathaslogoffevt FROM pg_database WHERE datname = :'DBNAME';
 dathaslogoffevt 
-----------------
 t
(1 row)

-- Cleanup
DROP TABLE user_logoffs;
DROP EVENT TRIGGER on_logoff_trigger;
DROP FUNCTION on_logoff_proc();
\c

补丁实现原理分析

注:此次的 patch 在实现上和 login 事件触发器非常类似,接下来 重点看一下核心逻辑即可。若有想了解更加详细的内容,请看本人之前的博客!

// src/backend/tcop/postgres.c

/* ----------------------------------------------------------------
 * PostgresMain
 *	   postgres main loop -- all backends, interactive or otherwise loop here
 *
 * dbname is the name of the database to connect to, username is the
 * PostgreSQL user name to be used for the session.
 *
 * NB: Single user mode specific setup should go to PostgresSingleUserMain()
 * if reasonably possible.
 * ----------------------------------------------------------------
 */
void
PostgresMain(const char *dbname, const char *username)
{
	...
	/* Fire any defined login event triggers, if appropriate */
	EventTriggerOnLogin();

	/*
	 * Register a callback to fire any defined logoff event triggers, if
	 * appropriate.
	 */
	if (IsUnderPostmaster)
		before_shmem_exit(EventTriggerOnLogoff, 0);
	...
}

在这里插入图片描述


在这里插入图片描述

因为是logoff事件触发器,所以这里选择before_shmem_exit注册EventTriggerOnLogoff函数,其逻辑如下:

// src/backend/storage/ipc/ipc.c

/* ----------------------------------------------------------------
 *		before_shmem_exit
 *
 *		Register early callback to perform user-level cleanup,
 *		e.g. transaction abort, before we begin shutting down
 *		low-level subsystems.
 *		
 *		注册早期回调以执行用户级清理,例如 在我们开始关闭低级子系统之前,事务中止。
 * ----------------------------------------------------------------
 */
void
before_shmem_exit(pg_on_exit_callback function, Datum arg)
{
	if (before_shmem_exit_index >= MAX_ON_EXITS)
		ereport(FATAL,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg_internal("out of before_shmem_exit slots")));

	before_shmem_exit_list[before_shmem_exit_index].function = function;
	before_shmem_exit_list[before_shmem_exit_index].arg = arg;

	++before_shmem_exit_index;

	if (!atexit_callback_setup)
	{
		atexit(atexit_callback);
		atexit_callback_setup = true;
	}
}

上述这些回调函数,具体调用的地方 如下:

// src/backend/storage/ipc/ipc.c

/* ------------------
 * Run all of the on_shmem_exit routines --- but don't actually exit.
 * This is used by the postmaster to re-initialize shared memory and
 * semaphores after a backend dies horribly.  As with proc_exit(), we
 * remove each callback from the list before calling it, to avoid
 * infinite loop in case of error.
 *  
 * 运行所有 on_shmem_exit 例程 --- 但实际上并不退出
 * 后端严重死机后,postmaster 使用它来重新初始化共享内存和信号量
 * 与 proc_exit() 一样,我们在调用每个回调之前从列表中删除它,以避免出现错误时无限循环
 * ------------------
 */
void
shmem_exit(int code)
{
	shmem_exit_inprogress = true;

	/*
	 * Call before_shmem_exit callbacks.
	 *
	 * These should be things that need most of the system to still be up and
	 * working, such as cleanup of temp relations, which requires catalog
	 * access; or things that need to be completed because later cleanup steps
	 * depend on them, such as releasing lwlocks.
	 */
	elog(DEBUG3, "shmem_exit(%d): %d before_shmem_exit callbacks to make",
		 code, before_shmem_exit_index);
	while (--before_shmem_exit_index >= 0)
		before_shmem_exit_list[before_shmem_exit_index].function(code,
																 before_shmem_exit_list[before_shmem_exit_index].arg);
	before_shmem_exit_index = 0;
	...
}

最后再看一下EventTriggerOnLogoff函数的实现,如下(该函数实现上类似于函数EventTriggerOnLogin):

// src/backend/commands/event_trigger.c

/*
 * Fire logoff event triggers if any are present.  The dathaslogoffevt
 * pg_database flag is left unchanged when an event trigger is dropped to avoid
 * complicating the codepath in the case of multiple event triggers.  This
 * function will instead unset the flag if no trigger is defined.
 */
void
EventTriggerOnLogoff(int code, Datum arg)
{
	List	   *runlist;
	EventTriggerData trigdata;

	/*
	 * See EventTriggerDDLCommandStart for a discussion about why event
	 * triggers are disabled in single user mode or via a GUC.  We also need a
	 * database connection (some background workers don't have it).
	 */
	if (!IsUnderPostmaster || !event_triggers ||
		!OidIsValid(MyDatabaseId) || !MyDatabaseHasLogoffEventTriggers)
		return;

	StartTransactionCommand();
	runlist = EventTriggerCommonSetup(NULL,
									  EVT_Logoff, "logoff",
									  &trigdata, false);

	if (runlist != NIL)
	{
		/*
		 * Event trigger execution may require an active snapshot.
		 */
		PushActiveSnapshot(GetTransactionSnapshot());

		/* Run the triggers. */
		EventTriggerInvoke(runlist, &trigdata);

		/* Cleanup. */
		list_free(runlist);

		PopActiveSnapshot();
	}

	/*
	 * There is no active logoff event trigger, but our
	 * pg_database.dathaslogoffevt is set. Try to unset this flag.  We use the
	 * lock to prevent concurrent SetDatabaseHasLogoffEventTriggers(), but we
	 * don't want to hang the connection waiting on the lock.  Thus, we are
	 * just trying to acquire the lock conditionally.
	 */
	else if (ConditionalLockSharedObject(DatabaseRelationId, MyDatabaseId,
										 0, AccessExclusiveLock))
	{
		/*
		 * The lock is held.  Now we need to recheck that logoff event triggers
		 * list is still empty.  Once the list is empty, we know that even if
		 * there is a backend which concurrently inserts/enables a logoff event
		 * trigger, it will update pg_database.dathaslogoffevt *afterwards*.
		 */
		runlist = EventTriggerCommonSetup(NULL,
										  EVT_Logoff, "logoff",
										  &trigdata, true);

		if (runlist == NIL)
		{
			Relation	pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
			HeapTuple	tuple;
			Form_pg_database db;
			ScanKeyData key[1];
			SysScanDesc scan;

			/*
			 * Get the pg_database tuple to scribble on.  Note that this does
			 * not directly rely on the syscache to avoid issues with
			 * flattened toast values for the in-place update.
			 */
			ScanKeyInit(&key[0],
						Anum_pg_database_oid,
						BTEqualStrategyNumber, F_OIDEQ,
						ObjectIdGetDatum(MyDatabaseId));

			scan = systable_beginscan(pg_db, DatabaseOidIndexId, true,
									  NULL, 1, key);
			tuple = systable_getnext(scan);
			tuple = heap_copytuple(tuple);
			systable_endscan(scan);

			if (!HeapTupleIsValid(tuple))
				elog(ERROR, "could not find tuple for database %u", MyDatabaseId);

			db = (Form_pg_database) GETSTRUCT(tuple);
			if (db->dathaslogoffevt)
			{
				db->dathaslogoffevt = false;

				/*
				 * Do an "in place" update of the pg_database tuple.  Doing
				 * this instead of regular updates serves two purposes. First,
				 * that avoids possible waiting on the row-level lock. Second,
				 * that avoids dealing with TOAST.
				 *
				 * It's known that changes made by heap_inplace_update() may
				 * be lost due to concurrent normal updates.  However, we are
				 * OK with that.  The subsequent connections will still have a
				 * chance to set "dathaslogoffevt" to false.
				 */
				heap_inplace_update(pg_db, tuple);
			}
			table_close(pg_db, RowExclusiveLock);
			heap_freetuple(tuple);
		}
		else
		{
			list_free(runlist);
		}
	}
	CommitTransactionCommand();
}

在这里插入图片描述

注:SetDatabaseHasLogoffEventTriggers有一处不同于SetDatabaseHasLoginEventTriggers,如下:

/*
 * Set pg_database.dathaslogoffevt flag for current database indicating that
 * current database has on logoff event triggers.
 */
void
SetDatabaseHasLogoffEventTriggers(void)
{
	/* Set dathaslogoffevt flag in pg_database */
	Form_pg_database db;
	Relation	pg_db = table_open(DatabaseRelationId, RowExclusiveLock);
	HeapTuple	tuple;

	/*
	 * Use shared lock to prevent a conflict with EventTriggerOnLogoff() trying
	 * to reset pg_database.dathaslogoffevt flag.  Note, this lock doesn't
	 * effectively blocks database or other objection.  It's just custom lock
	 * tag used to prevent multiple backends changing
	 * pg_database.dathaslogoffevt flag.
	 */
	LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessExclusiveLock);

	tuple = SearchSysCacheCopy1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
	db = (Form_pg_database) GETSTRUCT(tuple);
	if (!db->dathaslogoffevt)
	{
		db->dathaslogoffevt = true;
		CatalogTupleUpdate(pg_db, &tuple->t_self, tuple);
		CommandCounterIncrement();

		/* take effect for the current session */
		MyDatabaseHasLogoffEventTriggers = true; /* ----- here ----- */
	}
	table_close(pg_db, RowExclusiveLock);
	heap_freetuple(tuple);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/614769.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Stable Diffusion教程|图生图原理和实战

Stable Diffusion凭借其卓越的图生图功能,极大地提升了图像生成的可控性与输出品质,赋予用户前所未有的个性化创作风格表达能力。这一革新特性使得Stable Diffusion不仅能精准地捕捉用户的艺术愿景,更能以数字化手段孕育出新颖且极具创意的画…

论文 学习 Transformer : Attention Is All You Need

目录 概述: 对摘要的理解: 框架解析 按比例缩放的点积注意力 多头注意力机制 前馈神经网络与位置编码 概述: transformer 是一个encoder ——decoder 结构的用于处理序列到序列转换任务的框架,是第一个完全依赖自注意力机制…

写了 1000 条 Prompt 之后,我总结出了这 9 个框架【建议收藏】

如果你对于写 Prompt 有点无从下手,那么,本文将为你带来 9 个快速编写 Prompt 的框架,你可以根据自己的需求,选择任意一个框架,填入指定的内容,即可以得到一段高效的 Prompt,让 LLM 给你准确满意…

再谈毕业论文设计投机取巧之IVR自动语音服务系统设计(信息与通信工程A+其实不难)

目录 举个IVR例子格局打开,万物皆能IVR IVR系统其实可盐可甜。还能可圈可点。 戎马一生,归来依然IVR。 举个IVR例子 以下是IVR系统的一个例子。 当您拨打电话进入IVR系统。 首先检验是否为工作时间。 如是,您将被送入ivr-lang阶段&#xff0…

python3如何安装bs4

在python官网找到beautifulsoup模块的下载页面,点击"downloap"将该模块的安装包下载到本地。 将该安装包解压,然后在打开cmd,并通过cmd进入到该安装包解压后的文件夹目录下。 在该文件目录下输入"python install setup.py&quo…

程序人生 | 人生如棋,落子无悔

人生的开始,始于哭声,浮浮沉沉几十年。终了,一声长叹,在一片哭声中撒手离去。 人生的道路虽然漫长,但是关键就是那么几次机会的选择,可以决定此后几十年的光阴。 有个故事讲:古代有个人去砍柴…

搭建一个Xx431?

搭建一个Xx431? 嘿uu们!刚结束了一周六天班感觉如何? 我的状态倒还行,工作生活总能找到乐子,本周整活就是用纸巾和蛋糕托做的油灯,另外想制冷片做个温水冷水可调的杯托,但我还不会搞3d,希望今年能搞起来. 题外话就说到这,这个选题也是因为实际遇到的问题需要这玩意,下班路…

基于Matplotlib的模型性能可视化工作

一、项目简介 本项目是科技考古墓葬识别工作的中间过程,因为需要大量复用所以另起一章好了。 主要涉及到数据读取、数据可视化和少量的数据处理过程。 二、相关知识 PandasMatplotlib 三、实验过程 1. 数据探索性分析 1.1 准备工作–导入模块 import pandas…

【Python系列】Python中列表属性提取

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【Java orm 框架比较】十一 新增 原生jdbc对比

迁移到(https://gitee.com/wujiawei1207537021/spring-orm-integration-compare) orm框架使用性能比较 比较mybatis-plus、lazy、sqltoy、mybatis-flex、easy-query、mybatis-mp、jpa、dbvisitor、beetlsql、dream_orm、wood、hammer_sql_db、原生jdbc…

OpenCv中cv2.subtract(image,blurred)与(image-blurred)的区别

目录 一、cv2.subtract()函数二、cv2.subtract(image,blurred)和(image-blurred)处理效果对比2.1 代码2.2 输出结果 三、总结 一、cv2.subtract()函数 cv2.subtract是OpenCV库中的一个函数,用于进行图像减法运算。它可以很方便地进行两个图像…

LeetCode/NowCoder-链表经典算法OJ练习1

目录 说在前面 题目一:移除链表元素 题目二:反转链表 题目三:合并两个有序链表 题目四:链表的中间节点 SUMUP结尾 说在前面 dear朋友们大家好!💖💖💖数据结构的学习离不开刷题…

【C/C++笔试练习】DNS设置文件、应用层、Dos攻击、DNS服务、DNS、子网划分、http状态、路由设置、TCP连接、HTTP状态码、剪花布条、客似云来

文章目录 C/C笔试练习选择部分(1)DNS设置文件(2)应用层(3)Dos攻击(4)DNS服务(5)DNS(6)子网划分(7)http状态&am…

网络运维故障排错思路!!!!!(稳了!!!)

1 网络排错的必备条件 为什么要先讲必备条件?因为这里所讲的网络排错并不仅仅是停留在某一个小小命令的使用上,而是一套系统的方法,如果没有这些条件,我真的不能保证下面讲的这些你可以听得懂,并且能运用到实际当中&a…

Navicat 17:先睹为快

官方声明:Navicat 17(英文版)目前处于测试阶段中,并计划 5 月 13 日发布! 如果你觉得 Navicat 16 已经推出很多令人兴奋的新功能,那么这次你可能要好好看看 Navicat 17,本次升级涵盖了更多的内容…

ASP.NET WebApi 如何使用 OAuth2.0 认证

前言 OAuth 2.0 是一种开放标准的授权框架,用于授权第三方应用程序访问受保护资源的流程。 OAuth 2.0 认证是指在这个框架下进行的身份验证和授权过程。 在 OAuth 2.0 认证中,涉及以下主要参与方: 资源所有者(Resource Owner&…

【算法】动态规划之背包DP问题(2024.5.11)

前言: 本系列是学习了董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 动态规划系列 【算法】动态规划之线性DP问题-CSDN博客 01背包 步骤: 分析容量j与w[i]的关系,然后分析是否要放…

iLogtail 社区开源之夏活动来了!

作者:玄飏 在这个充满活力的夏日,随着阳光一同灿烂的是开源精神的光辉与创新的火花。iLogtail 社区高兴地宣布,我们正式加入开源之夏 2024 的行列,诚邀每一位怀揣梦想与激情的学生开发者,共同开启一场探索技术前沿、贡…

WP All Import Pro插件下载 - 一键导入,无限可能

在当今快节奏的数字时代,网站内容的更新和管理是每个网站管理员和开发者的日常工作。但是,传统的手动更新方法不仅耗时,而且容易出错。现在,有了WP All Import Pro,这一切都将改变。 WP All Import Pro 是一款专为Wor…

完美撤离暗区突围测试资格获取指南 超简单的暗区突围资格申请

完美撤离!暗区突围测试资格获取指南 超简单的暗区突围资格申请! 最近游戏圈关注度最高的一件事莫过于暗区突围国际服的上线,随着暗区突围PC端的上线,这款游戏的测试资格申请成为了玩家们心头的一个大问题,许多玩家爱不…