Spring Bootのバージョンアップで発生した仕様変更点と解消方法

f:id:urotazuka:20200622154440p:plain

こんにちは。ECプラットフォーム部マイグレーションチームの高橋です。

マイグレーションチームとは

ZOZOTOWNでは、システム的にレガシーな部分が多く存在しており、全体的なシステムリプレイスを進めています。その中でサーバーアプリケーションのリニューアルを行うために、2019年に発足したのがマイグレーションチームです。

現在は、データの取得・更新処理の実装の置き換えを主に対応しています。もともとのシステムではSQL Serverのストアドプロシージャを実行し、データの取得・更新を行っていた処理を、JavaでAPIとして実装し直し随時移行しています。

Spring Bootのバージョンアップ

チーム発足以来、参照系の処理をSpring Bootを採用してJavaのAPIに移行することを中心に進めてきました。

ようやく、参照系のAPI作成の終わりが少しずつ見え始めたため、先延ばしになっていたSpring Bootのバージョンアップに着手しました。使用していたSpring Bootのサポート期限が2019年8月1日までとなっており、既にサポートが切れていたため、セキュリティ面からも早急に対応する必要がありました。

環境

主な環境は以下の通りです。

種類 バージョン
Java 1.8
Maven 4.0.0
SQL Server 11.0
Swagger 2.0
MyBatis 3.4

後述しますが、Spring Bootのバージョンアップに伴いMyBatisのバージョンも3.4から3.5に上がりました。

バージョンアップ対象・内容

プロジェクト開始当時のSpring Boot最新バージョンは1.5.15(2018.7.30〜)、今回、バージョンアップの対象としたバージョンは着手当時の最新バージョンである2.2.5(2020.2.27〜)です。

メジャーバージョンも1つ上がっています。そのため、Spring Boot 2.0 Migration Guide を参考にspring-boot-properties-migrator を一時的に追加して進めました。

これを追加することで、バージョンアップによりプロパティが古くなってしまっているものが警告として表示されます。

例えば、以下のようなエラーメッセージがあります。

Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.

The use of configuration keys that are no longer supported was found in the environment:

Property source 'applicationConfig: [classpath:/application.yml]':
    Key: endpoints.health.sensitive
        Line: 63
        Reason: Endpoint sensitive flag is no longer customizable as Spring Boot no longer provides a customizable security auto-configuration .
    Key: management.security.enabled
        Line: 60
        Reason: A global security auto-configuration is now provided.

Please refer to the migration guide or reference guide for potential alternatives.

上記エラーの修正を参考に application.yml から以下の2つのプロパティを削除しました。

management:
  security:
    enabled: false
endpoints:
  health:
    sensitive: false

主なライブラリ・プラグインのバージョンアップ

プロジェクトで使用している主なライブラリ・プラグインのバージョンの変更点は以下の通りです。

ライブラリ・プラグイン 変更前 変更後
org.springframework.boot 1.5.15 2.2.5
org.apache.maven.plugins 2.17 2.22.2
org.mybatis.spring.boot 1.3.1 2.1.1
com.microsoft.sqlserver 7.0.0 8.2.1
org.mockito 2.2.7 3.3.0
swagger-ui 2.7.0 2.9.2

主な修正点

ライブラリ、プラグインのバージョンアップに伴うエラーの解消

ライブラリ・プラグインのバージョンを上げたところ、一部のライブラリの影響でビルドエラーが発生したため、解消を行いました。

発生したエラーの内容と対処方法について紹介します。

Description:

Failed to bind properties under 'spring.datasource.type' to java.lang.Class<javax.sql.DataSource>:

    Property: spring.datasource.type
    Value: org.apache.tomcat.jdbc.pool.DataSource
    Origin: class path resource [application.yml]:7:11
    Reason: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<javax.sql.DataSource>]

Action:

Update your application's configuration

こちらは、 spring.datasource.typeorg.apache.tomcat.jdbc.pool.DataSource を指定していますが、 spring-boot-starter-data-jpa のバージョンアップに伴って tomcat-jdbc に移行されたため、tomcat-jdbcを追加しました。

datetime型カラムのMyBatisとの関連付け

ZOZOTOWNのシステムでは、SQL Serverのdatetime型のカラムの値を、LocalDateTime型としてMyBatisで扱っています。

しかし、MyBatisのバージョンアップをした際に以下のエラーが発生しました。

Error attempting to get column 'startDatetime' from result set.  Cause: com.microsoft.sqlserver.jdbc.SQLServerException: datetime から DATETIMEOFFSET への変換はサポートされていません。

原因は OffsetDateTimeTypeHandler を利用している場合、MyBatisのバージョンアップに伴いタイムゾーン情報が失われてしまうためでした。

こちらはdatetime型に対する以下のようなHandlerを実装することで、対応できます。

@MappedTypes(OffsetDateTime.class)
public class OffsetDateTimeTypeHandler extends BaseTypeHandler<OffsetDateTime> {
  public OffsetDateTimeTypeHandler() {
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, OffsetDateTime parameter, JdbcType jdbcType) throws SQLException {
    ps.setTimestamp(i, Timestamp.valueOf(parameter.toLocalDateTime()));
  }

  @Override
  public OffsetDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return getOffsetDateTime(rs.getTimestamp(columnName));
  }

  @Override
  public OffsetDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return getOffsetDateTime(rs.getTimestamp(columnIndex));
  }

  @Override
  public OffsetDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return getOffsetDateTime(cs.getTimestamp(columnIndex));
  }

  private static OffsetDateTime getOffsetDateTime(Timestamp timestamp) {
    return timestamp != null ? OffsetDateTime.ofInstant(timestamp.toInstant(), ZoneId.of("Asia/Tokyo")) : null;
  }
}

application.ymlの設定値を修正

ライブラリのバージョンを更新した後、ビルド&エラー解消を繰り返す中で、application.ymlの設定値を変更する必要のあるものがありました。

Description:

The bean 'meterRegistryPostProcessor', defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [io/micrometer/spring/autoconfigure/MetricsAutoConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

こちらは、エラーメッセージの通りにapplication.ymlを以下のように修正することで解決できました。

spring:
  main:
    allow-bean-definition-overriding: true

metricsの変更点

ZOZOTOWNでは、Datadogというサービスを使用してメトリクス監視を行っています。ZOZOTOWNで使用している監視ツールについては、ZOZOTOWNの監視にモダンなツール(Datadog、PagerDuty、Sentry)を導入した話をご参照ください。

今回のバージョンアップによりメトリクスを収集できなくなってしまった項目が一部ありました。原因は、バージョンアップにより、デフォルトでエンドポイントが公開されなくなってしまったことと、メトリクス名が変わってしまったことでした。

エンドポイントの解決策として、application.ymlファイルに /prometheus/metrics といったエンドポイントを以下のように許可することで、バージョンアップ前と同様のメトリクスを取得できるようにしました。

management:
  endpoints:
    web:
      exposure:
        include: "metrics,prometheus"
      base-path: "/"

また、メトリクス名は以下のような変更があったため、Datadog側で取得するメトリクス名を修正しました。

修正前メトリクス名 修正後メトリクス名
jvm_threads_live jvm_threads_live_threads
tomcat_threads_busy tomcat_threads_busy_threads

バージョンアップをしてみて

チーム発足以降、フレームワークのバージョンアップにはなかなか着手ができなかったのですが、今後はできる限り短い間隔で最適なバージョンを取り入れていこうと思いました。

この記事を執筆している時点で、Spring Bootの最新バージョンは2.3.0へと上がっています。今回バージョンアップを実施して感じたことは、バージョンアップを長期間実施しなかったことで修正量が多くなってしまい、ハードルが上がってしまうことでした。

今後は、定期的にバージョンアップを実施し、チーム内メンバーの誰もが実施できるようにノウハウを共有していく予定です。

マイグレーションチームのこれから

2019年の春から本格的に参照系のストアドプロシージャをJava APIへ移行してきました。まだ一部ストアドプロシージャを使用している箇所もありますが、主要な部分の参照系の移行は終わっています。今後着手していく予定の更新系の処理については、参照系の数倍以上の量が予想されています。

ECプラットフォーム部マイグレーションチームでは、仲間を募集しています、ご興味のある方は、こちらからご応募ください。

hrmos.co

カテゴリー