DataValue::LogLiteral and IngestedLine now carry an Arc<str> instead of an owned String, so the two clones the ingest path paid per line (once to move text into the write closure, once to store it) become cheap refcount bumps instead of full string copies. Also cuts the per-string-content cost of left_right's full-EngineSnapshot deep clone on each reconciliation sync. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
3.3 KiB
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()'sEngineMessage::Ingestarm) — the closure passed towrite.mutateat :180-198 does header detection, storage,auto_declare_params, andsnap.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 callsapply_metric_to_log(:499-586) per match; currently only reachable as a method onEngineSnapshotinvoked 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.mutatetransaction 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::watchsignaling, and any change toEngineHandle's public API — this ticket is an internal refactor within the existing single-actor structure.