Heroku PostgreSQL メジャーバージョンアップ手順

## 概要

Heroku PostgreSQL のメジャーバージョンのアップグレードは `Heroku CLI` を介してのみ実行できます。
現在のデータベースの状態に合わせて2種類の方法から選択します。
どちらの方法でも、アプリケーションのダウンタイムが必要です。

### pg:upgrade

`hobby プラン`を除くすべての Heroku Postgres プランに有効です。

以下の場合を除き、この方法がおすすめです:

- `hobby プラン`の場合
- データベースで checksum が有効になっていない場合
- データベースに `bloat` が発生している場合

#### `bloat` とは

デッド行が原因でディスク上でデータベースオブジェクトが使用する余分なスペースです。
参考: [Managing VACUUM on Heroku Postgres | Heroku Dev Center](https://devcenter.heroku.com/articles/managing-vacuum-on-heroku-postgres#determining-bloat)

### pg:copy

すべての Heroku Postgres プラン（`hobby プラン`を含む）に対して機能しますが、
サイズが 10 GB 未満のデータベースに対してのみ機能します。

GB あたり約 3 分の停止時間が必要ですが、これは大幅に異なる可能性があります。

この手順では、主に `hobby プラン`の場合などを想定して `pg:copy` の場合の解説を行います。

## 手順

### 1. `pg:upgrade` に適した状況でないことの確認

以下のいずれかに該当することを確認します:

- `hobby プラン`の場合
- データベースで checksum が有効になっていない場合
- データベースに `bloat` が発生している場合

上記のいずれにも該当しない場合、
`pg:upgrade` コマンドを利用した方法の実施を検討します。
[Upgrading the Version of a Heroku Postgres Database | Heroku Dev Center](https://devcenter.heroku.com/articles/upgrading-heroku-postgres-databases#upgrading-with-pg-upgrade)

## 2. `pg:copy` が適した状況であることの確認

以下の要件を満たしていることを確認します:

- データベースのサイズが10GB未満
- GBあたり約3分の停止時間を許容できる

## 3. Heroku にログイン

```console
heroku login --interactive
heroku: Enter your login credentials
Email: ****@****
Password: ********
```

## 4. データベースをプロビジョンするコマンドの取得

下記のサイトの `Plans & Pricing` で移行先のデータベースのプランをクリックし、
`Install Heroku Postgres` のコマンドをコピーします。
[Heroku Postgres - Add-ons - Heroku Elements](https://elements.heroku.com/addons/heroku-postgresql)

## 5. 新しいデータベースのプロビジョン

コピーしたコマンドを実行し、新しいデータベースをプロビジョンします。
例:

```console
heroku addons:create heroku-postgresql:hobby-dev -a app-name
```

実行後に以下のような行が表示されます:

```console
Created postgresql-****-**** as ****
```

as 以降の文字列をコピーしておきます。

## 6. メンテナンスモードの有効化

データベースの書き込みを防ぐためにメンテナンスモードに入ります:

```console
heroku maintenance:on -a app-name
```

## 7. Dyno の状態の確認

データベースにデータを書き込んでいないことを保証するために、
Dyno の状態をチェックします:

```console
heroku ps -a app-name
```

## 8. Dyno のスケールイン

手順6.で稼働中の Dyno が見つかった場合、
Web および Web 以外の Dynos を 0 にスケールインします:

```console
heroku ps:scale worker=0 -a app-name
```

## 9. 新しいデータベースへのデータ転送

```console
heroku pg:copy DATABASE_URL DATABASE_URL_DESTINATION --a app-name
```

## 10. データが転送できたことの確認

PostgreSQL の client などを使って、
データが転送できたことを確認します。

## 11. 新しいデータベースの昇格

```console
heroku pg:promote DATABASE_URL_DESTINATION
```

このタイミングで環境変数 DATABASE_URL が新しいデータベースに更新されます。

※ 元のプライマリデータベースが複数のアプリにアタッチされている場合は、
　 herokuアドオンを使用して新しいデータベースをそれらのアプリにアタッチする必要があります。

※ 昇格後、元のプライマリデータベースのフォロワーが
　 自動的に新しいプライマリデータベースのフォローを開始することはありません。
　 必要に応じて、新しいプライマリデータベースに新しいフォロワーを作成します。
　 また、不要になった古いフォロワーをプロビジョン解除するようにしてください。

## 12. Dyno のレベルのリストア

通常のアプリケーション操作を再開するために、Web および Web 以外の Dyno を元のレベルに戻します:

```console
heroku ps:scale worker=1 -a app-name
heroku ps -a app-name
```

## 13. メンテナンスモードの終了

```console
heroku maintenance:off -a app-name
```

## 14. 古いプライマリデータベースの名前の確認

```console
heroku pg:info -a app-name
```

DATABASE_URL を持たない方の名前から “_URL” のサフィックスを除いた文字列が
古いプライマリ・データベースの名前です。

## 15. 古いプライマリデータベースをプロビジョニングの解除

```console
heroku addons:destroy HEROKU_POSTGRESQL_OLD -a app-name
```

※ 古いプライマリデータベースに関連付けられていたデータクリップは、
　 新しいデータベースに再割り当てする必要があります。
　 データクリップの回復の指示に従って、回復可能なデータクリップを解決します。

参考: [Upgrading the Version of a Heroku Postgres Database | Heroku Dev Center](https://devcenter.heroku.com/articles/upgrading-heroku-postgres-databases)