Using Sidekiq for Active Job and getting ActiveJob::DeserializationError

别说谁变了你拦得住时间么 提交于 2019-12-10 14:21:42

问题


I'm trying to use Sidekiq to run the below job.

The job performs fine when not queued (perform_now) but fails when called as (perform_later), which uses Sidekiq.

AddEmployeesToRoomJob.perform_now room  ## works fine
AddEmployeesToRoomJob.perform_later room  ## breaks in Sidekiq

Error:

AddEmployeesToRoomJob JID-da24b13f405b1ece1212bbd5 INFO: fail: 0.003     sec
2016-08-20T14:57:16.645Z 19456 TID-owmym5fbk WARN:     {"class":"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper","wrapped"    :"AddEmployeesToRoomJob","queue":"default","args":    [{"job_class":"AddEmployeesToRoomJob","job_id":"0ba5bd30-e281-49a7-a93f-    6e50445183ac","queue_name":"default","priority":null,"arguments":    [{"_aj_globalid":"gid://dragonfly/Room/1"}],"locale":"en"}],"retry":true,    "jid":"da24b13f405b1ece1212bbd5","created_at":1471704675.739077,"enqueued    _at":1471705036.6406531,"error_message":"Error while trying to     deserialize arguments: Couldn't find Room with     'id'=1","error_class":"ActiveJob::DeserializationError","failed_at":14717    04675.946183,"retry_count":4,"retried_at":1471705036.644416}
2016-08-20T14:57:16.645Z 19456 TID-owmym5fbk WARN:     ActiveJob::DeserializationError: Error while trying to deserialize     arguments: Couldn't find Room with 'id'=1
2016-08-20T14:57:16.645Z 19456 TID-owmym5fbk WARN:     /Users/tamlyn/.rvm/gems/ruby-2.2.3/gems/activerecord-    5.0.0.1/lib/active_record/relation/finder_methods.rb:357:in     `raise_record_not_found_exception!'

My Job class AddEmployeesToRoomJob < ApplicationJob queue_as :default

  def perform(room)
    employees = Employee.all
    if employees.length > 0
      employees.each do |employee|
        UserRoom.create(user: employee, room: room)
      end
    end
  end
end

My Thoughts I don't understand why it can't find the room which I'm passing into the perform method. It's as though it somehow loses that variable in the queueifying / JSONifying of the job?

The Sidekiq docs say

"Unfortunately this means that if the [Room] record is deleted after the job is enqueued but before the perform method is called, exception handling is different."

They suggest a workaround but I don't see how that would help me:

rescue_from ActiveJob::DeserializationError do |exception|
    # handle a deleted user record
end

Thanks in advance for any help!


回答1:


I don't think it is a good idea to pass the Room object into a Sidekiq worker. I've always passed the primary key for a database object and then re-queried. Try this.

AddEmployeesToRoomJob.perform_later room.id

def perform(room_id)
  room = Room.find(room_id)
  employees = Employee.all
    if employees.length > 0
      employees.each do |employee|
        UserRoom.create(user: employee, room: room)
      end
    end
  end
end



回答2:


Stumbled upon this myself and found the method discard_on very useful. Mostly it is very unnecessary to do jobs on deleted or never created records.

Example:

class ApplicationJob < ActiveJob::Base
  discard_on ActiveJob::DeserializationError do |job, error|
    Rails.logger.error("Skipping job because of ActiveJob::DeserializationError (#{error.message})")
  end
end



回答3:


You can pass a model to a job if you can ensure that this model has been commited. It's a classic error to enqueue jobs before commit the current transaction.

You can find more information here: https://github.com/mperham/sidekiq/wiki/FAQ#why-am-i-seeing-a-lot-of-cant-find-modelname-with-id12345-errors-with-sidekiq

In our project we coded a model concern to be able to add dynamic after commit code blocks.

module AfterCommitOnce
  extend ActiveSupport::Concern

  included do
    after_commit :execute_after_commit_handlers
  end

  def after_commit_once(&block)
    @after_commits = @after_commits || []
    @after_commits << block
  end

  private

  def execute_after_commit_handlers
    @after_commits = @after_commits || []
    @after_commits.each do |ac|
      ac.call
    end
    @after_commits = []
  end
end

Adding this concern to you Employee model to you can specify which job you want to execute after commit all changes to you database with:

...
employee.after_commit_once do
   AddEmployeesToRoomJob.perform_later
end
employee.save!



回答4:


It's not advisable to send Ruby object as parameter to sidekick worker. You can send Id as parameter and initialise the object inside perform method. If want to send object then you can dump Ruby object to any other format like Json/ Binary / yml as follows.

object.to_json 

Or

Marshal.dump(object)

And before using the objects inside perform method you can de-serialize the object to Ruby object as follows.

JSON.parse(serialized_object)

Or

Marshal.load(serialized_object)

These are solutions for your problem, but not ideal solution.



来源:https://stackoverflow.com/questions/39055708/using-sidekiq-for-active-job-and-getting-activejobdeserializationerror

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!