显然,这可能是新的 Cadence 开发人员最常问的问题之一。 Cadence 工作流程必须为deterministic algorithms。如果工作流算法不是确定性的,Cadence 工作人员在尝试重放历史记录时(即在工作人员故障恢复期间)将面临遇到非确定性工作流程错误的风险。
有两种方法可以解决这个问题:
-
创建全新的工作流程:这是最幼稚的方法
版本控制工作流程。这种方法听起来很简单:随时
你需要改变你的工作流程的算法,你做一个
原始工作流程的副本并按照您想要的方式对其进行编辑,给它
像 MyWorkflow_V2 这样的新名称并开始用于所有新实例
往前走。如果您的工作流程不是很长,您的
现有的工作流程将在某个时候“耗尽”,您将能够
完全删除旧版本。另一方面,这
方法可以很快变成维护的噩梦
显而易见的原因。
-
使用 GetVersion() API 派生工作流逻辑:Cadence 客户端具有
一个名为 GetVersion 的函数,它告诉你什么版本的
工作流当前正在运行。您可以使用返回的信息
通过这个函数来决定你的工作流算法的版本
需要使用。换句话说,您的工作流程既有旧的,也有
新算法并行运行,您可以选择
适合您的工作流实例的正确版本,以确保它们运行
确定性地。
以下是基于 GetVersion() 方法的示例。假设您要在工作流程中更改以下行:
err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
到
err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
这是一项重大更改,因为它运行的是 bar 活动而不是 foo。如果您只是在不担心确定性的情况下进行更改,那么您的工作流程将无法在需要时重播,并且会遇到 nondeterministic workflow 错误。正确进行此更改的正确方法是更新工作流程,如下所示:
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 个参数:
-
ctx 是标准的上下文对象
-
“fooChange” 是人类可读的 ChangeID 或您在工作流算法中所做的语义更改,会破坏确定性
-
DefaultVersion 是一个常量,仅表示 Version 0。在其他
话,第一个版本。它作为 minSupportedVersion 传递
GetVersion 函数的参数
-
1 是您当前可以处理的 maxSupportedVersion
工作流代码。在这种情况下,我们的算法可以支持工作流
从 DefaultVersion 到 Version 1(含)的版本。
当此工作流的新实例首次到达上述 GetVersion() 调用时,该函数将返回 maxSupportedVersion 参数,以便您可以运行最新版本的工作流算法。同时,它还会将该版本号记录在工作流历史记录中(内部称为标记事件),以便将来记住。稍后重放此工作流程时,Cadence 客户端将继续返回相同的版本号,即使您传递了不同的 maxSupportedVersion 参数(即,如果您的工作流程有更多版本)。
如果在历史回放期间遇到 GetVersion 调用,并且历史没有之前记录的标记事件,则该函数将返回 DefaultVersion,并假设 “ 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 客户端将引发错误并停止执行。
最终,您可能希望摆脱所有以前的版本,只支持最新版本。一种选择是完全摆脱 GetVersion 调用和 if 语句,只需一行代码即可完成正确的操作。但是,将 GetVersion() 调用保留在其中实际上是一个更好的主意,原因有两个:
- GetVersion() 可以让您更好地了解问题所在,如果您的
worker 尝试重放旧工作流实例的历史记录。
而不是调查一个神秘事件的根本原因
非确定性工作流错误,你会知道失败是
由该位置的工作流版本控制引起的。
- 如果您需要对同一步骤进行更多重大更改
工作流算法,您将能够重用相同的变更 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)