Dapr 1.17.13 补丁版深入解析:工作流 STALLED 状态自愈与 Azure 组件认证链回退修复

Dapr 1.17.13 补丁版深入解析:工作流 STALLED 状态自愈与 Azure 组件认证链回退修复 Dapr 1.17.13 补丁版深入解析工作流 STALLED 状态自愈与 Azure 组件认证链回退修复【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr本篇基于 Dapr 1.17.13 官方发布说明docs/release_notes/v1.17.13.md逐项拆解本次补丁版修复的两个关键缺陷一是工作流进入STALLED状态后因最后一个工作流 Worker 断连而永久卡死的问题二是 Azure 组件认证链在 SPIFFE 凭据处中断、不再向后回退的问题。读完本文你将理解 Dapr 工作流 Stalling 机制的底层锁语义、Workflow Actor 失活Deactivate与执行提醒Reminder重投递的完整链路以及 Microsoft Entra ID 链式凭据认证的判定规则并掌握 1.17.13 的升级意义与验证方法。修复一STALLED 工作流不再因最后一个 Worker 断连而永久卡死问题现象状态查询永远返回 STALLED唯一出路是重启 daprd在 Dapr 1.17.13 之前一个进入了STALLED状态的工作流如果在其停滞期间最后一个连接的工作流 Worker 断开了连接就会永久卡死。即便之后有新的 Worker 重新连接——哪怕重连的 Worker 恰好注册了该停滞所等待的那个精确工作流版本——工作流也无法恢复状态查询status query持续报告STALLED唯一的恢复手段是重启 daprd。此时 sidecar 日志中会出现如下错误error while disconnecting work item stream: failed to deactivate workflow id: actor is stalled影响范围谁会被这个缺陷击中Stalling 机制设计的初衷正是让工作流在 Worker 全部离开后依然存活并在具备能力的 Worker 回归后自动恢复。最常见的触发场景是滚动升级旧应用版本断连新应用版本重连期间停滞的工作流应能无缝续跑。因此以下用户会受影响使用了工作流版本化workflow versioning、补丁patching或配置了--max-body-size参数且在某工作流处于STALLED状态期间应用的所有工作流 Worker 都断开了连接例如应用重启期间滚动升级期间缩容到零scale-to-zero期间启用了WorkflowsClusteredDeployment时负载均衡器背后的 Worker 全部离线。换句话说只要“停滞工作流 全体 Worker 短暂离线”这两个条件同时成立旧版本就存在工作流永久卡死的风险。根因剖析停滞工作流 Actor 的锁语义与失活竞态要理解这个缺陷需要先看清停滞工作流在 Dapr 运行时内部的真实状态。从源码结构看一个停滞的工作流 Actor 会做三件事见 pkg/actors/targets/workflow/common/lock/stallable.go在进程内停车park其执行执行被挂起处于停滞等待中保持执行提醒execution reminder在途in flight提醒不确认、不删除等待上下文取消将自身的锁标记为停滞stalled锁进入特殊状态拒绝新的获取。Stallable锁的关键行为在ContextLock中当锁被标记为 stalled 时任何新的加锁请求会直接返回errors.NewStalled()——错误信息正是日志中看到的actor is stalled错误定义见 pkg/actors/targets/errors/stalled.goselect { case l.ch - struct{}{}: case -l.closeCh: return nil, errors.NewClosed(lock) case -stalledCh: return nil, errors.NewStalled() case -ctx.Done(): return nil, ctx.Err() }缺陷就发生在“最后一个 Worker 断连”这个时刻daprd 会注销unregister工作流 Actor 类型并对所有工作流 Actor 执行失活deactivate流程。而Deactivate的第一步恰恰是获取 Actor 的锁——于是与停滞状态的锁语义正面冲突停滞锁拒绝加锁失活失败。恢复是否发生最终取决于一场竞态情形 A能恢复如果 scheduler 的提醒流reminder stream拆除teardown动作抢在失活流程到达该 Actor 之前取消了停车中的执行那么失活成功提醒未确认当 Worker 重连后会被重新投递redeliver工作流得以续跑。情形 B永久卡死如果失活流程抢了先它因actor is stalled失败Actor 保持激活状态且执行提醒仍在途。于是重连的 Worker 没有任何可重新投递的东西工作流永远不再执行。关键实现在 pkg/actors/targets/workflow/orchestrator/orchestrator.go 的Deactivate方法中——旧版本在此处对停滞锁的错误直接返回func (o *orchestrator) Deactivate(ctx context.Context) error { unlock, err : o.lock.ContextLock(ctx) if targeterrors.IsStalled(err) { // The actor is parked in the stall hold; wake it so its invocation // returns and releases the lock, then take the lock to deactivate. o.lock.ReleaseStall() unlock, err o.lock.ContextLock(ctx) } if err ! nil { return fmt.Errorf(failed to deactivate workflow %s: %w, o.actorID, err) } defer unlock() // ... 表清理、状态缓存失效、锁关闭、流关闭、签名重置、等待在途 goroutine }解决方案失活时唤醒停车中的执行让提醒自然重投递1.17.13 的修复思路非常直接失活一个停滞的工作流 Actor 时不再失败而是先唤醒wake停车中的执行。修复后的Deactivate流程变为尝试加锁遇到actor is stalled错误调用o.lock.ReleaseStall()释放停滞标记、唤醒停车中的执行ReleaseStall的实现见 pkg/actors/targets/workflow/common/lock/stallable.go它会关闭stalledCh并重置停滞状态被唤醒的执行立即返回不确认执行提醒——提醒保持在途、未确认状态再次加锁成功失活流程完成。当工作流 Worker 重新连接后这个未确认的执行提醒会被 scheduler 重新投递工作流重新执行只要连接上的 Worker 满足停滞条件例如所需的工作流版本已注册或 daprd 已用更大的--max-body-size重启工作流就会继续运行直至完成。这一修复带来的两个关键收益停滞恢复不再依赖时序无论提醒流拆除与失活谁先谁后最终都能走到“提醒未确认 → 重连后重投递”的确定性路径不再需要重启 daprd此前唯一的恢复手段被彻底移除。从工作流停滞的触发面看与之配套的停滞原因判定在 pkg/actors/targets/workflow/orchestrator/payloadsize.go当工作流历史载荷超过 gRPC/HTTP 服务端最大消息大小时编排器会以StalledReason_PAYLOAD_SIZE_EXCEEDED停滞工作流而--max-body-size正是控制这个阈值的入口。相关参数--max-body-size--max-body-size是 daprd 启动选项用于设置 Dapr HTTP 与 gRPC 服务的请求体最大大小采用 Kubernetes 资源数量resource quantity格式如4Mi。其解析实现在 cmd/daprd/options/options.gofs.StringVar(maxBodySize, max-body-size, strconv.Itoa(runtime.DefaultMaxRequestBodySize20)Mi, Max size of request body for the Dapr HTTP and gRPC servers, as a resource quantity)要点由 cmd/daprd/options/options_test.go 中的测试用例印证支持带单位如2Mi与不带单位视为 Mi两种写法设置为0表示禁用 HTTP 侧的检查结合 pkg/actors/targets/workflow/orchestrator/payloadsize.go 的实现maxRequestBodySize非正数时会同时禁用停滞检查与比率指标优先级高于已废弃的dapr-http-max-request-size非法值会在启动时报错退出。因此如果你因为工作流历史载荷超限而遭遇停滞合理调大--max-body-size是解除停滞条件的正道——而在 1.17.13 中这个调整不再需要伴随 daprd 重启才能让停滞工作流恢复。修复二Azure 组件认证不再在 SPIFFE 凭据处中断问题现象链式凭据认证在 SPIFFE 一步戛然而止AzureMicrosoft Entra ID组件通过按顺序依次尝试一串凭据的方式完成认证直到某一个成功为止。当组件配置中设置了azureClientId和azureTenantId时默认凭据链中会包含 SPIFFE 工作负载身份workload identity凭据而如果环境中没有可用的 SPIFFE JWT SVID 源认证链会在这一步直接停止报错形如ChainedTokenCredential: failed to acquire a token. Attempted credentials: ClientAssertionCredential: failed to get JWT SVID source from context结果是凭据链中排在 SPIFFE 之后的凭据——例如托管身份managed identity或 Azure CLI——永远不会被尝试组件明明有可用的认证方式却认证失败。影响范围Dapr 1.16.0 及以后版本的特定配置SPIFFE 凭据加入默认认证链是从Dapr 1.16.0开始的。因此受影响的是 1.16.0 及以后版本中的以下两类配置默认链依赖Azure 组件设置了azureClientId和azureTenantId但没有配置客户端密钥client secret或证书期望依赖默认链中靠后的凭据如托管身份或 Azure CLI完成认证显式顺序链显式配置了azureAuthMethods列表且将spiffeworkloadidentity放在其他方法之前例如spiffeworkloadidentity,managedidentity期望在 SPIFFE 未配置时回退到后续方法。根因剖析ChainedTokenCredential 对错误类型的严格区分问题的根源在 Azure SDK 的ChainedTokenCredential语义只有当一个凭据返回credentialUnavailableError时认证链才会继续尝试下一个凭据任何其他类型的错误都被视为致命错误直接终止整条链。而 Dapr 的 SPIFFE 凭据在上下文context中没有携带 JWT SVID 源时返回的是一个普通错误plain error而非“不可用”信号。于是“前置条件缺失”被误判为“致命认证失败”链上后续凭据再无机会。从 Dapr 的安全基础设施看SPIFFE JWT SVID 源由 Sentry 体系提供daprd 与 Sentry 建立信任后获得 SPIFFE 身份相关实现见 pkg/security/spiffe/spiffe.go运行时组件通过上下文传递 SVID 源例如 pkg/runtime/processor/secret/svidcontext_test.go 验证了 SVID 上下文的传递路径。当工作负载没有注入 SPIFFE 身份例如未启用 mTLS 或非 Kubernetes 环境时这个上下文就缺失从而触发旧版本的错误路径。解决方案缺失 SVID 源时主动上报不可用1.17.13 的修复方式是让 SPIFFE 凭据在发起任何令牌请求之前先检查上下文中是否存在 JWT SVID 源没有 SVID 源→ 凭据将自己报告为“不可用”credentialUnavailable这正是ChainedTokenCredential继续下一位凭据所需的信号有 SVID 源→ 行为完全不变照常发起令牌请求。修复后默认认证链按预期一路回退直到找到可用的凭据显式排序的azureAuthMethods列表如spiffeworkloadidentity,managedidentity也能正确回退已配置 SPIFFE 源的环境行为零变化无回归风险。升级建议与验证要点是否应该升级到 1.17.13满足以下任一条件的部署强烈建议升级使用工作流版本化、patching 或配置了--max-body-size且存在应用整体重启、滚动升级、缩容到零或WorkflowsClusteredDeployment场景——升级后停滞工作流在 Worker 重连时可自动恢复无需重启 daprd使用 Dapr 1.16.0Azure 组件配置了azureClientId/azureTenantId但无密钥/证书、依赖托管身份或 Azure CLI 认证或显式使用了spiffeworkloadidentity开头的azureAuthMethods——升级后认证链能正确回退。升级后如何验证工作流修复复现“停滞 全部 Worker 断连”场景可借助滚动升级或缩容到零观察旧版本中error while disconnecting work item stream: failed to deactivate workflow id: actor is stalled的日志不应再出现Worker 重连后停滞工作流自动恢复执行并最终完成状态查询从STALLED转为正常运行态全程无需重启 daprd。认证修复在无 SPIFFE 环境的集群中为 Azure 组件配置azureClientIdazureTenantId无密钥/证书或配置azureAuthMethods: spiffeworkloadidentity,managedidentity确认组件能通过后续凭据如托管身份成功认证不再出现failed to get JWT SVID source from context错误。兼容性与回归说明本次补丁的两个修复均为行为修正不涉及 API 变更工作流停滞的恢复路径由“竞态决定”变为“确定性恢复”Azure 认证仅改变缺失 SVID 源时的错误分类已配置 SPIFFE 源的环境行为与 1.17.12 完全一致。从源码看修复点集中在 pkg/actors/targets/workflow/orchestrator/orchestrator.go 的Deactivate停滞唤醒逻辑与 Azure 认证链的 SPIFFE 凭据可用性判定改动面小、风险可控。小结Dapr 1.17.13 是一个小而关键的补丁版本工作流一侧修复了停滞工作流在“全体 Worker 断连”场景下的永久卡死问题把依赖时序的恢复路径改写为确定性路径核心是 pkg/actors/targets/workflow/common/lock/stallable.go 中停滞锁的ReleaseStall唤醒语义Azure 一侧则修复了 SPIFFE 凭据在缺少 SVID 源时误报致命错误、截断整条认证链的问题使默认链与显式azureAuthMethods列表都能按预期回退。对于使用工作流编排与 Azure 组件的生产环境这是值得立即纳入升级计划的版本。【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考