dapr v1.18.1 版本更新介绍
发布日期: 2026-06-16
版本号: v1.18.1
Dapr 1.18.1版本包含以下错误修复:解决了当重复等待同名事件时,工作流事件定时器提醒可能泄漏的问题,这会导致资源浪费并可能引发虚假唤醒;修复了工作流边车在配置热重载后可能永久不可用、需要重启Pod才能恢复的问题;避免了在Kubernetes模式下,操作符进行配置同步时(即使无实际变化)会不必要地触发边车完整重启的问题;修正了当子工作流或活动的完成跨越ContinueAsNew边界时,父工作流可能永久挂起在运行状态的问题;并确保通过Helm图表安装Dapr时,配置中设置的工作流并发限制能够被正确应用,而非被静默忽略。这些修复旨在提升工作流功能的稳定性、可靠性及运维友好性。
更新内容 (中文)
Dapr 1.18.1
本更新包含以下错误修复:
- 当重复等待相同事件名称时,工作流事件定时器提醒泄漏
- 配置重载后工作流 Sidecar 永久不可用
- 操作符配置重同步时 Sidecar 不必要重启
- 当子工作流完成跨越 ContinueAsNew 边界时,工作流在 RUNNING 状态挂起
- 通过 Helm 图表安装 Dapr 时,工作流并发限制被静默忽略
当重复等待相同事件名称时,工作流事件定时器提醒泄漏
问题
当工作流等待外部事件,且事件在等待的超时定时器触发之前到达时,定时器的提醒应被删除,因为它不再起作用。为查找要删除的提醒,运行时将每个新到达的事件与工作流历史记录中同一名称的最早未触发事件定时器进行匹配。在历史记录中,前一次运行中已取消的定时器与待处理的定时器无法区分——其 TimerCreated 已持久化,而 TimerFired 永远不会到达——因此,对于重复等待相同事件名称的工作流(例如,每轮包含 waitForExternalEvent 加一个活动的智能体循环),那些失效的定时器会永久保留在按名称排列的先进先出队列的头部。每次新事件都会重复删除第一轮早已消失的定时器提醒,而实际在该轮被取消的定时器的提醒却从未被删除。
影响
任何多次等待同一外部事件名称的工作流都会受到影响。
可见症状包括:
- 在第一次满足等待之后,调度器中每个满足的等待都会泄漏一个定时器提醒。对于无限等待(无超时),这些是远期的一次性触发器,永远不会触发,只会在工作流完成时通过提醒清理被清理;长时间运行的实例会在其整个生命周期中累积它们。
- 对于有限超时,泄漏的提醒最终会触发,导致虚假的工作流唤醒和一个异常的
TimerFired事件,然后 SDK 会将其作为意外事件丢弃。 - 调试日志显示每一轮都出现相同的
deleting cancelled event timer reminder 'timer-N'行(总是最早的失效定时器)。
根本原因
deleteCancelledEventTimers 从完整历史记录中构建未触发事件定时器的集合,但仅处理在当前运行中到达的事件条目。历史记录中已存在的事件——那些在之前运行中已执行取消操作的事件——从未推进先进先出队列,因此之前已取消的定时器永久遮蔽了每个新事件实际取消的定时器。
解决方案
事件到定时器的匹配现在按时间顺序在完整历史记录上确定性地重放:每个 EventRaised 消耗在其之前创建的同一名称的最早未触发事件定时器。由历史记录中已存在的事件驱动的匹配重现了之前运行执行的取消操作,并且只有由当前运行新事件消耗的定时器才会触发提醒删除。此外,创建时间约束保证了一个仍处于激活等待状态的守护定时器永远不会在其自身事件之前被删除,而之前的匹配在事件和下一个等待的定时器在同一次运行中持久化时可能会这样做。具有非事件来源的定时器(CreateTimer、ActivityRetry、ChildWorkflowRetry)无论名称冲突如何,都排除在匹配之外。
配置重载后工作流 Sidecar 永久不可用
问题
托管工作流的 Dapr Sidecar 在配置热重载(SIGHUP)后可能永久不可用。一旦 Sidecar 进入此状态,每个工作流操作都会失败,唯一的恢复方式是重启 Pod。
影响
在任何配置重载之后,受影响的 Sidecar 继续运行但永远无法恢复到可用状态。
可见症状包括:
- Sidecar 健康端点无限期报告
NotReady。 - Dapr gRPC API(端口
50001)停止接受连接,因此工作流调用会失败并出现诸如14 UNAVAILABLE: No connection established. Last error: connect ECONNREFUSED 127.0.0.1:50001的错误。 - Sidecar 日志显示工作流引擎在重载期间停止,但从未记录其再次启动(
API gRPC server is running on port 50001和Registering workflow engine for gRPC endpoint行从未再次出现)。 - 此状况持续存在,直到手动重启 Pod。
根本原因
配置重载会重新启动 Dapr 运行时,而新的运行时在旧运行时完全关闭之前无法启动。关闭过程会等待进行中的 gRPC 调用优雅地排空,但已连接的工作流工作者持有一个长期的 GetWorkItems 流式连接。工作流引擎从未发出信号关闭该流,因此优雅关闭阻塞在其上,运行时从未完成重启,导致 API 服务器和工作流引擎停机。对于自动重新连接的工作流 SDK 客户端,该流会持续重建,因此只要工作者连接,Sidecar 就会一直卡住。
解决方案
工作流引擎现在在关闭过程中,在排空工作者之前,会发出信号关闭所有已连接的 GetWorkItems 流,以便优雅关闭能及时完成,而不是阻塞在这些流上。配置重载后,运行时干净地重启:Sidecar 恢复到 Ready 状态,gRPC API 在端口 50001 重新绑定,已连接的工作流客户端会自动重新连接,无需人工干预。进行中的工作项不会丢失;未送达的项会在重启后从持久化 Actor 后端重新分发。
操作符配置重同步时 Sidecar 不必要重启
问题
在 Kubernetes 模式下,Dapr Sidecar 会对未带来实际变化的 Operator 事件响应执行完整的运行时重启(SIGHUP)。
影响
长期运行的 Sidecar 会在没有配置变更和人工操作的情况下周期性地重启其运行时。
根本原因
Operator 的 informer 将整个命名空间的资源事件流式传输到每个已连接的 Sidecar。Kubernetes informer 会定期重同步,将每个缓存的对象作为 UPDATED 事件重新传递,其 resourceVersion 未变,并且在重新连接时也会重新传递。Operator 原样转发了这些无操作重放,而 Sidecar 的 SIGHUP 调谐器会重启运行时。
解决方案
Operator 现在会在将 informer 事件流式传输到 Sidecar 之前,丢弃那些 resourceVersion 未变的事件。真正的变更总是会推进 resourceVersion,因此真正的更新仍然会被传递,热重载继续正常工作;只有无操作重同步和重连重放被抑制。Sidecar 不再因常规的 Operator 重同步而重启。
当子工作流完成跨越 ContinueAsNew 边界时,工作流在 RUNNING 状态挂起
问题
使用 ContinueAsNew 和子工作流(或活动)的工作流可能永久卡在 RUNNING 状态。子工作流或活动在应用端明显完成,但父工作流从未观察到完成并永久等待。daprd 日志显示工作流执行返回 ORCHESTRATION_STATUS_RUNNING,随后出现:
Workflow actor '<id>': dropping duplicate completion event already present in history/inbox
Workflow actor '<id>': ignoring run request for reminder 'new-event-...' because the workflow inbox is empty
影响
受影响的工作流会无限期冻结,永不完成、失败或超时。此故障与时间相关且是间歇性的:当在 ContinueAsNew 之前启动的子工作流或活动在其之后完成时发生。智能体工作流尤其容易受到影响,因为它们通常通过 ContinueAsNew 循环运行,同时运行具有可变延迟的子工作流和活动。
根本原因
ContinueAsNew 会重置工作流的任务 ID 序列。当被前一代放弃的子工作流或活动在 ContinueAsNew 之后完成时,其完成事件携带来自旧序列的任务 ID。新一代没有匹配的已计划操作,因此工作流执行消耗该事件而没有效果,并将其作为孤立项持久化到历史记录中。当新一代稍后计划自己的操作时,该操作合法地重用相同的任务 ID。其真实的完成事件随后与 v1.18.0-rc.3 中添加的完成去重(旨在丢弃重复传递的完成)在历史记录中的孤立事件进行匹配,并作为重复项被丢弃。发送者已被确认,因此该事件永远不会被重新传递,而重新唤醒的提醒触发时收件箱为空并被删除。工作流停留在 RUNNING 状态,没有任何东西驱动它。
解决方案
工作流 Actor 不再持久化解析事件(任务或子工作流完成和失败),如果它们与历史记录中任何计划的操作都不匹配。过时的跨代完成事件在被消耗时会被丢弃并发出警告,而不是写入历史记录,因此完成去重不再可能将稍后的合法完成误判为重复项。当前代操作的完成不受影响。
通过 Helm 图表安装 Dapr 时,工作流并发限制被静默忽略
问题
1.18.0 中引入的工作流并发限制(globalMaxConcurrentWorkflowInvocations、globalMaxConcurrentActivityInvocations、Configuration 资源 spec.workflow 下的 workflowConcurrencyLimits 和 activityConcurrencyLimits)已添加到 API 类型中,但 Helm 图表中捆绑的匹配 Configuration CRD 从未重新生成。因此,图表 CRD 的 OpenAPI 模式不知道这些字段。
影响
如果以下两个条件同时满足,则您受到影响:
- 您通过 Helm 图表(而非 CLI)安装了 Dapr 控制平面(该图表应用
charts/dapr/crds中的 CRD)。 - 您在
Configuration资源上设置了任何全局或按名称的工作流/活动并发限制。
根本原因
Helm 图表中附带的 CRD 是通过 controller-gen 从 pkg/apis 下的 Go API 类型生成的,但在添加并发限制字段后,生成的输出未被复制到 charts/dapr/crds/configuration.yaml 中。捆绑的 CRD 与 API 类型不同步。
解决方案
图表 Configuration CRD 已重新生成,因此其模式包含并发限制字段(以及它们引用的 NamedConcurrencyLimit 模式)。
更新内容 (原始)
Dapr 1.18.1
This update contains the following bug fixes:
- Workflow event-timer reminders leak when the same event name is awaited repeatedly
- Workflow sidecars become permanently unavailable after a configuration reload
- Sidecars restart unnecessarily on operator configuration resyncs
- Workflows hang in RUNNING when a child workflow completion crosses a ContinueAsNew boundary
- Workflow concurrency limits are silently ignored when Dapr is installed via the Helm chart
Workflow event-timer reminders leak when the same event name is awaited repeatedly
Problem
When a workflow waits on an external event and the event arrives before the wait’s timeout timer fires, the timer’s reminder is supposed to be deleted, since it no longer serves a purpose.
To find which reminder to delete, the runtime paired each newly arrived event against the oldest unfired event timer of the same name found anywhere in the workflow’s history.
A timer cancelled in a previous run is indistinguishable in history from a pending one — its TimerCreated is persisted and its TimerFired never arrives — so for workflows that wait on the same event name repeatedly (for example agent-style loops of waitForExternalEvent plus an activity per turn), those dead timers sat at the head of the per-name FIFO forever.
Every new event re-deleted the first turn’s long-gone timer reminder while the reminder of the timer actually cancelled that turn was never deleted.
Impact
Any workflow that awaits the same external event name more than once is affected.
Visible symptoms include:
- One leaked timer reminder in the scheduler per satisfied wait after the first. For indefinite waits (no timeout) these are far-future one-shots that never fire and only get cleaned up by the reminder sweep at workflow completion; long-running instances accumulate them for their entire lifetime.
- For finite timeouts, the leaked reminder eventually fires, causing a spurious workflow wake-up and a stray
TimerFiredevent that the SDK then discards as unexpected. - Debug logs showing the same
deleting cancelled event timer reminder 'timer-N'line (always the oldest dead timer) on every turn.
Root Cause
deleteCancelledEventTimers built the set of unfired event timers from the full history but only consumed entries for events arriving in the current run.
Events already in history — which had performed their cancellations in previous runs — never advanced the FIFO, so previously cancelled timers permanently shadowed the timer each new event had actually cancelled.
Solution
The event-to-timer pairing is now replayed deterministically over the full history in chronological order: each EventRaised consumes the oldest unfired event timer of the same name created before it.
Pairings driven by events already in history reproduce the cancellations performed by previous runs, and only timers consumed by the current run’s new events trigger reminder deletions.
The created-before constraint additionally guarantees that a timer guarding a still-armed wait is never deleted ahead of its own event, which the previous pairing could do when an event and the next wait’s timer were persisted in the same run.
Timers with non-event origins (CreateTimer, ActivityRetry, ChildWorkflowRetry) remain excluded from the pairing regardless of name collisions.
Workflow sidecars become permanently unavailable after a configuration reload
Problem
A Dapr sidecar that hosts workflows could become permanently unavailable after a configuration hot-reload (SIGHUP). Once the sidecar entered this state, every workflow operation failed and the only way to recover was to restart the pod.
Impact
After any configuration reload, an affected sidecar kept running but never came back to a usable state.
Visible symptoms include:
- The sidecar health endpoint reports
NotReadyindefinitely. - The Dapr gRPC API (port
50001) stops accepting connections, so workflow calls fail with errors such as14 UNAVAILABLE: No connection established. Last error: connect ECONNREFUSED 127.0.0.1:50001. - Sidecar logs show the workflow engine stopping during the reload but never logging it starting again (the
API gRPC server is running on port 50001andRegistering workflow engine for gRPC endpointlines never reappear). - The condition persists until the pod is manually restarted.
Root Cause
A configuration reload restarts the Dapr runtime, and the new runtime cannot start until the previous one has fully shut down.
Shutdown waits for in-flight gRPC calls to drain gracefully, but a connected workflow worker holds a long-lived GetWorkItems streaming connection.
The workflow engine never signalled that stream to close, so the graceful shutdown blocked on it and the runtime never finished restarting, leaving the API server and workflow engine down.
With workflow SDK clients that automatically reconnect, the stream was continually re-established, so the sidecar stayed stuck for as long as a worker was connected.
Solution
The workflow engine now signals all connected GetWorkItems streams to close as part of shutdown, before draining the worker, so graceful shutdown completes promptly instead of blocking on those streams.
After a configuration reload the runtime restarts cleanly: the sidecar returns to Ready, the gRPC API rebinds on port 50001, and connected workflow clients reconnect automatically with no manual intervention.
In-flight work items are not lost; undelivered items are re-dispatched from the durable actor backend after the restart.
Sidecars restart unnecessarily on operator configuration resyncs
Problem
In Kubernetes mode, a Dapr sidecar would perform a full runtime restart (SIGHUP) in response to operator events that carried no actual change.
Impact
Long-running sidecars restarted their runtime periodically with no configuration change and no human action.
Root Cause
The operator’s informer streams resource events for the whole namespace to every connected sidecar.
The Kubernetes informer periodically resyncs, re-delivering every cached object as an UPDATED event with an unchanged resourceVersion, and also re-delivers on reconnect.
The operator forwarded these no-op replays verbatim, and the sidecar’s SIGHUP reconciler restarts the runtime.
Solution
The operator now drops informer events whose resourceVersion is unchanged before streaming them to sidecars.
A genuine change always advances the resourceVersion, so real updates are still delivered and hot reload continues to work; only the no-op resync and reconnect replays are suppressed.
Sidecars no longer restart on routine operator resyncs.
Workflows hang in RUNNING when a child workflow completion crosses a ContinueAsNew boundary
Problem
A workflow that uses ContinueAsNew and child workflows (or activities) can permanently hang in the RUNNING state.
A child workflow or activity visibly completes on the application side, but the parent never observes the completion and waits forever. The daprd log shows the workflow execution returning ORCHESTRATION_STATUS_RUNNING followed later by:
Workflow actor '<id>': dropping duplicate completion event already present in history/inbox
Workflow actor '<id>': ignoring run request for reminder 'new-event-...' because the workflow inbox is empty
Impact
Affected workflows freeze indefinitely and never complete, fail, or time out. The failure is timing dependent and intermittent: it occurs when a child workflow or activity started before a ContinueAsNew completes after it. Agentic workflows are particularly exposed, as they commonly loop via ContinueAsNew while running child workflows and activities with variable latency.
Root Cause
ContinueAsNew resets the workflow’s task ID sequence. When a child workflow or activity abandoned by a previous generation completes after the ContinueAsNew, its completion event carries a task ID from the old sequence. The new generation has no matching scheduled operation, so the workflow execution consumes the event without effect and it is persisted into history as an orphan.
When the new generation later schedules its own operation, that operation legitimately reuses the same task ID. Its real completion event is then matched against the orphaned event in history by the completion deduplication added in v1.18.0-rc.3 (intended to drop redelivered completions), and is discarded as a duplicate. The sender is acknowledged, so the event is never redelivered, and the re-asserted wake-up reminder fires against an empty inbox and is deleted. The workflow is left RUNNING with nothing to drive it.
Solution
The workflow actor no longer persists resolution events (task or child workflow completions and failures) that match no operation scheduled in history. Stale cross-generation completions are discarded with a warning at the point they are consumed instead of being written into history, so the completion deduplication can no longer mistake a later, legitimate completion for a duplicate. Completions for operations of the current generation are unaffected.
Workflow concurrency limits are silently ignored when Dapr is installed via the Helm chart
Problem
The workflow concurrency limits introduced in 1.18.0 (globalMaxConcurrentWorkflowInvocations, globalMaxConcurrentActivityInvocations, workflowConcurrencyLimits, and activityConcurrencyLimits under spec.workflow of a Configuration resource) were added to the API types but the matching Configuration CRD bundled in the Helm chart was never regenerated.
The chart CRD’s OpenAPI schema therefore did not know about these fields.
Impact
You were affected if both of the following were true:
- You installed the Dapr control plane via the Helm chart (which applies the CRDs from
charts/dapr/crds) rather than via the CLI. - You set any of the global or per-name workflow/activity concurrency limits on a
Configurationresource.
Root Cause
The CRDs shipped in the Helm chart are generated from the Go API types under pkg/apis with controller-gen, but the generated output had not been copied into charts/dapr/crds/configuration.yaml after the concurrency-limit fields were added. The bundled CRD drifted out of sync with the API types.
Solution
The chart Configuration CRD was regenerated so its schema includes the concurrency-limit fields (and the NamedConcurrencyLimit schema they reference).
下载链接
- daprd_darwin_amd64.tar.gz
- daprd_darwin_amd64.tar.gz.sha256
- daprd_darwin_arm64.tar.gz
- daprd_darwin_arm64.tar.gz.sha256
- daprd_linux_amd64-stablecomponents.tar.gz
- daprd_linux_amd64-stablecomponents.tar.gz.sha256
- daprd_linux_amd64.tar.gz
- daprd_linux_amd64.tar.gz.sha256
- daprd_linux_arm-stablecomponents.tar.gz
- daprd_linux_arm-stablecomponents.tar.gz.sha256
- daprd_linux_arm.tar.gz
- daprd_linux_arm.tar.gz.sha256
- daprd_linux_arm64-stablecomponents.tar.gz
- daprd_linux_arm64-stablecomponents.tar.gz.sha256
- daprd_linux_arm64.tar.gz
- daprd_linux_arm64.tar.gz.sha256
- daprd_windows_amd64.zip
- daprd_windows_amd64.zip.sha256
- grafana-actor-dashboard.json
- grafana-actor-dashboard.json.sha256
- grafana-sidecar-dashboard.json
- grafana-sidecar-dashboard.json.sha256
- grafana-system-services-dashboard.json
- grafana-system-services-dashboard.json.sha256
- injector_darwin_amd64.tar.gz
- injector_darwin_amd64.tar.gz.sha256
- injector_darwin_arm64.tar.gz
- injector_darwin_arm64.tar.gz.sha256
- injector_linux_amd64.tar.gz
- injector_linux_amd64.tar.gz.sha256
- injector_linux_arm.tar.gz
- injector_linux_arm.tar.gz.sha256
- injector_linux_arm64.tar.gz
- injector_linux_arm64.tar.gz.sha256
- injector_windows_amd64.zip
- injector_windows_amd64.zip.sha256
- operator_darwin_amd64.tar.gz
- operator_darwin_amd64.tar.gz.sha256
- operator_darwin_arm64.tar.gz
- operator_darwin_arm64.tar.gz.sha256
- operator_linux_amd64.tar.gz
- operator_linux_amd64.tar.gz.sha256
- operator_linux_arm.tar.gz
- operator_linux_arm.tar.gz.sha256
- operator_linux_arm64.tar.gz
- operator_linux_arm64.tar.gz.sha256
- operator_windows_amd64.zip
- operator_windows_amd64.zip.sha256
- placement_darwin_amd64.tar.gz
- placement_darwin_amd64.tar.gz.sha256
- placement_darwin_arm64.tar.gz
- placement_darwin_arm64.tar.gz.sha256
- placement_linux_amd64.tar.gz
- placement_linux_amd64.tar.gz.sha256
- placement_linux_arm.tar.gz
- placement_linux_arm.tar.gz.sha256
- placement_linux_arm64.tar.gz
- placement_linux_arm64.tar.gz.sha256
- placement_windows_amd64.zip
- placement_windows_amd64.zip.sha256
- scheduler_darwin_amd64.tar.gz
- scheduler_darwin_amd64.tar.gz.sha256
- scheduler_darwin_arm64.tar.gz
- scheduler_darwin_arm64.tar.gz.sha256
- scheduler_linux_amd64.tar.gz
- scheduler_linux_amd64.tar.gz.sha256
- scheduler_linux_arm.tar.gz
- scheduler_linux_arm.tar.gz.sha256
- scheduler_linux_arm64.tar.gz
- scheduler_linux_arm64.tar.gz.sha256
- scheduler_windows_amd64.zip
- scheduler_windows_amd64.zip.sha256
- sentry_darwin_amd64.tar.gz
- sentry_darwin_amd64.tar.gz.sha256
- sentry_darwin_arm64.tar.gz
- sentry_darwin_arm64.tar.gz.sha256
- sentry_linux_amd64.tar.gz
- sentry_linux_amd64.tar.gz.sha256
- sentry_linux_arm.tar.gz
- sentry_linux_arm.tar.gz.sha256
- sentry_linux_arm64.tar.gz
- sentry_linux_arm64.tar.gz.sha256
- sentry_windows_amd64.zip
- sentry_windows_amd64.zip.sha256