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:
co-authored by
Claude Sonnet 5
parent
033e8bbab5
commit
ce3e741cb0
+60
-13
@@ -394,18 +394,52 @@ impl EngineSnapshot {
|
||||
Ok(re)
|
||||
}
|
||||
|
||||
/// Self-contained declare, backfilling from `self.logs_range()` — only used by tests that
|
||||
/// build a standalone `EngineSnapshot` without the actor split; production code always goes
|
||||
/// through the profile actor, which calls `declare_metric_with_lines` directly (its own
|
||||
/// snapshot holds no raw log text of its own to backfill from).
|
||||
#[cfg_attr(not(test), allow(dead_code))]
|
||||
pub(crate) fn declare_metric(&mut self, request: DeclareMetricRequest) -> Result<(), String> {
|
||||
let lines = self.owned_logs_range();
|
||||
self.declare_metric_with_lines(request, &lines)
|
||||
}
|
||||
|
||||
/// Like `declare_metric`, but backfills from `lines` instead of `self.logs_range()` — the
|
||||
/// profile actor (`engine::profile`) calls this, since its own snapshot holds no raw log
|
||||
/// text (only mirrored param declarations plus its own metric declarations/buckets); it
|
||||
/// sources backfill lines by reading through its cloned primary `IndexReader` instead.
|
||||
pub(crate) fn declare_metric_with_lines(
|
||||
&mut self,
|
||||
request: DeclareMetricRequest,
|
||||
lines: &[(u64, Timestamp, String)],
|
||||
) -> Result<(), String> {
|
||||
let re = self.validate_metric_request(&request)?;
|
||||
self.metric_regex_cache
|
||||
.insert(request.name.clone(), re.clone());
|
||||
self.resolve_backfill_and_store_metric(request, &re);
|
||||
self.resolve_backfill_and_store_metric(request, &re, lines);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shared by `declare_metric` and `update_metric`: resolves param declarations, backfills
|
||||
/// the metric's buckets from existing log history, then writes the `MetricDeclaration` entry.
|
||||
/// Callers differ only in what happens before this (rename bookkeeping for updates).
|
||||
fn resolve_backfill_and_store_metric(&mut self, request: DeclareMetricRequest, re: &Regex) {
|
||||
/// `logs_range` collected into owned `(sequence, timestamp, text)` triples, for callers
|
||||
/// (like `declare_metric`) that need to hand backfill lines to a method taking them by
|
||||
/// value rather than borrowing `self` for the duration.
|
||||
#[cfg_attr(not(test), allow(dead_code))]
|
||||
fn owned_logs_range(&self) -> Vec<(u64, Timestamp, String)> {
|
||||
self.logs_range()
|
||||
.map(|(sequence, timestamp, text)| (sequence, timestamp, text.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Shared by `declare_metric`/`update_metric` (and their `_with_lines` variants): resolves
|
||||
/// param declarations, backfills the metric's buckets from `lines`, then writes the
|
||||
/// `MetricDeclaration` entry. Callers differ only in what happens before this (rename
|
||||
/// bookkeeping for updates) and where `lines` comes from.
|
||||
fn resolve_backfill_and_store_metric(
|
||||
&mut self,
|
||||
request: DeclareMetricRequest,
|
||||
re: &Regex,
|
||||
lines: &[(u64, Timestamp, String)],
|
||||
) {
|
||||
let resolved_params = self.resolved_param_declarations();
|
||||
self.backfill_metric(
|
||||
&request.name,
|
||||
@@ -415,6 +449,7 @@ impl EngineSnapshot {
|
||||
&resolved_params,
|
||||
request.matched_labels.as_ref(),
|
||||
request.unmatched_labels.as_ref(),
|
||||
lines,
|
||||
);
|
||||
self.index.insert(
|
||||
IndexKey::MetricDeclaration {
|
||||
@@ -451,10 +486,25 @@ impl EngineSnapshot {
|
||||
self.validate_metric_request(request)
|
||||
}
|
||||
|
||||
/// Self-contained update, backfilling from `self.logs_range()` — see `declare_metric`'s doc
|
||||
/// comment; only used by tests that build a standalone `EngineSnapshot`.
|
||||
#[cfg_attr(not(test), allow(dead_code))]
|
||||
pub(crate) fn update_metric(
|
||||
&mut self,
|
||||
existing_name: &str,
|
||||
request: DeclareMetricRequest,
|
||||
) -> Result<(), String> {
|
||||
let lines = self.owned_logs_range();
|
||||
self.update_metric_with_lines(existing_name, request, &lines)
|
||||
}
|
||||
|
||||
/// Like `update_metric`, but backfills from `lines` instead of `self.logs_range()` — see
|
||||
/// `declare_metric_with_lines`.
|
||||
pub(crate) fn update_metric_with_lines(
|
||||
&mut self,
|
||||
existing_name: &str,
|
||||
request: DeclareMetricRequest,
|
||||
lines: &[(u64, Timestamp, String)],
|
||||
) -> Result<(), String> {
|
||||
let re = self.validate_update_metric(existing_name, &request)?;
|
||||
|
||||
@@ -468,7 +518,7 @@ impl EngineSnapshot {
|
||||
self.metric_regex_cache
|
||||
.insert(request.name.clone(), re.clone());
|
||||
|
||||
self.resolve_backfill_and_store_metric(request, &re);
|
||||
self.resolve_backfill_and_store_metric(request, &re, lines);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -593,11 +643,8 @@ impl EngineSnapshot {
|
||||
resolved_params: &HashMap<String, ResolvedParam>,
|
||||
matched_labels: Option<&BTreeMap<String, String>>,
|
||||
unmatched_labels: Option<&BTreeMap<String, String>>,
|
||||
lines: &[(u64, Timestamp, String)],
|
||||
) {
|
||||
let lines: Vec<(u64, Timestamp, String)> = self
|
||||
.logs_range()
|
||||
.map(|(sequence, timestamp, text)| (sequence, timestamp, text.to_string()))
|
||||
.collect();
|
||||
for (sequence, timestamp, text) in lines {
|
||||
self.apply_metric_to_log(
|
||||
metric_name,
|
||||
@@ -607,9 +654,9 @@ impl EngineSnapshot {
|
||||
resolved_params,
|
||||
matched_labels,
|
||||
unmatched_labels,
|
||||
sequence,
|
||||
timestamp,
|
||||
&text,
|
||||
*sequence,
|
||||
*timestamp,
|
||||
text,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user