Extract param extraction into its own EngineSnapshot::ingest_params stage (ticket 101)

Raw storage, log-stream header derivation, and auto-param detection
were inlined directly in the ingest actor's write closure alongside
metric computation. Pulling them into their own method gives the
ingest arm two clearly separated, independently-callable stages —
param extraction (ingest_params) and metric computation
(apply_all_metrics) — with no behavior change, in preparation for
splitting these onto separate actors (ticket 102).

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:27:49 +01:00
co-authored by Claude Sonnet 5
parent f43445b280
commit 756e054527
3 changed files with 33 additions and 79 deletions
@@ -1,62 +0,0 @@
# 101 — Metric computation is inlined into the single ingest actor's write closure, not a separately-callable stage
## Summary
Per-line ingest currently does ingest-lag bookkeeping, log-stream
header detection, raw-text storage, auto-param detection, *and* every
declared metric's regex evaluation, all synchronously inside one
`write.mutate(...)` closure in the actor's `run()` loop, followed by
alert evaluation against the result in the same message handler. This
is migration step 2 of [[0001-actor-split]]'s resolved plan: pull
metric computation out into a stage that can be called independently
of the ingest closure — still within the single actor for now — so
that step 3 (splitting into reader/primary/profile actors) is a
wiring change rather than also being the first time metric computation
is separated from raw ingest. Depends on [[100-arc-ify-stored-log-and-metric-strings]]
landing first (Arc'd text is what makes passing a line into a separate
stage cheap).
## Where
- `src/engine/handle.rs:132-224` (`run()`'s `EngineMessage::Ingest`
arm) — the closure passed to `write.mutate` at :180-198 does header
detection, storage, `auto_declare_params`, and
`snap.apply_all_metrics(sequence, timestamp, &text_for_op)` (:196)
all in one synchronous block; alert evaluation follows immediately
after at :199-219, reading the snapshot metric computation just
produced.
- `src/engine/snapshot/mod.rs:616` (`apply_all_metrics`) — the entry
point that iterates every declared metric and calls
`apply_metric_to_log` (:499-586) per match; currently only reachable
as a method on `EngineSnapshot` invoked from inside the ingest
closure above.
- `src/engine/alerts.rs:211` (`evaluate_alerts`) — currently called
directly from the ingest arm (`handle.rs:199-219`) against the
snapshot mutated by the same message; this is the alert-evaluation
call site that migration step 3 will later move to a profile actor
(per [[0001-actor-split]]'s "Resolved decisions" — not in scope for
this ticket).
## Scope
- Extract param-extraction (header detection, `auto_declare_params`,
raw storage) and metric-computation (`apply_all_metrics`) into
separate, independently-callable functions/methods with a clear
boundary between them — e.g. a function that takes a line and
returns/records derived params only, and a second that takes the
declared-metrics set plus a param-resolved line and returns bucket
updates — rather than one closure that does both inline.
- Keep both stages running inside the same actor and the same
`write.mutate` transaction for this ticket — the goal is a clean
internal seam, not concurrency yet. Step 3 (a separate ticket, not
this one) is what actually moves metric computation to a different
task.
- Do not change alert evaluation's behavior or its current call site
relative to metric computation (still immediately after, same
actor) — only prepare the metric-computation code so it's callable
as its own unit; moving alerts to a profile actor is explicitly step
3's concern per [[0001-actor-split]]'s resolved decisions.
- Out of scope: the reader/primary/profile actor split itself, any new
`tokio::sync::watch` signaling, and any change to `EngineHandle`'s
public API — this ticket is an internal refactor within the existing
single-actor structure.