You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sidekiq Worker批量处理3万+记录未完成且无报错问题排查

Sidekiq批量处理Worker异常排查与优化问题

问题背景

我遇到一个Sidekiq Worker批量处理大量记录的问题:Worker启动后无法正常完成流程,始终不会输出“finished”日志,且无明确报错,但处理未达预期。

具体现象

  • 开始处理约30000条HeroProfile记录;
  • 每次执行仅处理约800条记录;
  • 从未出现“HeroIntake::ReproveWorker finished”日志;
  • 日志及Honeybadger中均无明确报错;
  • 可看到处理记录的日志,但流程始终不结束。

相关代码

# frozen_string_literal: true

module HeroIntake
  class ReproveWorker
    include Sidekiq::Worker

    sidekiq_options queue: 'low', retry: 1

    def perform
      Rails.logger.info "HeroIntake::ReproveWorker started for: #{HeroProfile.analyze.count} at: #{Time.current}"

      HeroProfile.analyze.where(analyzed_at: nil).find_each(batch_size: 10) do |hero_profile|
        begin
          Rails.logger.info "HeroIntake::ReproveWorker Processing hero profile with ID: #{hero_profile.inspect}"
          Hero::Profile::Reprove.call(hero_profile)
          hero_profile.update_column(:analyzed_at, Time.current)
        rescue StandardError => e
          Honeybadger.notify(error_class: e, error_message: 'HeroIntake::ReproveWorker Error while processing hero rejection')
          Rails.logger.error "HeroIntake::ReproveWorker Error processing hero profile: #{e.message}  ||  #{hero_profile.inspect}"
        end
      end

      Rails.logger.info "HeroIntake::ReproveWorker finished at: #{Time.current}"
    rescue StandardError => e
      Honeybadger.notify(error_class: e, error_message: 'HeroIntake::ReproveWorker Error while processing the worker')
      Rails.logger.error "HeroIntake::ReproveWorker Error: #{e.message}"
    end
  end
end

已观察到的行为

  • Worker启动后无法完成,无结束日志;
  • 每次仅处理约800条,远少于总记录数;
  • 无明确报错日志。

已尝试的操作

  • 增加日志细节,未发现明显错误;
  • 未触发超时或其他显式异常;
  • 使用find_each(batch_size:10)分批处理,问题仍存在;
  • 修改Sidekiq retry设置为0,无效;
  • 检查时区配置,无异常。

待解答问题

  1. 无明确报错情况下,Worker无法完成处理的原因是什么?
  2. 使用find_each处理大数据集是否存在性能问题?
  3. 是否需要优化Sidekiq配置以适配批量处理?
  4. 如何确保Worker不受中断或阻塞,完成全部处理?

环境信息

  • Ruby版本:2.3.8
  • Rails版本:4.2.11
  • 环境:生产环境
  • Sidekiq相关gem:
    • gem 'sidekiq'
    • gem 'sidekiq-failures'
    • gem 'sidekiq-scheduler', '~> 3.0', '>= 3.0.1'

问题解答

1. 无明确报错时Worker无法完成的原因

大概率是线程被阻塞或静默挂起,常见场景包括:

  • Hero::Profile::Reprove.call内部存在未被捕获的异常(比如SystemStackError、SignalException这类不继承自StandardError的错误,当前代码仅捕获StandardError);
  • 数据库连接耗尽或死锁:批量处理中频繁的DB操作如果未正确释放连接,可能导致线程挂起等待连接;
  • Sidekiq的静默终止:比如Worker进程收到SIGTERM信号(如部署重启),会停止处理新任务但不会抛出异常,也不会执行后续代码;
  • 内存泄漏导致进程被OOM Killer杀死:处理大量记录时内存占用过高,系统直接终止进程,不会留下应用层日志。

2. find_each的性能问题

find_each本身是Rails为大数据集设计的分批处理方法,默认按ID顺序分批查询,避免一次性加载所有记录到内存,本身性能没问题。但当前设置的batch_size:10过小,会导致频繁的DB查询(30000条需要3000次查询),反而增加DB负载,拖慢处理速度,甚至可能因为连接占用时间过长引发问题。

3. Sidekiq配置优化建议

需要针对批量处理调整:

  • 调整超时设置:Sidekiq默认超时是25秒,如果Worker处理时间超过这个值,会被强制终止。可以在sidekiq_options中增加timeout: 3600(根据实际处理时间调整);
  • 调整队列优先级:low队列可能被其他任务抢占资源,或Sidekiq对低优先级队列的线程分配较少,可临时切换到更高优先级队列测试;
  • 优化重试策略:当前retry:1可能导致失败任务重复执行,占用资源,批量任务建议失败后手动处理而非自动重试;
  • 配置连接池:确保Sidekiq的数据库连接池足够,避免连接耗尽。在config/sidekiq.yml中设置concurrency和database_pool匹配。

4. 确保Worker完成全部处理的方案

  • 捕获所有异常:临时将rescue StandardError改成rescue Exception(不推荐长期使用,仅用于排查),或明确捕获SignalException、SystemStackError等特殊异常;
  • 拆分任务:不要在单个Worker中处理30000条记录,改成分阶段处理:先用一个Worker把任务拆分成多个子任务(比如每1000条一个子Worker),手动分发或使用Sidekiq::Batch;
  • 增加进度监控:在find_each循环中增加进度日志,比如每处理100条输出一次进度,同时记录当前处理的ID范围,方便排查中断点;
  • 优化DB操作:把batch_size调整为100-200,减少DB查询次数;用批量更新代替单条update_column,比如收集已处理的ID,每批量后执行一次HeroProfile.where(id: processed_ids).update_all(analyzed_at: Time.current);
  • 监控进程状态:用top、htop观察Worker进程的内存和CPU占用,排查是否OOM;查看系统日志(比如/var/log/syslog)是否有进程被杀死的记录;
  • 添加心跳机制:在循环中定期调用Sidekiq::Worker#heartbeat(版本支持的情况下),避免被Sidekiq判定为无响应。

内容的提问来源于stack exchange,提问作者Philipe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:35:06