1954

Thoughts, stories and ideas.

histogram_quantileはどのようにquantileを計算しているか

Prometheusには、quantileを計測する方法としてSummaryとHistogramの2種類があります。

prometheus.io

上記公式documentに記載がある通り、Summaryはclient sideでquantileを計測するのに対し、Histogramではprometheus sideでqueryを打つ際にアドホックに算出します。

Summaryはqueryの際にaggregationすることはできないため、たとえば「複数台のAPIサーバーがあり、それら全体での99th percentile response latencyをモニタリングしたい」といった場合は必然的にHistogramを選択することになります。(「1台1台からreportされた99th percentileの平均」などは、それはそれでモニタリングする価値のある指標ではあるかもしれませんが、統計的に意味をもつ値ではありません)

Histogramの使い方は次のようになります。(prometheus clientを使う場合はbuilderで簡単にセットアップできると思いますが)

  • histogram bucketをどのように取るか決める。たとえば次のような具合。
    • [0, 100ms]
    • [0, 200ms]
    • [0, 300ms]
    • [0, 400ms]
    • [0, +∞]
  • event発生時、対応するbucketの値をincrementする
    • たとえばresponse latencyが256msだった場合、[0, 300ms],[0, 400ms],[0, +∞]の3つをincrementする
  • それぞれのbucketについて、le=<upper bound of bucket> というlabelをつけてcountをexportする
  • histogram_quantile関数を使ってquantileを計算する。たとえば直近10minにおける99th percentileを計算したい場合は以下のようになる。
    • histogram_quantile(0.99, sum(rate(api_latency_seconds_bucket[10m])) by (le))

How histogram_quantile calculates quantile from buckets

さて、histogram_quantileはどのようにquantileを算出しているのでしょうか。

上記公式documentには

it applies linear interpolation

とあります。

さきほどの、0から400msまで100msごとに刻んでいる例を使って図示してみます。

f:id:ocadaruma:20200812003255p:plain

直近10minで合計20回のAPIリクエストがあり、latencyの分布が以下のようになったとします。(もちろんhistogramに記録した時点で個々の点のlatencyは消滅し、実際に取れるのはbucketごとの4,8,12,...というcountだけです)

f:id:ocadaruma:20200812003305p:plain

ここで75th-percentileを計算することを考えます。これは全体を小さい方から並べたときに75%のところに位置する値ですから、今回の場合小さいほうから数えて15番目の値ということです。

f:id:ocadaruma:20200812003317p:plain

これより75th percentileが[300ms, 400ms]のどこかに位置することは分かりますが、個々の点の情報はすでに消滅してhistogramになってるため、近似するしかありません。

bucket内に一様にlatencyが分布していると仮定して線形補間すれば、75th percentile = 375msが得られます。

Prometheusのコードを見ると、まさにこのような計算を行なっていることが確認できます。

prometheus/quantile.go at v2.20.1 · prometheus/prometheus · GitHub

もちろん次のように分布が一様でない場合は近似の精度が下がることになりますが、それでもbucketの[lower bound, uppder bound]内に正確な値があることは保証できます。

f:id:ocadaruma:20200812003335p:plain

そして、histogramはただのbucketごとのcounterですので、それぞれのserverからreportされた値をaggregateして全体でのquantileを計算することも容易です。

Conclusion

  • histogram_quantileの動作を理解することで、latencyの分布によっては精度に影響が出るものの、誤差はbucketの境界で抑えられることを再確認しました。
    • たとえばlatency quantileに対してSLAを設定するような場合、SLAターゲット近くのbucketを細かく取ることで精度を上げる、といったコントロールが可能です。