2019年12月20日金曜日

Sidekiqによる非同期処理

せっかく作ったが蔵入りになりそうなので残しておく

背景

昔作られていたオンライン処理で、データ量が想定より大きくなり過ぎたせいで
画面上で処理するとタイムアウトになってしまっていた。
タイムアウトと言ってもUI側だけで、バックグラウンドの処理は正常に行われている。
幸い社内のみ利用されている機能で、月一の処理だったので
保守係が手動で処理していた。
これをバッチ化できないかを検討。

業務要件

・ユーザ(経理)の方で任意の日付(処理日)を入れる必要がある為、オンライン上での入力は維持する必要がある。
・会計締め処理に合わせる必要がある為、なる早めの実行が必要。
・処理が終わった後、ユーザが作られたデータを確認する必要がある。

技術要件

・既に色んな機能がお互い依存しているスパゲティコードになっているので、修正箇所は最小限にしたい。
→既存の処理を切り出して非同期処理化する。
・ruby1.9, rails3.2.12

非同期処理実装のため検討したこと

Active Job
rails4.2以上対応だが、現在のバージョンは3.2.12だった為、断念

Delayed Job
キュー管理用のテーブルが必要で、Sidekiqより参考資料が少なそう。

Sidekiq
redisのインストールが必要だが、今のバージョンでも使えそうだったので採用

[Ruby on Rails] Sidekiq で非同期処理を実装する


Sidekiqのバージョン選びについて

・普通にインストール→Sidekiq 5.0.3が入った。
generateコマンドで使えるか確認
$ rails generate --help
Gem Load Error is: Sidekiq 5.0.3 does not support Ruby versions below 2.2.2.
→Requires Ruby 2.5+ and Redis 4.0+

ちなみに今のrubyバージョンは
$ ruby -v
ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-linux]
$ gem search ^sidekiq$ --all
Error loading RubyGems plugin "/usr/local/lib/ruby/gems/1.9.1/gems/yard-0.8.5.2/lib/rubygems_plugin.rb": can't modify frozen Hash (RuntimeError)

YAML safe loading is not available. Please upgrade psych to a version that supports safe loading (>= 2.0).


*** REMOTE GEMS ***
sidekiq (6.0.3, 6.0.2, 6.0.1, 6.0.0, 5.2.7, 5.2.6, 5.2.5, 5.2.4, 5.2.3, 5.2.2, 5.2.1, 5.2.0, 5.1.3, 5.1.2, 5.1.1, 5.1.0, 5.0.5, 5.0.4, 5.0.3, 5.0.2, 5.0.1, 5.0.0, 4.2.10, 4.2.9, 4.2.8, 4.2.7, 4.2.6, 4.2.5, 4.2.4, 4.2.3, 4.2.2, 4.2.1, 4.2.0, 4.1.4, 4.1.3, 4.1.2, 4.1.1, 4.1.0, 4.0.2, 4.0.1, 4.0.0, 3.5.4, 3.5.3, 3.5.2, 3.5.1, 3.5.0, 3.4.2, 3.4.1, 3.4.0, 3.3.4, 3.3.3, 3.3.2, 3.3.1, 3.3.0, 3.2.6, 3.2.5, 3.2.4, 3.2.3, 3.2.2, 3.2.1, 3.2.0, 3.1.4, 3.1.3, 3.1.2, 3.1.1, 3.1.0, 3.0.2, 3.0.1, 3.0.0, 2.17.8, 2.17.7, 2.17.6, 2.17.5, 2.17.4, 2.17.3, 2.17.2, 2.17.1, 2.17.0, 2.16.1, 2.16.0, 2.15.2, 2.15.1, 2.15.0, 2.14.1, 2.14.0, 2.13.1, 2.13.0, 2.12.4, 2.12.3, 2.12.1, 2.12.0, 2.11.2, 2.11.1, 2.11.0, 2.10.1, 2.10.0, 2.9.0, 2.8.0, 2.7.5, 2.7.4, 2.7.3, 2.7.2, 2.7.1, 2.7.0, 2.6.5, 2.6.4, 2.6.3, 2.6.2, 2.6.1, 2.6.0, 2.5.4, 2.5.3, 2.5.2, 2.5.1, 2.5.0, 2.4.0, 2.3.3, 2.3.2, 2.3.1, 2.3.0, 2.2.1, 2.2.0, 2.1.1, 2.1.0, 2.0.3, 2.0.2, 2.0.1, 2.0.0, 1.2.1, 1.2.0, 1.1.4, 1.1.3, 1.1.2, 1.1.1, 1.1.0, 1.0.0, 0.11.2, 0.11.1, 0.11.0, 0.10.1, 0.10.0, 0.9.1, 0.9.0, 0.8.0, 0.7.0, 0.6.0, 0.5.1, 0.5.0)

2.17.8でインストール成功

Gemfile
# 非同期実行用
gem 'sidekiq', '2.17.8'


デフォルトだとログが標準出力になるらしいので
Railsのログとして出力されるように変更する

$ cat config/initializers/sidekiq.rb
Sidekiq::Logging.logger = Rails.logger

Sidekiq のロギングに Rails logger を利用する

Redisのインストール

redisをインストールせずにsidekiqのアプリケーションを起動すると、下記のエラーになってしまう。

[FATAL] [2019-12-19 12:39:12 +0900] [b2b2ab87ca2f7d6425d549be8609b04b] Error connecting to Redis on 127.0.0.1:6379 (Errno::ECONNREFUSED)
apt install redis-server



コードの改修

コントローラ側の処理をワーカクラスに切り出し
app/workers/monthly_billing_request.rb

コントローラではワーカをキューに入れるように変更

class System::BillsController < ApplicationController
  def create
  :
    logger.debug('clear retry queue')
    Sidekiq::Queue.new.clear
    Sidekiq::ScheduledSet.new.clear
    logger.debug('MonthlyBillingRequest queuing start')
    MonthlyBillingRequest.perform_in(1.seconds, scheduled_at)
    logger.debug('MonthlyBillingRequest queuing end')
  :

ワーカはWorkerをincludeして実装
class MonthlyBillingRequest
    include Sidekiq::Worker
    sidekiq_options queue: :event, retry: false

  def perform(scheduled_at)
    ## 何か処理
  end
end

キューの確認について

バッチを実行するとキューに入るが、ちゃんとキューに入っているか・キューから実行されているかなどの確認をする為には redisで確認する必要がある。
redis-cli
Redis に保存されてる値を見ようと思った時に覚えておきたい redis コマンド 

画面から連続でボタンが押されると、キューも複数生成されてしまっていた。
アプリケーション側で制御する必要があるが、取り敢えず間違ったキューは削除したい。
Redisのデータを削除する方法


結局どうなったのか

非同期処理をしてもwebインスタンスに負荷が掛かることは変わらないことで
バッチ実行時にバッチ用のインスタンスを作って実行することで
この案は廃止された。