NBM2

natural born minority

みうみうのFlyway体験記

みうみうのFlyway体験記

2015/08/29 Hoge駆動な人々 - #Hogedriven


導入(思想と使い方)


抑えておきたい特徴

  • 基本CLIツール
  • Javaプログラムから呼び出すなど操作することも可能
  • 「任意のフォルダ」と「ファイル命名則」と「Mig対象DBに1つ特殊テーブル」を必要とする
  • 基本SQLがデフォだが「Javaプログラムでの操作」も書ける
  • 思想的に(つまりおそらく永久に)「ダウングレード」には未対応
  • 他に依存しているライブラリが無い

スクラップ

  • http://flywaydb.org/
    • 本家サイト
  • https://github.com/flyway/flyway
    • ソース
  • http://dev.classmethod.jp/tool/flyway-db-migration/
  • https://siguniang.wordpress.com/2013/11/10/db-schema-migration-made-easy-with-flyway/
    • 運用まで視野に入れた詳しめのTIPS
  • http://qiita.com/opengl-8080/items/6368c19a06521b65a655
  • http://tototoshi.hatenablog.com/entry/2015/01/27/234314
    • 複数人開発に向いてないんじゃないか問題

スクラップ - 2

  • http://erudoru.hatenablog.com/entry/2013/11/26/002659
    • SQLじゃなくてJavaで書く方法
    • http://erudoru.hatenablog.com/entry/2013/11/26/002659
      • こちらはSpring編
  • http://blog.livedoor.jp/ryu22e/archives/65722084.html
    • maven-flyway-plugin の詳しめの設定
  • http://stackoverflow.com/questions/12619446/can-i-use-property-file-in-maven-pom-xml-for-flyway-configuration
    • maven-flyway-plugin の接続設定を外部ファイルから読み込ませる方法。

マイグレーション一つに対する「結果ステータス」

「一つのマイグレーション結果」で代表的なものは4種類(本当はもっとあるのだが目につくものだけ)

  1. Success - 成功。順番どおり実行され成功に終わった。
  2. Failed - 失敗。何らかの理由でマイグレーション実行中に失敗。
  3. Ignored - 未実行。順序的に不正なので実行しなかった。
  4. OutOfOrder - 成功。順序はおかしかったが無視して実行し成功した。

実行中のログ or 管理テーブル中の”success”カラムにて確認することが出来る。


「outOfOrderルール」について

  • 設定に「outOfOrder」というフラグがある
    • CLI実行なら flyway.conf に “flyway.outOfOrder=true”
    • maven-plugin ならconfigのoutOfOrderタグでtrue
    • プログラムなら flyway.setOutOfOrder(true);
  • true にすると「後から若い番号のが来ても順序無視して実行」するようになる
    • outOfOrder=false(デフォルト)時
      • 取りうる結果ステータスは Success,Failed,Ignored
    • outOfOrder=ture時
      • 取りうる結果ステータスは Success,Failed,OutOfOrder

デモ

ここで見せるすべては、↓ここに。

https://github.com/kazuhito-m/flyway-sample-webapp


前提(今運用しているシステム)


比較的「小さい」Webアプリ

  • リリースは「50mくらいのWar一本」
  • DBも「一分かからずダンプ取れる」ような軽いもの
    • ただし本運用してない
    • 段階的に範囲に利用者が三倍になる
    • メイントランザクションデータの「削除」が無いので貯まる一方
    • つまり「軽い」のは今だけ?
  • データはDBの他に「とある領域の画像ファイル」もアプリの持ち物

ソース管理

  • 「master branch(trunk)一本」運用
    • 歴史的背景(SVN期間の長さ)などありこの形態
    • でかい修正or緊急本番対応の場合だけ時々branchが切られる
  • 「リリースタイミングがほぼ固定」「関係者は一箇所に集まる6人くらい」「外部から変更が無い」ので困ってない

バイナリ

  • プロファイルでリリースするごとに分けてビルド
    • 環境依存系はバイナリに織り込まれ同じバイナリが使えるわけではない
    • リリース都度ビルド

最近「ご破産にして作りなおして」る

  • 外部仕様とDBは極力似せて
  • ソースは「極力旧から持ってこない」
    • …が、時間の問題で多々持ってきてるダメさ

リリースについて


リリース対象の環境とタイミング

  • 環境はSTG,本番の2つ
  • 開発→ステージング→本番と「かならず手順を踏んで」マイグレーションする
  • タイミングは「一週に一回STGへ」「二週に一回本番へ」「本番へ行くのはSTGで一週以上動かしたもの」
  • 少し急ぎの修正は「STGのルールを崩し」てSTGに緊急リリース
    • 本番リードタイム=2週-1〜2日
  • 致命的な修正は「すべてのルールを崩し」てその日にSTG、次の日本番
    • リードタイム=STG=0〜1日、本番=1〜2日

Flywayの運用


導入経緯

  • 導入は「途中から」
    • スクラムマスターの提案による
      • 仕込みも本人によるものだが…
    • 「PJ途中からの導入」にも適していると言えそう
      • Flywayは「他に依存しないこと」に気を使ってるように見える(optional=true,scope=test,proided)

決めごと

  • ファイル命名則
    • V[000]_[アクション][対象テーブルor抽象的集合名].sql
  • ルール
    • 連番は最近000の三桁
      • 「ファイルが名前順に並んでくれない」は意外とストレスなんで…ご破算にする時に
    • アクションは単一ならCUDそうでなければ目的ベースのなまえみ
  • Webアプリ立ち上がり時に動くよう仕込んである
    • ローカル、ステージ、本番も皆同様
  • 「ロールバックな運用」(DDL,データともに)は出来ないししない

決めごと - 2

  • 基本、ミスがあっても「フォワードで直して」行く
  • 「トランザクションデータの修正」「超緊急の修正」以外は極力マイグレーションに寄せる
  • 一応「ローカル用」「ステージング環境用」など「ここに置けば環境限定で動作」という場所は設けてある
    • 作りこんだ(こまれてた)
    • 開発用環境だけ、ステージングだけ、かつ共有のような独特なものがあるため
    • 「環境限定」でもメインストリームと通し番号共有
    • もちろんながら「極力使わないように」

開発時

  • 各自がマイグレーションを使いたく成った時点で「この番号使うから!」宣言メール送る
    • Flywayに限らない一般論だが「マイグレーションのこの変更を無かったことにする」のはほぼ無理
    • 番号かぶりでコミットした場合も「どちらかの端末の変更を殺す必要があるのは変わらない
    • 「ファイルが存在した途端にVersionが進む(0行SQLが無事成功)」ので、「バージョンだけ予約しといてあとで実装」とかできない
    • 最悪ローカルで事故ってるうちはいいがpushしてしまうと事故が全端末に波及する
    • ので、このルール

利用のエピソード

  • 画像ファイルのディレクトリの移動/大文字小文字リネームに「Java側機能」使う
    • Webシステムだし「アップロード画像」も「利用者のデータ=DB相当」と捉えて
  • 画面の無い(お客様が本番で変更しようのない)マスタテーブルのメンテナンス
  • 俗に言う「データパッチ」も一部マイグレーションで
    • バリバリの利用者入力直結のトランザクションはしないが…
    • 「関連テーブル」などの「ユーザが見えない部分」かつ「法則性が一律に適用できる」場合

「何が正しいのか」結構もめる

  • 「一回使い捨て」「書き方自由」「SQLかJavaかすらも自由」という「自由過ぎ」さ
    • 同じ目的を達成するにも手段が何種類か幅がある
    • 個人の資質や経験や文化によってブレやすい
  • 揉め事の対象になりやすい…かも?
    • 例えば…
      • 「ユニークID直撃で行く」か「リレーションを追って意味ベースで行く」か
      • 「Aテーブル or BテーブルのJOINで行く」か
    • レビューは必須なのだが、宗教戦争にもなりがち
  • ある程度の「ルール」が必要かも?   —-

もたらした変化


注意

  • 一例です
  • 小並感の可能性があります
  • Flywayのことでなく「マイグレーションを導入した場合」という一般解かも

「データ変更はできるだけMigrationに寄せよう」キャンペーン

  • スクラムマスターが意図的に煽ってた
    • それまで緊急対応という「開発以外に手を取られるもの」が多かった
    • 事故も多かった
  • 「データの変更」を「ビビりながらキーを叩く作業」から「開発」の舞台へ持ってくる
    • 「テスト・リハ済み」「自動化」された「変更」という名の成果をデリバリ

「データ変更はできるだけMigrationに寄せよう」キャンペーン - 2

  • 開発者が「その楽さ」に気づく
    • プログラマは「手順書かかせて手でやる」より「開発の手法」のほうが得意
      • こだわりの「手作業」こだわりの「チェックシート」のバイアスはむしろ雇い主側から
    • 思考の「一発目のフィルタ」として「マイグレーションで片付けられないか?を模索するように

「DBへの変更の大半はマイグレーションの領域」という気付き

TODO 図や色々で説明しますわ

  • ただし、この領域を間違ってはいけない

「アプリ内におけるデータ」の 変更タイミングの”おしなべ”と「DBでなくても良いんちゃう?」気づき

  • アプリケーションにおける「すべてのデータ」には、なんとなく「硬さ(≒ライフサイクルかなぁ?)がある…けど上手く言えない。
  • 粒度はでたらめだが…脳内ではこんな感じ!
    • 左ほど静的(固く)で右に行くほど動的(やらかい)

俺が考える「アプリケーションにおけるすべてのデータ」

定数 → enum → リソース → 文字列リソース → (内部)設定ファイル → (外部)設定ファイル → 環境変数 → DB:スキーマ定義(DDL) → DB:1タプルしか無い全体設定系 → DB:環境依存設定系 → DB:画面無しマスタ → DB:マスタ → DB:システム都合で生むもの → DB:トラン系関係T等 → 入力系ファイル(画像とか) → DB:トラン系入力データ

  • 関係T等 = 利用者のアクションで生んでるが「直接目に触れない」もの

「一般的なギョームアプリ」の「リリースタイミングで変化するモノ」

定数 → enum → リソース → 文字列リソース → (内部)設定ファイル → (外部)設定ファイル → 環境変数 → DB:スキーマ定義(DDL) → DB:1タプルしか無い全体設定系 → DB:環境依存設定系 → DB:画面無しマスタ → DB:マスタ → DB:システム都合で生むもの → DB:トラン系関係T等 → 入力系ファイル(画像とか) → DB:トラン系入力データ


導入前→導入後でこう変化する

定数 → enum → リソース → 文字列リソース → (内部)設定ファイル → (外部)設定ファイル → 環境変数 → DB:スキーマ定義(DDL) → DB:1タプルしか無い全体設定系 → DB:環境依存設定系 → DB:画面無しマスタ → DB:マスタ → DB:システム都合で生むもの → DB:トラン系関係T等 → 入力系ファイル(画像とか) → DB:トラン系入力データ

※ このプロジェクトでは「マイグレーションでやりすぎている」疑惑はあるが…


「DBで無くても良んじゃね?」が浮かび上がって来た

  • 「画面無しマスタ」はほぼ「enum」と同義
  • データの「ライフサイクルが一緒」なら「わざわざ柔らかいものにしている」理由が無い
    • その分を「リリースコスト下げやスピードを上げ」に力入れたほうが美味しくいただける

もしかしたら

「マイグレーションがある場合の設計」というものもあるのかも?


以上

あとはなんでも聞いてつかあさい!

blog comments powered by Disqus