Schema 3.6.0
Schema and wire-format change
scoreReasons becomes a real protobuf map<string, double>. Producers and consumers should move to
the 3.6.0 generated schemas together.
Changed — scoreReasons is a map of contributions
ClientSample.scoreReasons, PeerConnectionSample.scoreReasons, InboundTrackSample.scoreReasons
and OutboundTrackSample.scoreReasons changed from an optional array of strings to an optional
map of doubles — Record<string, number> in the generated TypeScript (Avro:
["null", {"type": "map", "values": "double"}]), mapping each reason to how much it contributed to
the calculated score.
// 3.4.0 – 3.5.0
"scoreReasons": ["high-rtt", "high-packetloss"]
// 3.6.0
"scoreReasons": { "transport-delay-degraded": 2.5, "transport-loss-sustained": 2.5 }A list of labels says which findings applied; a map says which one actually cost the score. That is the difference between a dashboard that lists five reasons for a 4.8 and one that shows the single 0.2 that moved it.
On the wire
The generator learned to emit proto3 maps for primitive-valued Avro maps — union-valued maps
such as payload keep travelling as JSON strings. A map field sorts with the repeated group, so
neighbouring field numbers are unchanged.
Both codecs treat the field exactly as they treated the string array: written whole whenever present, never carried forward, and an empty map meaning the same as an absent one.
Upgrading
A consumer that summed or counted reasons needs to decide what it now means:
// how many reasons applied — unchanged in spirit
Object.keys(sample.scoreReasons ?? {}).length;
// what the score actually lost, which was not previously answerable
Object.values(sample.scoreReasons ?? {}).reduce((a, b) => a + b, 0);A mixed fleet still reads
observer-js folds a legacy string[] into the record shape with a magnitude of 0, which keeps
any summing of contributions honest about the magnitude the old wire never carried. So an older
client’s samples are readable alongside a newer one’s without special-casing.
On the producing side, client-monitor-js publishes exactly this
shape: scoreReasons is keyed by issue type wherever an issue is behind the charge, and by the name
of a continuous reading otherwise, and a charge of 0 is dropped rather than written — a reason
sitting at zero reads as a fault that was found and never resolved.
← Back to version history · 3.5.0 →