The whole pitch of Helix was one store, one language. Joins across metrics, logs, and traces in a single query finally make that literal.
We shipped HelixQL two years ago with the promise that you'd never need three separate tools to answer one question. What we hadn't shipped, until now, was the ability to actually join across signal types in a single query rather than running three queries and correlating the results by eye.
Why this took longer than it should have
Metrics are pre-aggregated time series, logs are unstructured text with sparse fields, and traces are a DAG of spans. Each has a different natural join key and a different cardinality profile, and our query planner had to learn to plan a join between a high-cardinality trace stream and a low-cardinality metric series without materializing either side fully in memory.
Joining a trace to the logs it produced
from traces
| where route == "/checkout" and status == "error"
| join logs on trace_id
| where logs.level == "error"
| project trace_id, logs.message, duration
That single query replaces the old workflow: find the slow trace, copy its trace ID, switch tools, paste the ID into a log search, and hope your logging pipeline actually attached trace_id consistently.
What it unlocked
One customer built a saved query that joins error traces to the deploy_events metric stream, so any spike in errors immediately shows whether it lines up with a recent deploy — a correlation that used to require someone opening the deploy calendar in a separate tab and eyeballing the timestamp.
The planner work nobody sees
Most of the six months went into a join strategy that avoids materializing the high-cardinality side of the join in memory. Early prototypes tried to hash-join traces against logs directly and fell over on any workspace ingesting more than a few million spans a day. The version that shipped instead streams the low-cardinality side (usually the metric or a filtered trace set) and probes it against a sorted, disk-backed index on the other side, which is why the join scales to workspaces with billions of daily events without a dedicated "big join" tier.
- Native joins between traces, logs, and metrics in one HelixQL query
- Planner-level optimization for high-cardinality-to-low-cardinality joins
- No separate correlation step or trace-ID copy-paste required
- Works with saved queries and dashboards-as-code panels