おまえらのSidekiq(OSS) + logrotateでUSR2を使う認識は改めたほうが良い

# はじめに

すんません。タイトルは若干釣りです。

[Ateam cyma Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ateam-cyma) の8日目は [株式会社エイチームライフスタイル](http://life.a-tm.co.jp/) CTO の @tsutorm が担当します！

実はcymaもちょっと見てるんだよ！

# おまえらのSidekiq + logrotateでUSR2を使う認識は改めたほうが良い

![sidekiq-logo](https://sidekiq.org/assets/kicker.svg)


## 背景

[cyma](https://cyclemarket.jp)はもともとPHPで実装されていましたが、2019/02にリプレイスを行い、現在はモノリシックなRailsアプリケーションとして実装されています。

非同期処理はセオリー通り`ActiveJob`を軸に、当初 `:async` モードを利用して実装されていましたが、非同期ジョブの動作担保をより確実に行うため、2019/08頃より `:sidekiq` モードを利用するよう変更しました。

そんな2019/09に、Sidekiq 6.0がリリースされ、当時まだcymaのRailsバージョンは5.1, Sidekiqも5系だったということもあり、Rails5.2, 6.0を踏まえた事前調査がてら、Wikiをなんとなく読んでいたところからお話は始まります。

## USR2の記述が変わってる？

`Sidekiq <6.0 used USR2 for logfile maintenance.  This functionality has been removed in 6.0.`

🤔こんな事書いてあったか？

https://github.com/mperham/sidekiq/wiki/Signals/_compare/51a6d0ce7adb7f469da9a4a2dd142c7528e1b322...d802d8c140e8fd356a973a8da9d9cd548e01285f#

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76630/ffecfe5c-6362-47f4-2bc0-871a9bdf02b7.png)

おぉ。ごっそり変わってますね。要約すると、

- USR2シグナルの利用は 6.0 で削除される
- Sidekiq Enterpriseではローリングリスタートとして引き続き利用できる

とのこと。なるほど。
[Logging](https://github.com/mperham/sidekiq/wiki/Logging)に `The Sidekiq process logs only to STDOUT. This is the way modern daemons should work.` ともあるように、Sidekiq自体がログファイルに対して責任を追う時代は終わりました。

## Sidekiq 5.xの時点でもSTDOUTをリダイレクトしているケースはreopenしない

一応おもしろいんで実装も確認しておこうと思ってソースを追ってみた所、

[tag:v5.2.7/lib/sidekiq/cli.rb#L154](https://github.com/mperham/sidekiq/blob/v5.2.7/lib/sidekiq/cli.rb#L154)

```ruby
      'USR2' => ->(cli) {
        if Sidekiq.options[:logfile]
          Sidekiq.logger.info "Received USR2, reopening log file"
          Sidekiq::Logging.reopen_logs
        end
      },
```

🤔 `logfile`オプション使ってないとそもそも何もしないぞ・・・

ということは、`bundle exec sidekiq > log/sidekiq.log` みたいなコードだと、`USR2`送っても何もしないんだーへー。なるほど・・・

## しかし一般の認識は・・・

Google で `sidekiq log` とかで検索すると、前提条件の話も特になく、とにかく`USR2 + logrotate` の話がいっぱいひっとするお・・・

**みんな！このままじゃ、bump upリリースで事故るよ！！！**

## じゃぁどうすれば？

cymaでは、標準出力からログファイルに出力する形式を取っていて、且つ一旦ファイルに吐いてから CloudWatch Logsエージェントを利用して転送していたこともあり、EC2上に残ったファイルのローテションには`logrotated`を引き続き利用していました。

ゆくゆくバージョンも上げていくと思うので、USR2を現段階で使わないけど、ちゃんとログのローテートもできるような`logrotate`にしなければ。
ということもあって、以下のような感じにUpdate

```shell
(中略)
    copytruncate
    prerotate
      pid=/app/cyma/tmp/pids/sidekiq.pid
      test -s $pid && kill -TSTP "$(cat $pid)"
      sleep 1
    endscript
    lastaction
      pid=/app/cyma/tmp/pids/sidekiq.pid
      test -s $pid && kill -TERM "$(cat $pid)"
      systemctl start sidekiq.service
    endscript
}
```

- `prerotate`時点で`TSTP`シグナルを利用して新規ジョブの流入をカット。ジョブ終了に概ね掛かりそうな秒数(ここでは1秒)分待機
- `copytruncate`を利用してログファイルのローテーションを実行することでFDの切り替えを考慮しなくてもオッケーにする
- `lastaction`時点で`TERM`シグナルでプロセスを一旦終了させ、再起動

てな感じにしてみました。

うーん。早くコンテナ化して、そもそも`logrotate`とか言わなくても良いようにしたい。

## まとめ

- Sidekiq への USR2 シグナルは v6.0 以降、log の reopen として機能しない
- 標準出力経由でログを吐いている場合、そもそも v5.x の時点でも USR2 は reopen もしない
- [ちゃんとドキュメントを読もう](https://github.com/mperham/sidekiq/wiki/Signals)
- Sidekiq Enterpriseを使ってたらこんな面倒な事しなくても`USR2`で引き続きよさげ

# 終わりに

[Ateam cyma Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ateam-cyma) の 8日目、いかがでしたでしょうか。
9日目は @sumiita が **アナログデザインの強さ** について記事を書かれるそうですので、お楽しみに！

株式会社エイチームでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。

エンジニアとしての働き方に興味を持たれた方は[cymaのQiita Jobs](http://bit.ly/35E1I08)をご覧ください。

そのほかの職種は、[エイチームグループ採用サイト](http://bit.ly/2XQpd3q)をご覧ください。