Files
ottyl/tickets/104-move-metric-computation-to-profile-actor.md
T
Hugh KaznowskiandClaude Sonnet 5 2c1aaa6e9c Break ticket 102's actor split into reviewable sub-tickets (103-108)
Ticket 102 (splitting the engine into reader/primary/profile actors)
was a single multi-thousand-line ticket covering a new actor
topology, an EngineHandle API redesign, a query fan-out layer, moving
alerts, and a full test-harness rewrite, with some design decisions
deliberately left open. Splitting it into six sequenced tickets
(profile actor skeleton + signaling, move metric computation, move
alert evaluation, split the handle API, query fan-out, test harness
rework) lets each land as its own reviewable commit instead of one
large speculative rewrite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
2026-09-07 13:33:01 +01:00

4.0 KiB

104 — Move metric declarations and bucket computation into the profile actor

Summary

Second slice of the split: metric declarations, apply_all_metrics, and the declare/update/remove/backfill machinery move off the primary EngineSnapshot and onto the profile actor's ProfileSnapshot (stood up in 103-profile-actor-skeleton-and-primary-signaling). The primary keeps raw log storage and param declarations only, per 0001-actor-split's resolved decision that the primary "extracts declared + auto params only... no metrics, no profile logic." The profile actor consumes newly-visible primary lines (via the watch-wake loop from ticket 103) and runs metric regexes against them itself, building its own bucket data independent of the primary.

Where

  • src/engine/snapshot/mod.rs:364-434 (validate_metric_request, declare_metric, resolve_backfill_and_store_metric) and :434-487 (validate_update_metric, update_metric, remove_metric_declaration) — the declare/update/remove machinery to move onto ProfileSnapshot.
  • src/engine/snapshot/mod.rs:587-617 (backfill_metric, apply_all_metrics) — backfill replays self.logs_range() (a primary-side accessor) through apply_metric_to_log; once metrics live on the profile actor, backfill needs to read logs through the profile actor's cloned primary IndexReader instead of self.
  • src/engine/handle.rs — EngineMessage::DeclareMetric/ UpdateMetric/RemoveMetricDeclaration arms (see the message list at src/engine/message.rs:30-63) currently route through the single actor's write.mutate against EngineSnapshot; these need to route to the profile actor's ProfileSnapshot instead.
  • src/engine/handle.rs's Ingest arm — snap.apply_all_metrics(...) is called directly after ingest_params today (see ticket 101's change); this call moves out of the primary's ingest closure entirely once the profile actor computes metrics itself from the lines it observes via the watch/read loop, rather than the primary computing them inline.

Scope

  • Move MetricDeclaration/bucket-related IndexKey/DataValue variants' usage (not necessarily their type definitions, which are shared) onto ProfileSnapshot's own index/map, separate from the primary's.
  • Move declare_metric/update_metric/remove_metric_declaration/ backfill_metric/apply_all_metrics (and their validation counterparts) to operate on ProfileSnapshot, reading raw log text via the profile actor's cloned primary IndexReader wherever they need logs_range-style access, instead of self.index on the primary.
  • Remove the apply_all_metrics call from the primary's Ingest arm in handle.rs — the primary no longer computes metrics inline; the profile actor's watch-wake loop (from ticket 103) now calls apply_all_metrics-equivalent logic itself per newly-visible line.
  • DeclareMetric/UpdateMetric/RemoveMetricDeclaration messages need a new route to the profile actor (a second mpsc channel, or folding into the profile actor's own message enum) — decide the concrete mechanism during implementation; EngineHandle's public API doesn't need to change yet if this ticket keeps routing internal to handle.rs (full API surface changes are 106-split-enginehandle-api-for-primary-and-profile's scope).
  • Out of scope: alert evaluation (still reads the primary's snapshot directly today — 105-move-alert-evaluation-to-profile-actor moves it), and the profile-switch (activate_profile) reload path (still targets whatever this ticket lands as the metric-owning actor, but retargeting src/api/mod.rs:812-852 explicitly is 106-split-enginehandle-api-for-primary-and-profile's scope).
  • Backfill cost and blocking behavior: per 0001-actor-split's performance rationale, this move is what stops a slow backfill from blocking the primary's ingest/query-serving — confirm with a test that ingesting new lines on the primary keeps working while the profile actor is mid-backfill on a newly declared metric.