背景
- Spring Boot 1.5 アプリケーション
- PostgreSQL 9.6
- DBアクセスはJDBC
PostgreSQLのJDBCドライバは、batchUpdate()
で複数行をINSERTしてもレコード数分のINSERT文を作成します。
これでは遅いので高速化のためにBULK INSERTしたい。
ドライバ9.4.1209
からreWriteBatchedInserts
というオプションが追加されており、これを設定することでBULK INSERTになるのですが、INSERT ON CONFLICT
使用時に不正なSQLを生成してしまう不具合があり、これが解消されているのが42.2.2
。
ところが、Spring Boot 1.5系で導入されるのは9.4.1212
。。。
ということで、ドライバのバージョンを上げることにしました。
バージョンアップしてみた
pom.xml
に以下を追加し、最新の42.2.4
を導入します。
<properties>
...
<postgresql.version>42.2.4</postgresql.version>
...
</properties>
これだけです。簡単ですね。
ところが。
ビルドするとテストがエラーを吐きました。
Expected :2015-12-15 23:30:59.999999
Actual :2015-12-15 23:31:00.0
ドライバのバージョンを戻すとエラーは消えるので、バージョンアップの影響であることは確実です。
調査
テストが落ちる原因はタイムスタンプが繰り上がってしまっているためです。
Read/Writeどちらで問題が発生しているのか確認するために、このドライバを経由せずにDBのデータを覗いてみたところ、繰り上がったレコードが登録されているので、どうやら書き込む際に既に不正なデータになっているようです。
何故だ。
あれこれググってみると、あるIssueを見つけました。
これによると、バージョン42.1.1
以降で発生する問題で、42.2.3
では修正されている。
あれ?じゃあ問題は修正されている・・・?
解決
このIssueがキッカケで、バージョン42.1.1
以降ではjava.sql.Timestamp
のnano秒を、micro秒までの精度であるDBのTimestampに変換する際に、四捨五入していることと、これより前のバージョンではnano秒を無視していたことが分かりました。
で、テスト用のConfigurationを確認してみると・・・。
public Clock clock() {
return Clock.fixed(ZonedDateTime.of(2015, 12, 15, 23, 30, 59, 999999999, ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
}
nano秒まで埋めてた!
ということで、999999999
を999999000
にして解決しました。