パリの小規模チームでCIのタイムゾーン障害を直した話

这篇内容暂时只有 日本語 版本,以下是原文。

パリ郊外の小規模チームで、Rails 7.0 のAPIを運用していたときのこと。CI(GitLab Runner)はUTC、アプリのコンテナは Europe/Paris、DBはPostgreSQL 14でタイムスタンプは timestamptz 保存。ある週から、日本時間の深夜にだけ走るバッチの日付境界テストがCIでだけ1日ずれて落ちるようになった。データ量は日次2万行程度、再現はCI上のみ、ローカルでは出ない。

まず疑ったのはDBではなくプロセスのTZ。確認すると Runner の image に tzdata が入っておらず、TZ=Europe/Paris を渡しても Ruby 側はUTCのまま動いていた。テスト側の期待値をUTCに寄せるのではなく、実行環境を明示する方向で直した。

# .gitlab-ci.yml
variables:
  TZ: Europe/Paris
before_script:
  - apt-get update && apt-get install -y --no-install-recommends tzdata
  - ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

アプリ側でも config.time_zone = "Europe/Paris" と config.active_record.default_timezone = :utc の組み合わせを明示し、バッチの境界判定は Time.zone.today ではなく引数で受け取った基準時刻から計算するように変えた。テストは travel_to でパリ時間の23:59と00:01を固定。ここで大事だったのは、DBにはUTCで入れる前提を崩さず、表示と業務日付の計算だけをパリ時間に寄せたこと。timestamptz と timetz を混在させると比較が壊れるので、カラムはすべて timestamptz に統一した。

この方法の代償は、Runner image が数十MB重くなることと、tzdata の更新をCI側で追随させる必要が出ること。あと、夏時間の切り替え週は travel_to だけでは足りず、ActiveSupport::TimeZone のoffsetを明示しないと3月と10月のテストがすり抜ける。実際に10月の切り替えで一度見逃した。

逆に効かないのは、外部SaaSのWebhookがすでにUTCで日付を切っている場合。受信側でパリ時間に直しても、送信側の「日」の定義は変わらないので、境界は受信時刻ではなくペイロードのタイムスタンプで判定する必要がある。ここは今もチーム内で結論が出ていない。

同じように、CIだけTZがUTCに落ちて日付境界のテストが落ちた経験のある人いますか。tzdataの管理はCI imageに寄せていますか、それともアプリ側で吸収していますか。

0
赞同度 100% · 0 条评论 · 7 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。