Prometheus Architecture: How It Actually Works
How to read this page
Symvanta parsed this repository into a code graph: every function, class, and method is a node, and every call or import between them is an edge. Everything on this page is computed from that graph at the commit shown above. The terms:
- Module (or cluster)
- A group of symbols that call each other far more than they call anything else. An algorithm called Louvain community detection finds these groups from the call traffic alone; nobody draws them by hand.
- Modularity (the Q number)
- A 0-to-1 score of how cleanly those groups separate. Higher means more call traffic stays inside its own group; scores around 0.7 and above read as clean boundaries.
- Hub
- The most depended-upon symbol inside one module.
- Load-bearing symbols
- PageRank, the algorithm Google originally used to rank web pages, run over the call graph instead: it surfaces the functions the rest of the codebase leans on hardest.
- Arrows and their numbers
- How many calls cross from one module into another. A heavier arrow means tighter coupling between those two parts.
- Dependency cycle
- File A imports B, which imports A again, sometimes through a longer loop. Cycles are not bugs, but a change inside one tends to ripple around the whole loop.
- Mutually recursive symbols
- Functions that call each other, usually the natural shape of parsers and tree-walking code.
Prometheus is a single Go binary that scrapes targets, writes samples into a
local time series database, and answers PromQL queries against it. Symvanta's
graph of the repo at e75af38 detects 53 functional modules at modularity
Q=0.71, and the weight sits almost entirely in storage: the three largest
clusters are built from tsdb, labels, chunkenc, and wlog, with the
scrape loop, service discovery, and remote write arranged around them.
The clearest structural signal is where the cycles are. All 8 dependency cycles
live in the web UI (web/ui/react-app, web/ui/mantine-ui, and the
codemirror-promql module). The Go packages that make up the server have none.
Module map
The diagram shows the 10 largest of the 53 detected modules, with edges weighted
by how many calls cross between them. The biggest holds 855 symbols and its hub
is tsdb.memChunk.len, which puts the chunk layer at the center of the
codebase. Module names come from the packages their members live in; the symbol
counts, hubs, and edge weights are what the graph computed.
Where to start reading
These are the most depended-upon symbols by PageRank over the call graph,
followed by the HTTP surface registered in web/web.go. Four entries from the
raw ranking are left out below: Set, Put, Inc, and Error are one-word
Go methods that dozens of packages define independently, and the graph groups
them by name, so they rank high without pointing anywhere useful.
tsdb.memChunk.lentsdbutil.DirLocker.Lockstats.Timer.DurationAppenderencoding.Encbuf.PutUvarintlabels.NewMatcherscrape.foreachAppendablechunkenc.bstream.bytesGET /graphGET /federateGET /consoles/*filepathGET /user/*filepathGET /-/healthyHEAD /-/healthyGET /-/readyHEAD /-/readyGET /-/quitGET /-/reload
Key subsystems
tsdb chunks and label matching
Chunk access (tsdb.memChunk.len, chunkenc.NewNopIterator) sits with label
handling (labels.NewFastRegexMatcher, strutil.SanitizeLabelName) and
postings iteration inside one 855-symbol cluster, because a query touches all
three on its way to a sample.
tsdb head and WAL
The mutable half of the database: the recent window held in memory and the log
that lets it survive a restart. 628 symbols covering tsdb.open,
tsdb.NewHead, tsdb.DefaultHeadOptions, and the write-ahead log
(wlog.NewSize, wlog.WL.Log).
Storage append and iterate
Everything that writes samples passes through the Appender and Iterator
interfaces collected here, including the newer AppenderV2. The two heaviest
edges on the map run between this 812-symbol cluster and the head and WAL
cluster: 262 calls out, 319 back.
HTTP API v1
Where the query engine meets the network. apiFuncResult, apiError, Query,
and SearchResult read from the storage clusters and shape what goes back over
the wire, 486 symbols in all.
Config loading and file handling
config.LoadFile, fileutil.Flock, tombstone writes, decode buffers: 517
symbols of plumbing that every other cluster calls. Its PageRank hub is the bare
Error method, which is what a cluster in that position looks like in Go.
Scrape loop and appendables
scrape.foreachAppendable and the appendable test doubles, 311 symbols. The
ingest path shows up in two edge weights: 56 calls into the discovery registry,
44 into the append interfaces.
Canonical request flow
The sequence worth reading first is the database opening. Lock the data
directory, clear temporary state, open the write-ahead log and repair it if the
process died mid-write, build the chunk pool and the compactor, then create the
head block and replay the WAL into it. Every step below is a call edge out of
tsdb.open, in the order the work happens.
tsdb.Open (tsdb/db.go:910)tsdb.open (tsdb/db.go:992)tsdbutil.NewDirLockertsdbutil.DirLocker.Locktsdbutil.RemoveTmpDirswlog.DeleteTempCheckpointswlog.NewSizewlog.WL.Repairchunkenc.NewPooltsdb.NewLeveledCompactorWithOptionstsdb.NewHeadtsdb.Head.Init
Health signals
Symvanta detected 8 dependency cycles across 53 modules (modularity Q=0.71). The largest cycle spans 16 files in the graph area. 10 sets of mutually recursive symbols were also detected, the largest being promql (9 symbols).
Auto-generated by Symvanta from the public repo prometheus/prometheus at commit e75af38 , licensed Apache-2.0 .
Machine-readable companion: data.json (module counts, subsystems, load-bearing symbols, health signals).