diamond-writer読解メモ
クラスと役割
Section titled “クラスと役割”- DiamondWriter: 全てをまとめているクラス
- MetricCache: Redisにストアするための操作が集まっているクラス
- SeriesTable: DynamoDBを操作するクラス
- MetricPurger: MetricCache から SeriesTable へ移動するためのクラス
メトリックの粒度と各種エポックタイム
Section titled “メトリックの粒度と各種エポックタイム”主にRedisで使われるデータポイントの時刻と、主にDynamoDBで使われるアイテムレコードの時刻がある。データポイントの時刻は MetricPointEpoch と表記される。アイテムレコードの時刻は主に ItemEpoch と表記される。
MetricPointEpoch と ItemEpoch は取りうる範囲が決まっている。MetricPointEpoch を1つ進めたときに移動する量を MetricPointEpochStep や interval と呼ぶ。同様に ItemEpoch を進める量は ItemEpochStep となる。
メトリックは粒度ごとに集計される。投稿されたメトリックそのものは1分粒度となる。1分粒度メトリックは、投稿された時刻が秒を含んでいたとしても1分単位に丸め(切り捨て)て記録するので、取得する場合は 21:10:00 のように秒は必ず 00 でなければ取得できない。
また、DiamondWriter は1分粒度を記録すると同時に5分、1時間、1日粒度のメトリックも集計して記録する。このとき addWithAggregation は、平均計算に必要な以前のメトリック値をRedisから取り出す。5分粒度を計算するなら直前の1分粒度を5つ、1時間粒度を計算する場合は直前の5分粒度を12個取り出して平均する。
Redisの構造
Section titled “Redisの構造”メトリック値
Section titled “メトリック値”1分、5分など粒度ごとに値を記録している。キーの名前は以下の形式。
"$(metricPointEpochStep):{$(metricName1)}$(metricName2)"metricName は一般的に見えるものではなく、OrgIdが頭に付いている。また、Redisクラスターのキー配置を活用するため {} で部分文字列を囲っている。
メトリックセット
Section titled “メトリックセット”その時刻に投稿された全てのメトリック名を記録するセット値。cleanup-metric-cache で利用する。
"metricSet:$(metricPointEpochStep):$(itemEpoch)"末尾が itemEpoch なので1分粒度の場合は4時間分が集められる。過去に複数の粒度が1つのシャードに乗ってしまって処理できなくなったので、1時間、1日粒度の場合は負荷低減するための v2 もある。v1 との違いは分割するかどうか。
"metricSetV2:$(metricPointEpochStep)-$(index):$(itemEpoch)"DynamoDBに書き込みが終わった時刻を保持する。
"threshold:$(metricPointEpochStep):{$(metricName1)}$(metricName2)"メトリックを集約するとき、すでにDynamoDBへ書き込まれた値はRedisに残っていない場合があるので、両方の値をマージして集約を行う必要がある。この「DynamoDBに書き込まれているか」を保持するキーが threshold: で始まるもので、DynamoDBに対するリードバッファなので一定期間で消える。
DynamoDBの構造
Section titled “DynamoDBの構造”基本的にはRedisの「メトリック値」がそのままコピーされるが、1つのレコードに記録される量が ItemEpochStep 単位となる。そのためレコードの名前も変わる。
DynamoDBそのままのはず。
ストレージを移動する時間
Section titled “ストレージを移動する時間”ストレージと粒度ごとに複数ある。
- RedisからDynamoDBへ移動する時間 … MetricPointEpochStep
- DynamoDBからS3へ移動する時間 … ItemEpochStep
前者は diamond-writer がメトリック処理時に行うか、投稿がなくなったメトリックは cleanup-metric-cache が行う。後者は diamond-exporter が行う。
事実上、粒度と各種エポックタイム幅の組み合わせは4通りしかない。
| 粒度 | MetricPointEpochStep | ItemEpochStep | TTL | DynamoDBのレコード |
|---|---|---|---|---|
| 1分 | 1m | 4h | 57h | 240ポイント |
| 5分 | 5m | 1d | 21d | 288ポイント |
| 1時間 | 1h | 7d | 90d | 168ポイント |
| 1日 | 1d | 365d | 730d | 365ポイント |
diamond-writer のクラス設計は以下のようになっている。見れば分かるが、各粒度ごとに SeriesTable, MetricCache, MetricPurger が存在していて、DiamondWriter がそれらを束ねる形となる。
class SeriesTable { itemEpochStep: ItemEpochStepValue metricPointEpochStep: MetricPointStepValue}
class MetricCache { seriesTable: SeriesTable interval = seriesTable.metricPointStepValue}
class DiamondWriter { metricCaches = { onemin: new MetricCache(seriesTables.onemin), fivemin: new MetricCache(seriesTables.fivemin), ..., } metricPurgers = { onemin: new MetricPurger( metricCaches.onemin, seriesTables.onemin, ..., ), ..., }}cleanupメソッドは特定の時刻しか処理しない?
Section titled “cleanupメソッドは特定の時刻しか処理しない?”DiamondWriter#cleanup は与えられたタイムスタンプしか処理していないが、cleanup-metric-cache 側で一定数(デフォルトは10)だけ幅を持たせているので、実行が多少遅れたとしても取り逃がす可能性は低い。diamondのcleanup-metric-cacheが処理する対象も参考に。