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

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

本文介绍了事务提交过程中,具体执行提交任务的AbortTransaction函数详细执行流程。

1. 执行条件

存在三种调用此函数的场景:

  1. SQL语法出错,由sigsetjmp函数跳回PostgresMain函数,从AbortCurrentTransaction函数中调用AbortTransaction函数。
  2. 事务执行过程中出错,跳出执行AbortTransaction函数。
  3. 主动放弃事务,执行AbortTransaction函数。

2. 执行过程

2.1 获取当前状态:

申请事务状态变量TransactionState s和并创建两个临时变量latestXid和is_parallel_worker。

将CurrentTransactionState变量赋值给TransactionState s。

TransactionState s = CurrentTransactionState;
TransactionId latestXid;
bool		is_parallel_worker;

2.2 开始事务资源清理:

关闭中断开始清理工作。

    /* Prevent cancel/die interrupt while cleaning up */
    HOLD_INTERRUPTS();

切换事务上下文和资源拥有者。

    /* Make sure we have a valid memory context and resource owner */
    AtAbort_Memory();
    AtAbort_ResourceOwner();
  1. AtAbort_Memory()负责切换事务上下文到TransactionAbortContext,如果这个上下文没有足够的空间就切换到TopMemoryContext。
  2. 因为Abort的操作肯定是从top开始,AtAbort_ResourceOwner负责将TopTransactionResourceOwner切换到CurrentResourceOwner 。

释放所有LW锁,清除等待信息和命令进度指示器。

    LWLockReleaseAll();
    /* Clear wait information and command progress indicator */
    pgstat_report_wait_end();
    pgstat_progress_end_command();

事务在Abort之前可能会遇到各种需要等待的情况,这些等待可能是由于资源争用、锁、系统限制或其他原因造成的,因此:

  1. 调用LWLockReleaseAll()函数释放所有LW锁,需要释放LW锁;
  2. 调用pgstat_report_wait_end()函数,需要pgstat_report_wait_end()通知数据库后台事务等待结束;
  3. 调用pgstat_progress_end_command()函数,需要pgstat_progress_end_command()通知数据库后台结束对当前事务进度的报告。

清除I/O和缓存相关的资源。

    /* Clean up buffer I/O and buffer context locks, too */
    AbortBufferIO();
    UnlockBuffers();

AbortBufferIO()的作用主要是在事务中止(Abort)时进行相关的清理工作。

  1. 中断 I/O 操作:如果事务在中止时有正在进行的 I/O 操作,AbortBufferIO 会尝试中断这些操作,以确保不会有数据被错误地写入磁盘。
  2. 清除缓冲区标记:事务可能会标记缓冲区为“脏”(即已修改),表示这些缓冲区的数据已经被更改,需要被刷新到磁盘。AbortBufferIO 会清除这些标记,因为中止的事务不会将更改永久化。
  3. UnlockBuffers()主要作用是在事务结束时释放事务过程中所持有的缓冲区锁,以及清理固定计数(PIN COUNT)
  4. 固定(Pinning)一个缓冲区意味着告诉系统在某些操作完成之前不要释放该缓冲区,因此,即使锁已经被释放,缓冲区仍然处于被固定状态,不能被操作。

重置WAL记录的构造状态。

    /* Reset WAL record construction state */
    XLogResetInsertion();

XLogResetInsertion()的作用是重置WAL记录的构造状态。重置插入状态意味着清除当前事务对WAL日志记录的所有影响,撤销任何未完成的 WAL 记录构造,确保 WAL 日志的整洁和正确性。具体的来说:

  1. 清除当前事务可能已经开始构建但未完成的WAL记录;
  2. 撤销对WAL缓冲区的任何修改,这些修改可能是由于事务中的操作而进行的;
  3. 确保在事务失败或需要回滚时,不会有部分完成的WAL记录留在系统中。

清理任何未完成的条件等待睡眠。

    /* Cancel condition variable sleep */
    ConditionVariableCancelSleep();

ConditionVariableCancelSleep()的作用是取消睡眠状态,将当前线程的等待状态从条件变量的等待队列中移除,如果线程没有处于睡眠状态(等待条件变量),则什么都不做。

取消所有挂起的锁等待要求并撤销进行中的强锁计数获取。

    LockErrorCleanup();
  1. LockErrorCleanup()主要作用:在事务中止时取消所有挂起的锁等待请求。挂起的锁等待指的是,某些操作因为等待锁的释放而被阻塞。比如,查询操作对数据的读取,创建一个索引的操作。
  2. LockErrorCleanup()还会撤销(revert)任何正在进行中的强锁计数获取。在 PostgreSQL 中,锁有一个计数机制,用于跟踪同一锁被请求的次数。如果事务在获取锁的过程中被中止,需要确保相关的锁计数被恢复到正确的状态。

重新安排超时事件。

	reschedule_timeouts();

reschedule_timeouts()的作用是重新安排超时事件。

  1. 如果存在任何仍然处于活动状态的超时事件,该函数确保已经安排了超时中断。在清理锁错误之后,系统会检查是否有任何超时事件仍然需要处理,并确保相应的超时中断被重新安排。
  2. 这样做可以防止由于异常退出信号处理器而导致的超时中断丢失,并避免重新安排锁或死锁的超时检查。

重新启用(解除屏蔽)信号。

	PG_SETMASK(&UnBlockSig);
  1. PG_SETMASK 宏或函数的作用是重新启用(解除屏蔽)信号。这是为了处理一种特定情况:程序可能通过 longjmp 函数从信号处理器(signal handler)中跳出。
  2. 在 C 语言中,longjmp 可以跳出最近的 setjmp 调用,通常用于异常处理或从信号处理器中恢复执行。如果程序通过这种方式跳出信号处理器,可能需要重新启用之前被屏蔽的信号。

设置用户ID和安全上下文。

	SetUserIdAndSecContext(s->prevUser, s->prevSecContext);

为了确保用户ID可能因为某些操作的临时改变能够被正确处理,调用SecurityRestrictionContext函数保存prior的ID和安全上下文,在恢复时能够恢复到一个正确的状态。

重置REINDEX状态、逻辑流复制状态和快照状态。

	/* Forget about any active REINDEX. */
	ResetReindexState(s->nestingLevel);
	/* Reset logical streaming state. */
	ResetLogicalStreamingState();
	/* Reset snapshot export state. */
	SnapBuildResetExportedSnapshotState();
  1. ResetReindexState用于重置当前会话或事务的REINDEX状态,取消或撤销任何当前处于活动状态的REINDEX操作。REINDEX是PostgreSQL中用来重建索引的命令,它可能会改变数据库的索引结构和统计信息。
  2. ResetLogicalStreamingState函数的作用是重置逻辑流复制的状态。CheckXidAlive = InvalidTransactionId; bsysscan = false;
  3. SnapBuildResetExportedSnapshotState函数的作用是重置由快照构建器(snapshot builder)导出的快照状态。在 PostgreSQL 中,快照是数据库在某一时刻的状态的视图,可以用于复制、备份或其他数据库维护任务。

执行事务放弃过程。

	/*
	 * do abort processing
	 */
	AfterTriggerEndXact(false); /* 'false' means it's abort */
	AtAbort_Portals();
	smgrDoPendingSyncs(false, is_parallel_worker);
	AtEOXact_LargeObject(false);
	AtAbort_Notify();
	AtEOXact_RelationMap(false, is_parallel_worker);
	AtAbort_Twophase();
  1. AfterTriggerEndXact(false)表示处理事务结束时的后触发器的函数,它接受一个布尔参数isCommit,该参数指示事务是提交 (true) 还是回滚 (false)。

    如果事务中有未触发的触发器,它们将被取消,并且与这些触发器相关的任何信息或状态都将被丢弃。在事务结束时,不需要执行这些未触发的触发器,也不需要保留它们的任何状态信息。

  2. AtAbort_Portals 函数的作用是在事务中止时对门户(Portals)进行清理中止处理。门户是存储的游标,它们可以包含复杂的查询和状态信息。当事务中止时,需要清理门户以释放资源并确保不会留下任何未完成的操作。

  3. smgrDoPendingSyncs函数会在当前事务期间,对那些被创建且其更改没有记录在 WAL(Write-Ahead Logging)日志中的文件进行同步。这是为了确保如果发生崩溃,这些文件的状态是一致的,并且不会留下已提交但损坏的文件。

  4. AtEOXact_LargeObject函数主要是清理一些超出常规字段所能存储限制的“大对象”,关闭与大型对象关联的文件描述符(LO fds),并清空 cookie 数组。

    这样做的结果是,这些文件描述符将不再有效,无法再被用来访问或操作大型对象。无论事务是提交还是中止,持有这些 LO fds 的内存上下文和资源所有者在事务结束时都将被销毁。这意味着这些资源在事务结束时无论如何都会被清理。

    在事务提交的情况下,需要关闭这些 LO fds,以避免在提交时出现关于资源泄露的警告。这是因为在提交时,系统会检查所有资源是否已经被正确管理,包括打开的文件描述符。

    在事务中止的情况下,可以跳过关闭 LO fds 的步骤。这是因为在中止事务时,系统会回滚所有更改,并且通常不需要执行与提交相同的清理操作。

  5. AtAbort_Notify 函数的作用是清除所有挂起的操作和出站通知(outbound notifies)。这里的“挂起的操作”指的是在事务中计划执行但尚未执行的动作。“出站通知”可能指的是使用NOTIFY命令发送的信号,这些信号原本计划在事务提交时发送,以通知其他进程或应用程序有关事件的发生,但现在事务放弃了,就不发送NOTIFY命令通知其他程序,直接放弃。

  6. AtAbort_Twophase 函数的作用是在事务中止时解锁我们正在工作的全局事务条目。非分布式的事务中,全局变量MyLockedGxact为NULL,函数不做任何操作直接返回。如果存在2PC,根据事务中止时的不同情况,如何处理全局事务条目。这包括在某些情况下从共享内存中移除条目,以及在其他情况下保留并解锁条目,以允许事务稍后完成或正确结束。

清理GUC、SPI、HashTables等资源的占用。

		ResourceOwnerRelease(TopTransactionResourceOwner,
							 RESOURCE_RELEASE_BEFORE_LOCKS,
							 false, true);
		AtEOXact_Buffers(false);
		AtEOXact_RelationCache(false);
		AtEOXact_Inval(false);
		AtEOXact_MultiXact();
		ResourceOwnerRelease(TopTransactionResourceOwner,
							 RESOURCE_RELEASE_LOCKS,
							 false, true);
		ResourceOwnerRelease(TopTransactionResourceOwner,
							 RESOURCE_RELEASE_AFTER_LOCKS,
							 false, true);
		smgrDoPendingDeletes(false);

		AtEOXact_GUC(false, 1);
		AtEOXact_SPI(false);
		AtEOXact_Enum();
		AtEOXact_on_commit_actions(false);
		AtEOXact_Namespace(false, is_parallel_worker);
		AtEOXact_SMgr();
		AtEOXact_Files(false);
		AtEOXact_ComboCid();
		AtEOXact_HashTables(false);
		AtEOXact_PgStat(false, is_parallel_worker);
		AtEOXact_ApplyLauncher(false);
		pgstat_report_xact_timestamp(0);

具体可以参考CommitTransaction函数的清理过程。

2.3 完成事务资源清理

完成事务的放弃操作,恢复中断。

	/*
	 * State remains TRANS_ABORT until CleanupTransaction().
	 */
	RESUME_INTERRUPTS();

这里只是完成了放弃事务需要的资源清理工作,对事务的放弃完成需要执行CleanupTransaction()函数。

作者介绍

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

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值