LegalOn Technologies Engineering Blog

LegalOn Technologies 開発チームによるブログです。

宣言的DBスキーマ管理ツールの導入

こんにちは、SRE&プラットフォームグループの和田です。LegalOn Technologiesでは、2019年4月に「LegalForce」、2021年1月に「LegalForceキャビネ」を正式リリースして以来、毎月機能アップデートをしています。また、リリース初期段階では予期できなかった技術的な課題に対処するために、バックエンドやインフラの改善も進めています。このような取り組みの中で、DBスキーマの更新も多数発生してきました。「LegalForce」では、シーケンシャルなDBスキーマ管理を採用していましたが、運用面での懸念が顕在化していました。一方、「LegalForceキャビネ」は、NoSQLからRDBMSへの移行計画の真っ只中で、将来的なDBスキーマ管理方法のプランを練っていました。 こういった文脈において今回は「LegalForce」と「LegalForceキャビネ」製品のCI/CDに宣言的DBスキーマ管理ツール( sqldef )を導入した事例についてご紹介します。

「LegalForce」製品での事例

導入以前の課題

2019年の正式リリース以降、「LegalForce」製品の開発コードには膨大な数のマイグレーションファイルが含まれるようになり、コードベースでのDBスキーマの把握が困難になっていました。また検証環境にて開発ブランチの検証を行う際に、それぞれのDBスキーマが異なる場合があるため開発者が確認を行い、必要ならば検証環境のロールバックを行っていました。この作業は認知負荷が高く、また手作業で行うため時間がかかるものとなっていました。さらに膨大なマイグレーションファイルを実行するため、実行時間が線形的に増加しており、DBスキーマ変更とマイグレーション実行速度に課題を抱えていました。

なぜsqldefを選定したか

「LegalForce」製品におけるテナントデータ管理は、マルチテナント方式におけるブリッジ型を採用しています。ブリッジ型では、テナントごとにDBスキーマを持つため、テナント数と同じ数のマイグレーション実行が必要となります。つまり、テナント数 × ALTER文の実行が必要になります。

シーケンシャルなDDL(データ定義言語、CREATEやRENAMEなど)管理では、DBスキーマ変更の履歴を一から適用する必要があります。一方、宣言的なDBスキーマ管理では、新規テナントのDBスキーマ適用に基本的にCREATE文を流すだけで済むため、その点が一番のメリットと感じています。

当初は Ridgepole も導入を検討していましたが、本番環境と同数のDBを用意した環境での検証では、実行時間比較で Ridgepole は約30分かかるのに対し、sqldef は約5分と、圧倒的に高速でした。こういった背景により、実際の運用におけるイテレーションスピードを考慮して、sqldef 導入に至りました。sqldef の 詳細解説については本家 README をご参照ください。

「LegalForce」製品における効果

シーケンシャルなマイグレーションファイルから脱却したことで、テーブル状態が一目瞭然になったことは最大の利点です。また、検証環境を使用した後に異なるDBスキーマを適用する場合でも、開発者は DDL を定義するだけでよく、対象環境の現状DBスキーマを意識することなく適用できるため、手作業でのロールバック対応が不要となりました。さらに、DBスキーマ変更のマイグレーション実行時間の時短を果たせたことのメリットも大きいです。

「LegalForceキャビネ」製品での事例

sqldef 導入経緯

「LegalForceキャビネ」では先に触れたNoSQLからRDBMSへの移行と併せて、開発言語の刷新を計画しておりましたので、言語に依存したマイグレーションスクリプトの実装を避けたかったことがあげられます。また、上記のように「LegalForce」製品での利用実績がありましたので、ツール統一化という点でも違和感なく sqldef の導入に踏み切りました。

オンラインDDL検証

「LegalForceキャビネ」製品では、2023年初頭にNoSQLからRDBMSに移行を行い、MySQL8.0を利用しています。MySQLでは、バージョン5.6 以降オンラインでDDLの操作を行うことができ、 バージョン8.0.12 で導入された INSTANT ALGORITHM を利用すると高速かつ負荷をかけずにカラム追加やテーブル名の変更などのDBスキーマ更新が行えるようになりました。これにより、サービス無停止でDBスキーマ更新を行うことができます。

ただし、同 ALGORITHM が利用できない場合、自動的に INSTANT → INPLACE → COPY の順番でフォールバックするため、sqldefによるDBスキーマ更新時に開発者が意図しないタイミングで パフォーマンス劣化やダウンタイムにつながる可能性があります。

それぞれの ALGORITHM は以下のような特徴があります。


  • COPY: 操作は元のテーブルのコピーに対して実行され、テーブルデータは元のテーブルから新しいテーブルの行ごとにコピーされます。 同時 DML は許可されません。
  • INPLACE: 操作ではテーブルデータのコピーは回避されますが、テーブルが適切に再構築される可能性があります。 操作の準備フェーズおよび実行フェーズでは、テーブルに対する排他的メタデータロックが短時間で取得される場合があります。 通常、同時 DML はサポートされています。
  • INSTANT: 操作では、データディクショナリ内のメタデータのみが変更されます。 準備および実行中にテーブルに対する排他的メタデータロックは行われず、テーブルデータは影響を受けず、操作が即時に行われます。 同時 DML が許可されます。 (MySQL 8.0.12 で導入)

パフォーマンスおよび領域要件 より抜粋 https://dev.mysql.com/doc/refman/8.0/ja/alter-table.html


ワーストケースとしては、DDL操作でCOPYアルゴリズムしかサポートしていない場合です。トランザクションが最も発生しやすい時間帯に実行すると同時DML(データ操作言語、SELECT、INSERT、UPDATEなど)を流せないために、アプリケーションのレイテンシー増大やエラーレート上昇などのサービス影響が発生する可能性があります。こういった事態はSREとしては避けたいイベントです。

DDL操作がどのALGORITHMをサポートしているかを把握することは容易ではありませんが、ALTER文を実行することでALGORITHMの利用可否を判断できます。そこで、適用直前ではなくPRレビューの段階で検知するためのGithub Actionsのワークフローを作成しています。このワークフローにより、ALTER文がALGORITHM=INSTANTを利用可能かどうかを判断できます。

  1. 開発ブランチからDBスキーマの更新をするPRを投げる
  2. 以下 Github Actions の workflow 内での処理

    1. 本番で利用するMySQLと同バージョンのコンテナ起動
    2. 本番環境のスキーマMySQLコンテナに投入

       cat *.sql | mysqldef -u${MYSQL_USER} -p${ROOT_PASSWORD} -h${MYSQL_HOST} ${MYSQL_DATABASE}
      
    3. MySQLコンテナに対して開発ブランチのDBスキーマを --dry-run オプションを付けて mysqldef を実行

       cat *.sql | mysqldef -u${MYSQL_USER} -p${ROOT_PASSWORD} -h${MYSQL_HOST} ${MYSQL_DATABASE} --dry-run > dry-run.txt
      
    4. ALTER文が検出された場合は、ALGORITHM=INSTANTを連結してMySQLコンテナにALTER文を流す

       grep ALTER dry-run.txt | sed -e 's/;/,ALGORITHM=INSTANT;/g' > alter_algorithm_instant.sql
       mysql -u${MYSQL_USER} -p${ROOT_PASSWORD} -h${MYSQL_HOST} ${MYSQL_DATABASE} < alter_algorithm_instant.sql 2>error
      
    5. ALGORITHM=INSTANTが利用できない場合は下図のようなコメントがGithub Actionsで投稿され、リリースタイミングやメンテナンス要否をリリースメンバーが判定を行う

      ALGORITHM=INSTANTが利用できない場合に投稿されるコメント

  3. PRレビューを行い、承認を得てマージを行う

Github Actions に組み込んでいるコードの一部を抜粋していますが、実際に使用する場合は実環境に合わせて変更してください。

「LegalForceキャビネ」製品における効果

sqldefは、DDLで定義されたDBスキーマを再現するツールです。ただし、sqldefはALTER文を適用した場合のDBへのパフォーマンス影響までは管理できず、利用者の自己責任となります。CIへ予防的にALTER ALGORITHMの検証を組み込むことで、DBスキーマ適用に注意が必要な場合には、自動的にGitHubのPRに警告コメントが投稿され、レビューの段階でデプロイの安全性について議論できるため、開発者としても安心してスキーマ更新を行えます。

導入による副次的効果

sqldefを導入したことで、グループ内の開発者やSREの作業負担を減らすことができるだけでなく、製品のDBスキーマを参照する機会があるグループともSQLを使用してコミュニケーションが可能となり、DBスキーマに関する議論が円滑に進んでいます。 例として、データ分析を行うグループからDBスキーマについて質問されたとき、sqldef導入前では開発者がスプレッドシートなどにDBスキーマを書き起こして連携していました。sqldef導入後は、データ分析グループがDBスキーマについて問い合わせること無く、リポジトリに登録されたDBスキーマを直接参照することで、分析データの抽出・変換のプランが立てられるようになりました。

今後の展開

本番環境への適用では滅多に起こらないことですが、リネームに関しては sqldef ではサポートされていないため、リネームを検知する仕組みを検査対象に組み込むことも検討したいと思います。

終わりに

今回は宣言的DBスキーマ管理ツール( sqldef )を導入した話をお届けしました。(情報提供にご協力いただいた、SRE&プラットフォームグループの田中さん、LegalForceグループの伊藤さんありがとうございました!)

LegalOn TechnologiesのSREグループでは、プロダクトの信頼性を担保する活動や開発者が楽に開発・運用できるような基盤を整備する活動を通じて顧客により大きな価値を届けられるようこれからも改善を続けていきます。

もし興味があれば下記の募集要項をご覧ください!

ぜひ私たちと一緒に、法とテクノロジーの力で、安心して前進できる社会を創っていきましょう。

https://herp.careers/v1/legalforce/3vMaOuULOPJm

参考資料

  1. Silo, Pool, and Bridge Models https://docs.aws.amazon.com/wellarchitected/latest/saas-lens/silo-pool-and-bridge-models.html
  2. MySQL 8.0: InnoDB now supports Instant ADD COLUMN https://dev.mysql.com/blog-archive/mysql-8-0-innodb-now-supports-instant-add-column/