ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Elsa 书签管理架构演进:在 WorkflowExecutionContext 中直接管理书签(ADR-0003)

Elsa 书签管理架构演进:在 WorkflowExecutionContext 中直接管理书签(ADR-0003) 后端工作流自动化流程编排低代码【免费下载链接】elsa-coreThe Workflow Engine for .NET项目地址https://gitcode.com/gh_mirrors/el/elsa-core点击查看免费下载本技术指南基于 Elsa 工作流引擎仓库中的架构决策记录 ADR-0003Direct Bookmark Management in WorkflowExecutionContext 展开聚焦 Elsa 核心运行时中书签Bookmark的生命周期管理从「活动临时保存书签、中间件事后拷贝」到「活动直接向WorkflowExecutionContext.Bookmarks写入、同时维护仅供内存使用的临时新书签列表」的架构转变。读完本文你将理解该决策的背景恢复执行后无法取消旧书签的缺陷、具体落地的源码机制ActivityExecutionContext.AddBookmark、NewBookmarks与AutoCompleteBehavior的协作以及它带来的架构简化与状态一致性收益并能据此在开发自定义长时运行活动如等待消息、定时器、用户输入时正确使用书签 API。一、决策背景为什么旧的「临时书签 中间件拷贝」架构存在缺陷在 Elsa 中一个工作流运行到需要暂停等待外部事件如 HTTP 请求、消息队列消息、定时触发、用户任务完成时承载该等待点的活动会创建一个书签Bookmark。书签本质上是一个“可恢复执行的位置标记”工作流执行上下文将书签持久化到数据库后工作流实例即进入暂停Suspended状态当外部事件到达时运行时用事件负载匹配书签从该位置恢复工作流。在 ADR-0003 之前书签的生命周期被拆分为两个阶段活动执行期间活动把创建的书签放进ActivityExecutionContext持有的一个临时Bookmarks列表活动完成之后由某个**中间件middleware**负责把临时列表中的书签拷贝进WorkflowExecutionContext.Bookmarks后者才是随后被持久化到数据库的“权威”列表。该架构存在一个不易察觉却影响正确性的缺陷——恢复执行时无法取消或修改旧书签工作流从持久化状态恢复时数据库只还原WorkflowExecutionContext及其Bookmarks而被重建的ActivityExecutionContext的临时Bookmarks列表是空的结果被恢复的活动若试图取消或修改它此前创建的书签将无从下手——因为它根本看不到这些书签任何变更都无法被跟踪。二、决策内容让活动直接写入权威书签列表ADR-0003 的决策非常直接消除ActivityExecutionContext中的临时Bookmarks列表改为让活动直接把书签添加进WorkflowExecutionContext.Bookmarks列表。这一改动之所以「可行且安全」关键在于Bookmark数据模型本身已经携带了足够的溯源信息。查看仓库中 Models/Bookmark.cs 的定义每个书签都包含属性说明Id书签的唯一 ID由IIdentityGenerator生成Name书签名称默认取活动类型名Activity.TypeHash书签哈希由IStimulusHasher依据「名称 负载 活动实例 ID」计算用于事件匹配Payload与书签关联的刺激数据stimulus即等待的事件负载ActivityId创建该书签的活动的 IDActivityNodeId创建该书签的活动节点 IDActivityInstanceId创建该书签的活动实例 ID可空CreatedAt创建时间AutoBurn恢复时是否自动烧掉burn该书签默认trueCallbackMethodName书签恢复时调用的活动方法名AutoComplete书签恢复后活动是否自动完成默认trueMetadata自定义键值属性其中ActivityId与ActivityInstanceId以及ActivityNodeId这三重引用让运行时在多个活动并发活跃时仍能精确地跟踪和过滤书签——这正是「把书签集中放到工作流级列表里」不会混淆归属的根基。从源码看ActivityExecutionContext.Bookmarks属性正是利用了这一能力它只是对工作流级列表的过滤视图public IEnumerableBookmark Bookmarks WorkflowExecutionContext.Bookmarks.Where(x x.ActivityInstanceId Id);见 Contexts/ActivityExecutionContext.cs三、源码落地_newBookmarks与新书签写入路径决策还明确了一个重要保留仍然维护一个临时的“新书签”列表但其用途被严格限定——只保存活动本次执行期间新建的书签且只存在于ActivityExecutionContext的内存生命周期内不再承担“事后拷入工作流列表”的职责。在 Contexts/ActivityExecutionContext.cs 中可以看到这一设计的实现private ListBookmark _newBookmarks []; public IEnumerableBookmark NewBookmarks _newBookmarks.AsReadOnly();AddBookmark是核心写入入口——它同时更新两个列表public void AddBookmark(Bookmark bookmark) { _newBookmarks.Add(bookmark); WorkflowExecutionContext.Bookmarks.Add(bookmark); Taint(); }也就是说每次活动创建书签书签立即进入WorkflowExecutionContext.Bookmarks后续被持久化的权威列表同时被记入_newBookmarks供AutoCompleteBehavior之类的内存内逻辑判断使用。Taint()调用则把当前活动执行上下文标记为“已修改”确保工作流状态在提交阶段被感知并持久化。AddBookmarks批量版本则只写入工作流级列表public void AddBookmarks(IEnumerableBookmark bookmarks) { WorkflowExecutionContext.Bookmarks.AddRange(bookmarks); Taint(); }工作流级列表的定义位于 Contexts/WorkflowExecutionContext.cs它同时维护三样东西OriginalBookmarks本次执行开始时从持久化状态恢复的原始书签Bookmarks执行过程中收集/变更后的当前书签集合BookmarksDiff由Diff.For(OriginalBookmarks, Bookmarks)计算出的二者差异新增、删除、变更供状态提取与持久化阶段使用。四、保留_newBookmarks的原因AutoCompleteBehavior依赖它ADR 明确指出有一个既有约定决定了为什么必须保留“新书签”列表如果活动在本次执行期间创建了书签该活动就不会自动完成——因为创建书签意味着活动要挂起等待外部事件而不是立即结束。该约定由 Behaviors/AutoCompleteBehavior.cs 实现它由CodeActivity以及部分自定义活动安装protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) { // If the activity created any bookmarks, do not complete. if (context.NewBookmarks.Any(x x.ActivityId context.Activity.Id)) return; await context.CompleteActivityAsync(); }注意这里的关键判断依据是NewBookmarks本次执行新创建的而不是Bookmarks包含历史上创建的所有书签。如果直接复用全部书签列表那么一个活动在第一次执行时创建了书签、在第二次恢复执行时虽然本次并未新建书签也会因为“列表里存在该书签”而错误地拒绝自动完成。_newBookmarks以“本次执行批次”为边界恰好提供了所需的语义粒度。五、书签创建 API从创建到挂起的完整调用链实践中自定义活动创建书签的标准方式是通过ActivityExecutionContext上的一组CreateBookmark重载。以最常用的形式为例Contexts/ActivityExecutionContext.cspublic Bookmark CreateBookmark(CreateBookmarkArgs? options null) { var payload options?.Stimulus; var callback options?.Callback; var bookmarkName options?.BookmarkName ?? Activity.Type; var bookmarkHasher GetRequiredServiceIStimulusHasher(); var identityGenerator GetRequiredServiceIIdentityGenerator(); var includeActivityInstanceId options?.IncludeActivityInstanceId ?? true; var hash bookmarkHasher.Hash(bookmarkName, payload, includeActivityInstanceId ? Id : null); var bookmarkId options?.BookmarkId ?? identityGenerator.GenerateId(); var bookmark new Bookmark( bookmarkId, bookmarkName, hash, payload, Activity.Id, ActivityNode.NodeId, Id, _systemClock.UtcNow, options?.AutoBurn ?? true, callback?.Method.Name, options?.AutoComplete ?? true, options?.Metadata); AddBookmark(bookmark); return bookmark; }从该实现可以梳理出完整的书签创建链路与可配置项确定书签名默认使用活动类型名Activity.Type可通过BookmarkName覆盖计算哈希IStimulusHasher以「书签名 刺激负载 活动实例 ID可选」计算Hash这是后续外部事件匹配书签的键生成 ID通过IIdentityGenerator生成书签 ID也可显式指定BookmarkId写入归属信息Activity.Id、ActivityNode.NodeId、当前ActivityExecutionContext.Id分别填入ActivityId、ActivityNodeId、ActivityInstanceId关键标志位AutoBurn恢复时自动消耗该书签默认true、AutoComplete恢复后自动完成活动默认true、Metadata自定义属性双重写入AddBookmark同时把书签加入WorkflowExecutionContext.Bookmarks权威持久化列表与_newBookmarks本次新书签列表触发挂起创建书签会自动在待执行活动全部执行完毕后挂起工作流CreateBookmark的 XML 注释明确说明Creating a bookmark will automatically suspend the workflow after all pending activities have executed。其它常用重载还包括CreateBookmark(object stimulus, IDictionarystring, string? metadata null)仅凭刺激负载创建最常见CreateBookmark(object stimulus, ExecuteActivityDelegate? callback, bool includeActivityInstanceId true, ...)指定恢复时的回调委托CreateBookmarks(IEnumerableobject payloads, ...)批量创建如等待多个事件源默认includeActivityInstanceId true、书签名默认活动类型。includeActivityInstanceId是一个需要重点理解的参数为true时活动实例 ID 参与哈希计算意味着书签与特定的活动执行实例绑定适合每个实例独立等待的场景为false时书签只按「书签名 负载」匹配适合多个实例共享同一等待点的场景。六、取消书签恢复执行后的正确性修复ADR-0003 修复的核心 bug 就是「恢复执行后无法取消旧书签」。在新架构下由于书签全部存在于WorkflowExecutionContext.Bookmarks恢复时该列表从数据库完整还原被恢复的活动通过ActivityExecutionContext.Bookmarks按ActivityInstanceId过滤的视图即可看到自己历史创建的书签进而可以对它们执行取消或修改操作变更会立即反映到权威列表并被持久化。作为对比工作流级取消路径可以在 Contexts/WorkflowExecutionContext.Cancel.cs 中看到当工作流被取消时CancelWorkflow会清空Bookmarks与完成回调条目并迁移到Cancelled子状态——这也印证了WorkflowExecutionContext.Bookmarks作为“单一事实来源single source of truth”的地位。七、决策的收益与权衡ADR-0003 列出了该架构决策的收益结合源码可以逐一印证收益源码印证简化架构不再需要中间件在活动执行后拷贝书签书签在CreateBookmark → AddBookmark中一次性写入权威列表不存在事后同步步骤修复恢复缺陷恢复后的活动可以取消/修改此前创建的书签恢复时WorkflowExecutionContext携带完整Bookmarks见CreateAsync重载中workflowState.Bookmarks的传入活动可按实例 ID 过滤访问降低状态不一致风险临时列表与持久化列表不再可能分叉_newBookmarks只读视图仅服务内存内判断不再参与持久化同步始终写入权威源书签变更总是作用于将被持久化的列表AddBookmark/AddBookmarks直接操作WorkflowExecutionContext.Bookmarks并由BookmarksDiff跟踪变更在备选方案权衡上ADR 明确拒绝了「保留临时列表 恢复时水合hydrateActivityExecutionContext.Bookmarks」的路线理由是该方案增加了复杂度并造成状态重复同一份书签在两个地方维护仍需额外同步逻辑——新方案则以“单一权威列表 只读新书签视图”在简单性与正确性之间取得了平衡。八、总结与自定义活动实践建议ADR-0003 确立了 Elsa 运行时书签管理的核心原则书签的权威归所是WorkflowExecutionContext.Bookmarks活动创建、取消、修改书签都应直接作用于该列表ActivityExecutionContext.Bookmarks只是按活动实例过滤的视图不要把它当作需要单独同步的存储NewBookmarks仅用于“本次执行是否新建了书签”的内存判断是AutoCompleteBehavior决定活动是否自动完成的关键依据每个书签自带ActivityId/ActivityNodeId/ActivityInstanceId溯源信息这使得集中存储而归属清晰成为可能。对需要开发自定义长时运行活动等待消息、HTTP 回调、定时器、用户操作等的开发者实践建议是在活动ExecuteAsync内通过context.CreateBookmark(stimulus, callback)系列 API 创建书签并返回恢复逻辑写在CallbackMethodName指向的方法中若活动在恢复后不再等待确保本次执行不再创建新书签AutoCompleteBehavior便会自动完成活动。如需进一步了解书签在持久化与事件触发层面的机制可继续查阅 WorkflowExecutionContext.csOriginalBookmarks与BookmarksDiff以及 Bookmark.cs书签数据模型并结合仓库中 Elsa.Workflows.IntegrationTests 下的用例观察真实工作流中的书签行为。赞分享后端工作流自动化流程编排低代码【免费下载链接】elsa-coreThe Workflow Engine for .NET项目地址https://gitcode.com/gh_mirrors/el/elsa-core点击查看免费下载相关推荐HTTP Prompt中的证书管理处理HTTPS自签名证书HTTP Prompt中的证书管理处理HTTPS自签名证书 在API测试过程中你是否经常遇到SSL证书验证失败的错误特别是在内部系统或开发环境中使用自开发工具接口测试IMS-Toucan的Docker部署快速搭建隔离的语音合成环境IMS Toucan的Docker部署快速搭建隔离的语音合成环境 IMS Toucan是由斯图加特大学语音与语言技术小组开发的文本转语音工具包以简洁性、模块懒猫书签清理器让浏览器书签整理变得轻松愉快的智能工具懒猫书签清理器让浏览器书签整理变得轻松愉快的智能工具 你是否曾因浏览器中杂乱无章的书签而感到困扰是否经常遇到失效链接或重复保存的网页现在有了 懒猫书签清前端数据分析上一篇手机号逆向查询QQ号3分钟快速找回遗忘账号的完整指南下一篇中兴光猫终极解锁指南一键开启工厂模式与永久Telnet访问创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表