こんにちは!バクラク事業部 Platform Engineering 部 SRE チームの id:sadayoshi_tadaです。趣味で筋トレをやっていてこれまでほぼ1人でやっていたのですが、最近社内の人達とトレーニングすることが増えて楽しい今日このごろです。
この記事ではSREチームで行っている、DBマイグレーションにおけるガードレールの取り組みについて紹介します。
DBマイグレーションにまつわる課題について
本題に入る前に、バクラクのデータベースのスキーママイグレーション(以降、DBマイグレーションと呼称します)で起こっていた課題について簡単に触れます。
バクラクではリリースにおいてアプリケーションのデプロイと合わせて、DBマイグレーションが発生することがあります。あるリリースでマイグレーションを実行したところ、テーブルの書き込みロックを取ってしまい、プロダクト利用時に書き込みが出来ないなど影響が出てしまったことがありました。
その対策として、特定条件に合致したテーブルへの変更を行う場合、プロダクトチームから実行予定のSQLをSREチームにレビュー依頼するフローと、DBマイグレーションのSQL解析を行うCIを追加したので、その紹介をします。
SREチームによるDBマイグレーション時のレビュー
バクラクではデータベースとしてAmazon Aurora MySQLのバージョン3を使用しています。そのため、DBマイグレーションのALTER TABLE実行時にALGORITHM句にINSTANTを指定し、メタデータ変更のみで高速にかつ、テーブル書き込みロックを回避できる場合はあまり問題になりません。しかし、テーブルデータへの書き込みを伴うINPLACE,COPYアルゴリズムを用いたALTER TABLEを実行する場合には、SQLレビューを実施する体制としました。
上記フロー図にある、テーブルのレコード数の確認と実行時間の計測にはBytebaseを使用しています。Bytebaseではテーブルの大まかなレコード件数の閲覧ができ、以前の記事でも紹介した、クエリのテスト環境にて本番相当のデータベースでクエリ実行時間の計測を行っています。
当該レビュープロセスを導入以降、事前レビューを実施し、通常のリリース時間帯での実行が難しい場合は別の実行時間帯を調整するなど、お客様の利用影響を抑えた選択を取りやすくなりました。
ALTER TABLE実行時にALGORITHMを明示するCIを追加
上記のレビューと併せて、ALTER TABLEを実行するSQLでALGORITHMを明示することをチェックする機能を追加しました。ALGORITHMを明示することで、そのALGORITHMで実行できないDDLに対してはエラーを返すようになります。これによって、予期しない方式でDDLが実行されることを防ぎ、パフォーマンス影響のあるDDLを事前に察知することができるようになります。
なお、当該機能を実装した背景に既に利用している、atlas や semgrep などのツールで要件を満たすルールがサポートされていなかったことがあり、自前で実装しました。
SQLの解析にはpingcap/tidb/pkg/parserを利用しました。これは別チームでも利用実績があったことや、SREチーム内でも候補にあがったので実装しながら動作確認してみることにしました。以下は簡素なコード例ですが、ALTER TABLEを実行するSQLを解析し、ALGORITHM指定が漏れていたり誤った処理方式を指定しているとエラーが発生します。サンプルのSQLではALGORITHM指定がないため、Error: No ALGORITHM is specified for the ALTER TABLE statementexit status 1
という結果になります。
package main import ( "fmt" "os" "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/parser/ast" _ "github.com/pingcap/tidb/parser/test_driver" ) func main() { // サンプルのSQL alterTableSQL := `ALTER TABLE my_table ADD COLUMN new_col INT;` p := parser.New() stmts, warn, err := p.Parse(alterTableSQL, "", "") if err != nil { fmt.Printf("SQL Parse Error: %v\n", err) } if len(warn) > 0 { fmt.Printf("SQL Parse Warn: %v\n", warn) } for _, stmt := range stmts { alterStmt, ok := stmt.(*ast.AlterTableStmt) // ALTER TABLE文がない場合はスキップ if !ok { continue } if !checkAlgorithmClause(alterStmt) { fmt.Println("Error: No ALGORITHM is specified for the ALTER TABLE statement") os.Exit(1) } } fmt.Println("OK: All ALTER TABLE statements have ALGORITHM specified.") } func checkAlgorithmClause(stmt *ast.AlterTableStmt) bool { for _, spec := range stmt.Specs { if spec.Tp == ast.AlterTableAlgorithm { if spec.Algorithm == ast.AlgorithmTypeInstant || spec.Algorithm == ast.AlgorithmTypeInplace || spec.Algorithm == ast.AlgorithmTypeCopy { return true } } } return false }
上記をベースにしたコードをGitHub Actions上で実行し、SQLのALGORITHM指定の有無をチェックします。ALGORITHM指定がない場合はPull Requestにgithub-commentで指摘のコメントが入ります。
- name: Parse SQL env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | github-comment hide && \ github-comment exec \ --config .github-comment.yml \ --template-key parse-sql --var title:'Validation failed: ALTER TABLE ALGORITHM' \ -- go run [ファイルパス]/main.go "[変更があったSQLファイルパスを指定]"
SQLファイルにALGORITHM指定を促すコメントが追加された例が以下のスクリーンショットです。document link
の中でSQLの修正の補足と、前述の特定条件に合致したテーブルへの変更を行う場合にSREチームへのレビュー依頼を行って欲しい旨を記載してます。
当該CI導入後は、DDLにおいてALGORITHM句が必ず明示されるようになります。パフォーマンス影響がありうるDDLの実行にあたり、前述したBytebaseのテスト環境で動作確認することで、開発初期段階で懸念を解消する動きが取られるようになってきました。
今後の課題
本記事で紹介した取り組みで一定のガードレールを敷くことができたと思いますが、まだ改善したいことも残っています。例えば、ALGORITHMの指定がない場合はマージブロックするように制御したり、SREチームのレビュー方針を詳細にしてPull Request時にALGORITHMの有無チェックと同時に、方針に合わせたレビューを生成AIで実現できないかなど改善したい部分があるので、今後も引き続きより良いフロー作りに取り組んでいきます。
まとめ
SREチームで行っている、DBマイグレーションにおけるガードレールの取り組みを紹介しました。引き続きお客様に安定したサービスをお届けするため、改善の取り組みを続けていきます。
最後に
SREチームでは人々の「働くをラクに。ラクをもっと創造的に。」を目指したプロダクトの信頼性を共に高めていく仲間を募集しています。
また、バクラクのPlatform Engineering部では、組織全体の開発生産性をあげるべく、各チームで様々な開発を行っています!関連するJDが以下になりますので気になる方はぜひご覧ください!
まずは話だけでも、という方は是非カジュアル面談やLayerX Casual Nightといったイベント(直近は3/19に開催されます)にぜひ応募ください。