中間データのスキーマを安全に変更する手順

ここのところデータパイプラインの配線繋ぎかえ工事をしていたのですが、とくに目立ったダウンタイムや障害なく終われたので、その基本パターンをメモしておきます。
主に繋ぎかえたのはデータベースかストレージ挟んだ多段バッチですが、ストリーミングやサービス間通信でも一方通行なら、だいたいこんな感じになるのではないかと思います。

# カラム（フィールド）追加パターン

とあるデータベース（ストレージ）のテーブル（パス）にカラム（フィールド） a があって、それを読み書きしているとします。この流れに b を加える手順を考えてみましょう。

※用語は環境によって適当に読みかえてください。

![SoWkIImgAStDuIeloYn9BLBGjLDGYawCLR9IIEG2CtJjYb8JKrABG08EgNafGBK0.png](https://qiita-image-store.s3.amazonaws.com/0/55208/3a966919-c719-53a5-bacc-038e0a666af8.png)

テーブルにカラム b を NULL 許可 （オプショナル）で追加します。
この段階では b は NULL で書かれていて、読まれません。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKT18qenLib98v4BmTEsAKXDJKej0eWwfUIb0NG00.png](https://qiita-image-store.s3.amazonaws.com/0/55208/67df5da7-e061-1e6c-bcc4-957bd5b55708.png)

カラム b の値を書くようにします。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKT18qenLib80ifAu40AwTaMfYIcfHK09BeVKl1IWHW00.png](https://qiita-image-store.s3.amazonaws.com/0/55208/ff627d16-3b60-040c-16e1-f1d9267405ac.png)

カラム b の値を読むようにします。
この時、NULL になっている古いレコードも読むのであれば writer で処理する前にデフォルト値埋めするなどの対応が必要になるでしょう。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKT18qenLib80ifAu40AwTaMfYIcfHL09XzIy5A1U0000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/222a2395-2ad8-3cda-5b4e-49f97a4b1405.png)

カラム b から NULL をなくせたら NULL 不許可にします。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKKYALR9I0D5SCAwkNL5gOafg4LJO8JKl1QWF0000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/918040cb-8e99-13c2-310c-9faad400a9b5.png)

この reader が更にどこかのテーブルへ b の情報を書きこんでいる場合は、今度は writer としてこのパターンを繰り返していきます。

# カラム（フィールド）削除パターン

追加パターンを逆走します。
ざっくり手順として、追加は上流から、削除は下流から、と覚えておけばよいです。
こうしてデータを上流から少しずつ流していく／下流から少しずつ断っていくことで、部分的なテストや切り戻しがしやすくなります

# テーブル（パス）切り替えパターン

a b を読み書きしていたとします。これをまるっと c d という別のカラムへ切り替えることを考えてみましょう。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKKYALR9I0D5SCAwkNL5gOafg4LJO8JKl1QWF0000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/d8613597-1628-0394-bffd-540dbf6d905a.png)

カラム c d をもった新テーブルを作成します。

![SoWkIImgAStDuIe0qfd9cGM9UIKAvKMP9Qb5A49AqOaw2acnIcY2oJeAAK11Y0O5NJkuEYi54Cq5u-hQ5QKcfgGMGOKTKlDIW9O20000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/fbaa227e-4ef9-0226-2a0c-1df6faad9fb0.png)

旧テーブルと新テーブルどちらにも書くようにします。
これらは異なるデータベースにあっても構いません。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKKYALR9I0D4e4iaw2YaW2H3D1LEdQrUKcfYIMWHLxv2Qbm8q4000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/db31f27c-f540-bcae-0203-43b77cb8a3a6.png)

旧レコードは旧テーブルから、新レコードは新テーブルから読むようにします。
新旧の峻別は、タイムスタンプなどの情報を使うことになるでしょう。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKKYALR9I0D4e4iaw2YaW2H3D1LEdQrUKcfYIMWHL3r6548QgTaZDIm7Q0m00.png](https://qiita-image-store.s3.amazonaws.com/0/55208/28394046-85fd-4346-3c27-1725939452e8.png)


旧テーブルにあるレコードが不要になったら、読みこみを止めます。
いつまで経っても不要にならないような場合は、どこかで旧レコード相当のものを新テーブルへ書きこんでおくなどの対応が必要になるでしょう。
![SoWkIImgAStDuIeloYn9BLBGjLDGYaxKKKYALR9I0D4e4iaw2YaW2H3D1UFgsXMb9gQa5a657LBpKe060W00.png](https://qiita-image-store.s3.amazonaws.com/0/55208/27bfdbcc-784e-c560-58a0-ad06d208beb6.png)

旧テーブルへの書きこみを止めます。

![SoWkIImgAStDuIe0qfd9cGM9UIKAvKMP9Qb5A49AqOaw2acnIcY2oJeAAK11Y0O5NJkua9K2YEQ2SNNjYb8JKrABeC8EgNafG4i1.png](https://qiita-image-store.s3.amazonaws.com/0/55208/61586b18-f898-a8b7-989e-3226a5e334ff.png)

旧テーブルを削除します。
必要があればアーカイブしておきましょう。

![SoWkIImgAStDuIeloYn9BLBGjLDGYaxMKKY9LR9I0D5SCAwkNL5gOafg4LJO8JKl1QWF0000.png](https://qiita-image-store.s3.amazonaws.com/0/55208/de06a950-aa70-d5a6-b8ab-8a6ca113e9b8.png)

要するに、これは追加パターンと削除パターンの合わせ技ですね。