Melchior

バンクーバーで働くエンジニアの備忘録

Sidekiqのベストプラクティス

概要

Sidekiqのベストプラクティスに沿って、メッセージ機能を実装していきます。

0. Sidekiqとは

Sidekiqとはbackgroundでタスクを処理してくれるライブラリです。日付指定でセットして実行したりもできます、便利そうです。

ベストプラクティスに沿ってWorker用のテーブルを用意します。

# messages
class CreateMessages < ActiveRecord::Migration[5.1]
  def change
    create_table :messages do |t|
      t.integer "sender_id"
      t.integer "recipient_id"
      t.string "body"
      t.timestamps
    end
  end
end
# message_tasks
class CreateMessageTasks < ActiveRecord::Migration[5.1]
  def change
    create_table :message_tasks do |t|
      t.integer "sender_id"
      t.integer "recipient_id"
      t.string "body"
      t.timestamps
    end
  end
end

今回はミニマムのデモのため構造は全く同じですが、実際は送信するユーザが複数だったりプッシュ通知をしたりするかもしれません。

1. ジョブのパラメータを小さく・シンプルに

上記MessageTasksのテーブルを用意することで、引数をtask_idのみにすることができます。

class MessageWorker
  include Sidekiq::Worker

  def perform(task_id)
  end
end

また呼び出し側では以下のようになります。

task = MessageTask.create(sender_id: sender_id,
                          recipient_id: recipient_id,
                          body: body)

MessageWorker.perform_async(task.id)

2. 冪等性とトランザクション

perform内でエラーが発生した場合、自動リトライ機能が働きます。デフォルトでは25回のリトライ後(おおよそ21日後)そのジョブは削除されマニュアルで対応しないといけません。 それを防ぐためにもMessageTasksトランザクションを使用することで、より安全にデータを扱うことができます。

class MessageWorker
  include Sidekiq::Worker

  def perform(task_id)
    task = MessageTask.find(task_id)
    if task.nil?
      return
    end

    ActiveRecord::Base.transaction do
      Message.create(sender_id: task.sender_id,
                     recipient_id: task.recipient_id,
                     body: task.body)
      task.destroy
    end
  end
end

3. Concurrencyの設定

Concurrencyを設定することによりsidekiqの処理を並列化することが可能です。デフォルト値は25スレッドで推奨値は50未満。100以上設定した場合、安定性の問題があるとの。 config/database.ymlを変更することで変えることができます。この値は可変ではないので、用途に応じて変更する必要があるようです。

production:
  adapter: mysql2
  database: foo_production
  pool: 25

まとめ

Sidekiqを使うことで同期処理が必要がないタスクは非同期で処理させることができます。同期処理させる必要ないものは非同期にしてレスポンス速度を改善できそうですね。

リンク