暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

海山数据库(He3DB)源码详解:AbortSubTransaction函数

dawn1221 2024-09-23
40

海山数据库(He3DB)源码详解:AbortSubTransaction函数

1、执行条件

  1. 在终端主动执行ABORT语句,终止事务会在CommitTransactionCommand函数中调用AbortSubTransaction函数;

  2. 在隐式事务中执行SQL语句发生错误时,底层源码会跳回PostgresMain函数中并调用AbortCurrentTransaction函数,终止事务并调用AbortSubTransaction函数。

2、执行过程

下面是一个简化的 AbortSubTransaction 函数执行流程的框架图:

Abort流程图.jpeg
Abort过程执行流程

这个流程图描述了从开始执行 AbortSubTransaction 命令到最终完成或发生错误的整个过程。每个步骤都是决策点或操作,其中:

  • B 代表获取子事务的信息。
  • C 是一个决策点,检查子事务是否存在。
  • DH 描述了正常的中止流程,包括锁定资源、回滚更改、释放资源和记录操作。
  • I 是另一个决策点,检查在中止过程中是否发生错误。
  • J 表示如果发生错误,则记录错误并尝试继续回滚。
  • K 表示成功清理子事务状态。
  • L 表示如果发生错误,则通知客户端中止中发生错误。
  • M 表示通知客户端子事务已中止。

请注意,这个流程图是一个高层次的抽象,实际的 PostgreSQL 源码实现会更加复杂,并且涉及到许多底层的函数和数据结构,因此本文会在下一节详细解读函数源码。

3、源码解读

1. 开始终止事务

获取事务状态并关闭中断。

TransactionState s = CurrentTransactionState; /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS();

从全局变量中获得需要终止的事务状态,并关闭中断开始事务的清理工作。

2. 清理事务资源

1、检查上下文和资源owner

/* Make sure we have a valid memory context and resource owner */ AtSubAbort_Memory(); AtSubAbort_ResourceOwner();
  1. AtSubAbort_Memory()函数将内存上下文切换到TransactionAbortcontext;

  2. AtSubAbort_ResourceOwner()函数将全局变量CurrentResourceOwner切换为s->curTransactionowner。

2、释放LW锁和缓冲区资源

LWLockReleaseAll(); pgstat_report_wait_end(); pgstat_progress_end_command(); AbortBufferIO(); UnlockBuffers(); /* Reset WAL record construction state */ XLogResetInsertion();
  1. 调用LWLockReleaseAll()释放所有的LW轻量锁,但是不包括缓冲区锁(后面再释放);

  2. 调用pgstat_report_wait_end()将事件等待列表信息(my_wait_event_info)置为0;

  3. 调用pgstat_progress_end_command()函数向统计收集器(Statistics Collector)报告命令的进度信息同时更新统计信息,将st_progress_command字段设置为PROGRESS_COMMAND_INVALID,将st_progress_command_target字段设置为InvalidOid;

  4. 在事务发生错误后调用AbortBufferIO()清理任何活动的缓冲区 I/O 操作,之后调用UnlockBuffers()释放缓冲区锁;

  5. 重置WAL日志记录状态。

3、释放锁和信号量,重置信号操作

/* Cancel condition variable sleep */ ConditionVariableCancelSleep(); LockErrorCleanup(); reschedule_timeouts(); PG_SETMASK(&UnBlockSig);
  1. 调用ConditionVariableCancelSleep()取消所有被信号量控制,处于睡眠等待状态中的操作;

  2. 调用LockErrorCleanup()取消任何待处理的锁等待,如果事务在获取更强的锁(例如,从行锁升级到表锁)的过程中被终止,LockErrorCleanup 会撤销这个过程中对锁计数的增加;

  3. 调用reschedule_timeouts()函数重新安排任何待处理的 SIGALRM 中断;

  4. 调用PG_SETMASK(&UnBlockSig)重新启用信号,以防从信号处理器中 longjmp出来,为了让超时机制能够在需要的时候能够发挥作用。

4、检查当前状态并开始终止事务

/* check the current transaction state */ ShowTransactionState("AbortSubTransaction"); if (s->state != TRANS_INPROGRESS) elog(WARNING, "AbortSubTransaction while in %s state", TransStateAsString(s->state)); s->state = TRANS_ABORT;
  1. 调用ShowTransactionState(“AbortSubTransaction”)记录一条日志;

  2. 检查当前状态是否正确(应处在TRANS_INPROGRESS),错误则记录一条错误日志;

  3. 修改当前事务状态为TRANS_ABORT。

5、重置user ID、活动的REINDEX和逻辑流状态

/* Reset user ID which might have been changed transiently */ SetUserIdAndSecContext(s->prevUser, s->prevSecContext); /* Forget about any active REINDEX. */ ResetReindexState(s->nestingLevel); /* Reset logical streaming state. */ ResetLogicalStreamingState();
  1. 调用SetUserIdAndSecContext(s->prevUser, s->prevSecContext)重置可能被临时更改的用户 ID,恢复SecurityRestrictionContext之前的值;

  2. 调用ResetReindexState(s->nestingLevel)恢复所有索引重置状态为默认值;

  3. 调用ResetLogicalStreamingState()重置逻辑流的状态到默认值。

6、检查事务并行状态并进行清理

/* Exit from parallel mode, if necessary. */ if (IsInParallelMode()) { AtEOSubXact_Parallel(false, s->subTransactionId); s->parallelModeLevel = 0; }
  1. 调用IsInParallelMode()判断当前事务的并行状态;

  2. 如果在并行事务状态下,清理子事务的并行模式上下文资源,并将并行模式恢复默认状态;

7、清理触发器、游标、大对象,并通知事务终止

AfterTriggerEndSubXact(false); AtSubAbort_Portals(s->subTransactionId, s->parent->subTransactionId, s->curTransactionOwner, s->parent->curTransactionOwner); AtEOSubXact_LargeObject(false, s->subTransactionId, s->parent->subTransactionId); AtSubAbort_Notify(); /* Advertise the fact that we aborted in pg_xact. */ (void) RecordTransactionAbort(true);
  1. AfterTriggerEndSubXact()在子事务终止过程中,函数会将触发器到恢复默认状态,并清理等待中的事件列表;

  2. AtSubAbort_Portals()函数会将该子事务创建或使用的游标全部停用,,释放portal对其缓存计划的引用和辅助上下文(subsidiary contexts),但是不会清理这些游标;

  3. AtEOSubXact_LargeObject()函数在终止过程中,会将大文件的描述符直接关闭;

  4. AtSubAbort_Notify()函数会清理掉在该子事务中的活动和通知,不进行出站通知。

  5. RecordTransactionAbort(true)记录子事务被终止,并进行后台通知。

8、开始预终止的清理工作

/* Post-abort cleanup */ if (FullTransactionIdIsValid(s->fullTransactionId)) AtSubAbort_childXids(); CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId, s->parent->subTransactionId); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); AtEOSubXact_RelationCache(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Inval(false); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_LOCKS, false, false); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, false); AtSubAbort_smgr(); AtEOXact_GUC(false, s->gucNestLevel); AtEOSubXact_SPI(false, s->subTransactionId); AtEOSubXact_on_commit_actions(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Namespace(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Files(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_HashTables(false, s->nestingLevel); AtEOSubXact_PgStat(false, s->nestingLevel); AtSubAbort_Snapshot(s->nestingLevel);
  1. 如果子事务XID有效,调用AtSubAbort_childXids()函数释放该事务的子事务XID;

  2. 调用AtEOSubXact_RelationCache()清理relation缓存;

  3. 调用AtEOSubXact_Inval()处理子事务的失效消息;

  4. 调用AtSubAbort_smgr()删除在子事务中创建的关系(例如,表或索引),并停止跟踪在子事务中标记为删除的关系。这意味着任何在子事务中创建的新关系都将被撤销,同时任何计划在子事务提交时删除的关系都将取消删除计划;

  5. 调用AtEOXact_GUC()函数处理数据库的GUC配置;

  6. 调用AtEOSubXact_SPI()函数处理SPI(Server Programming Interface,服务器编程接口)状态;

  7. 调用AtEOSubXact_on_commit_actions()函数针对ON COMMIT字句,立即删除在此子事务中创建的条目;

  8. 调用AtEOSubXact_Namespace()函数调用重置 MyProc 中的临时命名空间标志;

  9. 调用AtEOSubXact_Files()函数关闭子事务可能打开的临时文件;

  10. 调用AtEOSubXact_HashTables()函数在子事务结束时进行清理任何仍然打开的散列表;

  11. 调用AtEOSubXact_PgStat()函数在子事务结束时更新或清理pgStat模块的状态;

  12. 调用AtSubAbort_Snapshot()函数清理事务快照。

9、恢复上层节点只读并恢复中断

XactReadOnly = s->prevXactReadOnly; RESUME_INTERRUPTS();
  1. 恢复上层事务的只读状态,同时恢复中断机制;
  2. AbortSubTransaction函数执行完成,但是依旧有一些资源尚未清理,会在后续CleanupSubTransaction()函数中执行。

作者介绍

李超,移动云数据库工程师,负责云原生数据库He3DB的研发。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论