跳转到: 导航, 搜索

TaskFlow/Patterns and Engines/Persistence

修订于: 2014年4月26日 by Harlowja

大局观

我们如何持久化流程,以便在引擎发生故障时可以恢复、重新启动或回滚?

由于流程是一组任务以及任务之间的关系,我们需要创建一个模型和相应的的信息,以便持久化正确数量的信息,以在软件或硬件故障时保存、恢复和回滚流程。当流程加载到引擎时,首先进行一个转换步骤,将该流程与存储后端中的持久化数据关联起来。特别是,对于每个流程,都有一个相应的 FlowDetail 记录,对于每个任务,都有一个相应的 TaskDetail 记录。这些构成了流程将如何持久化的基本信息级别(有关其模式,请参见下文)。

为了允许恢复,taskflow 必须能够重新创建流程并重新连接任务之间的链接(以及任务->任务详细信息等),以便恢复它们或以正确的顺序恢复这些任务。为此,通常应提供一个工厂函数。函数的完全限定名称必须保存在 FlowDetail 中;然后,我们将能够导入并再次调用此函数,例如,在服务重新启动后。此函数将负责重新创建一组任务和流程(这些任务和流程可能相同或已更改,请参见下文的案例)。

注意: 将流程创建放在单独的函数中不应该造成太大的负担——该模式已经在使用,例如,请参阅 [cinder 代码]。

需求

  • 任务通过名称TaskDetail 关联。这要求任务名称在特定流程中是唯一的。

首次加载

当将新流程加载到引擎时,尚未为其持久化数据,因此将创建一个相应的 FlowDetail 对象,以及每个任务的 TaskDetail 对象,并将这些对象立即保存到配置的持久化后端。如果未配置持久化后端,那么如预期的那样,将不会保存任何内容,并且任务将以非持久化方式运行。

第二次加载

当从持久化后端恢复流程时(例如,如果流程中断并且引擎被销毁以节省资源,或者服务已重新启动),我们需要重新创建流程。为此,我们将调用首次加载时保存的构建流程的函数(即,上述描述的流程工厂函数)。

然后,我们将流程加载到引擎中。对于每个任务,必须已经存在一个 TaskDetail 对象。然后,我们使用任务名称重新关联任务与 TaskDetail 对象(如果存在匹配项),然后引擎恢复。在这种情况下,任务状态和结果应该是恢复流程所需的唯一信息。

注意: 允许在此重新关联过程中提供一个“迁移”对象(或对象列表)也可能很有用,以便可以将任务从其旧版本自动迁移到其新版本。

恢复场景

升级用例可能更有趣也更具挑战性。

虽然有几种选择,但升级的最佳(推荐)做法是加载新的(可能已更改的)流程/流程,并将旧流程的先前状态迁移合并到这些新的(可能已更改的)流程/流程中。这允许新的/更新的流程利用预先存在的(可能部分)完成的状态,或使用行为良好且定义明确的过程从该预先存在的状态中清理。

这里有几种可能的选项

- 这与加载未更改的流程相同:任务在流程加载到引擎时通过名称与保存的状态关联。

注意: 尽管如此,这可能无法在所有用例中开箱即用:有时,需要进行数据/任务迁移(类似于数据库迁移,但处于更高的级别)。

让我们考虑几个用例。

添加了任务

这是最简单的用例。

由于新任务没有状态,因此将自动为其创建一个新的 TaskDetail 记录。

删除了任务

无需执行任何操作——流程结构从工厂函数重新加载,并且删除的任务不在其中——因此,流程将像从未存在过一样运行,并且在完成之前返回的任何结果都将被忽略。

任务代码已更改

任务版本已被更改,我们可以在加载旧任务数据时识别到这一点。为了处理这种情况,可能需要每个任务(或者可能是流程)提供一个升级函数,该函数将在版本不匹配发生时自动激活,这些函数在任何任务代码运行之前运行,允许将现有的持久化数据迁移到新的数据再开始运行。

任务被拆分为两个任务,或从两个(或更多)任务合并为一个任务

当一个较大的任务被拆分为两个任务时,此用例很有用。为了正确完成此操作,我们可能需要允许在重新关联到新任务/流程之前,对现有的任务详细信息运行任意“迁移”。这将允许该“任务数据”迁移代码使用一组函数将旧任务数据转换为新任务数据。这是任务代码更改情况的概括,但涉及更多的灵活性。

注意: 我们可能需要在此处关联一种知道已完成哪些迁移的方法,通常通过迁移号(可能每个流程?)来完成。

流程结构已更改

如果手动添加或删除了图形中的链接,或者更改了任务要求,或者流程被重构(任务移动到子流程中或移出子流程,线性流程被替换为图形流程,线性流程中的任务被重新排序等),则无需执行任何特殊操作。所有任务状态都将像往常一样加载。

设计原理

Flow Factory

我们如何升级?

- 我们更改创建流程的代码,然后重新启动服务。

然后,当流程从存储中恢复时,我们真正想要的是加载新的流程,更新结构和更新的任务,但保留任务状态和结果。至少,这是最常见的情况。

因此,应运行代码以将任务放入模式并重新创建流程,但应查阅日志以加载任务的状态和结果。为了使这尽可能简单,流程的创建应放在一个可以位于服务重新启动时(模块级函数或类静态方法)的单独函数中。

使用名称将任务与状态关联

引擎获取流程和流程详细信息,并应重建其自身内部状态。

应以某种方式将任务与现有的 TaskDetail(s) 匹配。

匹配应

  • 如果添加或删除任务,则保持稳定;
  • 在服务重新启动、升级时不应更改;
  • 在 HA 设置中的所有服务器实例上应相同。


一种选择是应通过任务名称将任务与 TaskDetail(s) 匹配。

这有几个含义

  • 流程中的任务名称应唯一;
  • 更改任务名称变得过于困难。