You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When several data sources report the same event and something asks for one value per event, a choice
gets made. Today that choice follows three different rules depending on the case, one documented
rule is not implemented at all, and the last resort is the source's name in alphabetical order.
What actually happens today
Situation
What decides
Sources share a name, type and model (e.g. two rows for one scheduler)
latest version, then highest source id — freshness is not consulted
Sources differ in name, type or model (e.g. a meter and a scheduler)
highest version number, compared across unrelated producers, then freshest belief, then source name, alphabetically
The caller passed sources=[...] in a chosen order
nothing — the order is ignored
Probes against a development database, all on one event:
# the caller's order does nothing (aaa holds the older belief, zzz the fresher one)
sources=no list -> winner='zzz source' value=22.0
sources=[aaa, zzz] -> winner='zzz source' value=22.0
sources=[zzz, aaa] -> winner='zzz source' value=22.0
# with belief times tied, the name decides (ids deliberately opposed to names)
ids: zzz=1 aaa=2 -> winner: name='aaa reporter' id=2
# within one source family, the highest id wins even when the other is fresher
ids: a=1 b=2 (a holds the fresher belief) -> winner: id=2
# version numbers are compared between unrelated producers
no list -> forecaster v9 value=22.0
[scheduler, forecaster] -> forecaster v9 value=22.0
(scheduler v1 = 11.0, believed at the event; forecaster v9 = 22.0, believed 12h earlier)
That last one is the sharpest of the three. keep_latest_version compares versions only within a (name, type, model) family, which is the only place a version number means anything. But the
selection that runs afterwards ranks versions across every source in the frame:
So a forecaster at v9 outranks a scheduler at v1 on the strength of the number alone, whatever the
belief times say and whatever the caller asked for. Two teams' independent version numbers are being
compared as though they were the same series.
How it got here
Make source optional in the input field of the AggregatorReporter #819 (2023-08) added sources=[...] to the AggregatorReporter, for the case of one source taking over
from another on the same sensor: "computing the aggregated power of a group of devices including a scheduled
device. We could get actual power commands and scheduled power commands mixed up." It also added the ValueError when a sensor has several sources and none were named.
Asked during review whether "the first source" meant the array order or the oldest belief, the answer was
written into test_source_transition's docstring: "the first source defined in the sources array is
prioritized". That was never implemented. The implementation took head(1) over an ascending index, which
is the oldest belief per event, with the source name breaking exact ties. The reviewer noted at the time
that "this test doesn't actually check that", and it still does not: every belief in the fixture shares belief_horizon=24h, so those events tie on belief time and the name decides.
Fix/reporting/latest version and excludes source types #1306 (2025-01) deliberately changed the cross-source rule, in its own words: "favour latest version
first, freshest belief second". That flipped oldest-wins to freshest-wins. It added test coverage for the
version half, and fixed a typo in the docstring above while leaving its false claim standing.
So the docstring describes the intention of 2023, the code follows the decision of 2025, and the two have never
agreed.
Why it matters
Reporters and the transition case (#819's own use case). A meter takes over from a scheduler on one sensor.
The overlapping event goes to the meter today, because the meter believed it later — which is the wanted answer,
but it arrives by way of a rule chosen for other reasons in #1306. Under the 2023 rule the scheduler won it:
The sources=[...] order, which exists for exactly this case, contributes nothing either way.
Two forecasting models side by side. Two forecasters writing to one sensor during a transition, different
names, no versions: whichever forecasted most recently wins each event, and where they tie, the alphabetically
first name wins. Renaming a forecaster can therefore change what a report says about the past. This is where
the name rule bites hardest, because forecasters legitimately run in parallel for a while.
A bumped scheduler version. Within one scheduler, handled well and tested: the newer version wins. Across
schedulers it goes wrong in both directions. A plugin's scheduler and a built-in one are different families, so keep_latest_version will not compare their versions — but the selection afterwards does, so whichever happens
to carry the higher number wins, and a plugin that started at v10 outranks a built-in scheduler at v3 forever.
KPIs. With no source filter at all, a KPI lands on these rules for every event. #2472 stops it from summing
several sources; which one it then reports is decided by the chain above, so a rename can move a KPI.
Schedules recorded per configuration (#2464). Once a scheduler's data source records the flex config it
computed under, one sensor can hold schedules from several sources that share a name, type, model and version.
Those are one family, so the highest id wins and the freshest belief is ignored. That happens to mean "the most
recently created configuration", which is reasonable, but it is a different rule from the one applied to any
other pair of sources.
Recommendation
1. Delete the false claim now.test_source_transition's docstring promises precedence the code has never
had. Whatever else is decided, that line should go, so it stops being read as a contract.
2. Stop comparing versions across families. A version number orders the releases of one producer. It says
nothing about a different producer, so sorted(set(versions)) over the whole frame is comparing quantities that
were never on the same scale. Version should rank only within a (name, type, model) family, as keep_latest_version already does.
3. Decide the rest of the precedence once, and put it in one place. Proposed, in two steps:
Within a family, reduce to one row: latest version, then freshest belief, then highest id.
Highest source id. A last resort that is stable under renaming, unlike the name.
Under this, a scheduler at v1 listed before a forecaster at v9 wins, which is what the caller asked for; with no
list given, the fresher of the two wins. Neither outcome depends on version numbers from different series, or on
what the sources happen to be called.
Two behaviour changes beyond implementing the caller's order: version stops crossing family lines, and within a
family freshness starts to count before the id, where today the id wins outright. test_source_transition and test_keep_last_version would need updating.
4. Pin the whole chain in one test. Today the behaviour is inferred from aggregator tests that were written
for filtering. A single test naming each step, with a case per step, would have caught both the unimplemented
promise and the 2025 flip.
5. Note it in the changelog when it changes. Ambiguous events silently changing hands is exactly the sort of
thing a host notices in a report long after the upgrade.
If the caller's order is more than we want to take on, the minimum worth doing is (1), (2) and (5), plus
replacing the alphabetical last resort with the id. That removes the rename hazard and the cross-family version
comparison, which are the two that can silently pick a value nobody would defend.
Related
Came out of the review of #2472, which stops a KPI summing several sources for one event but deliberately does
not touch which source wins. Relevant to #2464, which makes multi-source schedule sensors ordinary.
When several data sources report the same event and something asks for one value per event, a choice
gets made. Today that choice follows three different rules depending on the case, one documented
rule is not implemented at all, and the last resort is the source's name in alphabetical order.
What actually happens today
sources=[...]in a chosen orderProbes against a development database, all on one event:
That last one is the sharpest of the three.
keep_latest_versioncompares versions only within a(name, type, model)family, which is the only place a version number means anything. But theselection that runs afterwards ranks versions across every source in the frame:
So a forecaster at v9 outranks a scheduler at v1 on the strength of the number alone, whatever the
belief times say and whatever the caller asked for. Two teams' independent version numbers are being
compared as though they were the same series.
How it got here
Make source optional in the input field of the
AggregatorReporter#819 (2023-08) addedsources=[...]to theAggregatorReporter, for the case of one source taking overfrom another on the same sensor: "computing the aggregated power of a group of devices including a scheduled
device. We could get actual power commands and scheduled power commands mixed up." It also added the
ValueErrorwhen a sensor has several sources and none were named.Asked during review whether "the first source" meant the array order or the oldest belief, the answer was
written into
test_source_transition's docstring: "the first source defined in the sources array isprioritized". That was never implemented. The implementation took
head(1)over an ascending index, whichis the oldest belief per event, with the source name breaking exact ties. The reviewer noted at the time
that "this test doesn't actually check that", and it still does not: every belief in the fixture shares
belief_horizon=24h, so those events tie on belief time and the name decides.[reporting] Add flag to filter the data with a the latest version source #1045 (2024-05) added version filtering (
keep_latest_version).Fix/reporting/latest version and excludes source types #1306 (2025-01) deliberately changed the cross-source rule, in its own words: "favour latest version
first, freshest belief second". That flipped oldest-wins to freshest-wins. It added test coverage for the
version half, and fixed a typo in the docstring above while leaving its false claim standing.
Speed up post-processing of sensor data searches #2328 (2026-07) extracted
_select_latest_version_and_belief_per_eventas a fast path, preserving Fix/reporting/latest version and excludes source types #1306'ssemantics.
So the docstring describes the intention of 2023, the code follows the decision of 2025, and the two have never
agreed.
Why it matters
Reporters and the transition case (#819's own use case). A meter takes over from a scheduler on one sensor.
The overlapping event goes to the meter today, because the meter believed it later — which is the wanted answer,
but it arrives by way of a rule chosen for other reasons in #1306. Under the 2023 rule the scheduler won it:
The
sources=[...]order, which exists for exactly this case, contributes nothing either way.Two forecasting models side by side. Two forecasters writing to one sensor during a transition, different
names, no versions: whichever forecasted most recently wins each event, and where they tie, the alphabetically
first name wins. Renaming a forecaster can therefore change what a report says about the past. This is where
the name rule bites hardest, because forecasters legitimately run in parallel for a while.
A bumped scheduler version. Within one scheduler, handled well and tested: the newer version wins. Across
schedulers it goes wrong in both directions. A plugin's scheduler and a built-in one are different families, so
keep_latest_versionwill not compare their versions — but the selection afterwards does, so whichever happensto carry the higher number wins, and a plugin that started at v10 outranks a built-in scheduler at v3 forever.
KPIs. With no source filter at all, a KPI lands on these rules for every event. #2472 stops it from summing
several sources; which one it then reports is decided by the chain above, so a rename can move a KPI.
Schedules recorded per configuration (#2464). Once a scheduler's data source records the flex config it
computed under, one sensor can hold schedules from several sources that share a name, type, model and version.
Those are one family, so the highest id wins and the freshest belief is ignored. That happens to mean "the most
recently created configuration", which is reasonable, but it is a different rule from the one applied to any
other pair of sources.
Recommendation
1. Delete the false claim now.
test_source_transition's docstring promises precedence the code has neverhad. Whatever else is decided, that line should go, so it stops being read as a contract.
2. Stop comparing versions across families. A version number orders the releases of one producer. It says
nothing about a different producer, so
sorted(set(versions))over the whole frame is comparing quantities thatwere never on the same scale. Version should rank only within a
(name, type, model)family, askeep_latest_versionalready does.3. Decide the rest of the precedence once, and put it in one place. Proposed, in two steps:
Within a family, reduce to one row: latest version, then freshest belief, then highest id.
Between families, choose one:
sourcesorder, when one was given. This is the only rule a caller controls, it is whatMake source optional in the input field of the
AggregatorReporter#819 set out to provide, and it makes the transition case explicit instead of accidental.Under this, a scheduler at v1 listed before a forecaster at v9 wins, which is what the caller asked for; with no
list given, the fresher of the two wins. Neither outcome depends on version numbers from different series, or on
what the sources happen to be called.
Two behaviour changes beyond implementing the caller's order: version stops crossing family lines, and within a
family freshness starts to count before the id, where today the id wins outright.
test_source_transitionandtest_keep_last_versionwould need updating.4. Pin the whole chain in one test. Today the behaviour is inferred from aggregator tests that were written
for filtering. A single test naming each step, with a case per step, would have caught both the unimplemented
promise and the 2025 flip.
5. Note it in the changelog when it changes. Ambiguous events silently changing hands is exactly the sort of
thing a host notices in a report long after the upgrade.
If the caller's order is more than we want to take on, the minimum worth doing is (1), (2) and (5), plus
replacing the alphabetical last resort with the id. That removes the rename hazard and the cross-family version
comparison, which are the two that can silently pick a value nobody would defend.
Related
Came out of the review of #2472, which stops a KPI summing several sources for one event but deliberately does
not touch which source wins. Relevant to #2464, which makes multi-source schedule sensors ordinary.