【问题标题】:Can't pass CollectionProxy object to ActiveJob无法将 CollectionProxy 对象传递给 ActiveJob
【发布时间】:2015-11-16 05:15:30
【问题描述】:

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

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

在我的 ActiveJob 类中,我执行任务。

// 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 对象。我该如何解决/解决这个问题?

PS:我考虑过

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

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

【问题讨论】:

    标签: ruby-on-rails delayed-job rails-activejob


    【解决方案1】:

    我认为最简单的方法是将消息 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
    

    【讨论】:

    • 这是一个非常不错且性能友好的解决方案。谢谢!
    • @BurakÖzmen 很高兴,编码快乐!
    • messages = Message.where(id: message_ids)
    【解决方案2】:

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

    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
    

    未测试,希望没问题

    【讨论】:

      【解决方案3】:

      对于较大的数据集,传递 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
      

      【讨论】:

      • 这很聪明(如“我希望我能想到它”)。我认为的风险是,如果 SQL 返回的 ID 发生变化,那么如果 SQL 代表您希望选择对象的方式,那么这实际上是一件好事。很棒的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2011-09-27
      • 2014-12-01
      • 1970-01-01
      相关资源
      最近更新 更多