fix: aggregation surfaces materialize/state-load failures loudly

The debounced materialization caught its failure with an empty `catch (() => {})`,
silently leaving the materialized Measurement entity stale; the aggregation-index
init() state-load failure was swallowed the same way, leaving aggregates reading
empty with no signal. Both are non-fatal (values rebuild via backfill-on-query),
but a silent stale/empty read violates loud-errors-never-quiet-losses. Both now
emit a loud warning naming the affected group / cause.
This commit is contained in:
David Snelling 2026-07-13 09:49:38 -07:00
parent ba958d97b5
commit 02eff64b78
2 changed files with 61 additions and 2 deletions

View file

@ -14,6 +14,7 @@ import type {
AggregateMetricDef
} from '../types/brainy.types.js'
import { serializeGroupKey } from './AggregationIndex.js'
import { prodLog } from '../utils/logger.js'
/**
* Callback interface for the materializer to create/update Brainy entities.
@ -86,8 +87,15 @@ export class AggregateMaterializer {
if (existing) clearTimeout(existing)
this.debounceTimers.set(key, setTimeout(() => {
this.materializeOne(key).catch(() => {
// Non-fatal — materialization is derived data
this.materializeOne(key).catch((err) => {
// Materialization is derived data (rebuildable via backfill-on-query), so
// a failure is non-fatal — but it leaves the materialized Measurement
// entity STALE. Surface it loudly rather than swallow it silently.
prodLog.warn(
`[Aggregation] Failed to materialize aggregate group '${key}': ` +
`${(err as Error).message}. The materialized value is stale until the ` +
`next successful materialization or a backfill-on-query.`
)
})
}, debounceMs))
}