Cloud Spanner でも schemalex 見たいなスキーマ管理がしたかったので作った
スキーマ管理ツールはだいたい二種類あって、ActiveRecord見たいな変更差分のDDLをどんどん書いていって順番よく適用していく奴と、schemalexやRidgepoleみたいなテーブル定義のファイルから差分を出して都度適用していく奴があってこのツールは後者の方。
Spannerのスキーマ管理ツールを作る上で多分考える必要がありそうな以下の2点にも対応している。
- Interleave
- Not Null
Interleaveに関しては単純にテーブル定義でツリーを作り、自身が再作成でしか適用できない差分だった場合(主キーの変更やPARENTの変更)に子を全て削除してから自分を削除する。
Not Nullに関しては一度Nullableなカラムを追加した後にPartitionedUpdateでデフォルト値を設定してNot Nullを再設定する力技で解決している。
どちらもリリース済みの本番環境で気軽に行えることではなかったりするので注意が必要だけど、開発ではかなり役立つと思う。その他にもSTRING BYTE変換や長さ制限の変更など、ALTER出来る差分に関してはその様に適用してくれる。(そもそもSpannerはAlterで出来ることが大分少ないけど...)
使い方
$ hammer -h
hammer is a command-line tool to schema management for Google Cloud Spanner.
Usage:
hammer [command]
Examples:
* Export spanner schema
hammer export spanner://projects/projectId/instances/instanceId/databases/databaseName > schema.sql
* Apply local schema file
hammer apply spanner://projects/projectId/instances/instanceId/databases/databaseName /path/to/file
* Create database and apply local schema (faster than running database creation and schema apply separately)
hammer create spanner://projects/projectId/instances/instanceId/databases/databaseName /path/to/file
* Copy database
hammer create spanner://projects/projectId/instances/instanceId/databases/databaseName1 spanner://projects/projectId/instances/instanceId/databases/databaseName2
* Compare local files
hammer diff /path/to/file /another/path/to/file
* Compare local file against spanner schema
hammer diff /path/to/file spanner://projects/projectId/instances/instanceId/databases/databaseName
* Compare spanner schema against spanner schema
hammer diff spanner://projects/projectId/instances/instanceId/databases/databaseName1 spanner://projects/projectId/instances/instanceId/databases/databaseName2
Available Commands:
apply Apply schema
create Create database and apply schema
diff Diff schema
export Export schema
help Help about any command
Flags:
-h, --help help for hammer
Use "hammer [command] --help" for more information about a command.
これが
CREATE TABLE users (
user_id STRING(36) NOT NULL,
) PRIMARY KEY(user_id);
こうして
CREATE TABLE users (
user_id STRING(36) NOT NULL,
age INT64,
name STRING(MAX) NOT NULL,
) PRIMARY KEY(user_id);
CREATE INDEX idx_users_name ON users (name);
こうなる。
$ hammer diff old.sql new.sql
ALTER TABLE users ADD COLUMN age INT64
ALTER TABLE users ADD COLUMN name STRING(MAX)
UPDATE users SET name = '' WHERE name IS NULL
ALTER TABLE users ALTER COLUMN name STRING(MAX) NOT NULL
CREATE INDEX idx_users_name ON users(name)
Data Source
データの取得先はSourceという概念で抽象化されていて、SpannerとSpannerのDiffを取ったりSpannerのDBの定義を元にして別のDBを作成とかも出来たりする。
Spannerの接続先はこんな感じのフォーマットで、credentialsは省略するとApplication Defaultが使用される。
spanner://projects/{projectId}/instances/{instanceId}/databases/{databaseName}?credentials=/path/to/file.json
現在はローカルファイルとSpannerからのスキーマ取得に対応していて、必要に応じていろいろなData Sourceを追加できる様にしている。
create
最初はdiffとapplyだけのツールにしようと思っていたんだけど、SpannerはDBの作成と一緒にスキーマ定義を渡すとDB作成と同時にスキーマを適用してくれるAPIになっており、スキーマ定義なしでDB作成を行った場合とほぼ同程度の時間でスキーマを適用することが出来るのでコマンドとして用意した。
なのでCIの実行毎に1からDBを作成する際などにスキーマ適用時間(MySQLとは比べ物にならないほど長い)分まるっと短縮できるのでもし機会があれば活用して欲しい。
スキーマ管理で困っていたら是非
1ヶ月くらい実際のチーム開発とCIでガンガン使ってスキーマの更新をかけてみたけど問題ないので、もしスキーマ管理で困っていることがあれば是非試してみてください。