16. Per-Array Rendering and Series Colour by Entity
Status
Accepted. Supersedes the fixed read/write series-colour clause of ADR-0014; the rest of ADR-0014 stands.
Context
Every bundled dashboard declares array as multi: true, includeAll: true, so multi-array
selection is offered on every screen. Two defect classes then discarded it, both reported by
an operator running two arrays against one exporter:
- Fourteen panels across six dashboards aggregated with no
by (...)clause, collapsing all selected arrays into a single unattributable value.avg()of a data reduction ratio across arrays is also arithmetically meaningless — a ratio of ratios is not a ratio. - ADR-0014's
read = blue, write = orange, implemented asbyRegexpoverrides pinningcolor.mode: fixed, erased the entity dimension. OnRead Bandwidth per Appliancethe query returns only read series, so every appliance matched/ read$/and rendered blue.
A third defect had not yet been reported. powerstore_drive_state and
powerstore_replication_session_state are info series that are always 1, with the state in a
label. A healthy array matches no series, so count by (array) returned nothing and Grafana drew
no tile — indistinguishable from an array that is not being scraped.
Decision
- Every aggregating panel groups by
arrayand legends as{{array}}.statandgaugepanels already render one tile per series, so no panel is repeated and nogridPoschanges. - Info-series counts are zero-filled with
or sum by (array) (powerstore_up{...} * 0).powerstore_upcarries exactly thearraylabel; thesum by (array)on the filler pins its label set so theorunion is label-compatible. Boolean comparisons use theboolmodifier (== bool 0) instead, which yields1/0per series rather than filtering series away. - Hue encodes identity, line style encodes direction. Timeseries panels set
color.mode: palette-classic-by-name, which derives hue from each series' full display name. Within any one panel every array or appliance is therefore distinguishable, and its colour is stable across refreshes;/ write$/setscustom.lineStyleto a dash. Because the hash includes the read/write suffix and legends differ between panels, the same entity is not guaranteed the same hue on a different panel or dashboard — only line style (solid vs. dashed) carries direction consistently.statpanels have no line and simply drop the overrides. Threshold-coloured panels (CPU, drive wear, capacity gauges) are unaffected: their hue encodes a value against a threshold, not an identity. - A CI guard. ADR-0014 deferred a consistency linter until "drift becomes a problem".
Fourteen panels found by a customer is that problem.
internal/dashboardswalks the bundled JSON and failsmake cion bare aggregations and on fixed direction colours. It is stdlib-only and adds no toolchain, so the ADR-0014 rejection of a generator still holds.
Consequences
- No collector change: every metric and label required already existed.
- Per-array tiles stay legible to roughly six arrays. Past that, Fleet Health's stat rows crowd,
and the follow-up is a weighted rollup (
sum(logical_used) / sum(physical_used)for DRR) shown beside the per-array breakdown — not a return toavg(). - The linter is deliberately syntactic; it greps expression strings rather than parsing PromQL.
It permits
sum by (appliance_name) (...), which omitsarraywhile grouping by something else. Two arrays sharing an appliance, volume or volume-group name therefore still merge onblock/02-appliances,block/03-volumes,block/04-volume-groupsandfile/01-file-systems. PowerStore derives default appliance names from the serial number, so collision is unlikely; widening those groupings would change legends on every single-array install. Tracked, not fixed. - Existing users see colour changes on ten panels. Dashboards are provisioned read-only from this
repo, so there is nothing to migrate. Screenshots under
docs/showing the old colours are stale.