Rails фоновые задачи с Sidekiq

Rails фоновые задачи с Sidekiq

Допусти нам нужно по требованию выполнить импорт данных из CSV и отобразить результаты. При стандартном подходе в контроллере создадим метод  и выполним обработку файла.

class HomeController < ApplicationController
  require 'csv'
                                            
  def upload
    csv_path = File.join Rails.root, 'db', 'data.csv'                             CSV.foreach(csv_pathm headers: true) do |item|
      Element.create(name: item[0], value: item[1])   
    end
    flash[:notice] = 'All elements created!'                                     redirect_to elements_path       
  end
                                            
  def destroy_all
    Element.destroy_all
  end
end

При таком подходе, если данных будет достаточно много, пользователь получит блокировку браузера на время выполнения и это может длиться достаточно долго. Конечно можно использовать ajax, но сейчас разговор не о нём. На некоторых проектах мы вообще можем получить ошибку Request Time-out.

Теперь решим эту задачу с использованием Sidekiq. Мы не будем использовать основной процесс приложения для обработки, а передадим задачу в фоновому процессу Sidekiq.

Создадим worker

# app/workers/upload_worker.rb

class UploadWorker
  include Sidekiq::Worker
  sidekiq_options retry: false

  require 'csv'

  def perform(csv_path)
    CSV.foreach(csv_path, headers: true) do |item|
      Element.create(name: item[0], value: item[1])
    end
  end
end

class DestroyWorker
  include Sidekiq::Worker
  sidekiq_options retry: false

  def perform
    Element.destroy_all
  end
end
  

Важно отметить опцию retry: false - это важно, иначе sidekiq будет повторять работу при возникновении ошибки, что в данном случае совсем не нужно.

Теперь мы сможем перекинуть работу на Sidekiq

# app/controllers/home_controller.rb
class HomeController < ApplicationController
  def upload
    csv_path = File.join Rails.root, 'db', 'data.csv'
    UploadWorker.perform_async(csv_path)
    flash[:notice] = 'Element getting added to app'
    redirect_to elements_path
  end
                                            
  def destroy_all
    DestroyWorker.perform_async
    flash[:notice] = 'Elements getting removed from app'
    redirect_to elements_path
  end
end