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

海山数据库(He3DB)源码详解:子事务定义和启动

数据库 2024-10-21
59

Table of Contents

海山数据库(He3DB)源码详解:子事务定义和启动

子事务的创建过程分为两步,分别为通过DefineSavepoint函数调用PushTransaction构建一个子事务节点,并在CommitTransactionCommand函数中调用StartSubTransaction函数启动一个新的子节点事务

定义子事务的执行条件

在终端以事务块方式执行SQL命令,在事务块状态为TBLOCK_INPROGRESS或TBLOCK_SUBINPROGRESS时,终端执行SAVEPOINT语句,数据库底层会调用DefineSavepoint函数,创建子事务并通过链栈保存父节点。以下是定义子事的流程图:

image1.png
DefineSavepoint函数流程图的步骤解释如下:

  1. 开始:流程的开始。
  2. 调用DefineSavepoint函数:函数接收到定义保存点的请求。
  3. 检查事务状态:检查当前是否有活跃的事务。
  4. 事务未启动:如果没有活跃的事务,函数将进行错误处理。
  5. 事务已启动:如果事务已启动,继续检查是否有活跃的子事务。
  6. 是否有活跃的子事务:确定当前事务是否有未完成的子事务。
  7. :如果有活跃的子事务,函数将进行错误处理,因为保存点不能在子事务中定义。
  8. :如果没有活跃的子事务,继续创建保存点。
  9. 创建保存点:在事务日志中创建一个新的保存点。
  10. 记录保存点信息:记录保存点的名称和相关事务信息。
  11. 保存点定义成功:保存点成功定义。
  12. 结束:流程结束。

DefineSavepoint函数的执行过程

1、检查当前状态

通过全局状态变量获取当前事务状态,并检查是否为并行事务模式。

TransactionState s = CurrentTransactionState; /* * Workers synchronize transaction state at the beginning of each * parallel operation, so we can't account for new subtransactions * after that point. * (Note that this check will certainly error out if s->blockState * is TBLOCK_PARALLEL_INPROGRESS, so we can treat that as an invalid * case below.) */ if (IsInParallelMode()) ereport(ERROR, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), errmsg("cannot define savepoints during a parallel operation")));

2、创建子事务

通过swtich-case结构,同时完成事务块检查和子事务创建任务。

  1. 事务块为TBLOCK_INPROGRESS或TBLOCK_SUBINPROGRESS状态:正常状态,通过PushTransaction()函数创建子事务切换事务状态,为子事务name申请内存空间并命名。
case TBLOCK_INPROGRESS: case TBLOCK_SUBINPROGRESS: /* Normal subtransaction start */ PushTransaction(); s = CurrentTransactionState; /* changed by push */ /* * Savepoint names, like the TransactionState block itself, live * in TopTransactionContext. */ if (name) s->name = MemoryContextStrdup(TopTransactionContext, name); break;
  1. 事务块为TBLOCK_IMPLICIT_INPROGRESS状态:错误状态,事务处在隐式状态,即事务完成会自动提交或者出错自动放弃,定义子事务会与这个目的冲突。(子事务可以回滚放弃部分操作,但是隐式事务要求回滚放弃全部操作)
case TBLOCK_IMPLICIT_INPROGRESS: ereport(ERROR, (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), /* translator: %s represents an SQL statement name */ errmsg("%s can only be used in transaction blocks", "SAVEPOINT"))); break;
  1. 事务块处在上述三种状态之外的其他状态:这些状态均是无效的状态,提交FATAL日志。
/* These cases are invalid. */ case TBLOCK_DEFAULT: case TBLOCK_STARTED: case TBLOCK_BEGIN: case TBLOCK_PARALLEL_INPROGRESS: case TBLOCK_SUBBEGIN: case TBLOCK_END: case TBLOCK_SUBRELEASE: case TBLOCK_SUBCOMMIT: case TBLOCK_ABORT: case TBLOCK_SUBABORT: case TBLOCK_ABORT_END: case TBLOCK_SUBABORT_END: case TBLOCK_ABORT_PENDING: case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBRESTART: case TBLOCK_SUBABORT_RESTART: case TBLOCK_PREPARE: elog(FATAL, "DefineSavepoint: unexpected state %s", BlockStateAsString(s->blockState)); break;

启动子事务的执行条件

  1. 在数据库客户端,用户成功执行并提交SAVEPOINT语句时,CommitTransactionCommand函数会调用StartSubTransaction函数启动一个新的子节点事务。
  2. 在数据库客户端,用户成功执行并提交ROLLBACK语句时,CommitTransactionCommand函数会根据chain的值调用StartSubTransaction函数重新启动一个子节点事务。

以下是StartSubTransaction函数的执行流程图:
image2.png

  1. 开始:流程的开始。
  2. 获取当前事务状态:获取 CurrentTransactionState 以获取当前事务的状态。
  3. 检查事务状态:检查当前事务状态是否不是 TRANS_DEFAULT
  4. 不是TRANS_DEFAULT:如果当前事务状态不是 TRANS_DEFAULT,则记录一条警告日志,说明当前状态。
  5. 是TRANS_DEFAULT:如果当前事务状态是 TRANS_DEFAULT,继续执行子事务的启动流程。
  6. 设置事务状态为TRANS_START:将事务状态设置为 TRANS_START,表示开始一个新的子事务。
  7. 初始化子事务资源:初始化子事务所需的各种资源。
  8. 内存管理子事务开始:调用 AtSubStart_Memory 函数,进行内存管理方面的子事务开始处理。
  9. 资源所有者子事务开始:调用 AtSubStart_ResourceOwner 函数,进行资源所有者方面的子事务开始处理。
  10. 触发器子事务开始:调用 AfterTriggerBeginSubXact 函数,进行触发器方面的子事务开始处理。
  11. 设置事务状态为TRANS_INPROGRESS:将事务状态设置为 TRANS_INPROGRESS,表示子事务正在进行中。
  12. 调用子事务回调:调用 CallSubXactCallbacks 函数,执行所有注册的子事务开始回调函数。
  13. 显示事务状态:调用 ShowTransactionState 函数,显示当前事务状态。
  14. 结束:流程结束。

StartSubTransaction函数的执行过程

1. 切换当前状态

切换当前状态,并检查当前状态是否正确并开始子事务。

TransactionState s = CurrentTransactionState; if (s->state != TRANS_DEFAULT) elog(WARNING, "StartSubTransaction while in %s state", TransStateAsString(s->state)); s->state = TRANS_START;

当前子事务状态变量由函数PushTransaction创建,并由全局变量传递到StartSubTransaction函数中。检查当前子事务状态并开始子事务。

2. 初始化子事务资源

切换当前状态,并检查当前状态是否正确并开始子事务。

/* * Initialize subsystems for new subtransaction * must initialize resource-management stuff first */ AtSubStart_Memory(); AtSubStart_ResourceOwner(); AfterTriggerBeginSubXact();
  1. AtSubStart_Memory():创建事务的上下文并切换到上下文;
  2. AtSubStart_ResourceOwner():创建当前事务的ResourceOwner,并修改全局变量CurTransactionResourceOwner和CurrentResourceOwner;
  3. AfterTriggerBeginSubXact():申请更多的trans_stack(指向事务栈的指针)空间,用于管理当前事务中的触发器状态,并在不同的触发器事件中用于保存和恢复状态。

3. 完成子事务启动

完成子事务的启动操作,并调用回调函数和记录日志。

s->state = TRANS_INPROGRESS; /* * Call start-of-subxact callbacks */ CallSubXactCallbacks(SUBXACT_EVENT_START_SUB, s->subTransactionId, s->parent->subTransactionId); ShowTransactionState("StartSubTransaction");
  1. 子事务启动完成,将事务状态设置为TRANS_INPROGRESS;
  2. 利用ShowTransactionState函数记录子事务操作的日志。

作者介绍

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

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

评论