ridgepoleのメリットと使い方

Ruby on Railsの標準的なデータベース定義ツールはActiveRecordの”マイグレーション”です。これは、データベース定義の変更点をSQLではなくRubyスクリプトで記述し、バージョン管理を可能にするものです。
ただ、マイグレーションはDB定義の変更が増えてくると無数のファイルが生成されて見通しが悪くなります。

ridgepoleを使うことで、マイグレーションと同様の記法でデータベース定義を1つの設定ファイルに集約でき、冪等な設定で運用できるようになります。また、RDBMSのドライバーを切り替えることで、 PostgreSQL / MySQL / SQLite などデータベース間のスキーマ移行も簡単になります。

CREATE DATABASE/DROP DATABASEはRails標準のrails dbを利用して、以下のようなコマンドセットでライフサイクル管理が可能になります。(PostgreSQL/MySQLに直接接続してCREATE DATABASEする手順でもとくに問題なく動作します)

クックパッドにおける最近のActiveRecord運用事情の説明から、クックパッド本サービスの大規模環境で利用されていることが分かります。

Rails以外のフレームワーク向けの活用

ridgepoleはRailsをターゲットとして作られたツールですが、RDBMSのスキーマ管理に特化しているため、他の用途にも活用できます。
たとえばPHPのWebフレームワークでもマイグレーションによるスキーマ管理を行うものがあり、メリット・デメリットの事情は同じです。

結局のところDB管理は、本線アプリのコーディングと別の管理が求められるため、ridgepoleを利用する余地はあります。
ただし、利用しているWebフレームワークとDB上のスキーマの規約を理解したうえで使う必要はあるでしょう。

インストールと基本的な使い方

セットアップは、bundlerを利用する場合、Gemfileに gem “ridgepole” を追加してbundle installです。

ridgepoleの基本的な使い方は、Schemafileというファイルにデータベース定義を記述しておき、以下のようにridgepoleコマンドでDBMSに設定を反映するだけです。

$ bundle exec ridgepole -c config/database.yml -f config/Schemafile -a

このコマンドにより、データベースのスキーマがSchemafileの定義と同じ状態になるようにDDLが発行されます。(--dry-runオプションを付けるとDDLの内容を確認できます)

引数の-aがapply、-cはRails標準のデータベース接続設定ファイルdatabase.ymlを指定しています。database.ymlは一般的なRailsの接続設定と全く同じです。

Schemafileにはカラムの定義にくわえて、インデックス、複合インデックスの定義も可能です。テーブル定義がまとまっているので、MySQLでdescribeしなくてもSchemafileを読めばクラス構造をほぼ把握できます。

運用中の既存DBに途中から導入

既存のデータベースのスキーマ管理のため、途中からridgepoleを導入したい場合、-eオプションのスキーマダンプを活用できます。

データベース接続設定(ホスト、ID・パスワードなど)をconfig.ymlに記載し、

$ ridgepole -c config.yml -e -o Schemafile
を実行すると、データベース内のテーブル一式を定義したスキーマ設定ファイルを得られます。

初歩的な注意点

ridgepoleを使う場合、マイグレーションを併用しないことが重要です。一見当然ですが、たとえばrailsコマンドでモデルを生成するとデフォルトでmigrationファイルも生成されるため、

$ rails g model SomeNewModel --skip-migration

のようにマイグレーションを作らないオプションを付けます。
不要なマイグレーションファイルが存在していると実行時にエラーになります。誤ってマイグレーションファイルが生成されてしまった場合には、単純にdb/migrate/にあるマイグレーションファイルをrmで削除します。

また、べき等な動作の結果、テーブル名・カラム名変更のつもりで安直に記述するとデータが消えうるため注意が必要です。名称変更の手順は、ridgepoleでテーブル名・カラム名の変更手順で解説しています。

安易に定義ファイルを分割すると危険

ridgepole -aでは、定義ファイルにデータベース内の全テーブルのスキーマが記述されていることが前提となります。
たとえば、ファイルをテーブル別に分割していて一部のファイルだけ参照してapplyしてしまうと、記述のないテープルについてはDROP TABLEの動作になり削除されてしまうので注意が必要です。

運用中のデータベースでこのようなケースに遭遇してしまうとテーブルごとデータロストしてしまうため、原則として1データベースの定義は1ファイルにまとめて管理した方が安全です。

スキーマ変更後にインスタンス再起動

ridgepoleコマンドでDBスキーマを変更したあと、Railsから認識するためにはAPサーバーインスタンスを再起動すべきなようです。

カラム追加後、再起動しない状態でアクセスしたところ、ActiveModel::MissingAttributeError (can’t write unknown attribute…のようなエラーに直面して、再起動したところ直りました。

database.ymlの環境変数をコマンドラインから指定

docker / kubernetes の環境で、databaseの接続ホストが動的に決まるためdatabase.ymlにerbを用いて変数で指定したいケースがあります。Railsは

host: <%= ENV['PG1_SERVICE_HOST'] %>
password: <%= ENV['POSTGRES_PASSWORD'] %>

のような記述で環境変数から接続情報を取得できるのですが、ridgepoleコマンドがテンプレート変数を処理できないケースに遭遇しました。

この場合、

ridgepole -c config/database.yml -f config/Schemafile -E PG1_SERVICE_HOST=192.168.0.1 -E POSTGRES_PASSWORD=connectionpass -a

と-Eオプションで設定することで期待どおりに動作しました。

ridgepole + PostgreSQLでUUIDを使う手順は手順が変わったため記事を分けました。