自学内容网 自学内容网

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

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

1、执行条件

  1. 在终端中执行release savepoint <pointname>命令时,底层会在ReleaseTransaction函数中调用CommitSubTransaction函数,提交保存点内的子事务;
  2. 在事务提交之后,底层会在CommitTransactionCommand函数中调用CommitSubTransaction函数,提交所有子事务。

2、执行流程

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

在这里插入图片描述

Commit过程执行流程

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

  • B 代表获取子事务的信息。
  • C 是一个决策点,检查子事务是否存在。
  • DK 描述了正常的提交流程。
  • L 是另一个决策点,检查在提交过程中是否发生错误。
  • M 表示如果发生错误,则进行子事务的回滚。
  • N 表示成功返回提交状态给客户端。
  • O 表示如果发生错误,则通知客户端提交失败。
  • P 表示通知客户端提交成功。

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

3、源码解读

1. 获取当前节点状态:

获得当前节点状态变量并坚持事务状态。

TransactionState s = CurrentTransactionState;
ShowTransactionState("CommitSubTransaction");
    if (s->state != TRANS_INPROGRESS)
        elog(WARNING, "CommitSubTransaction while in %s state",
             TransStateAsString(s->state));
  1. 通过全局变量CurrentTransactionState获得当前子事务的状态,并记录一条CommitSubTransaction日志;
  2. 检查当前子事务的状态,如果不在TRANS_INPROGRESS,在日志中记录当前状态。

2. 检查并行状态

检查是否处在并行事务状态,并开始子事务提交。

/* If in parallel mode, clean up workers and exit parallel mode. */
if (IsInParallelMode())
{
AtEOSubXact_Parallel(true, s->subTransactionId);
s->parallelModeLevel = 0;
}
/* Do the actual "commit", such as it is */
s->state = TRANS_COMMIT;
  1. 如果在并行事务状态下,清理子事务的并行模式上下文资源,并将并行模式恢复默认状态;
  2. 修改子事务状态TRANS_COMMIT,开始正式的子事务提交过程。

3. 执行命令计数器递增

通过执行命令计数器递增(Command Counter Increment, CCI)操作,以确保子事务

(subtransaction)中的命令被视为已经完成。

/*Must CCI to ensure commands of subtransaction are seen as done*/
CommandCounterIncrement();
  1. 命令计数器递增(Command Counter Increment, CCI)操作,用于跟踪事务内执行的每个命令的唯一序列号。这个序列号对于维护数据库的多版本并发控制(MVCC)至关重要;
  2. CommandCounterIncrement函数通确保每个事务命令都被正确地跟踪,确保子事务中的所有命令对其他事务都是可见的,即被视为已经完成的状态。

4. 提交前的清理

在提交子事务之前,先清理触发器、游标、大对象等资源。

/* Post-commit cleanup */
if (FullTransactionIdIsValid(s->fullTransactionId))
AtSubCommit_childXids();
AfterTriggerEndSubXact(true);
AtSubCommit_Portals(s->subTransactionId,
s->parent->subTransactionId,
s->parent->nestingLevel,
s->parent->curTransactionOwner);
AtEOSubXact_LargeObject(true, s->subTransactionId,
s->parent->subTransactionId);
AtSubCommit_Notify();

CallSubXactCallbacks(SUBXACT_EVENT_COMMIT_SUB, s->subTransactionId,
 s->parent->subTransactionId);
ResourceOwnerRelease(s->curTransactionOwner,
 RESOURCE_RELEASE_BEFORE_LOCKS,
 true, false);
AtEOSubXact_RelationCache(true, s->subTransactionId,
  s->parent->subTransactionId);
AtEOSubXact_Inval(true);
AtSubCommit_smgr();
  1. 判断当前子事务的事务ID,并调用AtSubCommit_childXids函数,收集当前子事务的XID事务ID,以及所有由当前子事务生成的子事务(孙子事务)的XID,将这些XID传递给父事务,确保所有相关的XID都被正确地记录和传递;
  • 当父事务最终提交时,它需要知道所有子事务的状态。AtSubCommit_childXids 确保父事务有一个完整的已提交子事务的列表,这样在父事务提交时,可以正确地将这些子事务的更改永久化到数据库中。
  1. AfterTriggerEndSubXact(bool isCommit)函数在参数true表示子事务为提交状态,负责关闭子事务的触发器,而触发器相关的资源会在CommitTransaction函数中安全释放;如果参数是false,表示子事务为Abort状态,这时释放与触发器相关的资源。
  2. AtSubCommit_Portals()将当前子事务中创建或使用的portal重新分配给父事务。这意味着,当子事务提交时,所有在该子事务中打开的portal都会变成父事务的一部分,而不是随着子事务的结束而消失。这些portal会在CommitTransaction函数中安全释放。
  3. AtEOSubXact_LargeObject()函数在参数isCommit为true时表示子事务为提交状态,会将大对象的文件描述符交给父事务;当参数isCommit为false时表示子事务为Abort状态,这时会关闭大对象的文件描述符。
  4. AtSubCommit_Notify()函数将将所有在挂起列表(pending lists)中的项重新分配给父事务。挂起列表可能包含等待处理的通知消息或事件。
  5. ResourceOwnerRelease函数释放参数ResourceOwner owner及其后代拥有的所有资源,但不删除“资源Owner”对象本身,并且通过三个阶段释放资源:
    • RESOURCE_RELEASE_BEFORE_LOCKS:释放缓冲区(buffers)关系缓存引用(relcache references)动态共享内存段(dynamic shared memory segments)即时编译(JIT)上下文密码散列(cryptohash)上下文HMAC 上下文的资源。
    • RESOURCE_RELEASE_LOCKS:对于顶层xact,将释放所有锁(或至少所有非会话锁),因此只需在递归的顶层调用一次 lmgr 即可;对于非顶层xact,释放retail locks。如果是顶层提交事务,则将锁进行释放;如果是子事务提交事务,则将锁交给父节点。
    • RESOURCE_RELEASE_AFTER_LOCKS:释放系统目录缓存引用(catcache references)系统目录列表缓存固定(catcache-list pins)计划缓存引用(plancache references)元组描述符(tuple descriptor)引用快照(snapshot)引用和**打开的临时文件(open temporary files)**的资源。
  6. AtEOSubXact_RelationCache()将在处理失效(invalidation)消息之前清理关系缓存(relation cache),用于维护数据库一致性和正确性的内部机制之一。
  7. AtEOSubXact_Inval(bool isCommit)函数在参数true表示子事务为提交状态,处理当前命令的失效消息,然后将当前命令的失效消息和之前的失效消息都附加到父事务的之前命令的失效消息列表中;参数false表示子事务为中止状态,处理当前子事务的失效消息。
  8. AtSubCommit_smgr()将待删除列表中的所有项目重新分配给父事务。

5. 释放子事务锁

子事务提交时,释放的唯一一个锁是事务的XID锁。

CurrentResourceOwner = s->curTransactionOwner;
if (FullTransactionIdIsValid(s->fullTransactionId))
  XactLockTableDelete(XidFromFullTransactionId(s->fullTransactionId));
  1. 将事务的资源Owner赋值给全局变量CurrentResourceOwner;
  2. 判断事务XID并释放事务XID锁。

6. 清理事务相关资源

将其他资源移交给父节点的资源Owner。

   /*Other locks should get transferred to their parent resource owner*/
ResourceOwnerRelease(s->curTransactionOwner,
 RESOURCE_RELEASE_LOCKS,
 true, false);
ResourceOwnerRelease(s->curTransactionOwner,
 RESOURCE_RELEASE_AFTER_LOCKS,
 true, false);

AtEOXact_GUC(true, s->gucNestLevel);
AtEOSubXact_SPI(true, s->subTransactionId);
AtEOSubXact_on_commit_actions(true, s->subTransactionId,
  s->parent->subTransactionId);
AtEOSubXact_Namespace(true, s->subTransactionId,
  s->parent->subTransactionId);
AtEOSubXact_Files(true, s->subTransactionId,
  s->parent->subTransactionId);
AtEOSubXact_HashTables(true, s->nestingLevel);
AtEOSubXact_PgStat(true, s->nestingLevel);
AtSubCommit_Snapshot(s->nestingLevel);
    /*
    * We need to restore the upper transaction's read-only state, 
    * in case the upper is read-write while the child is read-only; 
    * GUC will incorrectly think it should leave the child state in place
    */
XactReadOnly = s->prevXactReadOnly;
CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner;
ResourceOwnerDelete(s->curTransactionOwner);
s->curTransactionOwner = NULL;

AtSubCommit_Memory();
  1. ResourceOwnerRelease()释放当前事务资源拥有者的锁和其他系统表资源。
  2. AtEOXact_GUC()进行GUC处理,或退出有proconfig设置的函数时,或撤销对某些GUC变量的对某些 GUC 变量进行瞬时赋值时。
  3. AtEOSubXact_SPI()在子事务提交或中止时清理SPI(Server Programming Interface,服务器编程接口)状态。
  4. AtEOSubXact_on_commit_actions()针对ON COMMIT字句在提交后或中止后清理。在subabort期间,可以立即删除在此子事务中创建的条目。 在subcommit期间,只需将在子事务中标记的条目,然后交个父节点提交时处理。
  5. AtEOSubXact_Namespace()在子事务提交时,将临时名称空间创建标记传播给父子事务;在子事务终止时,重置 MyProc 中的临时命名空间标志。
  6. AtEOSubXact_Files() 在中止时,会关闭子事务可能打开的临时文件; 提交时,会将打开的文件重新分配给父子事务。
  7. AtEOSubXact_HashTables()是在子事务结束时进行清理任何仍然打开的散列表。
  8. AtEOSubXact_PgStat()作用是在子事务结束时更新或清理pgStat模块的状态。
  • pgStat是 PostgreSQL的统计收集模块,负责收集数据库的各种统计信息,如表的访问频率、索引的使用情况等。
  1. AtSubCommit_Snapshot()作用是在子事务提交时进行相关的快照(Snapshot)管理,包括快照清理和引用计数更新。
  2. XactReadOnly需要恢复上层事务的只读状态,以防上层事务为读写状态,而子代事务为只读状态,使得GUC会错误地认为应该保留子事务的状态。
  3. 将当前的ResourceOwner和当前事务的ResourceOwner移交到父节点,并调用ResourceOwnerDelete()删除当前事务的ResourceOwner。
  4. AtSubCommit_Memory()函数会切换内存上下文到父节点的内存上下文,并释放当前事务的内存上下文。

7. 完成子事务提交

修改子事务状态并从事务链栈中弹出子事务节点。

s->state = TRANS_DEFAULT;

PopTransaction();
  1. 恢复事务状态为默认状态。
  2. 调用PopTransaction()函数从事务链栈中弹出子事务节点。

作者介绍

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

弹出子事务节点。

s->state = TRANS_DEFAULT;

PopTransaction();
  1. 恢复事务状态为默认状态。
  2. 调用PopTransaction()函数从事务链栈中弹出子事务节点。

作者介绍

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


原文地址:https://blog.csdn.net/weixin_42744640/article/details/142494063

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!