【发布时间】:2018-07-30 11:56:15
【问题描述】:
我正在为学生构建电子邮件提醒功能。他们应该在教师指定的时间间隔内收到一封电子邮件,说明他们取得了多大的进步。
我的方法是使用 ActiveJob 来安排发送电子邮件的作业,然后在执行作业时,我使用 after_perform 回调来安排下一个作业(取自 this post)。看起来是这样的:
class StudentReminderJob < ApplicationJob
queue_as :default
after_perform do |job|
# (get user update frequency preference)
self.class.set(wait: update_frequency).perform_later(job.arguments[0])
end
def perform(user)
# Send email containing student stats information
UserMailer.send_student_reminder_email(user)
end
end
初始提醒集将由 rake 任务启动。
我的问题是:尝试使用perform_enqueued_jobs 测试上述代码时,我似乎遇到了无限循环。这是我的测试:
require 'rails_helper'
describe StudentReminderJob, active_job: true, type: :job do
include ActiveJob::TestHelper
include ActiveSupport::Testing::TimeHelpers
let(:user) { create :user }
subject(:job) { described_class.perform_later(user) }
it 'gets enqueued' do
# Passes
expect { job }.to have_enqueued_job(described_class)
end
it 'receives perform later with user and base URL' do
# Passes (may be duplicatative)
expect(StudentReminderJob).to receive(:perform_later).with(user)
job
end
it 'calls UserMailer with #send_student_reminder_email when performed' do
# Fails - (SystemStackError: stack level too deep)
expect(UserMailer).to receive(:send_student_reminder_email)
perform_enqueued_jobs { job }
end
after do
clear_enqueued_jobs
clear_performed_jobs
end
end
第三个 it 阻止失败并显示 SystemStackError: stack level too deep。有没有办法只执行队列中的一项作业而避免从回调中执行作业?
理想情况下,我想扩大我的测试范围,以确保在给定的日期和时间将新作业加入队列。
【问题讨论】:
-
这不是最好的方法。当你的工人崩溃时会发生什么?你如何发现工人已经崩溃并且你必须重新启动它?我建议使用基于 cron 的调度程序,每 1m\1h\1d 运行一次(取决于您的电子邮件的频率)并存储在最后一封电子邮件的 db 时间戳中,以便您可以计算下一封。
-
@Avdept 感谢您的回复!我考虑使用像 Heroku 调度程序这样的基于 cron 的东西,但是电子邮件频率会有很大差异(想想几千个具有不同通知时间表的教室)。此外,用户的首选项控制器需要能够在收到电子邮件时进行更新,因此我需要能够以编程方式从应用程序控制器设置发送频率。
标签: ruby-on-rails rspec delayed-job rails-activejob