如何通过TCC指令执行的原子性

如何通过TCC指令执行的原子性

Content #

当接收到外部指令时,需要实现操作 1、2、3,其中任何一个操作失败,都需要暂停指令执行,将系统恢复到操作未执行状态,然后再重试。

其中,操作 1、2、3 的含义具体如下。

  1. 操作 1:生成指定 URL 页面对应的图片,并持久化存储。
  2. 操作 2:调用内部系统 1 的接口,禁用指定域名的访问权限。
  3. 操作 3:通过 MySQL XA 更新多个数据库的数据记录。

那么我是如何使用 TCC 来解决这个问题的呢?

答案是我在实现每个操作时,都会分别实现相应的预留、确认、撤销三操作。

首先,因为操作 1 是生成指定 URL 页面对应的图片,可以这么实现 TCC 三操作。

  1. 预留操作:生成指定页面的图片,并存储到本地。
  2. 确认操作:更新操作 1 状态为完成。
  3. 撤销操作:删除本地存储的图片。

其次,因为操作 2 是调用内部系统 1 的接口,禁用该域名的访问权限,那么,我是这么实现 TCC 三操作的。

  1. 预留操作:调用的内部系统 1 的禁用指定域名的预留接口。这时我们先通知系统 1 预留相关的资源。
  2. 确认操作:调用的内部系统 1 的禁用指定域名的确认接口。我们执行禁用域名的操作,这时,禁用域名的操作的生效了。
  3. 撤销操作:调用的内部系统 1 的禁用指定域名的撤销接口。我们撤销对该域名的禁用,并通知内部系统 1 释放相关的预留资源。

最后,操作 3 是通过 MySQL XA 更改多个 MySQL 数据库中的数据记录,并实现数据更新的事务。我是这么实现 TCC 三操作的:

  1. 预留操作:执行 XA START 和 XA END 准备好事务分支操作,并调用 XA PREPARE,执行二阶段提交协议的提交请求阶段,预留相关资源。
  2. 确认操作:调用 XA COMMIT 执行确认操作。
  3. 撤销操作:调用 XA ROLLBACK 执行回滚操作,释放在 Try 阶段预留的资源。

可以看到,确认操作是预留操作的下一个操作,而撤销操作则是用来撤销已执行的预留操作对系统产生的影响,类似在复制粘贴时,我们通过“Ctrl Z”撤销“Ctrl V”操作的执行,就像下图的样子。而这是理解 TCC 的关键。

在操作 1、2、3 的预留操作执行结束,如果预留操作都执行成功了,那么我将执行确认操作,继续向下执行。但如果预留操作只是部分执行成功,那么我将执行撤销操作,取消预留操作执行对系统产生的影响。通过这种方式(指令对应的操作要么全部执行,要么全部不执行),我就能实现指令执行的原子性了。

另外,在实现确认、撤销操作时,有一点需要我们尤为注意,因为这两个操作在执行时可能会重试,所以,它们需要支持幂等性。

Viewpoints #

From #

加餐 | TCC如何实现指令执行的原子性?