Skip to content

Commit e9eb84e

Browse files
authored
feat: Add MVs to Demo Traces source config (#1562)
# Summary This PR adds MV configurations to the play environment, and backports [a materialized view fix from EE](https://github.com/DeploySentinel/hyperdx-ee/pull/1012/files) to this repo. <img width="775" height="390" alt="Screenshot 2026-01-06 at 3 03 06 PM" src="https://github.com/user-attachments/assets/53d694f2-8754-4ce2-8b01-eeefef71b6b1" /> <img width="647" height="657" alt="Screenshot 2026-01-06 at 3 05 43 PM" src="https://github.com/user-attachments/assets/d9e2a724-4481-4e6b-ae29-01f3d9b1fa22" /> <img width="767" height="354" alt="Screenshot 2026-01-06 at 3 03 05 PM" src="https://github.com/user-attachments/assets/81bf9c8b-ed7e-4259-86ec-7d933a767374" />
1 parent ebaebc1 commit e9eb84e

3 files changed

Lines changed: 70 additions & 2 deletions

File tree

packages/api/.env.e2e

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ OPAMP_PORT=24320
1616
# Auto-create default connections and sources for new teams
1717
# Uses public demo ClickHouse instance with pre-populated data
1818
DEFAULT_CONNECTIONS='[{"name":"local","host":"https://sql-clickhouse.clickhouse.com","username":"otel_demo","password":""}]'
19-
DEFAULT_SOURCES='[{"kind":"log","name":"Demo Logs","connection":"local","from":{"databaseName":"otel_v2","tableName":"otel_logs"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, SeverityText, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body","displayedTimestampValueExpression":"Timestamp","sessionSourceId":"Demo Sessions","traceSourceId":"Demo Traces","metricSourceId":"Demo Metrics"},{"kind":"trace","name":"Demo Traces","connection":"local","from":{"databaseName":"otel_v2","tableName":"otel_traces"},"timestampValueExpression":"Timestamp","defaultTableSelectExpression":"Timestamp, ServiceName, StatusCode, round(Duration / 1e6), SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"SpanName","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanKindExpression":"SpanKind","spanNameExpression":"SpanName","logSourceId":"Demo Logs","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","spanEventsValueExpression":"Events","metricSourceId":"Demo Metrics","sessionSourceId":"Demo Sessions"},{"kind":"metric","name":"Demo Metrics","connection":"local","from":{"databaseName":"otel_v2","tableName":""},"timestampValueExpression":"TimeUnix","serviceNameExpression":"ServiceName","metricTables":{"gauge":"otel_metrics_gauge","histogram":"otel_metrics_histogram","sum":"otel_metrics_sum","summary":"otel_metrics_summary","exponential histogram":"otel_metrics_exponential_histogram"},"resourceAttributesExpression":"ResourceAttributes","logSourceId":"Demo Logs"},{"kind":"session","name":"Demo Sessions","connection":"local","from":{"databaseName":"otel_v2","tableName":"hyperdx_sessions"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceSourceId":"Demo Traces","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body"},{"kind":"trace","name":"ClickPy Traces","connection":"local","from":{"databaseName":"otel_clickpy","tableName":"otel_traces"},"timestampValueExpression":"Timestamp","defaultTableSelectExpression":"Timestamp, ServiceName, StatusCode, round(Duration / 1e6), SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"SpanName","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanKindExpression":"SpanKind","spanNameExpression":"SpanName","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","spanEventsValueExpression":"Events","highlightedTraceAttributeExpressions":[{"sqlExpression":"if((SpanAttributes['http.route']) LIKE '%dashboard%', concat('https://clickpy.clickhouse.com', path(SpanAttributes['http.target'])), '')","alias":"clickpy_link"}],"sessionSourceId":"ClickPy Sessions"},{"kind":"session","name":"ClickPy Sessions","connection":"local","from":{"databaseName":"otel_clickpy","tableName":"hyperdx_sessions"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceSourceId":"ClickPy Traces","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body"}]'
19+
DEFAULT_SOURCES='[{"kind":"log","name":"Demo Logs","connection":"local","from":{"databaseName":"otel_v2","tableName":"otel_logs"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, SeverityText, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body","displayedTimestampValueExpression":"Timestamp","sessionSourceId":"Demo Sessions","traceSourceId":"Demo Traces","metricSourceId":"Demo Metrics"},{"kind":"trace","name":"Demo Traces","connection":"local","from":{"databaseName":"otel_v2","tableName":"otel_traces"},"timestampValueExpression":"Timestamp","defaultTableSelectExpression":"Timestamp, ServiceName, StatusCode, round(Duration / 1e6), SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"SpanName","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanKindExpression":"SpanKind","spanNameExpression":"SpanName","logSourceId":"Demo Logs","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","spanEventsValueExpression":"Events","metricSourceId":"Demo Metrics","sessionSourceId":"Demo Sessions","materializedViews": [{"databaseName": "otel_v2","tableName": "otel_traces_1m","dimensionColumns": "ServiceName, StatusCode","minGranularity": "1 minute","timestampColumn": "Timestamp","aggregatedColumns": [{"mvColumn": "count","aggFn": "count","sourceColumn": ""},{"mvColumn": "max__Duration","aggFn": "max","sourceColumn": "Duration"},{"mvColumn": "avg__Duration","aggFn": "avg","sourceColumn": "Duration"}]},{"databaseName": "otel_v2","tableName": "otel_traces_1m_v2","dimensionColumns": "ServiceName, SpanName, SpanKind","minGranularity": "1 minute","timestampColumn": "Timestamp","aggregatedColumns": [{"mvColumn": "count","aggFn": "count","sourceColumn": ""},{"mvColumn": "max__Duration","aggFn": "max","sourceColumn": "Duration"},{"mvColumn": "avg__Duration","aggFn": "avg","sourceColumn": "Duration"},{"mvColumn": "quantile__Duration","aggFn": "quantile","sourceColumn": "Duration"}]}]},{"kind":"metric","name":"Demo Metrics","connection":"local","from":{"databaseName":"otel_v2","tableName":""},"timestampValueExpression":"TimeUnix","serviceNameExpression":"ServiceName","metricTables":{"gauge":"otel_metrics_gauge","histogram":"otel_metrics_histogram","sum":"otel_metrics_sum","summary":"otel_metrics_summary","exponential histogram":"otel_metrics_exponential_histogram"},"resourceAttributesExpression":"ResourceAttributes","logSourceId":"Demo Logs"},{"kind":"session","name":"Demo Sessions","connection":"local","from":{"databaseName":"otel_v2","tableName":"hyperdx_sessions"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceSourceId":"Demo Traces","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body"},{"kind":"trace","name":"ClickPy Traces","connection":"local","from":{"databaseName":"otel_clickpy","tableName":"otel_traces"},"timestampValueExpression":"Timestamp","defaultTableSelectExpression":"Timestamp, ServiceName, StatusCode, round(Duration / 1e6), SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"SpanName","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanKindExpression":"SpanKind","spanNameExpression":"SpanName","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","spanEventsValueExpression":"Events","highlightedTraceAttributeExpressions":[{"sqlExpression":"if((SpanAttributes['http.route']) LIKE '%dashboard%', concat('https://clickpy.clickhouse.com', path(SpanAttributes['http.target'])), '')","alias":"clickpy_link"}],"sessionSourceId":"ClickPy Sessions"},{"kind":"session","name":"ClickPy Sessions","connection":"local","from":{"databaseName":"otel_clickpy","tableName":"hyperdx_sessions"},"timestampValueExpression":"TimestampTime","defaultTableSelectExpression":"Timestamp, ServiceName, Body","serviceNameExpression":"ServiceName","severityTextExpression":"SeverityText","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","traceSourceId":"ClickPy Traces","traceIdExpression":"TraceId","spanIdExpression":"SpanId","implicitColumnExpression":"Body"}]'
2020

packages/app/src/components/OnboardingModal.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ async function addOtelDemoSources({
4040
traceSourceName,
4141
traceSourceTableName,
4242
traceSourceHighlightedTraceAttributes,
43+
traceSourceMaterializedViews,
4344
}: {
4445
connectionId: string;
4546
createSourceMutation: ReturnType<typeof useCreateSource>;
@@ -62,6 +63,7 @@ async function addOtelDemoSources({
6263
traceSourceName: string;
6364
traceSourceTableName: string;
6465
traceSourceHighlightedTraceAttributes?: TSource['highlightedTraceAttributeExpressions'];
66+
traceSourceMaterializedViews?: TSource['materializedViews'];
6567
}) {
6668
const hasLogSource =
6769
logSourceDatabaseName && logSourceName && logSourceTableName;
@@ -121,6 +123,7 @@ async function addOtelDemoSources({
121123
spanEventsValueExpression: 'Events',
122124
highlightedTraceAttributeExpressions:
123125
traceSourceHighlightedTraceAttributes,
126+
materializedViews: traceSourceMaterializedViews,
124127
},
125128
});
126129
let metricsSource: TSource | undefined;
@@ -287,6 +290,53 @@ function OnboardingModalComponent({
287290
traceSourceDatabaseName: 'otel_v2',
288291
traceSourceName: 'Demo Traces',
289292
traceSourceTableName: 'otel_traces',
293+
traceSourceMaterializedViews: [
294+
{
295+
databaseName: 'otel_v2',
296+
tableName: 'otel_traces_1m',
297+
dimensionColumns: 'ServiceName, StatusCode',
298+
minGranularity: '1 minute',
299+
timestampColumn: 'Timestamp',
300+
aggregatedColumns: [
301+
{ mvColumn: 'count', aggFn: 'count', sourceColumn: '' },
302+
{
303+
mvColumn: 'max__Duration',
304+
aggFn: 'max',
305+
sourceColumn: 'Duration',
306+
},
307+
{
308+
mvColumn: 'avg__Duration',
309+
aggFn: 'avg',
310+
sourceColumn: 'Duration',
311+
},
312+
],
313+
},
314+
{
315+
databaseName: 'otel_v2',
316+
tableName: 'otel_traces_1m_v2',
317+
dimensionColumns: 'ServiceName, SpanName, SpanKind',
318+
minGranularity: '1 minute',
319+
timestampColumn: 'Timestamp',
320+
aggregatedColumns: [
321+
{ mvColumn: 'count', aggFn: 'count', sourceColumn: '' },
322+
{
323+
mvColumn: 'max__Duration',
324+
aggFn: 'max',
325+
sourceColumn: 'Duration',
326+
},
327+
{
328+
mvColumn: 'avg__Duration',
329+
aggFn: 'avg',
330+
sourceColumn: 'Duration',
331+
},
332+
{
333+
mvColumn: 'quantile__Duration',
334+
aggFn: 'quantile',
335+
sourceColumn: 'Duration',
336+
},
337+
],
338+
},
339+
],
290340

291341
updateSourceMutation,
292342
});

packages/app/src/components/SearchTotalCountChart.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { useMemo } from 'react';
2+
import {
3+
filterColumnMetaByType,
4+
JSDataType,
5+
ResponseJSON,
6+
} from '@hyperdx/common-utils/dist/clickhouse';
27
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
38
import { Text } from '@mantine/core';
49
import { keepPreviousData } from '@tanstack/react-query';
@@ -7,6 +12,18 @@ import api from '@/api';
712
import { convertToTimeChartConfig } from '@/ChartUtils';
813
import { useQueriedChartConfig } from '@/hooks/useChartConfig';
914

15+
function inferCountColumn(meta: ResponseJSON['meta'] | undefined): string {
16+
if (!meta) return 'count()';
17+
if (meta.find(col => col.name === 'count()')) {
18+
return 'count()';
19+
}
20+
21+
// The column may be named differently, particularly when using Materialized Views.
22+
return (
23+
filterColumnMetaByType(meta, [JSDataType.Number])?.[0].name ?? 'count()'
24+
);
25+
}
26+
1027
export function useSearchTotalCount(
1128
config: ChartConfigWithDateRange,
1229
queryKeyPrefix: string,
@@ -50,8 +67,9 @@ export function useSearchTotalCount(
5067
const isTotalCountComplete = !!totalCountData?.isComplete;
5168

5269
const totalCount = useMemo(() => {
70+
const countColumn = inferCountColumn(totalCountData?.meta);
5371
return totalCountData?.data?.reduce(
54-
(p: number, v: any) => p + Number.parseInt(v['count()']),
72+
(p: number, v: any) => p + Number.parseInt(v[countColumn]),
5573
0,
5674
);
5775
}, [totalCountData]);

0 commit comments

Comments
 (0)