以下の内容はhttps://tech.unifa-e.com/entry/2025/02/14/192212より取得しました。


Shoryukenからaws-activejob-sqs-rubyへの移行を検討してみる

こんにちは、サーバーサイドエンジニアの本間です。

弊社ではSQSをジョブキューとして使い、バックグラウンドジョブを処理させているサービスがそれなりにあります。 その際、 shoryuken を使うことで、自分たちで実装する工数をなるべく少なくしています。 過去にこのgemを使ったエントリも何個か投稿しています。

tech.unifa-e.com

tech.unifa-e.com

安定して動作もしており、大変お世話になっているgemなのですが、一時期このgemのGithubリポジトリがアーカイブ状態になっており、今後のメンテナンスやセキュリティアップデートが難しい状況になっていました。

現在はアーカイブ予定は解除されており一安心ではあるのですが、もしアーカイブされたままだった場合、forkして自分たちでメンテナンスするか、それとも別の何かに移行するか、何らかのアクションが必要になっていたと思います。

今回、もし移行するとなった場合に有力な候補になるであろうgemである aws-activejob-sqs-ruby への移行を検討したので、その内容をここで共有したいと思います。

aws-activejob-sqs-ruby

aws-activejob-sqs-ruby は、AWS公式が提供するRailsからAWSを便利に使うSDKの aws-sdk-rails の一つです。 gemの名前から分かる通り、ActiveJobのバックエンドにSQSを利用できるようにする機能を提供します。

移行するにあたり、まずこのgemの使い勝手を確認してみようと思います。 確認した時のコード一式は、以下のリポジトリに公開しております。

https://bitbucket.org/unifa-public/aws_activejob_sqs_ruby_test/src/main/

はじめに rails new で今回確認するためのRailsアプリケーションを作ります。

$ rails new aws_activejob_sqs_ruby_test

$ cd aws_activejob_sqs_ruby_test

localstack

ローカルでSQSをエミュレートするため、 localstack を立ち上げます。 今回はDocker Composeを使って立ち上げています。

localstackをローカルで立ち上げるための compose.yaml の例になります。

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"
    environment:
      DEBUG: ${DEBUG:-0}
      AWS_DEFAULT_REGION: ap-northeast-1
      SERVICES: sqs
    volumes:
      - "./localstack/init_scripts:/etc/localstack/init"

また、localstackが立ち上がった時に確認用のSQSキューが作成済みだと便利です。 localstackには起動時にスクリプトを流す機能があるため、その機能を使って立ち上げ直後にテスト用のSQS queueを1つ作るようにします。

localstack/init_scripts/ready.d/create_queue.sh ( chmod 744 してスクリプトとして実行できるようにしてください )

#!/bin/sh
awslocal sqs create-queue --queue-name aws_activejob_sqs_ruby_test

これらのファイルを用意したら、ローカルで立ち上げておきます。

$ docker compose up -d

# awsコマンドがあれば動作しているか確認できます。
$ export AWS_ACCESS_KEY_ID="test"
$ export AWS_SECRET_ACCESS_KEY="test"
$ export AWS_DEFAULT_REGION="ap-northeast-1"
$ aws --endpoint-url=http://localhost:4566 sqs list-queues
{
    "QueueUrls": [
        "http://sqs.ap-northeast-1.localhost.localstack.cloud:4566/000000000000/aws_activejob_sqs_ruby_test"
    ]
}

aws-activejob-sqs-rubyの設定

次に aws-activejob-sqs-ruby を使うための設定を、公式の README を見ながら行います。

まず Gemfile に必要なgemを追加しています。

--- a/Gemfile
+++ b/Gemfile
@@ -8,6 +8,9 @@ gem "puma", ">= 5.0"
 # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
 # gem "bcrypt", "~> 3.1.7"
 
+gem 'aws-activejob-sqs'
+gem 'aws-sdk-rails'
+
 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
 gem "tzinfo-data", platforms: %i[ windows jruby ]

修正後、 bundle install しておいてください。

次にActiveJobのAdapterに aws-activejob-sqs-ruby を使う設定をします。

--- a/config/application.rb
+++ b/config/application.rb
@@ -38,5 +38,7 @@ module AwsActivejobSqsRubyTest
 
     # Don't generate system test files.
     config.generators.system_tests = nil
+
+    config.active_job.queue_adapter = :sqs
   end
 end

テスト用のActiveJobも作成しておきます。

app/jobs/test_job.rb

class TestJob < ApplicationJob
  queue_as :default

  def perform(message)
    puts message
    logger.info(message)
  end
end

最後に aws-activejob-sqs-ruby 自体の設定をします。これには、以下の方法があるようです。( 参考リンク )

  • yamlファイル経由で設定
  • config/initializers に初期化コードを置いて設定
  • 環境変数経由で設定

実際に利用する場合、yamlファイルかコードによる設定を使うと思うのですが、今回は最も簡易な「環境変数経由で設定」を選択しました。

$ export AWS_ACTIVE_JOB_SQS_DEFAULT_URL="http://localhost:4566/000000000000/aws_activejob_sqs_ruby_test"

これで準備は完了のはずです。次のステップでJobを追加、実行してみます。

ActiveJobの追加と実行

Railsコンソール経由でJobを追加してみます。

$ bin/rails console
Loading development environment (Rails 8.0.1)

# ジョブを3つ追加してみる。
aws-activejob-sqs-ruby-test(dev)> TestJob.perform_later('test')
=> 
#<TestJob:0x0000000125544108
 @_halted_callback_hook_called=nil,
 @arguments=["test"],
 @exception_executions={},
 @executions=0,
 @job_id="56860f0b-be1e-4fd5-99dd-31fda2e48457",
 @priority=nil,
 @queue_name="default",
 @scheduled_at=nil,
 @successfully_enqueued=true,
 @timezone="UTC">
aws-activejob-sqs-ruby-test(dev)> TestJob.perform_later('test2')
=> ...
aws-activejob-sqs-ruby-test(dev)> TestJob.perform_later('test3')
=> ...

aws-activejob-sqs-ruby-test(dev)> quit

# キューの中身を確認してみる。queue-urlはlist-queuesで返される値をセット。
$  aws --endpoint-url=http://localhost:4566 sqs receive-message --queue-url "http://sqs.ap-northeast-1.localhost.localstack.cloud:4566/000000000000/aws_activejob_sqs_ruby_test"
{
    "Messages": [
        {
            "MessageId": "...",
            "ReceiptHandle": "...",
            "MD5OfBody": "...",
            "Body": "{\"job_class\":\"TestJob\",\"job_id\":\"56860f0b-be1e-4fd5-99dd-31fda2e48457\",\"provider_job_id\":null,\"queue_name\":\"default\",\"priority\":null,\"arguments\":[\"test\"],\
"executions\":0,\"exception_executions\":{},\"locale\":\"en\",\"timezone\":\"UTC\",\"enqueued_at\":\"2025-02-13T06:30:47.811134000Z\",\"scheduled_at\":null}"
        },
        ...
    ]
}

次にキューに追加したActiveJobを別プロセスで実行してみます。

$ bundle exec aws_active_job_sqs --queue default
{default: {url: "http://localhost:4566/000000000000/aws_activejob_sqs_ruby_test"}}
test
test2
test3

# キューが空になったことを確認
$ aws --endpoint-url=http://localhost:4566 sqs receive-message --queue-url "http://sqs.ap-northeast-1.localhost.localstack.cloud:4566/000000000000/aws_activejob_sqs_ruby_test"
# =>出力なし

# 念の為キューの属性を確認。メッセージ数関連の属性が全て0になっている。
$ aws --endpoint-url=http://localhost:4566 sqs get-queue-attributes --queue-url "http://sq
s.ap-northeast-1.localhost.localstack.cloud:4566/000000000000/aws_activejob_sqs_ruby_test" --attribute-names All
{
    "Attributes": {
        "ApproximateNumberOfMessages": "0",
        "ApproximateNumberOfMessagesNotVisible": "0",
        "ApproximateNumberOfMessagesDelayed": "0",
        "CreatedTimestamp": "1739425689",
        "DelaySeconds": "0",
        "LastModifiedTimestamp": "1739425689",
        "MaximumMessageSize": "262144",
        "MessageRetentionPeriod": "345600",
        "QueueArn": "arn:aws:sqs:ap-northeast-1:000000000000:aws_activejob_sqs_ruby_test",
        "ReceiveMessageWaitTimeSeconds": "0",
        "VisibilityTimeout": "30",
        "SqsManagedSseEnabled": "true"
    }
}

TestJob にセットした文字列が出力され、無事workerで処理できていることを確認できました。

aws-activejob-sqs-ruby を一通り触りましたが、Shoryuken(ActiveJob利用)と同じ感覚で利用できそうだなと感じました。ActiveJobを介しているというのが大きいかもしれません。

Shoryukenとの比較

ドキュメントやコードを見て確認したことがメインとなりますが、Shoryukenaws-activejob-sqs-ruby の共通点や違いをまとめてみました。

まず、共通点になります。

  • SQSをジョブキューとしたバックグラウンドジョブを処理するシステムを少ない実装で構築できる。
  • ActiveJobに対応している。
  • ActiveJobを利用した場合、リトライはデフォルトでActiveJobの仕組みを利用する(リトライ用のメッセージを追加し、元のメッセージは削除する)。
  • 標準キューのほか、FIFOキューにも対応している。
  • workerは1プロセス、複数スレッドで動作する。
  • workerは1プロセスで複数のキューをpollingできる。ただし、 Shoryuken の方が重みづけなど細かい指定が可能。
  • workerにLambdaを使うパターンがある。
  • 日時を指定しての実行は、SQSの制限により現在時刻から15分後までしか指定できない。

aws-activejob-sqs-ruby のみにある特徴としては、以下になります。

  • AWS公式で開発、メンテナンスされている。
  • Shoryuken はActiveJobがなくても動かすことができるが、 aws-activejob-sqs-ruby はActiveJob前提になっている。
  • SQSへのメッセージ送信を非同期で行う sqs_async がある。
  • workerはデフォルトでlong pollingに対応しており、無駄なメッセージ受信のリクエストを極力少なくするようにしている。( Shoryukenオプション設定が必要 )
  • Shoryuken はMiddlewareをはじめとして 様々なオプション、カスタマイズ例 があるが、 aws-activejob-sqs-ruby にはあまりなくシンプルな使い方になりそう。
  • job-iteration に対応してあることが明記されている。
  • aws-activejob-sqs-ruby のworkerはデフォルトで1回のpollingで10メッセージを取得して処理をしている。

細かい違いはありますが、ActiveJobを使うのであれば、その差はほとんど気にならないレベルなのかなと感じました。

今回調査してみて、どちらもworkerをLambdaで動かす方法があることを知りました。これは時間があればどこかで試してみて、実際の使い勝手を確認してみようと思います。

Shoryukenからaws-activejob-sqs-rubyへの移行

次に、現在Shoryuken を使っているアプリケーションが aws-activejob-sqs-ruby に移行する場合、どういった移行方法があるか検討します。

パターン1 別のSQSキューを使う

Shoryuken 用のSQSキューとは別に、 aws-activejob-sqs-ruby 用のSQSキューを作成することができれば、特別な手順をへずとも安全に移行をすることができそうです。以下に手順を記載します。

  1. もともと Shoryuken で使っているSQSとは別に aws-activejob-sqs-ruby 用のキューを作成する。
  2. アプリケーションを修正し、 Shoryuken から aws-activejob-sqs-ruby を利用するようにする。また、利用するSQSのキューも新規作成したものに変更する。
  3. 修正したアプリケーションをデプロイする。
  4. Shoryuken で使っているSQSにメッセージが残っている場合、別途修正前のworkerを動かして、キューのメッセージ数が0件になるまで待つ。
  5. Shoryuken で使っていたSQSキューを削除する。

この手順が第一候補になるかなと思います。

パターン2 メンテナンスに入れる

基本は別々のSQSキューを作るのが安全ですが、なんらかの事情で同じキューを使い続けたい場合、メンテナンスに入れることで安全に移行することができると思われます。

  1. アプリケーションを修正し、 Shoryuken から aws-activejob-sqs-ruby を利用するようにする。
  2. デプロイを行う前にアプリケーションをメンテナンスに入れ、新しいメッセージがSQSに送信されないようにする。
  3. メンテナンスに入れたままworkerを動かし、SQSのキューのメッセージが0になるようにする。
  4. SQSのメッセージが0になったことを確認したら、修正後のアプリケーションをデプロイする。
  5. アプリケーションのデプロイが完了したらメンテナンスを解除する。

ある程度の期間メンテナンスに入れてもよいアプリケーションであれば、こちらの方式の方が工数少なめで移行することができそうです。

安全に移行する場合、以上の2つのどちらかのパターンになるかなと考えています。

まとめ

今回 Shoryuken からの移行先として aws-activejob-sqs-ruby を触ってみて、実際に移行する場合の手順を検討してみました。

今回確認した限りだと使用感にはほとんど差がなく、移行も比較的簡単に実行できると感じました。今回の調査で、今後移行が必要になったとしてもスムースに移行ができそうです。参考になれば幸いです。

ユニファでは一緒に働く仲間を募集しています!今回のようなAWSとRailsを組み合わせた開発が気になった方は、ぜひ採用ページをチェックしてみてください!

jobs.unifa-e.com

最後までお読みいただきありがとうございました。




以上の内容はhttps://tech.unifa-e.com/entry/2025/02/14/192212より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14