Back to Netdata

metrix

src/go/pkg/metrix/README.md

2.10.315.3 KB
Original Source

metrix

metrix is the metrics storage and read API used by go.d ModuleV2 collectors and runtime/internal components.

Audience: ModuleV2 collector authors and framework contributors.

See also: charttpl (template DSL), chartengine (compile + plan).

Purpose

ConsumerStore typeTypical usage
Collector jobs (ModuleV2)CollectorStoreCycle-scoped writes, snapshot reads, chart planning input
Internal/runtime instrumentationRuntimeStoreStateful immediate-commit writes, runtime metrics planning

Core Concepts

  • Immutable reads — Readers observe immutable snapshots that are swapped atomically on commit. Multiple goroutines can read concurrently without locking.
  • Cycle-scoped collector writesCollectorStore writes are staged between BeginCycle and CommitCycleSuccess. Nothing is visible to readers until commit.
  • Stateful runtime writesRuntimeStore writes are committed immediately (no cycle API). Each write produces a new overlay snapshot.
  • Label canonicalization — Label maps (map[string]string) are sorted and encoded into a canonical key that uniquely identifies a series (metric name + labels).
  • Typed + flattened views — Reader supports canonical typed families (Histogram, Summary, StateSet, MeasureSet) and a flattened scalar view where complex types are projected into individual scalar series.

Key Definitions

  • Freshness controls which series appear in non-raw reads. FreshnessCycle = series must be observed in the latest successful cycle to be visible. FreshnessCommitted = series is visible as long as it's committed, even if not re-observed.
  • Window controls how stateful histogram/summary instruments accumulate observations. WindowCumulative = observations accumulate across cycles. WindowCycle = observations reset each cycle.

Stores and Interfaces

InterfaceKey methodsNotes
CollectorStoreRead(...), Write()Default collector-facing store
RuntimeStoreRead(...), Write()Stateful-only writes
CycleManagedStoreCycleController()Runtime/orchestrator-only cycle control
ReaderValue/Delta/Histogram/Summary/StateSet/MeasureSet/...Immutable snapshot read API

Write Model

Collector store

PhaseAction
BeginOpen staged frame (BeginCycle)
CollectCollector writes metrics through Write().SnapshotMeter(...) or Write().StatefulMeter(...)
SuccessCommitCycleSuccess publishes new snapshot and advances success sequence
FailureAbortCycle drops staged writes

ModuleV2 collectors should write metrics only; cycle control is handled by job runtime.

Runtime store

  • No cycle API — writes commit immediately.
  • Stateful only — snapshot-mode instrument registration returns an error.

[!CAUTION] Calling snapshot-mode record methods (ObserveTotal, ObservePoint) on a RuntimeStore panics.

  • Fixed freshness — runtime store enforces FreshnessCommitted semantics; other freshness policies are rejected.

Instrument Modes and Defaults

ModeTypical meterFreshness defaultWindow default
SnapshotSnapshotMeter(...)FreshnessCycleWindowCumulative
StatefulStatefulMeter(...)FreshnessCommittedWindowCumulative

Instrument Options

OptionScope
WithFreshness(...)Freshness policy override (subject to mode constraints)
WithWindow(...)Stateful histogram/summary window mode
WithHistogramBounds(...)Histogram bucket boundaries
WithSummaryQuantiles(...)Summary quantile output (required for quantile series in flattened view)
WithSummaryReservoirSize(...)Stateful summary estimator size
WithStateSetStates(...)StateSet allowed states
WithStateSetMode(...)ModeBitSet (multiple simultaneous active states) or ModeEnum (exactly one active state)
WithMeasureSetFields(...)MeasureSet fixed ordered field schema (required for MeasureSet instruments)
WithDescription(...), WithChartFamily(...), WithUnit(...), WithFloat(...)Metric metadata hints for downstream consumers (e.g., autogen chart identity + float SET mode)

MeasureSet

  • Structured numeric familyMeasureSet stores one logical metric family with a fixed ordered list of named numeric fields.
  • Family-level semantics — one MeasureSet family is either gauge-like or counter-like; semantics are never mixed per field.
  • Family-level metadataDescription, ChartFamily, and Unit apply to the whole family.
  • Field-level schemaMeasureFieldSpec declares per-field Name and Float.
  • Chartengine integration — chart autogen treats MeasureSet as a structured family, similar to StateSet; flatten remains the generic reader/tooling path.

Writers

ModeGauge-like familyCounter-like family
SnapshotMeasureSetGauge(...).ObservePoint(...) or preferred ObserveFields(...)MeasureSetCounter(...).ObserveTotalPoint(...) or preferred ObserveTotalFields(...)
StatefulMeasureSetGauge(...).SetPoint(...), AddPoint(...), SetFields(...), AddFields(...), SetField(...), AddField(...)MeasureSetCounter(...).AddPoint(...), AddFields(...), AddField(...)

Phase-1 write contract

  • Preferred collector-facing API — use named write helpers instead of raw positional MeasureSetPoint values whenever practical.
  • Snapshot handles support:
    • full-family positional writes (ObservePoint(...), ObserveTotalPoint(...))
    • full-family named writes (ObserveFields(...), ObserveTotalFields(...))
  • Stateful handles support:
    • full-family positional writes
    • full-family named writes
    • singular field writes (SetField(...), AddField(...))
  • Snapshot singular field writes do not exist in phase 1.
    • MeasureSet still models one sampled family point per collect cycle in snapshot mode.
    • Partial snapshot field visibility/completeness semantics are intentionally deferred.
  • Named full-family writes require the exact declared field set.
    • Missing fields panic.
    • Unknown extra fields panic.
  • Stateful singular field writes update only the addressed field.
    • Gauge-like SetField(...) overwrites one field on top of the committed/staged family.
    • Gauge-like AddField(...) and counter-like AddField(...) apply a delta to one field.

Schema example

go
store := metrix.NewCollectorStore()
meter := store.Write().SnapshotMeter("svc")
latency := meter.MeasureSetGauge(
	"latency",
	metrix.WithMeasureSetFields(
		metrix.MeasureFieldSpec{Name: "value"},
		metrix.MeasureFieldSpec{Name: "ratio", Float: true},
	),
	metrix.WithUnit("seconds"),
)
latency.ObserveFields(map[string]metrix.SampleValue{
	"value": 1.5,
	"ratio": 0.5,
})

Re-registering the same metric name with a different MeasureSet schema is rejected like other structured families.

Stateful singular-write example

go
store := metrix.NewRuntimeStore()
meter := store.Write().StatefulMeter("svc")
usage := meter.MeasureSetGauge(
	"usage",
	metrix.WithMeasureSetFields(
		metrix.MeasureFieldSpec{Name: "value"},
		metrix.MeasureFieldSpec{Name: "limit"},
	),
)

usage.SetFields(map[string]metrix.SampleValue{
	"value": 10,
	"limit": 20,
})
usage.SetField("value", 15)
usage.AddField("limit", 3)

This yields a committed MeasureSet point equivalent to:

go
metrix.MeasureSetPoint{Values: []metrix.SampleValue{15, 23}}

Read Modes

Read(...) accepts option functions that control two independent axes:

  • Raw (ReadRaw()) — bypasses freshness filtering, returning all committed series regardless of when they were last observed.
  • Flatten (ReadFlatten()) — projects complex types (Histogram, Summary, StateSet, MeasureSet) into individual scalar series.
Read optionsVisibilityShape
Read()Freshness-filteredCanonical typed families
Read(ReadRaw())All committed seriesCanonical typed families
Read(ReadFlatten())Freshness-filteredFlattened scalar view
Read(ReadRaw(), ReadFlatten())All committed seriesFlattened scalar view

Flattened View Mapping

Read(ReadFlatten()) projects non-scalar families into scalar series:

Source kindFlattened outputs
Histogram<name>_bucket{le=...}, <name>_count, <name>_sum
Summary<name>_count, <name>_sum (always); <name>{quantile=...} (only when WithSummaryQuantiles() is configured)
StateSet<name>{<name>=state} with scalar 0/1 values
MeasureSet<name>_<field>{measure_field=field}; flattened kind follows family semantics (Gauge or Counter)

Flatten metadata is exposed via SeriesMeta.Kind, SeriesMeta.SourceKind, and SeriesMeta.FlattenRole.

MeasureSet flattening keeps per-field metric names for MetricMeta(name) compatibility and also adds a synthetic measure_field=<field> label. This gives chartengine explicit field identity without widening the reader metadata API.

Minimal Usage Snippets

Collector write path

go
store := metrix.NewCollectorStore()
meter := store.Write().SnapshotMeter("mysql")
qps := meter.Counter("queries_total")
qps.ObserveTotal(42)

Read path for planning

go
reader := store.Read(metrix.ReadRaw(), metrix.ReadFlatten())
value, ok := reader.Value("mysql.queries_total", nil)
_ = value
_ = ok

Direct MeasureSet read

go
reader := store.Read()
point, ok := reader.MeasureSet("svc.latency", nil)
_ = point
_ = ok

For a complete collector integration pattern (cycle management, error handling), see how-to-write-a-collector.md.

Contracts and Pitfalls

  • Label setsLabelSet is store-owned; do not share between different stores.
  • Counter deltasDelta() requires contiguous sequence (N, N+1). In CollectorStore this is per-cycle: missing one successful cycle breaks the delta. In RuntimeStore this is per-series per-write: the sequence always increments on each write, so skipping a write cycle does not break deltas.
  • Snapshot freshness — Snapshot-mode instruments cannot use FreshnessCommitted.
  • Runtime writesRuntimeStore rejects snapshot-mode instrument registration with an error. Calling snapshot-mode record methods (ObserveTotal, ObservePoint) panics.
  • MeasureSet runtime writesRuntimeStore supports both gauge-like and counter-like MeasureSet families, but only through StatefulMeter(...).
  • MeasureSet named writesObserveFields(...), ObserveTotalFields(...), SetFields(...), and AddFields(...) require the exact declared field set. Snapshot singular field writes are intentionally absent in phase 1.
  • Window/freshness coupling — Stateful histogram/summary with WindowCycle requires (and silently forces) FreshnessCycle. Setting an explicit non-Cycle freshness with WindowCycle returns an error.
  • Schema stability — Re-registering an existing metric name with different kind/mode/schema returns an error (or panics in strict runtime paths).
  • MeasureSet flatten naming — Flattened MeasureSet series use per-field metric names like <name>_<field> and also carry a synthetic measure_field=<field> label.
  • MeasureSet counter semantics — Stateful counter-like MeasureSet families reject negative AddPoint(...) deltas, just like scalar counters.
  • Collector retentionCollectorStore evicts series not seen for 10 successful cycles by default.

Internal Architecture Notes

AreaImplementation pattern
Snapshot publishRead snapshots are immutable and atomically swapped
Collector commitStaged frame merges into new snapshot on successful cycle commit
Runtime commitOverlay/compaction strategy with retention pruning
IterationName-indexed deterministic iteration for reader traversal
IdentityCanonical metric+labels key with stable SeriesIdentity hash