Mastodonのサーバ間通信が切れた場合のリカバリ

注: この記事に書かれている接続切れ現象は結構特殊な状況による現象です。
サーバでcronを回すのを忘れている場合以下の物とは関係なく有効期限切れにより接続が切れます。
サーバ管理者の人はDocker使用/不使用に関わらずページ末尾にある追記3を読んでcronが正常に実行されていることを確認して下さい。よろしくおねがいします。

# Mastodonのサーバ間通信って切れるの？
こういう現象が起こります。
[Mastodon でリモートインスタンスから自分のトゥートがみえなくなることがある](http://cryks.hateblo.jp/entry/2017/04/18/125351)
メンテ時などに特定のエラーが起こると相手のサーバが接続を切ってしまいます。

# リカバリ方法は？
* 受信側からsubscribeを再実行する(手動)。
* 7日間待って受信側のcronが有効期限更新のためにsubscribeを実施するのを待つ。
* バックアップからsubscriptionsテーブルのレコードを戻す。
* Mastodonにパッチを当ててsubscriptionsが論理削除になるようにする(https://github.com/clworld/mastodon/pull/1/files)

# どうすればいい？(送信側)
* バックアップでも論理削除でも何かしらの準備が必要
* 何も準備していなかった場合は7日間待つ以外の方法は無い

# どうすればいい？(受信側)
送信側サーバのsubscriptonが消えてたらsubscribeを再実行すれば良い
### どうやって？
subscriptionsが生きているか確認するAPIがきっとどこかに => 探してもどこにもなかった
### どうしよう
1. とりあえずリモートフォローしているアカウントを全部subscribeしなおす => 負荷とかいろいろつらい
2. どうにかしてどのアカウントのsubscriptionが切れたのかを調べる

### Nginxのログからsubscribeが切れたアカウントを探す
/api/subscriptions/xxx にアクセスされてステータスが 3xx, 4xx (429除く) だったらそのsubscriptionsは切れたとみなす。

```
cat (nginxのログ) | grep /api/subscriptions/ | egrep '" [34][0-9][0-9] ' | grep -v '" 429 '
```

出てきたログのうち、Mastodon(UAは"Mastodon/PubSubHubbub")の物が対象。注: Mastodon以外のGNU Social互換鯖のことも一応考える必要があるかもしれない。

```
xxx.xxx.xxx.xxx - - [xx/Apr/2017:xx:xx:xx +0900] "POST /api/subscriptions/107 HTTP/1.1" 404 39 "-" "Mastodon/PubSubHubbub"
```
こんなログが見つかります。
/api/subscriptions/ 後にある数字のidをaccountsから探してsubscribeしてあるべきユーザかどうかを確認しましょう。

### subscribeが切れたアカウントをリカバリする。
subscribeしてあるべきユーザだった場合リカバリを実施します。
量にもよりますがrails consoleで直接SubscribeServiceを呼び出すのが楽でしょう

```
RAILS_ENV=production bundle exec rails console
SubscribeService.new.call(Account.find(107))
```

## まとめ
* メンテ時には可能な限り/api/subscriptions/ で 3xx, 4xx のレスポンスを返さないように注意しましょう。
* subscriptionsテーブルのバックアップや論理削除は予め設定しておきましょう。
* 送信側で出来る対応は準備が無いと7日間待つだけになってしまうので対応可能な受信側で復旧した方が良いと思います。

## 追記
* 受信側からはsubscribeが切れたことを検知出来ないため受信側からのcron(mastodon:push:refresh)による再接続は期限切れ直前を待つ必要があります。
* subscribeはユーザ単位で行なわれるため、同じサーバでも接続が切れるユーザと切れないユーザが発生します。

## 追記2 全ユーザ再Subscribe手順

```
RAILS_ENV=production bundle exec rails console
#docker利用の場合は docker-compose run --rm web rails console
Account.remote.with_followers.where.not(subscription_expires_at: nil).find_each do |a| SubscribeService.new.call(a) end
```

## 追記3 cronは必ず回せ。絶対回せ。Dockerを使ってても自動では設定してくれないので設定すべし。(Mastodon 1.3.2以下の場合)
* 上に書いてあるのは全部ちゃんとcronでmastodon:dailyを起動していることを前提にしています。
* 7日間待てば回復するというのもmastodon:daily(の中のmastodon:push:refresh)が再subscribeを行なえば回復するという意味です。
* cronは絶対に回して下さい。Mastodonサーバ管理者の義務です。
* 参考として私のサーバのmastodonユーザのcrontabは以下のような感じです(別々に実行していますがmastodon:dailyにまとめても大丈夫だと思います)

```
MAILTO=root
10 4,17 * * * /bin/bash -l -c 'cd /home/mastodon/live && RAILS_ENV=production bundle exec rake mastodon:feeds:clear'
15 4,17 * * * /bin/bash -l -c 'cd /home/mastodon/live && RAILS_ENV=production bundle exec rake mastodon:media:clear'
20 4,17 * * * /bin/bash -l -c 'cd /home/mastodon/live && RAILS_ENV=production bundle exec rake mastodon:users:clear'
25 4,17 * * * /bin/bash -l -c 'cd /home/mastodon/live && RAILS_ENV=production bundle exec rake mastodon:push:refresh'
```

* dockerの場合以下のような感じでcronを実行して下さい(私はDockerを使ってないのでちゃんと動くことを確認して下さい)

```
25 4,17 * * * /bin/bash -l -c 'cd <Mastodonがあるディレクトリ> && docker-compose run --rm web rake mastodon:daily'
```

※メモリ1GBでswap無しのサーバの場合mastodon:dailyがメモリ不足で落ちるという現象があるらしいことが報告されています。(swapあっても落ちる時もあるらしいです)
そのような場合はmastodon:feeds:clear mastodon:media:clear mastodon:users:clear mastodon:push:refresh のタスクをそれぞれ個別で登録しましょう。

## 追記4 Mastodon1.3.3以降から mastodon:push:refresh の処理は Sidekiq が実行するようになりました

config/sidekiq.yml に設定が追加されているのでsidekiqを複数起動していたりsidekiq.ymlに修正を入れている人は注意しましょう。
Sidekiqで処理されるようになったので1.3.3以降rakeのmastodon:push:refreshの処理は何もしない処理に置き換えられています。
処理するキューの指定もsidekiq.ymlに書かれるようになっていたりもします。

```
# config/sidekiq.yml から引用
:schedule:
  subscriptions_scheduler:
    cron: '0 5 * * *'
    class: Scheduler::SubscriptionsScheduler
```