无法将 CollectionProxy 对象传递给 ActiveJob

Can't pass CollectionProxy object to ActiveJob

我需要在后台标记一组消息(我正在使用delayed_job gem),因为它在前台需要一些时间。所以我创建了一个 ActiveJob class MarkMessagesAsReadJob,并传递给它 usermessages 变量以标记所有 messages 已读user.

// passing the values in the controller
@messages     = @conversation.messages
MarkMessagesAsReadJob.perform_later(current_user, @messages) 

并在我的 ActiveJob class 中执行任务。

// MarkMessagesAsReadJob.rb
class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default

  def perform(user, messages)
    messages.mark_as_read! :all, :for => user
  end
end

但是,当我尝试执行任务时,出现错误 ActiveJob::SerializationError(不支持的参数类型:ActiveRecord::Associations::CollectionProxy):

我读到我们只能将支持的类型传递给 ActiveJob,我认为它不能序列化 CollectionProxy 对象。我怎么能workaround/fix这个?

PS: 我考虑过

@messages.map { |message| MarkMessagesAsReadJob.perform_later(current_user, message) } 

但是我认为一个一个标记它们非常昂贵。

我认为简单的方法是将消息 ID 传递给 perform_later() 方法,例如:

在控制器中:

@messages = @conversation.messages
message_ids = @messages.pluck(:id)
MarkMessagesAsReadJob.perform_later(current_user, message_ids) 

并在ActiveJob中使用它:

def perform(user, message_ids)
  messages = Message.where(id: message_ids)
  messages.mark_as_read! :all, :for => user
end

因为作业会在稍后执行,我认为我们应该将 id 作为参数而不是集合作为参数传递

ActiveJob需要序列化参数,不支持参数类型会抛出SerializationError http://api.rubyonrails.org/classes/ActiveJob/SerializationError.html

例如:

@message_ids = @conversation.messages.pluck(:id)
# use string if array is not supported
# @message_ids = @message_ids.join(", ")
MarkMessagesAsReadJob.perform_later(current_user, @message_ids) 

然后再次查询那些消息并标记它

class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default
  def perform(user, message_ids)
    # use string if array is not supported
    # message_ids = message_ids.split(",").map(&:to_i)
    messages = Message.where(id: message_ids) #change this to something else
    messages.mark_as_read! :all, :for => user
  end
end

未测试,希望没问题

对于较大的数据集,传递 message_ids 是不切实际的。相反,为消息传递 SQL:

@messages = @conversation.messages
MarkMessagesAsReadJob.perform_later(current_user, @messages.to_sql) 

然后从作业中查询它们:

class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default
  def perform(user, messages_sql)
    messages = Message.find_by_sql(messages_sql) 
    messages.mark_as_read! :all, :for => user
  end
end