Search code examples
ruby-on-railsrubyrails-activejob

Enqueue ActiveJob task with destroyed object


In controller's action, I destroy a record and then pass it as an argument to ActiveJob.

def destroy
  post = Post.find params[:id]
  post.destroy
  CleanUpJob.perform_later post
end

And in my job's perform, I need to do some cleanup actions with that destroyed record.

def perform(post)
  log_destroyed_content post.id, post.title
end

When I call it as delayed with .perform_later - it does not execute at all. But when I change to .perform_now - it works as expected. This Job needs to deal with both destroyed and persisted records.

I'm using lates Rails, development env with default async activejob adapter.


Solution

  • When you call .perform_later with an ActiveRecord object, ActiveJob will try to serialize it into a global id

    You are deleting your record from the database, which means your job won't find it when it runs.

    You could pass a hash with all the attributes: CleanUpJob.perform_later(post.attributes)

    Alternatively, you could flag your model for deletion and call destroy in the job when you are actually done with the record. Think of it as soft-deleting the record first:

    # in the controller
    def destroy
      post = Post.find params[:id]
      post.update(state: :archived) # or whatever makes more sense for your application
      CleanUpJob.perform_later(post.id, post.title)
    end
    
    # in the job
    def perform(post_id, post_title)
      log_destroyed_content(post_id, post_title)
      post.destroy
    end
    

    You will want to make sure to exclude 'soft-deleted' records from your user-facing queries.