当前位置: 首页 > 知识库问答 >
问题:

我如何对Cadence工作流进行版本控制?

傅新
2023-03-14

Cadence工作流需要具有确定性,这意味着如果使用相同的输入参数执行工作流,工作流预计会产生完全相同的结果。

当我作为Cadence的新用户了解到上述需求时,我想知道当需要打破确定性的变化时,我如何才能长期维护工作流。

一个示例场景是,您有一个连续执行Activity1和Activity2的工作流,然后您需要更改这些活动的顺序,以便工作流在Activity1之前执行Activity2。还有许多其他方法可以进行像这样打破决定论的更改,我想了解如何处理这些更改。

在工作流可以长时间运行(如几天、几周甚至几个月)的情况下,这一点尤其重要!

共有1个答案

锺离锦
2023-03-14

显然,这可能是Cadence新html" target="_blank">开发人员最常问的问题之一。Cadence工作流必须是确定性算法。如果一个工作流算法不是确定性的,Cadence工作人员在尝试重放历史(即在工人故障恢复期间)。

有两种方法可以解决这个问题:

    < li >创建全新的工作流:这是版本化工作流最简单的方法。这种方法听起来很简单:每当你需要对工作流的算法进行更改时,你可以制作一份原始工作流的副本,并按照你想要的方式进行编辑,给它起一个新的名字,如MyWorkflow_V2,并开始使用所有新的实例。如果你的工作流不是很持久,你现有的工作流会在某个时候“耗尽”,你可以删除旧版本。另一方面,由于显而易见的原因,这种方法会很快变成一场维护噩梦。 < li >使用GetVersion() API派生工作流逻辑:Cadence客户端有一个名为GetVersion的函数,它会告诉您当前运行的工作流版本。您可以使用此函数返回的信息来决定需要使用哪个版本的工作流算法。换句话说,您的工作流同时运行旧算法和新算法,并且您能够为工作流实例选择正确的版本,以确保它们确定性地运行。

下面是一个基于GetVersion()的方法的例子。假设您想要更改工作流中的以下行:

err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)

err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)

这是一个突破性的变化,因为它运行的是bar活动而不是foo。如果您只是在不担心确定性的情况下进行更改,您的工作流将无法在需要时重播,并且它们将被不确定性的工作流错误所困扰。正确进行此更改的正确方法是按如下方式更新工作流:

v :=  GetVersion(ctx, "fooChange", DefaultVersion, 1)
if v  == DefaultVersion {
   err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
} else {
   err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
}

GetVersion函数接受4个参数:

    < li>ctx是标准上下文对象 < Li >“foo change”是一个人类可读的ChangeID,或者是您在工作流算法中进行的语义更改,它打破了确定性 < li>DefaultVersion是一个常量,仅表示版本0。换句话说,第一个版本。它作为minSupportedVersion参数传递给GetVersion函数 < li>1是当前工作流代码可以处理的maxSupportedVersion。在这种情况下,我们的算法可以支持从DefaultVersion到Version 1(包含)的工作流版本。

当此工作流的新实例首次达到上面的 GetVersion() 调用时,该函数将返回 maxSupupedVersion 参数,以便您可以运行最新版本的工作流算法。同时,它还会在工作流历史记录中记录该版本号(内部称为标记事件),以便将来记住它。稍后重播此工作流时,即使您传递了不同的 maxSupportedVersion 参数(即,如果您的工作流具有更多版本),Cadence 客户端仍将不断返回相同的版本号。

如果在历史记录重播期间遇到 GetVersion 调用,并且历史记录没有之前记录的标记事件,则该函数将返回默认版本,并假定此工作流实例的上下文中从未存在过“fooChange”。

如果您需要在工作流程的同一个步骤中再做一个突破性的更改,您只需像这样更改上面的代码:

v :=  GetVersion(ctx, "fooChange", DefaultVersion, 2) // Note the new max version
if v  == DefaultVersion {
   err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
} else if v == 1 {
   err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
} else { // This is the Version 2 logic
   err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
}

当您对放弃对版本0的支持感到满意时,您可以像这样更改上面的代码:

v :=  GetVersion(ctx, "fooChange", 1, 2) // DefaultVersion is no longer supported
if v == 1 {
   err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
} else { 
   err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
}

在此更改之后,如果您的工作流代码针对具有DefaultVersion版本的旧工作流实例运行,Cadence client将引发错误并停止执行。

最终,您可能希望摆脱所有以前的版本,只支持最新版本。一种选择是简单地完全摆脱 GetVersion 调用和 if 语句,只使用一行代码来执行正确的事情。但是,保留 GetVersion() 调用实际上是一个更好的主意,原因有两个:

    < li >如果您的工作人员试图重放旧工作流实例的历史记录,GetVersion()可让您更好地了解问题所在。您不必调查神秘的不确定性工作流错误的根本原因,而是会知道失败是由该位置的工作流版本控制引起的。 < li >如果您需要对工作流算法的同一步骤进行更多的突破性更改,您将能够重复使用相同的更改ID并继续遵循与上述相同的模式。

考虑到上面提到的两个原因,当需要删除以支持所有旧版本时,您应该更新您的工作流代码,如下所示:

GetVersion(ctx, "fooChange", 2, 2) // This acts like an assertion to give you a proper error
err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil) 
 类似资料:
  • 节奏工作流版本控制: 工作流执行具有以下代码: 工作流1: o/p的开始--版本1:1 工作流实施中的更改: 工作流2: o/p的开始--版本1:2 工作流实施中的更改: 工作流3: o/p的开始--版本1:3 工作流2的重放:o/p版本1: -1 工作流重播 1 : o/p --version1: -1 我期待低于o / p 重播工作流程2: o/p--版本1:2 工作流重播 1 : o/p -

  • 我了解我无法对工作流进行向后不兼容的更改:如何在不破坏确定性的情况下对优步节奏工作流进行更改或修复? 然而,我不确定这里的“向后不兼容”是什么意思。如果不使用getVersion,period,我就不能简单地部署新代码吗?或者,只要用于恢复的历史记录跟踪在两个版本之间兼容,那么我就可以在不使用getVersion的情况下进行更新?或者在某些情况下我可以不使用getVersion进行更新?

  • 我偶然创建了一个错误的域名,或者经过一些测试后,我想删除该域名。我应该那样做吗?又是怎么做到的?

  • 在我们开始搞明白 Git 命令之前,你有必要先了解一下版本控制的基本流程。这本书会向你一步步地详细阐述各种不同的工作流程。但是首先还是让我们先来一起了解一下版本控制的最基本的流程。 版本控制中的最基本的模块就是 “仓库(Repository)”。 名词解释 仓库(Repository) 你可以把一个仓库想象成一个数据库,在那里你的版本控制系统存储了项目积攒的所有版本和元数据(metadata)。在

  • 用例: 我有一个用户订阅的实体(订单项)。 假设该实体每月可用一次,直到用户从其列表中删除该实体或该实体不再有效(例如订阅已过期)。 因此,实体可以处于的有效状态为“可用”、“已停止”、“已终止”。 当实体处于可用状态时,我想检查它是否可以通过调用外部API(定期)转换为订单,如果可以,则调用另一个API来创建订单。这些API本质上保证是幂等的。 实体可以在预订期之间停止,并在一段时间后变得可用。

  • 在Cadence/Temoral工作流编程中: < li >不允许使用本机线程库。例如,在Java中,线程必须通过< code>Async.procedure或< code>Async.function创建,而在Golang中,线程必须通过< code>workflow创建。去吧。那为什么呢? < li >有没有类似使用本机线程的竞争条件?例如,为了线程安全,应该使用< code>Hashtabl