今の職場ではマイグレーションツールの類いを全く使っておらず、メンテナンスが辛いことがあるので、以前から話に聞くFlywayを調べていてはまった障害についてメモを残す。

発生条件と現象

MySQLモードのH2DBとFlywayを組み合わせて作ったSpring Bootのアプリを起動すると以下のエラーが発生する。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.internal.dbsupport.FlywaySqlScriptException: 
Script failed
-------------
SQL State  : 90079
Error Code : 90079
Message    : スキーマ "public" が見つかりません
Schema "public" not found; SQL statement:
CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(100) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
) [90079-196]
Line       : 17
Statement  : CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(100) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
)

これはMySQLモードを指定しないH2DBでは発生しない。

調べてみると、H2DBではMySQL時のスキーマ名がPUBLICと大文字になってしまう?らしく、Flywayでバージョン管理テーブルを作る際にエラーが発生する模様。

解決策第1弾

とりあえずapplication.propertiesにてFlywayに大文字のスキーマ名を指定する。

flyway.schemas=PUBLIC

すると状況は変わるがやはりエラー。

Caused by: org.h2.jdbc.JdbcSQLException: スキーマ "public" が見つかりません
Schema "public" not found; SQL statement:
SET SCHEMA "public" [90079-196]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.message.DbException.get(DbException.java:179) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.message.DbException.get(DbException.java:155) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.engine.Database.getSchema(Database.java:1755) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.command.dml.Set.update(Set.java:408) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.command.CommandContainer.update(CommandContainer.java:101) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.command.Command.executeUpdate(Command.java:260) ~[h2-1.4.196.jar:1.4.196]
	at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:207) ~[h2-1.4.196.jar:1.4.196]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
	at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) ~[tomcat-jdbc-8.5.23.jar:na]
	at com.sun.proxy.$Proxy73.execute(Unknown Source) ~[na:na]
	at org.flywaydb.core.internal.dbsupport.JdbcTemplate.execute(JdbcTemplate.java:219) ~[flyway-core-3.2.1.jar:na]
	at org.flywaydb.core.internal.dbsupport.h2.H2DbSupport.doSetCurrentSchema(H2DbSupport.java:70) ~[flyway-core-3.2.1.jar:na]
	at org.flywaydb.core.internal.dbsupport.DbSupport.setCurrentSchema(DbSupport.java:101) ~[flyway-core-3.2.1.jar:na]
	... 23 common frames omitted

解決策第2弾

これに加えてマイグレーションスクリプトの一つ目でスキーマを作成することでエラーは消える。

create schema if not exists "public";

この問題は分かりにくい上、まともな解決策とも思えないので何とかして欲しいのだが、参考ページを見る限りは望み薄という印象。

プロジェクトはこちら。

springboot-study/flyway-h2db-mysqlmode-trouble at master · orimajp/springboot-study

参考