kikeda1104's blog

備忘録・技術に関することを書いています。(webエンジニア)

sidekiq + redisの構築(Rails 4系)

非同期処理を取り入れる提案をして、採用されたので構築していました。アウトプットとして何回かに分けて記事を書いていきます。

前提環境

前提環境の背景を話すと、Rails 5系にアップデートできていないプロジェクトです。テストは随時入れていますので、工数が取れればアップデートは出来ますが今のところ予定はないです。

サーバ構成はこんな感じ AWS EC2( Application Server Rails) <=> AWS EC2(Batch Server Rails, sidekiq(worker), Batch(cron)) <=> Aws ElastiCache(Redis, クラスターモード無し)

Rails 4.2系でもActive Jobがあり、retry機能が想定しているエラーを迂回できそうになかったので、workerクラスを実装して進めています。ActiveJobを利用するとRailsがadapterをラップしていることによるメリットもあり、ユースケースを満たせれば,テストコードも楽に書けるようになり採用は出来たと思います。

まずredisのインストールは、この記事ではローカル上にインストールします。(Dockerはまた別の機会に書きます) Hometbrewを利用してインストールします。

redis

  brew install redis
  redis-server
   `/usr/local/etc/redis.conf`が保存されている。

続いてAppに実装

# Gemfile
  gem 'sidekiq'
  bundle install
 # app/workers/high_worker.rb
  class HighWorker
    include Sidekiq::Worker
    sidekiq_options queue: :high, retry: false
    
    def perform(csv_file_id)
       AbcCsvImporterService.call(csv_file_id)
       # 完了通知 or logging
    end
  end

# AbcCsvImportクラスは実装無し。クラス内の例外についても対応する。thread safeのコードにする。(gemが提供するライブラリを使う場合も注意)
# retry: falseなので、Dead Job Queueにはenqueueされません。
# config/application.rb
...
config.active_job.queue_adapter = :sidekiq
....
# config/sidekiq.rb
---
:verbose: false
:timeout: 15
:concurrency: 2
staging:
  :concurrency: 5
production:
  :concurrency: 5
:queues:
  - high
# database.yml
development:
  adapter: mysql2
  encoding: utf8
  database: my_db
  pool: 7
  username: my_user
  password: my_password
  host: localhost
....
# config/initializes/sidekiq.rb
Sidekiq.configure_server do |config|
  config.redis = { url: 'redis://localhost:6379', network_timeout: 5 }
end

Sidekiq.configure_client do |config|
  config.redis = { url: `redis://localhost:6379` network_timeout: 5 }
end

ローカル上で動作することに限定するなら、設定ファイルをなくして、デフォルトにしてもいいです。上記のurlは、sidekiqのデフォルトになります。本番、ステージングなどある場合は環境別に変えましょう。network_timeoutオプションは、EC2を利用する都合timeout時間を伸ばしています。デフォルトは1です。

database poolと同値かそれ以下にconcurrencyを設定。

# csv_import_controller
class CsvImportController < ApplicationController
  before_action :set_csv_file, only: %w[new create]
  before_action :set_csv_files, only: %w[index]

  def index; end
  def new; end

  def create
     if @csv_file.save
         redirect_to new_csv_import_path, notice: '保存に成功しました'
     else
      render :new, notice: '保存に失敗しました'
     end
  end

  private
 
  def csv_file_params
     return params.require(:csv_file).permit(CsvFile.column_names) if params[:csv_file]
     {}
  end
 
  def set_csv_file
     @csv_file = Csvfile.new(csv_file_params)
  end

  def csv_files
    # index用
  end
end
# app/model/csv_file.rb

class CsvFile < ActiveRecord
   after_commit :import_data, on: :create

   def import_data
      CsvFileWorker.perform_async(id)
   end
end

viewsは省きます。CSVファイルの入力フォームのイメージです。

bundle exec sidekiq -C config/sidekiq.yml

以上です。次回、AWSの構成、テストコード、redisなどの記事を書ければ書きます。色々ノウハウ溜まっているのでそのほかにElasticsearchなど。そのうち。

参考

Home · mperham/sidekiq Wiki · GitHub

Error Handling · mperham/sidekiq Wiki · GitHub

https://github.com/mperham/sidekiq/wiki/Problems-and-Troubleshooting#threading

https://github.com/mperham/sidekiq/wiki/Problems-and-Troubleshooting#cannot-find-modelname-with-id12345