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

news2024/12/25 2:39:21

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

转载:ubuntu18.04 安装wine以及添加mono和gecko打开简单.net应用的方法

https://www.cnblogs.com/jinanxiaolaohu/p/12191576.html 1. 今天突然想试试能不能用ubuntu跑一下公司的.net的智能客户端(SmartClient). 想到的办法就是 安装wine 但是过程略坑..这里简单说一下总结之后的过程. 2. 第一步安装wine相关内容 查了下有winehq和wine两种. …

凸优化理论学习二|凸函数及其相关概念

系列文章目录 凸优化理论学习一|最优化及凸集的基本概念 文章目录 系列文章目录一、凸函数(一)凸集(二)凸函数的定义及举例(三)凸函数的证明1、将凸函数限制在一条直线上2、判断函数是否为凸函数的一阶条件…

iphone进入恢复模式怎么退出?分享2种退出办法!

iPhone手机莫名其妙的进入到了恢复模式,或者是某些原因需要手机进入恢复模式,但是之后我们不知道如何退出恢复模式怎么办? 通常iPhone进入恢复模式的常见原因主要是软件问题、系统升级失败、误操作问题等导致。那iphone进入恢复模式怎么退出&…

【工具篇】-什么是.NET

“.NET":.NET Core是由Microsoft开发,目前在.NET Foundation(一个非营利的开源组织)下进行管理。.NET Core是用C#和C编写的,并采用MIT协议作为开源协议。 简单来说:就是开发框架。 .NET 又称 .NET 平台或 .NET 框架&#xf…

Centos 7.9 安装 tigervnc-server

环境:当前使用的 Centos 7.9 的光盘作为的本地源。 1 检查是否已安装 tigervnc [rootlocalhost /]# rpm -q tigervnc tigervnc-server 未安装软件包 tigervnc tigervnc-server-1.8.0-21.el7.x86_64 如果安装过卸掉 卸载: rpm -e [rootlocalhost /]# rpm -e tige…

Django项目运行报错:ModuleNotFoundError: No module named ‘MySQLdb‘

解决方法: 在__init__.py文件下,新增下面这段代码 import pymysql pymysql.install_as_MySQLdb() 注意:确保你的 python 有下载 pymysql 库,没有的话可以使用 pip install pymysql安装 原理:用pymysql来代替mysqlL…

【Qt 学习笔记】Qt常用控件 | 容器类控件 | Group Box的使用及说明

博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt常用控件 | 容器类控件 | Group Box的使用及说明 文章编号&#xff…

力扣例题(用栈实现队列)

目录 链接. - 力扣(LeetCode) 描述 思路 push pop peek empty 代码 链接. - 力扣(LeetCode) 描述 思路 push 例如我们将10个元素放入栈中,假设最左边为栈顶,最右侧为栈底 则为10,9,8,7,6,5,4,3,…

JavaScript之数据类型(3)——object进阶

前言&#xff1a; 利用基础知识来构建对象会发现十分复杂&#xff0c;我们可以结合其他的知识点来为我们object的构建进行优化。 <1>工厂法&#xff1a; 基本格式&#xff1a; function creatObject(属性值1,属性值2,属性值3,...,属性值n) {var 对象名 new Object();对…

VR全景技术在养老院的应用优势浅析

随着时代的快速发展&#xff0c;人口老龄化越来越严重&#xff0c;如何利用VR技术提升养老服务的质量&#xff0c;成为了社会各界关注的焦点。为养老院拍摄制作VR全景&#xff0c;不仅能够为养老院的老人子女们跨越空间限制&#xff0c;实现与家人的情感连接&#xff0c;还可以…

Debian安装Redis、RabbitMQ、Nacos

安装Redis&#xff1a; 启动Redis、开机自启动 sudo systemctl start redis-server #启动sudo systemctl enable redis-server #开机自启 Redis状态(是否在运行) sudo systemctl status redis-server #查看运行状态 redis-cli ping # 客户端尝试连接 安装RabbitMQ&#xff0c;…

Zynq开发-使用PYNQ快速入门摄像头MIPI驱动(OV5640)

目录 1. 简介 2. 配置代码 2.1 初始化寄存器 2.2 分辨率寄存器 2.3 白平衡寄存器 2.4 配置寄存器代码 2.5 顶层代码 3. 细节指引 4. 总结 1. 简介 PYNQ是一种基于Python的开发环境&#xff0c;专门设计用于快速、简便地在Xilinx的Zynq平台上进行开发。在《Zynq开发之…

STM32睡眠模式

文章目录 前言PWR介绍电源框图上电复位和掉电复位可编程电压检测器低功耗模式模式选择电源控制寄存器 睡眠模式停止模式待机模式 前言 在单片机产品中&#xff0c;例如遥控这类产品&#xff0c;长时间处于待机状态下&#xff0c;所以对于这类产品在待机时就应该尽可能的减少不…

您的文件和驱动器上的“密码保护”有多安全?

某些行业&#xff08;例如医疗保健、法律和公司&#xff09;的人们在通过电子邮件发送文件时通常依赖密码保护&#xff0c;认为它可以提供足够的安全性来防止窥探。然而&#xff0c;对 PDF 或 Excel 文件进行简单的密码保护并不像看起来那样万无一失。 使用密码保护文件而不加…

01.Net基础知识

.Net的用途 Web、移动、云、桌面、游戏开发、物联网 &#xff08;IDE&#xff1a;集成开发环境&#xff09; .Net学习资源 Microsoft Learn、GitHub、G码云&#xff08;Gitee&#xff09; Visual Studio初步使用 1&#xff09;可创建的项目种类&#xff08;主要学习以下四…

前后端完全开源!功能丰富的在线教室项目:Agora Flat

Agora Flat&#xff1a;高效集成的在线教室解决方案&#xff0c;重塑互动学习新体验。- 精选真开源&#xff0c;释放新价值。 概览 Agora Flat是在GitHub平台上公开分享的一个全面开源项目&#xff0c;它精心设计为一个高性能的在线教室解决方案&#xff0c;旨在便捷地搭建支持…

JavaEE技术之MySql主从复制及mycat[了解,不讲]

文章目录 1. 主从复制1.1. 主从同步的原理1.2. 检查数据库远程访问权限1.3. 主从配置1.3.1. master配置1.3.2. slave配置1.3.3. 主库创建同步用户1.3.4. 从库配置主从关系1.3.5. 重置主从关系 1.4. 测试主从复制 2. Mycat2.1. Mycat简介2.2. MyCat读写分离原理2.3. 不废话&…

pikachu靶场(xss通关教程)

&#xff08;注&#xff1a;若复制注入代码攻击无效&#xff0c;请手动输入注入语句&#xff0c;在英文输入法下&#xff09; 反射型xss(get型) 1.打开网站 发现有个框&#xff0c;然后我们在框中输入一个“1”进行测试&#xff0c; 可以看到提交的数据在url处有显示&#xf…

[Linux][网络][数据链路层][二][MTU][ARP协议]详细讲解

目录 1.MTU1.认识MTU2.MTU对IP协议的影响3.MTU对UDP协议的影响4.MTU对TCP协议的影响 2.ARP协议0.情景铺垫1.ARP协议的作用2.能不能根据路由器 该报文的目标IP&#xff0c;确认清楚这个IP一定在它的子网当中&#xff1f;3.以太网协议(MAC帧)与ARP协议之间有什么关系&#xff1f…

OpenCV-android-sdk配置及使用(NDK)

opencv官网下载Android版Releases - OpenCV 下载好OpenCV-android-sdk并解压好,然后新建一个jni文件夹测试,测试项目目录结构如下: ├── jni │ ├── Android.mk │ ├── Application.mk │ └── test.cpp Application.mk: APP_STL := c++_static APP_CPP…