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
3.5 KiB
103 — Stand up the profile actor skeleton and primary→profile signaling, with no behavior moved yet
Summary
First slice of 0001-actor-split's step 3 (splitting the single
engine actor into reader/primary/profile actors). This ticket is pure
plumbing: a second actor task with its own left_right write side for
profile-owned state, a way for it to read the primary's index
(EngineHandle's IndexReader is already Clone, so no primary-side
change is needed to hand out a read-only view), and a
tokio::sync::watch the primary signals on after each left_right
sync so the profile actor wakes without busy-polling. No metric
computation, alert evaluation, or query routing moves in this ticket —
that's 104-move-metric-computation-to-profile-actor and
105-move-alert-evaluation-to-profile-actor. Depends on
101-extract-metric-computation-into-separate-stage (done) for the
ingest_params/apply_all_metrics seam this split will eventually
route across two actors.
Where
src/engine/handle.rs:33-47(IndexWriter) —mutate()callsself.handle.append(...)thenself.handle.publish(); this is where the new watch-sender notification hooks in, right afterpublish().src/engine/handle.rs:48-73(IndexReadHandle/IndexReader) — the existing read-side pattern to mirror for the profile actor's own derived state;IndexReaderis alreadyClone(handle.rs:61-67is a template, not something to reuse directly — the profile actor needs its ownleft_rightpair for its own state, plus a cloned primaryIndexReaderfor read access).src/engine/handle.rs:74-78(EngineHandle) and:119-124(spawn) — the shape to parallel for a new profile-actor spawn function.src/engine/snapshot/mod.rs:208-255(logs_range/logs_in_sequence_range) — the read-side primary API the profile actor will use (via its clonedIndexReader) to pull newly-visible lines after waking on the watch signal.
Scope
- Add a
ProfileSnapshottype (new module, e.g.src/engine/profile.rs) with its ownleft_rightwrite/read pair, parallel toEngineSnapshot's pattern inhandle.rs— for now it can be effectively empty (a placeholder struct), since no state moves into it yet. - Add a
tokio::sync::watch<u64>(carrying, e.g., the primary's latest processed sequence) thatIndexWriter::mutatesends on afterpublish()succeeds. - Add a profile actor task: holds a cloned primary
IndexReaderplus its ownProfileSnapshotwrite side, wakes on the watch channel, and — for this ticket only — reads whatever new lineslogs_in_sequence_rangeexposes since its last-seen sequence and does nothing with them (e.g. just advances its own watermark). The goal is proving the wake/read loop works end-to-end, not doing real work with the data yet. - Wire
spawn(src/cli/mod.rs:95) to also spawn the profile actor alongside the primary, but do not yet change whatApiStateholds or what any API handler talks to —EngineHandleand its public API stay exactly as they are today. That's 106-split-enginehandle-api-for-primary-and-profile's concern. - Out of scope: moving
apply_all_metrics, metric declarations, or alert evaluation; changingEngineHandle's public surface; eviction coordination beyond what already exists (primary still evicts on its own schedule per 0001-actor-split's resolved decision — this ticket doesn't need to handle the profile actor falling behind eviction yet, since it isn't consuming anything meaningful).