Hatchet 版本演进深度解析:从动态并发控制到嵌入式引擎的全功能全景
【免费下载链接】hatchet🪓 An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet
本篇技术指南以 Hatchet 官方 CHANGELOG.md 为主体脉络,结合仓库源码(并发调度、配置加载、SDK、CLI 等实现)为你系统梳理 v0.70 至 v0.106 版本的核心功能演进:动态并发限制、共享并发、两类新的队列深度并发策略、幂等性与批处理任务、工作流暂停、嵌入式引擎、无认证开发模式与可观测性增强。读完本文,你将掌握 Hatchet 近期版本新增能力的语义、配置参数、底层实现原理与升级注意事项,并能在自己的任务编排实践中直接应用这些新特性。
版本节奏与演进主线
Hatchet 采用语义化版本号,迭代节奏紧凑:从 2025 年 8 月的 v0.70 系列到 2026 年 9 月的 v0.106.5,一年内跨越 40 多个版本。整体演进主线可以归纳为四条:
- 并发与调度能力深化:从基础并发策略演进到动态并发限制(per-key CEL 表达式)、跨工作流共享并发、内存索引并发调度;
- 任务控制面扩展:引入幂等性 Key、批处理任务、工作流暂停、slot cost 高级分配;
- 开发者体验升级:嵌入式引擎(Embedded Mode)、无认证开发镜像、CLI quickstart 用例模板、四个官方 SDK(Go / Python / TypeScript / Ruby)同步迭代;
- 可观测性与运维硬化:丰富的 Prometheus 指标、独立的数据保留期配置、TLS 版本控制、会话表自动清理、大量调度器与队列死锁/竞态修复。
下表是主要里程碑的速览(完整细节见后文各节):
| 版本 | 日期 | 核心主题 |
|---|---|---|
| v0.106.5 | 2026-09-08 | 动态并发限制、共享并发、两个新并发策略、租户隔离安全修复 |
| v0.105.16 | 2026-08-31 | CANCEL_QUEUED_EXCEPT_NEWEST / OLDEST 策略落地、引擎修复 |
| v0.105.2 | 2026-08-25 | 工作流暂停、成员级 payload 可见性、TS/Python Embedded 早期 Beta |
| v0.101.27 | 2026-08-17 | 幂等性 Key、批处理任务、只读 VIEWER 角色、队列深度指标 |
| v0.98.9 | 2026-07-28 | slot cost、幂等性(四 SDK)、Go 嵌入式引擎、内存索引默认开启 |
| v0.94.10 | 2026-07-14 | 无认证开发镜像、quickstart --use-case、独立保留期、流监听器自动重连 |
| v0.90.13 | 2026-06-29 | UserSession 自动清理、多单位时长解析修复、调度冷启动优化 |
| v0.89.0 | 2026-06-09 | TLS 最低版本配置、cron 按需触发、性能优化 |
| v0.83.0 ~ v0.85.x | 2026-03 ~ 05 | 组织/租户管理、RBAC、SSO、追踪(Trace)支持、负载测试增强 |
| v0.78.x ~ v0.82.x | 2026-01 ~ 03 | 官方 Ruby SDK、durable execution 重构、OTel 可观测性大改 |
| v0.70.x ~ v0.75.x | 2025-08 ~ 2026-01 | v1 API 全面铺开、webhook、依赖注入、组织 UI、Hatchet CLI |
并发控制:从固定策略到动态表达式
并发控制是近期版本投入最多的领域,v0.106.5 直接被称为 "concurrency-focused release"。
并发策略家族与调度分发
在底层实现上,所有并发策略都收敛到统一的入口RunConcurrencyStrategy,它根据策略枚举分发到不同的执行函数。见 scheduler_concurrency.go:
switch strategy.Strategy { case sqlcv1.V1ConcurrencyStrategyGROUPROUNDROBIN: res, err = c.runGroupRoundRobin(ctx, tenantId, strategy) case sqlcv1.V1ConcurrencyStrategyCANCELINPROGRESS: res, err = c.runCancelInProgress(ctx, tenantId, strategy) case sqlcv1.V1ConcurrencyStrategyCANCELNEWEST: res, err = c.runCancelNewest(ctx, tenantId, strategy) case sqlcv1.V1ConcurrencyStrategyCANCELQUEUEDEXCEPTNEWEST: res, err = c.runCancelQueuedExceptNewest(ctx, tenantId, strategy) case sqlcv1.V1ConcurrencyStrategyCANCELQUEUEDEXCEPTOLDEST: res, err = c.runCancelQueuedExceptOldest(ctx, tenantId, strategy) }策略枚举定义在 models.go:
V1ConcurrencyStrategyCANCELQUEUEDEXCEPTNEWEST V1ConcurrencyStrategy = "CANCEL_QUEUED_EXCEPT_NEWEST" V1ConcurrencyStrategyCANCELQUEUEDEXCEPTOLDEST V1ConcurrencyStrategy = "CANCEL_QUEUED_EXCEPT_OLDEST"两类新的队列深度策略:CANCEL_QUEUED_EXCEPT_NEWEST / OLDEST
v0.105.16 与 v0.106.5 先后引入并完善了CANCEL_QUEUED_EXCEPT_NEWEST与CANCEL_QUEUED_EXCEPT_OLDEST两个策略(对应 PR #4793、#4847 等)。它们的语义是:
- CANCEL_QUEUED_EXCEPT_NEWEST:在同一个并发组内,取消所有已排队(QUEUED)的运行,仅保留最新排队的那个,直到在途运行完成——概念上类似"防抖(debouncing)";
- CANCEL_QUEUED_EXCEPT_OLDEST:对称地,仅保留最旧排队的运行,取消其余排队项。
两者的典型价值:当高频触发大量排队任务时,只让最新的(或最旧的)请求真正执行,避免"排队长龙"浪费资源。从源码看,其实现位于 scheduler_concurrency.go 的runCancelQueuedExceptNewest/runCancelQueuedExceptOldest:两者都先通过TryAdvisoryLock(而非阻塞锁)获取策略级 advisory lock 以降低锁竞争,未获取到锁时直接返回空的RunConcurrencyResult(标记FailedAdvisoryLock: true),随后创建父级临时表、执行RunParentCancelQueuedExceptNewest/RunChildCancelQueuedExceptNewestSQL 查询挑出被取消的任务,再调用DeleteTasksFromQueue将它们从队列中移除。父子(parent/child)策略链路都走同一套"父级准入 + 子级出队"的流程。
动态并发限制:per-key CEL 表达式
v0.106.5 允许按并发 Key 动态设置并发上限:并发限制不再是一个固定数字,而是一个 CEL 表达式,针对每个 key 独立求值。官方示例:
input.tier == 'premium' ? 10 : 1含义:premium等级的用户可以同时运行 10 个工作流,其他用户只能同时运行 1 个。这一能力在存储层由V1StepConcurrency结构体的MaxRunsExpression字段承载(见 scheduler_concurrency.go),与静态的MaxConcurrency并存——MaxRunsExpression负责动态上限,MaxConcurrency仍是静态上限的兜底。
共享并发:跨工作流的并发规则
在此之前,如果要限制workflow_a与workflow_b合计每个用户最多 10 个并发运行,通常需要引入一个父级 router 任务做中介。v0.106.5 引入共享并发(shared concurrency):直接创建一条并发规则,让多个任务与工作流同时消费它,从而替代 router 任务模式,减少一跳编排开销。
内存索引并发调度(默认开启)
v0.98.9 将SERVER_CONCURRENCY_IN_MEMORY_INDEX_ENABLED的默认值改为true:并发策略改为默认基于内存索引 + outbox方式评估,而非每个调度周期都查询 Postgres。配置项在 server.go 中定义并绑定环境变量:
// ConcurrencyInMemoryIndexEnabled controls whether the in-memory index + outbox approach is used for concurrency strategies ConcurrencyInMemoryIndexEnabled bool `mapstructure:"concurrencyInMemoryIndexEnabled" json:"concurrencyInMemoryIndexEnabled,omitempty" default:"true"`同时 v0.83.2 引入了"内存中、带 Key 的锁队列"来保护RunConcurrencyStrategy,v0.79.4 修复了链式并发闸门中的跨策略 slot 污染问题——这些修复共同保证了并发调度在高负载下的正确性。
幂等性与批处理任务
v0.101.27 正式发布幂等性 Key(Idempotency Keys)与批处理任务(Batch Tasks),v0.98.9 将幂等性 Key 扩展至全部四个 SDK(Go / Python / TypeScript / Ruby)。
- 幂等性 Key:任务和工作流可以声明幂等性 Key,重复触发不会产生重复运行。Key 在 TTL 内或直到认领它的运行达到终态之前一直被持有。v0.101.27 同时优化了幂等性查找——当任务不携带任何 Key 时直接跳过查找,避免无谓查询。
- 批处理任务:将多个任务聚合为一次批量提交,减少 API 调用次数与下游资源占用。
补充细节:v0.73.40 起对定时运行的幂等性 Key 错误做了静默处理,v0.73.0 修复了定时运行与幂等性 Key 检查之间的竞态——说明该功能从引入到稳定经历过多轮打磨。
工作流暂停与调度管理
v0.105.2 正式上线工作流暂停(Workflow Pause),适用于"工作流错误率高需要紧急止血"或"白天排队、夜间集中执行"等场景。暂停的语义:
- 在途(in-flight)运行继续执行完毕;
- 新触发的运行保持排队,直到取消暂停(排队受队列 TTL 限制);
- cron 与定时触发可以选择"排队"或"丢弃"运行。
配套调度增强:v0.79.16 增加"按需更新定时 cron 触发"的队列;v0.89.0 允许通过 Dashboard 直接按需触发 cron 与定时运行,不必等待下一个调度 tick;v0.79.14 为 cron 增加了秒级粒度。引擎侧 v0.105.16 修复了暂停工作流时并发 slot 迁移到暂停队列表(paused queue items table)的问题,v0.79.23 则移除了已被废弃的旧暂停查询。
嵌入式引擎与本地开发体验
Go SDK Embedded:进程内引擎
v0.98.9 起,Go SDK 可以在进程内运行完整引擎(指向你自己的 Postgres),用于本地开发与端到端测试。API 见 embedded.go:
func WithEmbedded(opts ...EmbeddedOption) v0Client.ClientOpt func WithEmbeddedDatabaseURL(url string) EmbeddedOption用法示例:
client, err := hatchet.NewClient(hatchet.WithEmbedded( hatchet.WithEmbeddedDatabaseURL("postgres://..."), ))参考完整示例 embedded/main.go。v0.105.2 将 Embedded 扩展为 TypeScript 与 Python SDK 的早期 Beta:SDK 可以直接在你自己的 worker 旁边拉起一个完整引擎,非常适合本地开发、端到端测试与 CI。
无认证开发镜像
v0.94.10 引入一组开发镜像(hatchet-api-dev、hatchet-engine-dev、hatchet-admin-dev、hatchet-dashboard-dev),无需管理 API Token 即可运行,适合开发与测试环境。通过 CLI 启动:
hatchet server start --disable-auth该命令以hatchet-lite-dev替代hatchet-lite。需要明确的是:这种模式在 Hatchet 二进制中内嵌了一个全局 worker API key,只能用于开发与测试环境。
CLI 与 quickstart 用例模板
v0.75.0 引入 Hatchet CLI;v0.94.10 为hatchet quickstart增加--use-case标志,首个用例是scheduled——一个按 cron 调度、同时支持按需运行的工作流 Go 模板(实现见 quickstart.go,模板布局契约见 templater/selection.go)。v0.98.9 起,所有接受--profile的 CLI 命令在没有显式配置时使用默认或唯一 profile 而不再交互式询问;在无终端会话(如 CI)中,需要交互选择的命令会以明确的错误提示告终,hatchet server start在无默认配置时会将新 profile 设为默认。
v0.105.2 还让hatchet-lite将SIGTERM传递给引擎,容器关闭时优雅退出而不再等待被强制杀死。
可观测性增强
Prometheus 指标
近期版本持续扩充租户级指标(tenant metrics):
- v0.101.27:
hatchet_tenant_queued_to_assigned与hatchet_tenant_queued_to_assigned_time_seconds按工作流名拆分导出;hatchet_tenant_queue_size增加workflow_name标签;新增hatchet_tenant_additional_metadata_queue_sizegauge,按 additional metadata 键值对报告队列深度——只有以prom_前缀开头的 Key 才会被导出(显式 opt-in,控制基数);注意一个条目会计入它所携带的每一个 metadata Key,跨 Key 求和没有意义。 - v0.94.10:新增 worker pool 的 slot 利用率指标
hatchet_tenant_worker_label_slots(总槽位)、hatchet_tenant_used_worker_label_slots(在用)、hatchet_tenant_available_worker_label_slots(空闲),按标签分组。 - v0.72.2:新增 worker slot Prometheus 指标。
- v0.86.9:task stats 端点新增
taskNames查询参数,便于 KEDA 场景使用;v0.73.92 为 task stats 增加最旧排队与运行中任务信息。
独立的数据保留期配置
v0.94.10 起,OLAP 与核心数据保留期可以独立配置(此前共用一个默认值):
| 环境变量 | 作用 |
|---|---|
SERVER_LIMITS_OLAP_PARTITION_RETENTION | OLAP 分区保留期 |
SERVER_LIMITS_CORE_PARTITION_RETENTION | 核心分区保留期 |
SERVER_LIMITS_DEFAULT_TENANT_RETENTION_PERIOD | 未设置上述两项时的兜底默认值 |
对应绑定代码见 server.go。
追踪与日志
- v0.82.0 完成可观测性大改与 traces 支持;v0.83.x 系列持续修复 OTel 相关死锁、span 命名与自引用问题;
- v0.98.9 起,run trace 视图中的引擎行会携带 workflow / task / event 名称徽章,重试任务显示重试序号,使重复 span(如
hatchet.engine.workflow_run)一眼可辨; - v0.79.35 池化 gzip writer 降低 RabbitMQ 消息压缩分配;v0.73.73 增加 gzip 压缩支持;
- v0.73.66 增加 REST API 插桩;v0.77.32 引入 OTel Collector。
TLS 与安全基线
v0.89.0 为引擎与客户端引入最低 TLS 版本配置:
| 环境变量 | 默认值 |
|---|---|
SERVER_TLS_MIN_VERSION | 1.3 |
HATCHET_CLIENT_TLS_MIN_VERSION | 1.3 |
绑定见 server.go。v0.106.5 还发布了安全公告:引擎侧的 satisfied durable event 查找现在按租户过滤,强制 durable event 日志的租户隔离(对应 GHSA-992g-9cr3-vm5x)。
稳定性、运维与升级注意
UserSession 自动清理
v0.90.13 修复了UserSession表无界增长问题:新增每小时清理任务,自动删除过期与孤儿会话。升级前可对 Postgres 执行可选的一次性批量清理(次数不限):
DELETE FROM "UserSession" WHERE ctid IN ( SELECT ctid FROM "UserSession" WHERE NOT ( ("userId" IS NOT NULL AND "expiresAt" >= NOW()) OR ("userId" IS NULL AND "createdAt" >= NOW() - INTERVAL '24 hours') ) LIMIT 10000 );注意:10000的 LIMIT 可按需调整。
更严格的时长校验
v0.90.13 修复了多单位时长字符串(如42m30s)被静默截断的解析 bug,并收紧注册时的时长校验。升级前需检查工作流定义:以下形式将被拒绝——带符号值、裸数字、亚毫秒单位(ns/us)、以及混用d/w/y的形式。这些写法过去会被接受并静默转换,现在会在注册时直接报错。
调度器与队列的持续修复
CHANGELOG 中反复出现调度器与消息队列的稳定性修复,例如:
- v0.105.2:被逐出(evicted)的任务现在可以超时而不是挂起;重试退避在高重试次数下不再溢出;缺失行查找返回
NotFound而非Internal; - v0.105.16:durable DAG 的 on-failure 任务不再被跳过;worker 心跳在拒绝非活跃监听流之前记录;
- v0.106.5:分区在
UpdateDurableEventLogEntriesSatisfied中被剪枝;CEL 求值不一致修复;任务分配时重复行不再触发 on-conflict 错误; - v0.101.27:每租户操作定时器在启动时跨完整间隔抖动、持久化间隔懒加载,避免多租户部署中控制器重启引发的查询风暴;worker label 亲和性相同的任务在并列 worker 间轮询分配,而不是挤满单台 worker。
其他值得关注的运维项
- 仪表盘子路径:v0.101.27 起仪表盘可通过
BASE_PATH(静态文件服务器)或LITE_FRONTEND_BASE_PATH(hatchet-lite)部署在子路径而非域名根路径; - 只读角色:v0.101.27 新增只读
VIEWER租户成员角色;v0.105.2 允许按成员限制 payload 可见性——VIEWER成员可以查看运行、工作流、worker、事件、日志与指标,但看不到 payload 内容、也不能做任何修改; - 消息队列配置解耦:v0.98.9 起 best-effort pub/sub 与 durable queue 独立配置,pub/sub 可运行在 Postgres 上而 durable 消息保留在 RabbitMQ,现有部署无需新配置;
- CLI 匿名遥测:v0.98.9 起 CLI 在调用时上报匿名使用数据(可用
hatchet rate-limits管理限流,或从 TUI 交互式操作); - v1 化推进:v0.74.x 移除 v0 专有数据库查询与路径,v0.83.57 起所有 list partition 调用仅针对 v1 租户,
v1_(runs|dags|tasks)_olap移除状态分区(v0.83.46)——升级到这些版本意味着全面切换到 v1 数据模型。
总结:如何跟上 Hatchet 的演进
Hatchet 的版本演进呈现清晰的路径:并发调度从"静态规则"走向"动态表达式 + 共享规则 + 内存索引",任务控制面从"单次触发"走向"幂等、批量、可暂停",开发体验从"完整自托管"走向"嵌入式引擎 + 无认证开发镜像 + CLI 模板",可观测性从"基础日志"走向"全量 trace + 细粒度租户指标"。
对使用者而言,本文建议按以下顺序落地新能力:
- 升级前先阅读本文的"稳定性、运维与升级注意"一节,重点检查时长字符串与
UserSession清理脚本; - 对高频、易重放的触发,启用幂等性 Key;对大批量提交,改用批处理任务;
- 需要按用户等级或业务维度差异化限流时,使用动态并发限制(CEL 表达式);需要跨工作流限流时,使用共享并发;
- 对"高频触发只执行最新一次"的场景,优先尝试
CANCEL_QUEUED_EXCEPT_NEWEST; - 本地开发与 CI 优先尝试 Embedded 模式或无认证开发镜像,降低环境搭建成本。
相关源码与配置的进一步探索入口:并发调度实现、并发策略枚举、服务端配置项、Go SDK 嵌入式 API、嵌入式示例、CLI quickstart。
【免费下载链接】hatchet🪓 An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考