Search code examples
rubyruby-on-rails-4rails-activerecorddelayed-job

Is it possible to preserve tracked changes in model passed to a delayed job?


I wish to perform some expensive operation when my model changes, so I do it asynchronously in a delayed job. Inside this job, I need to be able to tell what exactly the changes were, so I'm passing my changed model as a parameter to the delayed job.

Now, it seems that delayed-job fetches a new (pristine) version of the model from the database when the job starts running, which does not have the tracked changes that I need.

class MyModel < ActiveRecord::Base
    after_save :perform_async_job

    def perform_async_job
        # Here, "self.changes" contains the changes
        Delayed::Job.enqeue(AsyncJobPerformer.new(self), ...)
    end
end

class AsyncJobPerformer
    def initialize(model)
        @model = model
    end

    def perform
        # Here, "@model.changes" is empty
    end
end

I can, of course, pass just the changes hash, and not the model itself, but it would make the delayed job code much more complex, as it needs to use the model instance anyway (so I'll need to fetch it and use both the model and the changes as separate objects).

Is there some way to pass the model to the delayed job while preserving the tracked changes?


Solution

  • Never send model objects to workers, just send id's. You will need to pass the changes hash. The worker won't be able to preserve the changes created by the object instance. So yes, you'll need to pass the changes to your worker.

    class MyModel < ActiveRecord::Base
      after_save :perform_async_job
    
      def perform_async_job
        # Here, "self.changes" contains the changes
        Delayed::Job.enqeue(AsyncJobPerformer.new(self.class.name, self.id, self.changes), ...)
      end
    end
    
    class AsyncJobPerformer
      def initialize(model_name, model_id, params)
        changes = params
        @model = model_name.constantize.find(model_id)
        #handle changes here with @model.update(changes) or a seperate method if need.
      end
    
      def perform
        # Here, "@model.changes" is empty
      end
    end