master
md 226 lines 16.4 KB
Rendered Raw
1 # chartengine
2
3 `chartengine` compiles chart templates and builds deterministic chart plans (`create`, `update`, `remove`) from `metrix.Reader` snapshots.
4
5 **Audience**: `ModuleV2` collector authors and framework contributors.
6
7 **See also**: [charttpl](/src/go/plugin/framework/charttpl/README.md) (template DSL),
8 [metrix](/src/go/pkg/metrix/README.md) (metrics storage and read API).
9
10 ## Purpose
11
12 | Stage | Responsibility |
13 |-----------------------|----------------------------------------------------------|
14 | `charttpl` | Template decode/defaults/validation |
15 | `chartengine.Compile` | Build immutable program IR |
16 | `Engine.PreparePlan` | Prepare plan actions plus explicit commit/abort boundary |
17 | `chartemit.ApplyPlan` | Emit plan to Netdata wire protocol |
18
19 ## Collector-Facing Contract
20
21 For `ModuleV2` collectors, the runtime integration expects:
22
23 | Requirement | Why it matters |
24 |---------------------------------------------------------------------------|---------------------------------------------------------------------|
25 | `MetricStore()` returns `metrix.CollectorStore` (cycle-managed) | Job runtime controls cycle boundaries and success/failure semantics |
26 | `ChartTemplateYAML()` returns valid `charttpl` YAML | Loaded once at autodetection/post-check |
27 | Collector writes metrics during `Collect()` only | Planner runs after a successful cycle commit |
28 | Metric names used in template selectors are present in group metric scope | Compile/validate consistency |
29
30 ## Public API Surface
31
32 | API | Purpose |
33 |-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
34 | `New(opts...)` | Create engine with policy/runtime options |
35 | `Load(spec, revision)` / `LoadYAML(data, revision)` | Compile and publish program revision |
36 | `PreparePlan(reader)` | Build deterministic action plan from reader snapshot and return an explicit attempt |
37 | `RuntimeStore()` | Access chartengine internal runtime metrics store |
38 | `WithEnginePolicy(...)` | Configure selector + autogen behavior |
39 | `WithRuntimeStore(...)` | Override/disable self-metrics store |
40 | `WithSeriesSelectionAllVisible()` | Process all visible series instead of filtering to latest successful collect cycle. Intended for runtime/internal stores that commit immediately (no cycle boundaries). |
41 | `WithEmitTypeIDBudgetPrefix(...)` | Set the effective type-id prefix used by autogen budget checks |
42 | `WithRuntimePlannerMode(...)` | Enable runtime planner mode with no-write-tick semantics, for jobs/tests that drive planning directly from runtime metrics instead of collect-cycle boundaries. |
43
44 ## End-to-End Example (Single Flow)
45
46 ```go
47 // 1) Collector writes metrics.
48 store := metrix.NewCollectorStore()
49 meter := store.Write().SnapshotMeter("app")
50 meter.Counter("requests_total").ObserveTotal(100)
51
52 // 2) Engine loads chart template.
53 engine, err := chartengine.New(
54 chartengine.WithEnginePolicy(chartengine.EnginePolicy{
55 Autogen: &chartengine.AutogenPolicy{Enabled: false},
56 }),
57 )
58 // handle err
59
60 err = engine.LoadYAML([]byte(`
61 version: v1
62 groups:
63 - family: App
64 metrics: [app.requests_total]
65 charts:
66 - id: requests
67 title: Requests
68 context: requests
69 units: requests/s
70 dimensions:
71 - selector: app.requests_total
72 name: requests
73 `), 1)
74 // handle err
75
76 // 3) Prepare plan from flattened+raw reader, emit it, then commit.
77 // ReadFlatten() is included even for templates with static dimensions
78 // because it is required for inferred dimensions, structured-family autogen,
79 // and is the standard pattern.
80 attempt, err := engine.PreparePlan(store.Read(metrix.ReadRaw(), metrix.ReadFlatten()))
81 // handle err
82 plan := attempt.Plan()
83 defer attempt.Abort()
84
85 err = chartemit.ApplyPlan(api, plan, chartemit.EmitEnv{
86 TypeID: "plugin.job",
87 UpdateEvery: 1,
88 Plugin: "example",
89 Module: "example",
90 JobName: "example",
91 })
92 // handle err
93 err = attempt.Commit()
94 // handle err
95
96 ```
97
98 ## PreparePlan Lifecycle
99
100 `PreparePlan` executes a deterministic phase pipeline.
101 Terms like "materialized state" and "route cache" are defined in the Engine State section below.
102
103 | Phase | Summary |
104 |-----------------|------------------------------------------------------------------------------------------------|
105 | Prepare | Resolve program/index/cache/materialized state |
106 | Validate reader | Ensure flattened metadata is available when inferred dimensions are used |
107 | Scan | Iterate series, filter by success sequence and selector, route to chart/dimension accumulators |
108 | Cache retain | Prune route cache entries not seen in the latest successful sequence |
109 | Lifecycle caps | Enforce chart/dimension cap policy |
110 | Materialize | Emit create/update actions from accumulated state |
111 | Expiry | Emit removals for stale charts/dimensions |
112 | Sort | Deterministically sort inferred dimension output |
113
114 ## Reader Requirements
115
116 | Scenario | Required reader mode |
117 |--------------------------------------------------------------------------------|----------------------------------------------------------------------------|
118 | Static named dimensions only | `Read(...)` is sufficient (no flatten needed) |
119 | Inferred dimensions (`name` and `name_from_label` omitted) | Must use flattened reader metadata (`ReadFlatten`) |
120 | Structured autogen families (`Histogram`, `Summary`, `StateSet`, `MeasureSet`) | Must use flattened reader metadata (`ReadFlatten`) or they are not visible |
121 | Runtime/default `ModuleV2` path | `Read(ReadRaw(), ReadFlatten())` |
122
123 If inferred dimensions are present without flattened reader metadata, `PreparePlan` returns an explicit error.
124
125 ## Action Semantics
126
127 | Action | Meaning |
128 |-------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
129 | `CreateChartAction` | Materialize chart instance (with chart metadata and labels) |
130 | `CreateDimensionAction` | Materialize dimension for a chart |
131 | `UpdateChartAction` | Emit chart values for current cycle; unseen dims, and dims whose value is non-finite (NaN/Inf), become `IsEmpty=true` (gap, never 0) |
132 | `RemoveDimensionAction` | Obsolete one dimension |
133 | `RemoveChartAction` | Obsolete one chart |
134
135 `chartemit` normalizes emitted action order by phase:
136
137 1. create chart/dimensions
138 2. update values
139 3. remove dimensions/charts
140
141 ## Routing and Collision Rules
142
143 Each metric series is routed to a chart and dimension based on template selectors.
144 The following rules apply when routing conflicts arise:
145
146 | Rule | Behavior |
147 |-----------------------------------------------|------------------------------------------------------------------------------------------------|
148 | Template vs autogen chart ID collision | Template wins; autogen chart is replaced |
149 | Cross-template chart ID collision | Existing owner keeps ownership; subsequent series are **silently ignored** (see warning below) |
150 | Duplicate dimension observations within build | First observed dimension metadata wins; values are reduced (summed) |
151
152 > [!WARNING]
153 > Cross-template chart ID collisions cause silent data loss — conflicting series are dropped with no error and no log entry. If metrics are missing, check for duplicate rendered chart IDs across template groups.
154
155 ## Lifecycle Defaults and Policy
156
157 Default lifecycle policy when template omits lifecycle:
158
159 | Policy | Default |
160 |----------------------------------|----------------|
161 | `max_instances` | `0` (disabled) |
162 | `expire_after_cycles` | `5` |
163 | `dimensions.max_dims` | `0` (disabled) |
164 | `dimensions.expire_after_cycles` | `0` |
165
166 ## Autogen Notes
167
168 | Topic | Behavior |
169 |-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
170 | Trigger | Unmatched series only when autogen is enabled |
171 | Context namespace | Autogen context = top-level `context_namespace` + the full metric name (which includes any `SnapshotMeter` prefix); empty namespace leaves the bare name. A non-empty meter prefix stacks after `context_namespace`, so pair `context_namespace` with `SnapshotMeter("")` to avoid a doubled prefix |
172 | Structured families | Autogen has dedicated source builders for flattened `Histogram`, `Summary`, `StateSet`, and `MeasureSet` families |
173 | Metric metadata usage | Uses `metrix.MetricMeta` hints for title/family/unit where allowed |
174 | Type ID budget | Enforced via `AutogenPolicy.MaxTypeIDLen` + effective emit type-id prefix (`WithEmitTypeIDBudgetPrefix(...)`) |
175 | Lifecycle | Autogen applies `ExpireAfterSuccessCycles` to **both** chart and dimension expiry (unlike template lifecycle where they default independently) |
176
177 `MeasureSet` autogen specifics:
178
179 - chartengine treats `MeasureSet` as a structured family, similar to `StateSet`, not as grouped scalar coincidence
180 - flattened `MeasureSet` inputs are expected to carry:
181 - `SourceKind = MetricKindMeasureSet`
182 - `FlattenRole = FlattenRoleMeasureSetField`
183 - per-field metric names like `<name>_<field>`
184 - a synthetic reserved field label (`measure_field=<field>`)
185 - the synthetic `measure_field` label is the authoritative field-identity channel; the per-field metric-name suffix remains for `MetricMeta(name)` compatibility
186 - gauge-like `MeasureSet` fields autogen with absolute algorithm behavior; counter-like `MeasureSet` fields autogen with incremental algorithm behavior
187
188 ### Reserved Flattened Label Keys
189
190 These label keys are treated specially by chartengine when consuming flattened structured-family or distribution inputs:
191
192 | Key / Pattern | Meaning |
193 |-----------------|------------------------------------------------------------------------------------------------------------------------|
194 | `le` | Histogram bucket bound label |
195 | `quantile` | Summary quantile label |
196 | `measure_field` | `MeasureSet` field identity label |
197 | `<metric-name>` | `StateSet` special case: the flattened state name is carried under a synthetic label whose key is the base metric name |
198
199 Notes:
200
201 - `le`, `quantile`, and `measure_field` are static reserved flattened-label keys in chartengine.
202 - `StateSet` is different: it does not use a global static key; it uses the base metric name itself as the synthetic flattened label key.
203 - These keys are part of the flatten contract between `metrix` and chartengine. Reusing them as ordinary user labels on those flattened inputs is not supported.
204
205 ## Runtime Metrics
206
207 `chartengine` self-instruments to a runtime store by default (disable with `WithRuntimeStore(nil)`).
208
209 | Family | Examples |
210 |---------------------------|----------------------------------------------------------------|
211 | `ChartEngine/Build` | build success/error/skipped counters, build duration summaries |
212 | `ChartEngine/Actions` | action counters by kind |
213 | `ChartEngine/Series` | scanned/matched/filtered series counters |
214 | `ChartEngine/Route Cache` | hit/miss/entries/prune counters |
215 | `ChartEngine/Lifecycle` | removal counters by scope/reason |
216 | `ChartEngine/Plan` | gauges for chart instances/inferred dimensions |
217
218 ## Engine State
219
220 | Area | Design |
221 |--------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
222 | Program | Immutable compiled IR per revision |
223 | Engine state | Serialized under `Engine.mu` for load/build transitions |
224 | Route cache | Series identity + revision keyed cache; retained by successful sequence, pruned on each build |
225 | Materialized state | Tracks existing chart/dimension instances for incremental create/update/remove decisions; persists across cycles, resets on template reload |
226 | Determinism | Sorted chart IDs and inferred dimensions provide stable action ordering |