我正在使用排队系统 (Sidekiq) 并希望转移到 ActiveJob 以获得性能优势,因为每次将 ActiveRecord 对象传递给工作人员时都不必查询数据库。我想询问并确认,因为我不是 100% 确定,但我的理解是,当 ActiveJob 使用 GlobalID 传递所有在内存中完成的 ActiveRecord 对象并且没有完成对数据库的单独查询时,对吗?
我正在为学生构建电子邮件提醒功能。他们应该按照讲师指定的时间间隔收到一封电子邮件,表明他们取得了多少进步。
我的方法是使用 ActiveJob 来安排发送电子邮件的作业,然后当执行该作业时,我使用回调after_perform来安排下一个作业(摘自这篇文章)。看起来是这样的:
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
Run Code Online (Sandbox Code Playgroud)
最初的一组提醒将由 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 …Run Code Online (Sandbox Code Playgroud) 很高兴看到Rails 4.2附带Active Job作为后台作业的通用界面.但是我找不到如何在文档中启动一个worker.似乎该文档仍然不成熟(例如,正确版本的Sneakers仅在Rails中提及Gemfile),因此我不确定"正在运行的工作者"部分是否处于"活动作业"中或者仅在文档中未提及.
因此,对于Active Job,我是否仍需要手动启动作业观察程序线程,就像sidekiq在我的情况下一样rake sneakers:run?如果是这样,我应该在哪里放置这些命令以便rails server在开发环境中自动运行这些并行任务?
我想限制的数量retries当作业使用失败ActiveJob与Sidekiq作为适配器.
使用Sidekiq,我可以这样做:
class LessRetryableWorker
include Sidekiq::Worker
sidekiq_options :retry => 5
def perform(...)
end
end
Run Code Online (Sandbox Code Playgroud)
Sidekiq配置不提供全局retry配置.每个工人负责设置retry选项.所以我想我必须在ActiveJob端实现它才能正确完成它.
有没有办法将 ActiveJob 队列适配器设置为内联特定后台作业?
就我而言,我想在测试中运行一些后台作业来构建集成测试。我不关心作业内部细节,因为我的目的只是运行后台作业并断言结果。但是,这些集成测试并未涵盖所有后台作业,因此我不想全局设置队列适配器。
所以我对 sidekiq 和 activejob 很陌生,我知道我不需要后台工作一个在创建帐户时发送的邮件,但是我真的很想了解这一切是如何工作的,最好的学习方法就是这样做。 ..
我设置了以下 Job、Mailer 和 Controller 操作,但是当我创建一个新帐户时它失败并出现错误:(为了简洁,我已经发布了整个堆栈)
e_owner: processed outbound mail in 1.3ms
[ActiveJob] [WelcomeEmailJob] [18ba16e9-e3d9-4dd1-b6f4-0a435449c68c] Performed WelcomeEmailJob from Sidekiq(default) in 26.39ms
Completed 404 Not Found in 25051ms (ActiveRecord: 91.9ms)
ActiveRecord::RecordNotFound - Couldn't find Account with 'id'=:
activerecord (5.0.0.1) lib/active_record/core.rb:173:in `find'
app/mailers/welcome_notification_mailer.rb:4:in `welcome_owner'
actionpack (5.0.0.1) lib/abstract_controller/base.rb:188:in `process_action'
actionpack (5.0.0.1) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:97:in `__run_callbacks__'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:750:in `_run_process_action_callbacks'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:90:in `run_callbacks'
actionpack (5.0.0.1) lib/abstract_controller/callbacks.rb:19:in `process_action'
actionpack (5.0.0.1) lib/abstract_controller/base.rb:126:in `process'
actionmailer (5.0.0.1) lib/action_mailer/rescuable.rb:23:in …Run Code Online (Sandbox Code Playgroud) 我有一个create动作,如果记录成功保存,则调用ActiveJob。
def create
@object = Object.new(importer_params)
respond_to do |format|
if @object.save
MyJob.perform_later( @object.id )
format.html { redirect_to @object, notice: t('.notice') }
else
format.html { render :new }
end
end
end
Run Code Online (Sandbox Code Playgroud)
我想测试控制器规范中正确调用了Job。
describe "POST #create" do
it {
expect {
post :create, { object: valid_attributes }
}.to change(Object, :count).by(1)
}
it {
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
}
end
Run Code Online (Sandbox Code Playgroud)
但是我明白了
Failure/Error:
expect {
post :create, { object: valid_attributes }
}.to have_enqueued_job(MyJob)
expected to enqueue exactly 1 …Run Code Online (Sandbox Code Playgroud) 我试图在我的User对象被销毁之前挂钩邮件程序.
我正在使用Sidekiq发送电子邮件,但是当用户被销毁时会出现此错误.
ActiveJob::DeserializationError: Error while trying to deserialize arguments: Couldn't find User with id [id]
在我的模型中,我有这个:
class User < ApplicationRecord
before_destroy :goodbye_user
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable
private
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
def goodbye_user
GoodbyeUser.goodbye_user(self).deliver_later
end
end
Run Code Online (Sandbox Code Playgroud)
告别用户邮件永远不会发送.
任何的想法?
我们可以检查enqueued_jobs.length,前提是电子邮件是唯一可能的后台作业类型。
it 'sends exactly one, particular email' do
expect { post :create }.to(
# First, we can check about the particular email we care about.
have_enqueued_mail(MyMailer, :my_particular_email)
)
# But we also have to check that no other email was sent.
expect(ActiveJob::Base.queue_adapter.enqueued_jobs.length).to eq(1)
end
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来断言:
MyMailer.my_particular_email已排队,我有一个包含 2 个作业的 Rails 应用程序(ImportCsvJob 和 ProcessCsvJob)。这样我就可以在应用程序中明显地提示队列中仍然有作业,我有这个辅助方法(在 application_helper.rb 内):
module ApplicationHelper
def queued_job_count
Sidekiq::Queue.new.size
end
end
Run Code Online (Sandbox Code Playgroud)
然后我在索引控制器上使用它,然后将其传递到视图进行处理并在应用程序中给出视觉提示。
def index
@still_have_jobs = !queued_job_count.zero?
end
Run Code Online (Sandbox Code Playgroud)
但是,当我仍然有 1 个后台作业 (ImportCsvJob) 时,此方法有效,但当我添加 (ProcessCsvJob) 时,它不再有效。
导入_csv_job.rb
require 'open-uri'
class ImportCsvJob < ActiveJob::Base
queue_as :default
def perform(csv_record)
csv_record[:object_changes] = ApplicationController.helpers.generate_hash(csv_record[:object_changes])
ObjectRecord.create(csv_record)
end
end
Run Code Online (Sandbox Code Playgroud)
process_csv_job.rb
class ProcessCsvJob < ActiveJob::Base
queue_as :default
def perform(csv_path)
csv_file = open(csv_path,'rb:UTF-8')
options = {
row_sep: :auto, col_sep: ",",
user_provided_headers: [:object_id, :object_type, :timestamp, :object_changes],
remove_empty_values: true,
headers_in_file: true
}
SmarterCSV.process(csv_file, options) do |array| …Run Code Online (Sandbox Code Playgroud) 我有一个执行状态更改的工作,而且我使用它的方式是,只有在经过一定时间后才会更改状态。
在我的工作课上:
class ItemJob < ApplicationJob
queue_as :default
def perform(item)
item.do_this!
end
end
Run Code Online (Sandbox Code Playgroud)
它是从模型调用的:
def create_item_worker
ItemJob.set(wait: 30.seconds).perform_later(self)
end
Run Code Online (Sandbox Code Playgroud)
但是,这是行不通的,因为 ActiveJob 有一个用于不再存在的记录的作业队列。我输入bundle exec sidekiq终端,它吐出像ActiveJob::DeserializationError: Error while trying to deserialize arguments: Couldn't find Item with 'id'=67.
那么我该如何清除呢?Sidekiq::Queue.all.each(&:clear)似乎没有做任何事情。
rails-activejob ×11
ruby-on-rails ×10
ruby ×6
sidekiq ×4
rspec ×3
activerecord ×1
delayed-job ×1
email ×1
rspec-rails ×1