tail -f pinzo.log: Deviseの有効期限設定をテストする で書いた有効期限検証のコードで不思議な事が起こった。Macでは正常にパスするのだが、Amazon Linux上ではパスしない。
ミニマムなコードで示すとこんな感じ
で、こんなデバッグコードを仕込んで実行してみた
Macで実行するとこんな感じ
[test-code][sent_at] to_s: 2016-02-01 23:51:31 +0900, to_i: 1454338291, usec: 197593, subsec: 197593/1000000
[test-code][added] to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise][now] to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise][sent_at] to_s: 2016-02-01 23:51:31 +0900, to_i: 1454338291, usec: 197593, subsec: 197593/1000000
[devise][added] to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise] now > added: false
[devise] now.subsec > added.subsec: false
Amazon Linuxで実行するとこんな感じ
[test-code][sent_at] to_s: 2016-02-01 23:53:20 +0900, to_i: 1454338400, usec: 369658, subsec: 369658841/1000000000
[test-code][added] to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 369658841/1000000000
[devise][now] to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 369658841/1000000000
[devise][sent_at] to_s: 2016-02-01 23:53:20 +0900, to_i: 1454338400, usec: 369658, subsec: 184829/500000
[devise][added] to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 184829/500000
[devise] now > added: true
[devise] now.subsec > added.subsec: true
usecまでは同じなのだが、subsec をみると Amazon Linux では、桁落ちが発生している。
ためしにそれぞれで Time.now.subsec
としてみる。
# mac
$ ruby -e 'puts Time.now.subsec'
276987/1000000
# Amazon Linux
$ ruby -e 'puts Time.now.subsec'
704462593/1000000000
つまりこういうことが起こっている
- OSにより
Time#subsec
が返す精度が異なる
- FactoryGirlが
create
で返すのは、コードで指定された値を格納したオブジェクトであり、DBに登録後取得したものではないので confirmation_sent_at
はそのままの精度である。つまり、スタブで突っ込んでいるのは元の精度
- DB(sqlite)に格納するとき、DBの型に合わせて精度の桁落ちが発生する
- 実際に処理されるときは、DBから取得したデータなので桁落ちが発生したデータになる
- DB格納前の元の精度の値と、DBから取得した桁落ちした値を比較しているため期限を超えたことになってしまう
- Mac上ではたまたま、
Time#subsec
の精度がsqliteの精度と同じだったため桁落ちが発生せず正常に動作する
これを避けるためには、スタブに突っ込む値をDBから取得したものにすれば良さそうだ。たとえばこんな感じ
ちなみに Mac でも、桁落ちを引き起こすことは可能で、こんな感じで usec を超えた範囲を指定すれば桁落ちさせられる。
あー疲れた、腹減った