Search code examples
ruby-on-railsrubysidekiq

Rails 6 grab results from Sidekiq Job - is it possible?


I've got Sidekiq job (SyncProductsWorker) which fires a class Imports::SynchronizeProducts responsible for a few external API calls.

module Imports
  class SyncProductsWorker
    include Sidekiq::Worker
    sidekiq_options queue: 'imports_sync'

    def perform(list)
      ::Imports::SynchronizeProducts.new(list).call
    end
  end
end

The Imports::SynchronizeProducts class gives an array of monads results with some comments e.g.

=> [Failure("999999 Product code is not valid"), Failure(" 8888889 Product code is not valid")]

I would like to catch these results to display them on FE. Is it possible to do so? If I do something like:

def perform(list)
  response = ::Imports::SynchronizeProducts.new(list).call
  response
end

And then inside of the controller:

def create
  response = ::Imports::SyncProductsWorker.perform_async(params[:product_codes])

  render json: { result: response, head: :ok }
end

I'll have some number as a result

=> "df3615e8efc56f8a062ba1c2"

Solution

  • I don't believe what you want is possible.

    https://github.com/mperham/sidekiq/issues/3532

    The return value will be GC'd like any other unused data in a Ruby process. Jobs do not have a "result" in Sidekiq and Sidekiq does nothing with the value.

    You would need some sort of model instead that keeps track of your background tasks. This is off the cuff but should give you an idea.

    EG

    # @attr result [Array]
    # @attr status [String] Values of 'Pending', 'Error', 'Complete', etc..
    class BackgroundTask < ActiveRecord
      attr_accessor :product_codes
      after_create :enqueue
      
      def enqueue
        ::Imports::SyncProductsWorker.perform_async(product_codes, self.id)
      end
    end
    
    def perform(list, id)
      response = ::Imports::SynchronizeProducts.new(list).call
      if (response.has_errors?)
        BackgroundTask.find(id).update(status: 'Error', result: response)
      else
        BackgroundTask.find(id).update(status: 'Complete', result: response)
      end
    end
    

    Then just use the BackgroundTask model for your frontend display.