内部実装
コミット
fc561264のソースを読んだもの。ここでの主張はすべてファイルと行を指す。
コードマップ
| パス | 責務 |
|---|---|
tsdb/ | ローカル時系列ストレージ: head (インメモリ)、block (永続)、WAL、mmapped chunk |
scrape/ | scrape ループとメトリクスパース |
promql/ | PromQL パーサ (promql/parser/) と評価エンジン (promql/engine.go) |
storage/ | Storage / Appender / Querier の抽象: fanout, remote, merge |
discovery/ | サービスディスカバリプラグイン。build tag で選択 (README:114-144) |
web/ | HTTP API v1 と React UI (web/ui/) |
cmd/prometheus/ | 全体を配線するエントリポイント func main() (cmd/prometheus/main.go:365) |
中核データ構造
Head (tsdb/head.go:71) は、現在進行中でインメモリのブロック。series 集合を series *stripeSeries で保持し (tsdb/head.go:115)、ラベル term から series ID への転置インデックス postings *index.MemPostings を持つ (tsdb/head.go:121)。write-ahead log の wal, wbl *wlog.WL (tsdb/head.go:87)、isolation 用の iso *isolation (tsdb/head.go:125) も抱える。
memSeries (tsdb/head.go:2515) は 1 つの時系列。chunk を 3 層で保持する。ディスクに mmap 済みの過去 chunk である mmappedChunks []*mmappedChunk (tsdb/head.go:2538)、構築中の chunk を linked list で持つ headChunks *memChunk (tsdb/head.go:2543)、out-of-order データ用の ooo *memSeriesOOOFields (tsdb/head.go:2546)。isolation 用に txs *txRing も持つ (tsdb/head.go:2574)。
headAppender (tsdb/head_append.go:425) は 1 回の scrape のトランザクション。サンプルをバッチに溜め、Commit で一括反映する。WAL に書く最小単位は record.RefSample (series ref, timestamp, value) で、Append で生成される (tsdb/head_append.go:497)。
追う価値のあるパス
headAppender.Append (tsdb/head_append.go:434) は、パース済みサンプルが永続状態になる場所。まず series ref で series を引き、未知の ref なら生成にフォールバックする。
s := a.head.series.getByID(chunks.HeadSeriesRef(ref))このルックアップは tsdb/head_append.go:442。次に、順序と out-of-order ウィンドウを踏まえてサンプルが受理可能かを series に問う。
isOOO, delta, err := s.appendable(t, v, a.headMaxt, a.minValidTime, a.oooTimeWindow)この appendable チェック (tsdb/head_append.go:475) が、in-order と out-of-order の扱いを分けるゲートである。サンプルが通れば、即座に書くのではなく record.RefSample としてバッチに溜める (tsdb/head_append.go:497)。実際の WAL 書き込みと head chunk への反映は後段、scrape ループが仕込んだ defer された Commit で行われる (scrape/scrape.go:1368)。
読んで驚いた点
- chunk の 3 段ライフサイクル。 head chunk はインメモリで構築され、一定サイズに達すると
mmappedChunkとしてディスクに mmap され、最終的に compaction で block に落ちる。memSeriesのコメントがこれを、compaction がmmappedChunksのポインタをずらす様子も含めて説明している (tsdb/head.go:2529-2544)。クラッシュ復旧は WAL replay。結果として RAM を抑えつつ最近データを hot に保つ。 - series ごとの MVCC。 isolation は series ごとに
txs *txRingで実装され (tsdb/head.go:2574,tsdb/isolation.go)、未 commit のサンプルが進行中のクエリに見えない。読み取り一貫性はグローバルロックではなくこの ring から来る。 - stale marker のファストパスを Append にインライン。 stale NaN は
Append内でインラインに histogram / float histogram の stale へ変換される (tsdb/head_append.go:451-469)。新バッチの生成を避けるため。 - scrape バッファの pool 再利用。 scrape body の
[]byteは pool から取得し pool に返す (scrape/scrape.go:1410-1411)。hot path のアロケーション削減はAGENTS.mdで明文化されたプロジェクトの慣習。 - out-of-order サンプルは物理的に別。
oooTimeWindowが遅延データを許容する場合、それは別 chunk (oooHeadChunk) と別 WAL (wbl) に入り、in-order のパスを単純に保つ (tsdb/head_append.go:475,memSeriesOOOFields)。