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

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

1、执行条件

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

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

2、执行过程

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

在这里插入图片描述

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的研发。

y = s->prevXactReadOnly;
RESUME_INTERRUPTS();

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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值