prometheus/prometheus
Index and postings
The block index turns label-name/value combinations into ordered series IDs. It is what makes up{job="api-server", env="prod"} cheap to evaluate over a 31-day block.
Files
tsdb/index/
├── index.go # Index reader/writer (~1,500 lines).
├── postings.go # In-memory and on-disk posting list operations.
├── postingsstats.go # Stats about postings cardinality (used by /api/v1/status/tsdb).
└── *_test.goBlock index format (overview)
The on-disk index file (<block>/index) starts with a header (magic + version) followed by:
- Symbol table — deduplicated string table for label names, label values, and metric names.
- Series section — for each series: ref, label set encoded as symbol indices, list of chunks
{minTime, maxTime, ref}. - Label index — for each label name, the sorted list of values seen.
- Postings section — for each
(label name, label value), a sorted list of series refs. - Postings offset table — mapping from
(label, value)to byte offset of its postings list. - Table-of-contents — fixed-size footer with absolute offsets to each section.
The format is documented inline in tsdb/index/index.go and matches the spec used by Cortex/Thanos/Mimir for compatibility.
In-memory postings
MemPostings (in tsdb/index/postings.go) is the head's posting structure. It maintains:
m map[string]map[string][]storage.SeriesRef— per label name, per value, the sorted refs.lvs map[string][]string— per label name, the sorted unique values.
It supports the same postings operations as the on-disk variant but is mutable — insertions happen during series creation in the head.
Postings operations
The query planner operates on iterators of series refs. Key combinators:
| Function | What it does |
|---|---|
Intersect(a, b, …) |
Series present in all input postings (galloping merge). |
Merge(a, b, …) |
Union of postings. |
Without(p, exclude) |
Postings in p but not in exclude. |
Sort(p) |
Order postings by ref (only used for un-ordered inputs). |
EmptyPostings() |
Iterator over the empty set. |
ErrPostings(err) |
Iterator that surfaces an error on first call. |
Performance optimisations in tsdb/index/postings.go:
- Galloping search for intersections of mismatched-cardinality lists.
- Per-call buffer pooling (
util/zeropool). - Fast-paths for single-list and two-list intersections.
- The 3.10
LabelValues intersectionoptimisation (#18069) avoids a quadratic blow-up when a matcher hits many label values.
Label values intersection
When a query has multiple matchers, the planner asks LabelValues(name, matchers) per matcher and intersects. The 3.10 PR #18069 rewrote this to compute the intersection in a single pass instead of nesting per-matcher loops.
Reading the index
Index.LookupLabelValues, Index.Postings, and Index.Series are the three primitives a querier uses. The block querier in tsdb/querier.go chains them:
- Resolve postings for each matcher.
- Intersect the postings.
- For each surviving ref, fetch the series record (labels + chunk refs).
- Open chunks via
ChunkReader.
Index-related metrics
prometheus_tsdb_postings_for_matchers_cache_hits_total/_misses_totalprometheus_tsdb_postings_for_matchers_cache_*_secondsprometheus_tsdb_storage_blocks_postings_count(per-block)
A postings-for-matchers LRU cache lives in tsdb/postings_for_matchers_cache.go; it stores the resolved series-ref list for a given (block, matcher set) to avoid recomputing on repeated identical queries (common in dashboards).
Custom postings decoder
Some operators wrap the index reader to skip postings decoding when their plan can prove a postings list is empty or trivially derivable. The hook is the PostingsDecoderFactory field on tsdb.Options. The default factory uses the standard format.
Entry points for modification
- New posting operator: add to
postings.goand reuse thePostingsinterface; cover with property-based tests inpostings_test.go. - Index format extension: must be backward compatible. Bump the version constant in
tsdb/index/index.goand document it intsdb/CHANGELOG.md.
See Blocks for how the index ties into a block's lifecycle and PromQL for how query planning uses postings.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.