mcpField
R
rails-jobs

rails-jobs

github.com/marckohlbrugge开发73055 次安装20 周前更新
代码仓库

关于 rails-jobs

rails-jobs 是用于设计和审查 Rails 后台任务的技能,涵盖安全重试、队列安排、周期任务、错误处理与测试规范。

主要功能

  1. 01浅层且幂等的任务设计
  2. 02队列与负载建议
  3. 03重试和错误处理模式
  4. 04周期任务与批量处理模式
  5. 05异步行为测试建议

适用场景

  1. 01审查任务的重试安全性
  2. 02设计队列和租户上下文处理
  3. 03规划长时间运行或周期性工作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marckohlbrugge/37signals-skills --skill rails-jobs

安装后在 Claude Code 中输入/rails-jobs确认已加载。

详细介绍

Rails 任务

用于后台任务设计和评审。模式来自 Campfire(Resque、4 个轻量任务)和 Fizzy(Solid Queue、多队列)。

默认设置

  • 保持任务精简;任务主体只写一行,调用模型方法:def perform(card) = card.notify_recipients。
  • 让任务具备幂等性并可安全重试(例如,在领域操作中捕获 RecordNotUnique)。
  • 遇到真正的错误时明确失败;避免静默捕获错误的模式。
  • 在应用默认设置中设置 ActiveJob::Base.enqueue_after_transaction_commit = true——从根本上解决任务先于数据提交的竞态问题。
  • 新应用优先使用 Solid Queue(基于数据库),而非基于 Redis 的队列;在小型部署中与 Puma 同机运行(SOLID_QUEUE_IN_PUMA)。

命名约定

  • _later 用于入队,普通名称用于执行工作,需要显式同步版本时使用 _now:
def notify_recipients          # the work — public API the job calls
def notify_recipients_later   # enqueues NotifyRecipientsJob (often private, called from callbacks)
  • 将入队时的上下文捕获为关键字参数(Mention::CreateJob.perform_later(self, mentioner: Current.user))——不要依赖执行任务时的 Current。
  • 在多租户应用中,将租户上下文序列化到每个任务中:为 ApplicationJob 添加扩展,在入队时捕获 Current.account(作为 GlobalID),并在 perform_now 中使用 Current.with_account 包装执行过程。

良好模式

  • 使用精简的任务负载;传递记录(GlobalID)或 ID,绝不传递庞大的对象图。
  • 按重要性拆分队列(default、backend、webhooks),并在队列配置中明确优先级顺序。
  • 将编排任务与繁重的处理任务分开。
  • 将定时任务错开安排在不规则的分钟数(12、27、50),避免负载峰值同步出现。
  • 批量入队:due.in_batches { |batch| ActiveJob.perform_all_later(batch.map { DeliverJob.new(it) }) }。
  • 使用 ActiveJob::Continuable 实现崩溃安全的长时间迭代:step :dispatch + find_each(start: step.cursor) + step.advance!——对于扇出任务(webhook、广播、回填)至关重要。
  • 使用 limits_concurrency to: 1, key: ->(owner) { owner } 按所有者串行处理工作(Solid Queue)。
  • 对于高扇出 HTTP 请求(Web Push),采用两级异步处理:先用一个任务处理领域事件,再由进程内线程池处理 HTTP 调用。将所有 AR 数据解析完毕后再提交到线程池;遇到 RejectedExecutionError 时丢弃请求,以实现背压。
  • 对于 after_destroy_commit 异步工作,在 before_destroy 中快照所需关联数据——任务运行时父级记录可能已被删除。

定时与维护

  • 定时任务调用普通模型类方法(command: "MagicLink.cleanup"),而非专用任务类。
  • 清理过期数据时,对作用域使用 delete_all(stale.delete_all)——跳过过期记录的回调。
  • 安排清理已完成的队列任务、过期令牌和旧投递记录。
  • 优先采用使用时重置,而非定时重置:在领域方法中检查并重置(spend 调用 reset_if_due),而不是安排定时任务。
  • 在 beta/staging 环境中,将定时任务精简到必要项目。

错误处理策略

  • 使用 retry_on ..., wait: :polynomially_longer 重试暂时性故障(超时、DNS、Net::SMTPServerBusy)。
  • 不要重试永久性故障:捕获错误,按错误类或消息分类,以 :info 严重级别记录日志(这是预期情况——地址无效、邮箱已满),然后继续处理。将队列资源留给能够成功的工作。
  • 区分“目标端失败”(记录结果并完成任务)和“我们的代码引发异常”(标记为错误,并重新引发以便重试)——参见 rails-webhooks。
  • 将错误分类封装为 concern,并引入框架任务(ActionMailer::MailDeliveryJob.include SmtpDeliveryErrorHandling)。

测试

  • 不要对简单的任务类进行单元测试;在模型测试中使用 perform_enqueued_jobs(only: Mention::CreateJob) { ... } 和 assert_enqueued_with(job: ...) 测试异步行为。
  • 验证可观察到的结果(已创建提及、已发送邮件),而非任务内部实现。

危险信号

  • 作业包含复杂的分支逻辑和直接嵌入的业务规则。
  • 未设置保护措施的非幂等副作用。
  • 重试永久无效的输入。
  • 在必需记录提交前就将作业加入队列。
  • 在 perform 中读取 Current.user/Current.account,却未在入队时对其进行序列化。
  • 在循环中逐项调用 perform_later,而使用 perform_all_later 或 Continuable 迭代会更合适。
  • 使用 Cron 风格的重置作业,而按需重置逻辑更简单、更安全。
  • 后台作业触发多余的 Turbo 广播(请用 suppressing_turbo_broadcasts 包裹)。

评论

支持 Markdown