Move metric declarations and bucket computation to the profile actor (ticket 104)

Metric declare/update/remove, backfill, and per-line apply_all_metrics
now run on the profile actor (engine::profile) instead of inline in the
primary's ingest closure, per idea 0001's resolved decision that the
primary handles params only. The profile actor reuses EngineSnapshot as
its own snapshot type -- declare_metric/update_metric/apply_all_metrics
work unchanged -- and mirrors the primary's param declarations into its
own snapshot on every wake (and synchronously before handling a
DeclareMetric/UpdateMetric message, since there's no ordering guarantee
between the watch-wake and message branches in tokio::select!) so
metric label/value resolution keeps working without storing a second
copy of raw log text; backfill reads lines through its cloned primary
IndexReader instead.

EngineHandle gained a profile_reader field so MetricDeclarations/
BucketValues/Promql queries route to the profile actor's snapshot
instead of the primary's -- without it, metrics could be declared but
never queried, since the primary no longer computes or stores them.
DeclareMetric/UpdateMetric/RemoveMetricDeclaration messages are still
sent to the primary's mpsc channel and proxied to the profile actor by
handle.rs's run() loop; EngineHandle's write-side API is otherwise
unchanged (ticket 106 gives API handlers a direct route). Alert
evaluation still reads the primary's (now metric-less) snapshot
directly -- an accepted gap until ticket 105 moves it, per idea 0001's
resolved decision that alerts move with metric computation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
This commit is contained in:
Hugh Kaznowski
2026-09-07 13:53:06 +01:00
co-authored by Claude Sonnet 5
parent 033e8bbab5
commit ce3e741cb0
6 changed files with 434 additions and 191 deletions
+13 -1
View File
@@ -751,7 +751,19 @@ mod tests {
}
async fn ask(engine: &EngineHandle, query: EngineQuery) -> EngineResponse {
let read_handle = engine.reader.handle();
// Metric declarations and bucket data live on the profile actor (ticket 104), not the
// primary -- mirrors the routing in `api::EngineHandle::ask`.
let profile_query = matches!(
query,
EngineQuery::MetricDeclarations
| EngineQuery::BucketValues { .. }
| EngineQuery::Promql { .. }
);
let read_handle = if profile_query {
engine.profile_reader.handle()
} else {
engine.reader.handle()
};
let guard = read_handle.enter().expect("engine unavailable");
guard.handle_query_ro(query)
}