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 $$
BEGININSERT 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://xiahunao.cn/news/3032730.html

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

相关文章

Linux 第三十一章

🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C,linux 🔥座右铭:“不要等到什么都没有了…

SAP BSEG VS ACDOCA 差异

温习一下 ACDOCA VS BSEG matinal:S4 HANA 详解ACDOCA与BSEG的区别都在这了_sap acdoca-CSDN博客

品鉴中的食物搭配:如何创造美味的红酒与食物组合

品鉴云仓酒庄雷盛红酒时,食物搭配是一个不可忽视的环节。通过巧妙的搭配,红酒与食物可以相互衬托,呈现出更加美妙的风味。下面就让我们一起探讨如何创造美味的红酒与食物组合。 首先,了解红酒与食物的搭配原则是关键。一般来说&a…

An 2024下载

An2024下载: 百度网盘下载https://pan.baidu.com/s/1cQQCFL16OUY1G6uQWgDbSg?pwdSIMS Adobe Animate 2024,作为Flash技术的进化顶点,是Adobe匠心打造的动画与交互内容创作的旗舰软件。这款工具赋予设计师与开发者前所未有的创意自由&#x…

力扣【旋转函数】python

如果直接用暴力的话,只能过4个样例好像,超时 因此得用递推公式 F1F0前n-1个数-(n-1)*第n个数 F0sum(nums)-n*第n个数 nlen(nums) ans[]#定义一个存最大值值的列表 ss sum(nums) dm 0 for j in range(n):dm j * nums[j] ans.append(dm) print(dm) n…

Coursera吴恩达深度学习专项课程01: Neural Networks and Deep Learning 学习笔记 Week 01

Week 01 of Neural Networks and Deep Learning Course Certificate 本文是学习 https://www.coursera.org/learn/neural-networks-deep-learning 这门课的笔记 Course Intro 文章目录 Week 01 of Neural Networks and Deep Learning[0] Welcome to the Deep Learning Spec…

“网络安全新纪元:等保2.0的详细解读与实践”

网络安全等级保护基本要求》(等保2.0)于2019年6月发布,是我国网络安全等级保护制度的一项重要标准。等保2.0主要针对关键信息基础设施的网络安全保护,对数据安全和个人信息保护提出了更高的要求。本文将对等保2.0进行详细解读&…

GPT 4o意味着什么?OpenAI加苹果,AI手机势不可挡!

ChatGPT带飞Siri? 当地时间5月13日上午10点,OpenAI在春季发布会上发布了GPT-4的迭代版本——GPT-4o。据悉,GPT-4o的速度是上一代的两倍,并且对用户更友好:语音唤起、实时对话、无需注册、免费使用。 OpenAI表示,GPT…

【联通支付注册/登录安全分析报告】

联通支付注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨…

Pencils Protocol 宣布再获合作伙伴 Galxe 的投资

近日,Scroll生态项目Penpad将品牌进一步升级为Pencils Protocol,全新升级后其不仅对LaunchPad平台进行了功能上的升级,同时其也进一步引入了Staking、Vault以及Shop等玩法,这也让Pencils Protocol的叙事方向不再仅限于LaunchPad&a…

C++ requires关键字简介

requires 是 C20 中引入的一个新关键字,用于在函数模板或类模板中声明所需的一组语义要求,它可以用来限制模板参数,类似于 typename 和 class 关键字。 requires关键字常与type_traits头文件下类型检查函数匹配使用,当requires后…

Win7远程桌面连接不上:原因及专业解决方案

Win7远程桌面连接作为一种方便的工具,使得用户可以从一台计算机远程访问和操作另一台计算机。然而,有时用户可能会遇到Win7远程桌面连接不上的情况,这可能是由于多种原因导致的。 一、原因分析 1. 网络设置问题:确保计算机与远程…

静态IP代理:网络世界的隐秘通道

在数字化时代,网络安全和隐私保护日益受到重视。静态IP代理作为一种网络服务,为用户提供了一个稳定且可预测的网络连接方式,同时保护了用户的在线身份。本文将从五个方面深入探讨静态IP代理的概念、优势、应用场景、技术实现以及选择时的考量…

Springboot3 链接Redis遇到的报错(本文仅记录保存,优质文章移步springboot专栏)

出现的报错: cannot connect to Redisedis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is setredis wrong number of arguments for ‘auth’ command 其实上面的三个报错是不同界面显示的,后面两个是通过Ide…

「AIGC算法」K-means聚类模型

本文主要介绍K-means聚类模型原理及实践demo。 一、原理 K-means聚类是一种经典的、广泛使用的无监督学习算法,主要用于将数据集划分为多个类别或“簇”。其目标是将数据集中的每个点分配到K个聚类中心之一,使得簇内的点尽可能相似,而簇间的…

【SRC实战】信息泄露管理员token

【SRC实战】信息泄露管理员token 原创 挖个洞先 挖个洞先 2024-05-12 17:04 湖北 “ 以下漏洞均为实验靶场,如有雷同,纯属巧合 ” 01 — 漏洞证明 1、使用灯塔收集到config.js,存在VUE_APP_USERTOKEN参数 灯塔ARL https://github.com/Top…

日本率先研发成功6G设备,刺痛了谁?为何日本能率先突破?

日本率先研发成功6G设备,无线数据速率是5G的百倍,这让日本方面兴奋莫名,毕竟日本在科技方面从1990年代以来太缺少突破的创新了,那么日本为何如今在6G技术上能率先突破呢? 日本在1980年代末期达到顶峰,它的科…

研究生学习---找工作

规划 研一~研二上学期完成小论文,实习,秋招 竞赛:kaggle? 面试题一般简单且为原题,笔试题目很难,不会出原题 项目 找工作软件

立创EDA绘制PCB电路板

1、绘制好原理图后,点击设计---原理图转PCB,生成PCB文件 2、将元器件拖入电路板方框内,摆放布局并使用工具栏布线、放置过孔及丝印 3、然后顶层和底层铺铜 4、后面就可以生成制板文件发送嘉立创制板了。

Java为什么会成为现在主流的编程语言

Java为什么会成为现在的主流语言 前言一、Java语言概述Java是什么为什么大多数人会选择从事Java为什么从事Java的工作者数量从年递减 二、Java语言的特点简单性面向对象分布式(微服务)健壮性安全性体系结构中立可移植性解释型高性能多线程动态性 三、Jav…