Route bare ottyl_seq/ottyl_ts Promql queries to the primary (ticket 107)

Audited every EngineQuery variant for the cross-snapshot join ticket
107 anticipated: Logs/Timeline/Stats/ParamDeclarations only ever read
primary data (raw text + param declarations, which never left the
primary), and MetricDeclarations/BucketValues/Promql only ever read
profile data -- no variant actually needs both, so no join/fan-out
layer is needed after all.

One real gap surfaced by that audit: a bare ottyl_seq/ottyl_ts Promql
query is evaluated by iterating self.logs_range() (evaluate_promql's
reserved-metric branch), but such queries were routed to the profile
actor's snapshot alongside real declared metrics -- and the profile
actor stores no raw log rows, so these queries silently returned empty
series instead of real data. EngineHandle::ask now parses the query
just far enough to check its metric name and routes ottyl_seq/ottyl_ts
to the primary instead, where evaluate_promql already works correctly
against real log data. Added an actor-level integration test
(promql_bare_ottyl_seq_works_through_the_real_actor_pipeline) that
fails without this fix -- verified by temporarily disabling the
routing check and confirming the test catches it.

Also deduplicated config/profile.rs's local `ask` test helper, which
had already once diverged from api::EngineHandle::ask's routing logic
and caused ticket 104 test failures (fixed then, but the duplication
itself was the root cause) -- it now delegates to the real one.

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 14:01:29 +01:00
co-authored by Claude Sonnet 5
parent 3ee6dddb0e
commit 9322aac7e1
3 changed files with 52 additions and 81 deletions
@@ -1,58 +0,0 @@
# 107 — Fan out and merge queries that need both raw logs and profile-derived data
## Summary
Today, `Logs`/`Timeline` queries read raw log text *and*
profile-derived params/metrics from a single `EngineSnapshot` in one
pass — e.g. `query.rs`'s log-entry building calls `log_entry_params`
against `compiled_params` from the same snapshot the raw text came
from. Once params/logs live on the primary and metrics live on the
profile actor (per tickets 103-106), any query needing both becomes a
real join across two snapshots instead of one borrow. This is the
piece of [[0001-actor-split]]'s design explicitly called out as "a
query-fan-out/merge layer is still needed."
## Where
- `src/engine/snapshot/query.rs` (`handle_query_inner` and the log-
entry-building helpers around line 70-117 per prior research) — the
read path that currently assumes one `EngineSnapshot` has everything
a `Logs`/`Timeline` response needs.
- `src/engine/message.rs:12-27` (`EngineQuery`) — currently answered
entirely off the primary's `left_right` reader
(`EngineHandle::ask`/`handle_query_ro`, per
[[0001-actor-split]]'s "queries already bypass this actor" note);
after the split, some query variants need data from both actors'
read sides.
- `src/api/mod.rs` — wherever `EngineQuery` results are turned into
HTTP responses; the merge/join logic likely belongs here or in a new
helper alongside `query.rs`, not duplicated per handler.
- Dashboard/PromQL panel queries (`src/engine/promql.rs`) — confirm
whether these are profile-only reads (metrics live entirely on the
profile actor) and therefore need no fan-out, versus `Logs`/
`Timeline` which do.
## Scope
- Identify every `EngineQuery` variant that currently reads both raw
log data and profile-derived data (params, metrics) from one
snapshot, and design the two-snapshot join for each — likely: read
matching log entries from the primary by sequence range, then look
up each entry's profile-derived fields (declared param values,
metric-driven fields) from the profile actor's snapshot by the same
sequence, filtering/merging results.
- Confirm ordering/consistency expectations: the profile actor can lag
the primary (per [[0001-actor-split]]'s accepted eviction/lag
tradeoff), so a joined query may find a primary-side log entry with
no corresponding profile-side data yet — decide and document the
behavior (omit those fields vs. error) rather than leaving it
implicit.
- Update PromQL/dashboard panel query handling only if it's confirmed
to need primary-side data too (e.g. `ottyl_seq`/`ottyl_ts` bare
queries per prior ticket history) — otherwise leave it reading the
profile actor alone.
- Out of scope: query result caching or performance optimization
beyond what's needed for correctness — this ticket is about
producing correct joined results, not making them fast (see
[[0005-additional-read-indexes]] for read-path performance work,
which should be sequenced after this settles).