Karakuri.com

ベンチャー企業で働くソフトウェアエンジニアの技術録

Ruby on RailsでTimeとDateTimeとPostgreSQLのTimestampの精度の差でRspecが失敗した話

スポンサーリンク

言わずもがな保守運用をやってるレガシーシステムで時刻で使っている型がバラバラでして、ちょっと不具合修正をしたらジェンガのように音を立ててRspecが失敗するようになってしまいました。原因を調べていくと、DBで使用しているPostgreSQLのTimestamp型まで含めた精度の話に帰着したのでまとめておきます。

なにが起きたのか

先にも書いたのですが、意図が分からない to_datetime がソースコードに散らばっていて、ここら辺の不具合を修正したときに to_datetime するかどうかでテスト結果が変わるという謎の状態に陥ってしまったんですね。to_datetime する意図が分からないので、とりあえず全部消してみたら更にテスト失敗数が増えてどんどんシステムが壊れていきました。

expected: 2019-12-07 23:59:59.999999999 +0900
     got: 2019-12-07 23:59:59.999999000 +0900

こんな失敗が頻発してたんですね…。どういうこと…。

それぞれの型の精度を調べてみる

結果から察するとナノ秒の情報が欠落しているのが原因みたいです。それでRspecのコードを調べてみると、

  • expected: Time型
  • got: PostgreSQLのTimestamp型(ActiveRecordで取ってきた)

ということが分かりました。

Time型

Time型はナノ秒までの精度があるみたいです。文字列変換するとデフォルトでナノ秒まで含んだ文字列になります。
class Time (Ruby 2.6.0)

PostgreSQLのTimestamp型

Timestamp型はマイクロ秒までの精度があるようです。
https://www.postgresql.jp/document/9.4/html/datatype-datetime.html

ということで問題の原因はこの精度の差でナノ秒の情報が欠落していたからでした。

なぜ今までテストが通っていたのか?

ここで1つ疑問が残ります。なぜ今までテストが通っていたのか?

DateTime型の精度は?

先に書きましたが、意図が分からない to_datetime が散らばっていました。もしかしてこいつが原因かもしれません。DateTime型の精度を調べてみると、、、
docs.ruby-lang.org
精度が記載されていません( ノД`)シクシク…
ただ、文字列にして表示してみるとナノ秒の精度があるっぽいです。もしかすると今まではActiveRecordで取ってきても to_datetime されてナノ秒を復旧させていたのかな…。いずれにせよ to_datetime したらテスト通るからという理由で to_datetime していたんじゃないか疑惑が浮上しました。レガシーシステムは闇が深いですね。そもそも end_of_day で保持して比較している時点で…以下略