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 doublesRecord<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 →