Rails6にdelayed_jobでメールの非同期実行を実装し、AmazonLinux2のマルチDockerのコンテナで動かす

新規サービスの立ち上げでメールの非同期実行処理を実装するのに、delayed_jobを使う。
まだユーザーも多くないし、実装やお金の面でもとりあえず低コスト。
AWSのElasticBeanstalkで[AmazonLinux2のマルチDokckerプラットフォーム](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker-multicontainer-migration.html)での実装の備忘録。

### 前提
- docker for mac でrailsアプリケーションを動作させていること。
- アプリケーションのコンテナ名を`web`とし、`docker-compose run --rm web `のエイリアスを `dcrw` として登録していること。（コマンドで使っているので読み替えて下さい。）

### delayed_jobの基本的な実装

```:Gemfile
gem 'delayed_job_active_record'
gem 'devise-async'  # devise のメールを非同期実行するのに必要。
```

```sh
docker-compose build
dcrw rails g delayed_job:active_record
dcrw rails db:migrate
```
`bin/delayed_job` が生成される。
delayed_jobsテーブルがcreateされる。

que_adapterを設定。

```ruby:config/application.rb
config.active_job.queue_adapter = :delayed_job
```

コンテナとして起動。
本番用のdocker-compose.ymlもこれに準拠。

```yaml:docker-compose.yml
version: '3'
services:  # webコンテナとworkerのコンテナを共通化。
  app:
    build: .
...
  web:
    extends:
      service: app
    command:
        ["sh", "-c", "rm -f tmp/pids/server.pid; bin/rails server -b 0.0.0.0"]
    ...
  jobworker:  # このコンテナを追加。
    extends:
      service: app
    command: bundle exec bin/rails jobs:work
...

```
##### 参考
- [GitHub \- collectiveidea/delayed\_job: Database based asynchronous priority queue system \-\- Extracted from Shopify](https://github.com/collectiveidea/delayed_job)
- [Railsでの非同期処理とDelayed Job \| RE:ENGINES](https://re-engines.com/2018/09/25/rails-async-job/)
- [Railsで非同期処理の実装 \- eviry tech & service blog](https://blog.eviry.com/entry/2019/07/23/160702_1)

### deviseのメール対応

これだけだとdeviseが送信するメールは非同期処理の対象にならない。
`devise-async`gemをインストールし、deviseを適用しているモデルのdeviseの設定に`:async`を追加。
[Readme](https://github.com/mhfs/devise-async)で書かれているオプションの`Devise::Async.enabled = true`だけでは非同期にならず。 

```ruby
  devise :database_authenticatable,
         :recoverable, :rememberable, :validatable,
         :async  # 追加

```
##### 参考
- [deviseで送信されるメールを非同期処理にする方法 \- Qiita](https://qiita.com/shhmd92/items/8b3468def18f300b9bf3)
- [GitHub \- mhfs/devise\-async: Send Devise's emails in background\. Supports Resque, Sidekiq, Delayed::Job and QueueClassic\.](https://github.com/mhfs/devise-async)


### Rspecでキューイングしたメールのテストを行う

細かいことは参考のリンク先参照。
メールの本文までテストするのに、キューに入れたメールを実行する必要がある。
`perform_enqueued_jobs` メソッドを使う。

```ruby:spec/rails_helper.rb
RSpec.configure do |config|
  config.include ActiveJob::TestHelper  # 追加
end
```

specのメール送信処理を`perform_enqueued_jobs` ブロックで囲う。

```spec/feature/xxx_spec.rb
...
expect {
  perform_enqueued_jobs { click_button '送信する' }
}.to change { ActionMailer::Base.deliveries.size }.by(1)

mail = ActionMailer::Base.deliveries.last
expect(mail.subject).to have_content('メールタイトル')
expect(mail.body).to have_content('メール本文…')
```

##### 参考
- [RSpec でキューイングした ActiveJob を同期実行する \- Qiita](https://qiita.com/upinetree/items/41a2a8fe9e1dd7c291ab)
- [ActiveJob::TestHelper](https://api.rubyonrails.org/classes/ActiveJob/TestHelper.html#method-i-perform_enqueued_jobs)

### AWSでのエラー備忘録

AWSにデプロイした際に、コンテナが落ちる現象が発生。
ログの出力を制御できていなくて時間食ったが、アプリケーションのログにエラーを吐いていた。
`docker logs [docker id]` するとアプリケーションのログが見れる。
原因はDBに接続していなかったこと。
ENVの設定が必要だった。

##### 参考

- [How to know the reason why a docker container exits? \- Stack Overflow](https://stackoverflow.com/questions/38112968/how-to-know-the-reason-why-a-docker-container-exits)