bug: Redis Sentinel connections permanently broken after data node failover (missing sentinelRetryStrategy)

当 Redis Sentinel 集群中的数据节点发生故障转移时,Langfuse worker 因缺少 sentinelRetryStrategy 配置,ioredis 默认重试策略耗尽后连接永久中断。优先排查 createRedisSentinelInstance 方法是否添加了无限重试的 se

快速结论:当 Redis Sentinel 集群中的数据节点发生故障转移时,Langfuse worker 因缺少 sentinelRetryStrategy 配置,ioredis 默认重试策略耗尽后连接永久中断。优先排查 createRedisSentinelInstance 方法是否添加了无限重试的 sentinelRetryStrategy

适用环境:Langfuse 自托管版本 3.167.4 (Helm chart 1.5.26),ioredis 5.10.1,Redis Sentinel 模式(REDIS_SENTINEL_ENABLED=true),2 Redis 数据节点 + 3 Sentinel 节点(Spotahome Redis Operator / RedisFailover CRD)。

最快修复方案:在代码文件 packages/shared/src/server/redis/redis.tscreateRedisSentinelInstance 函数中,在 ...additionalOptions 之前添加如下配置(已验证可自愈):

sentinelRetryStrategy: (retries: number) => Math.min(retries * 200, 10_000),

该策略使重试间隔从 200ms 开始指数退避,最长等待 10 秒,但永不会停止重试,从而在 Sentinel 故障转移完成后自动恢复连接。

注意事项:该方案为 Issue 作者提出的建议,Langfuse 核心团队尚未合并正式 PR,属于社区验证的修复方式。若您使用自定义 additionalOptions 传入了 sentinelRetryStrategy,会被覆盖 — 请确保自定义参数生效顺序。

问题场景

在自托管的 Langfuse 环境中,worker 组件(langfuse-worker Pod)依赖 Redis Sentinel 集群处理队列任务(DataRetentionProcessingQueue、EvalExecutionQueue 等)。当任意一个 Redis 数据节点因故障转移或主动重启(如 kubectl rollout restart statefulset/<redis-statefulset>)而短暂不可用时,worker 所有队列同时报错 All sentinels are unreachable,随后彻底静默,不再处理任何任务。唯一恢复手段是手动重启 worker Pod。

报错原文

2026-05-27T10:05:15.059Z error  DataRetentionProcessingQueue error All sentinels are unreachable. Retrying from scratch after 10ms. Last error: Connection is closed.
Error: All sentinels are unreachable. Retrying from scratch after 10ms. Last error: Connection is closed.
    at connectToNext (.../ioredis/built/connectors/SentinelConnector/index.js:64:31)
    at connectToNext (.../ioredis/built/connectors/SentinelConnector/index.js:117:24)

2026-05-27T10:05:15.059Z error  Redis sentinel error All sentinels are unreachable. ...
2026-05-27T10:05:15.059Z error  EvalExecutionQueue shard 0 error All sentinels are unreachable. ...

在 worker 进入“假死”状态后,日志中持续出现类似错误(但队列不再主动输出错误,仅残留上述重复报错)。

原因分析

根本原因是 ioredis 的 Sentinel 连接器在尝试所有 Sentinel 节点均不可达时会执行 sentinelRetryStrategy 回调。Langfuse 在 createRedisSentinelInstance 中从未配置该选项,因此 ioredis 使用默认策略,该策略在有限次重试后放弃重连,使得 Redis 实例永久处于“断开状态”。即使 Sentinel 集群恢复正常,worker 也不会自动重新连接。而队列任务使用的 redisQueueRetryOptions 仅设置了针对 READONLY 错误的 retryStrategyreconnectOnError,对 Sentinel 连接失效无效。

环境排查

  • 确认 Langfuse 版本(Helm chart 版本,例如 1.5.26)
  • 确认 ioredis 版本(Issue 中为 5.10.1)
  • 确认 Redis Sentinel 部署方式(Spotahome Redis Operator / RedisFailover CRD 或其他)
  • 确认 REDIS_SENTINEL_ENABLED=true 开启
  • 确认 REDIS_SENTINEL_NODES 配置正确(单服务端点或节点列表)
  • 检查 packages/shared/src/server/redis/redis.tscreateRedisSentinelInstance 函数是否缺少 sentinelRetryStrategy 配置

解决步骤

  1. 打开文件 packages/shared/src/server/redis/redis.ts,定位到 createRedisSentinelInstance 函数。
  2. ...additionalOptions 之前(即 const instance = new Redis({ ... }); 的对象中),在 sentinels、name 等参数之后,添加:
    sentinelRetryStrategy: (retries: number) => Math.min(retries * 200, 10_000),

    注意不要丢到 ...additionalOptions 之后,否则无法保证优先级(additionalOptions 中的同名配置会覆盖此项)。

  3. 重新构建并部署 worker(例如使用 Helm 升级或重建 Docker 镜像)。
  4. 触发一次 Redis 数据节点重启(如 kubectl delete pod <redis-data-pod>),观察 worker 是否能在几秒后自动恢复,不再出现“All sentinels are unreachable”错误。

可优先尝试:若无法立即修改代码,也可尝试在环境变量或额外配置中通过 additionalOptions 传入 sentinelRetryStrategy,但需验证是否被覆盖。

验证方法

部署修复后的 worker,人为触发 Redis 数据节点故障转移(如重启一个数据 Pod),观察 worker 日志:

  • 故障瞬间可能出现少量 All sentinels are unreachable 错误,但随后应自行消失。
  • 等待 10–30 秒后,确认所有队列(如 DataRetentionProcessingQueue)恢复正常工作,新任务能被正常消费。
  • 不再需要手动重启 worker Pod。

参考来源

langfuse/langfuse #13880

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 15685

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注