理系学生日記

おまえはいつまで学生気分なのか

Google Public NTP の Leap Smear

ずっとエントリ書こうと思っていたんですが、Google Public NTP で使われている Leap Smear についてです。 Leap Smear について書き始めたは良いものの、思っていたよりコンテキストが厚くて、閏秒から話をしないといけませんでした。 そうすると、Leap Smear については相当後の方でないと話ができないので、今回のエントリは、「Leap Smear とは何か」について冒頭で説明したあと、そのほかの関連情報を出すような構成になってます。

なお、閏秒については一応色んな情報を当たってはいるんですが、ちょっと知識不足なところもあるので、なんか間違ってるところもあるかもしれません。そのへんあったらご指摘ください。

Leap Smear

Leap Smear は、過去にグローバルを含む多数の障害を引き起こしてきた閏秒への対策に端を発してます。

誤解を恐れずに言ってしまうと、閏秒による余分な 1 秒を他の「秒」にまぶしてしまおうという NTP サーバ側の技術になります。 わかりやすい例でいえば、「正の閏秒」を「まぶす」場合、一定期間の「秒」の定義を 1.001 秒に改変してしまえば、仮想の 1,000 秒で、実質的に閏秒の「1 秒」を稼ぎ出せます。 このような「まぶされた」時刻情報を NTP サーバからクライアントに伝えることで、クライアント側は、閏秒のような情報を気にする必要がなくなります。

もちろん、Leap Smear はメリットばかりではありません。 Leap Smear によるまぶされた仮想的な秒は、実際の UTC における秒とは異なるため、

  • 一般に、Leap Smear 対応の NTP サーバと非対応のサーバ間では時刻に差が出ます。これらを 1 システム内で混合させると痛い目を見ます (混ぜるな危険)
  • 混ぜるな危険のみならず、正確な「時刻」が必要とされる場合 (法的なものとか) の取り扱いには注意

このLeap Smearing を実装した NTP サーバを一般公開している Google のサーバが Google Public NTP になります。

Google Public NTP において、閏秒の 1 秒を「どの期間」に「どのように」のかは変遷がありました。 2018 あるいは 2019 年に挿入予定の閏秒に対しては、「24 時間」、閏秒の挿入タイミングの前後 12 時間で線形に"まぶす"ようです。

We would like to propose to the community, as the best practice for leap seconds in the future, a 24-hour linear smear from noon to noon UTC. We plan to use this smear starting from the next leap second, which is likely to be in 2018 or 2019.

https://developers.google.com/time/smear

2011 年のブログ には、期間  w に対し、各「秒」に付与する追加秒が  \text{lie}(t) = \frac{1.0 - \cos \pi \frac{t}{w} }{2.0} という話だったので、現在の「線形」という定義であれば、ずいぶんシンプルになったように思います。このへんについては、以下のように言及もありますね。

Compared to a cosine smear, the linear smear is simpler, easier to calculate, and minimizes the maximum frequency change.

無料公開されているので、ぼくも NTP は Google Public NTP に向けています。

f:id:kiririmode:20171015074431p:plain
MacOS X設定

閏秒について

そのまえに閏年

閏年というのは有名なもので、暦と太陽の運行を補正するために、2/29 が挿入される日のことです。有名ですね。

いきなり余談ですけど、閏年は 4 年に一度、オリンピックの年が必ず閏年ってわけじゃねーからな。

よくプログラミングの演習で閏年判定アルゴリズムを書けとかあるけど、wikipedia:閏年 とかで分かるように

( year % 4 ) == 0 && (( year % 100 ) != 0 || ( year % 400 ) == 0)

ていう判定になります。

閏秒

閏秒というのも、上記の閏年とよく似たような概念で、不定期に、日本時間 1/1 または 7/1 の 08:59:60 (09:00:00 ではない) という秒を追加することで、UTC (世界標準時) と UT1 (国際原子時) とのズレを補正します。 通常は存在しない「秒」が出現することになること、通常のテストケースでは実行しないことが多い(※)ので、システム的にはバグの温床になったりします。

※閏秒をアプリに意識させるかどうかは、インフラ/アプリチーム間で切り分けされないことがほとんど。

  • アプリケーションが 08:59:60 という時刻を正しく処理できない
  • (08:59:59 が 2 回発生させる場合、時間遡行が発生するので) 秒の単調増加を前提とするアプリケーションが時刻を正しく処理できない

後者については Resolve Leap Second Issues in Red Hat Enterprise Linux - Red Hat Customer Portal から引用しますが、以下のようなかんじ。

2008-12-31 23:59:59:052549000 UTC    <-- 1st occurrence of the 60th second
2008-12-31 23:59:59:259988000 UTC
2008-12-31 23:59:59:465214000 UTC
2008-12-31 23:59:59:669629000 UTC
2008-12-31 23:59:59:873936000 UTC
2008-12-31 23:59:59:079184000 UTC    <-- 2nd occurrence of the 60th second
2008-12-31 23:59:59:284011000 UTC
2008-12-31 23:59:59:488648000 UTC
2008-12-31 23:59:59:692691000 UTC
2008-12-31 23:59:59:896577000 UTC
2009-01-01 00:00:00:052378000 UTC

なお、正確には、閏秒では秒を追加する「正の閏秒」だけでなく、08:59:59 が存在しなくなる「負の閏秒」も存在しますが、未だかつて実施されたことはありません。 ちなみに、正の閏秒は、直近だと今年(2017)の 01/01 に実施されました。インフラエンジニアの方々はお疲れさまでした。

NTP と閏秒

時刻情報を取り扱うプロトコルといえば NTP であって、NTP の世界においては 1 日は 86400 秒ですが、閏秒を適用する日は 1 秒長くなるのでその対策が必要です。

前提として、NTP の世界では、より時刻情報が正確な「親」から「子」そして「孫」というように、時刻情報が階層構造を持って流れていきます(階層は、ストレイタム(Stratum) という値によって、区別されます)。 正の閏秒が適用される場合、NTP における Stratum 0 のサーバは、LI (Leap Indicator) bit を "1" にして、Stratum 1 の NTP サーバにその情報を送り、その伝搬が各層によって繰り返されます。

この閏秒の扱い方には以下のような種類があります。

  • カーネルに閏秒の処理をまかせる
    • この場合、設定に依存して、08:59:60 が挿入されるか、08:59:59 が 2 回繰り返す
  • ntpd 等のデーモンが時刻を巻き戻す (08:59:59 を 2 回繰り返す) (STEP)
    • カーネルではなく、ntpd が強引に時刻を修正する
  • ずれた 1 秒を徐々に時間退行なしに巻き戻していく (SLEW)
  • Leap Smearing

Leap Smear はこのあたりの技術になります。

参考文献