bug(worker): worker silently stops consuming all queues after Redis lock loss (FLUSHALL / eviction) — stays ‘healthy’, needs manual restart

当 Langfuse Worker 因 Redis 锁丢失(FLUSHALL、volatile-lru 驱逐或哨兵故障转移)后,所有 BullMQ 队列的消费者线程永久挂起,进程仍保持健康状态,外部监控无法察觉。优先排查是否出现锁丢失导致的 Missing lock for job 错误,并通过外部

快速结论:当 Langfuse Worker 因 Redis 锁丢失(FLUSHALL、volatile-lru 驱逐或哨兵故障转移)后,所有 BullMQ 队列的消费者线程永久挂起,进程仍保持健康状态,外部监控无法察觉。优先排查是否出现锁丢失导致的 Missing lock for job 错误,并通过外部方式监控队列消费停滞。

适用环境:Langfuse v3 自托管(worker 镜像 langfuse/langfuse-worker:3),ECS Fargate,Redis ElastiCache Valkey 7.2 with TLS,单 worker 部署;触发条件为 maxmemory-policy=volatile-lru 驱逐锁键或手动执行 FLUSHALL

最快修复方案:暂无确认的一步修复方案。官方正在通过 PR #15478 添加 Sentinel 断开保护,但未覆盖锁丢失场景。

注意事项:以下方法为推测性缓解措施,需结合环境验证:设置 Redis 驱逐策略为 noeviction 可避免锁键被驱逐;添加外部存活探针监控 BullMQ 队列深度(如 CloudWatch 指标),当队列深度持续增长且 Worker 状态健康时触发容器重启。

问题场景

用户运行 Langfuse 自托管 Worker 时,因 Redis 锁键被意外清除(例如内存满触发 volatile-lru 驱逐、运维执行 FLUSHALL、哨兵故障转移),Worker 进程不崩溃、不退出,但停止消费所有队列中的任务,日志完全停滞,同时进程仍保持健康状态(ECS 报告任务 RUNNING,CloudWatch 指标持续发布),导致数据积压直至 Redis OOM。

报错原文

bug(worker): worker silently stops consuming all queues after Redis lock loss (FLUSHALL / eviction) — stays 'healthy', needs manual restart
Missing lock for job <N>. moveToDelayed
Missing lock for job <N>. moveToFailed

原因分析

BullMQ Worker 使用 Redis 锁(Lock keys)保证同一时刻只有一个 Worker 消费任务。当这些锁键被驱逐(volatile-lru)或被 FLUSHALL 清除后,Worker 对队列内所有正在处理的任务轮询失败,持续抛出 Missing lock for job 错误。Worker 内部消费者循环未设计恢复机制,在出现批量锁丢失后进入永久空闲状态,不再尝试消费任何新任务。由于健康检查仅验证 Postgres/Redis 连接性,不检查队列消费是否活跃,导致进程“健康”但实质瘫痪。

环境排查

  • 确认 Langfuse Worker 镜像版本是否为 langfuse/langfuse-worker:3(可能波及更早期版本,需自行验证)。
  • 确认 Redis 最大内存策略(maxmemory-policy)。若为 volatile-lruallkeys-lru,锁键可能被驱逐。
  • 检查 Redis 日志或 AOF 中是否出现 FLUSHALL 操作。
  • 确认 Worker 日志中是否存在大量 Missing lock for job 且之后日志完全停止。
  • 确认集群部署中是否使用哨兵模式(Sentinel),该模式也可能触发锁丢失。

解决步骤

  1. 立即恢复:手动重启 Worker 容器(若使用 ECS Fargate,执行任务重新启动;若使用 k8s,删除 Pod 触发重建)。
  2. 防止因内存驱逐触发:将 Redis 最大内存策略改为 noeviction,并保证内存足够(BullMQ 官方推荐)。注意:仅消除驱逐导致锁丢失,不能防御 FLUSHALL 或故障转移。
  3. 添加外部队列深度告警(可优先尝试):监控各队列的等待任务数(例如利用 Langfuse 已发布的 CloudWatch langfuse.queue.* 指标)。若队列深度持续增长,且 Worker 健康检查通过,且无新 Worker 日志产生,则应立即自动重启 Worker。
  4. 考虑部署多 Worker 实例:增加 Worker 数量可以减少单点故障影响,但需注意锁丢失仍会清空所有 Worker 上的消费者线程(当前 Bug 不受实例数影响)。此项仅作为容错补充。
  5. 跟踪上游修复:关注 BullMQ #4378 以及 Langfuse 的 PR #15478Issue #13880,这些可能在未来版本中解决根本问题。

验证方法

在修复后,观察 Worker 日志是否在锁丢失后恢复消费(例如出现 Processing job、<

参考来源

langfuse/langfuse #15509

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 15680

发表回复

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