Skip to content

Commit bc8c4ee

Browse files
authored
feat: allow applying session settings to queries (#1609)
Closes HDX-3154 This PR adds a feature that allows the user to add settings to a source. These settings are then added to the end of every query that is rendered through the `renderChartConfig` function, along with any other chart specific settings. See: https://clickhouse.com/docs/sql-reference/statements/select#settings-in-select-query Most of the work was to pass the `source` or `source.querySettings` value through the code to the `renderChartConfig` calls and to update the related tests. There are also some UI changes in the `SourceForm` components. `SQLParser.Parser` from the `node-sql-parser` throws an error when it encounters a SETTINGS clause in a sql string, so a function was added to remove that clause from any sql that is passed to the parser. It assumes that the SETTINGS clause will always be at the end of the sql string, it removes any part of the string including and after the SETTINGS clause. https://github.com/user-attachments/assets/7ac3b852-2c86-4431-88bc-106f982343bb
1 parent 00854da commit bc8c4ee

37 files changed

Lines changed: 902 additions & 126 deletions

‎.changeset/hip-icons-build.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@hyperdx/common-utils": minor
3+
"@hyperdx/api": minor
4+
"@hyperdx/app": minor
5+
---
6+
7+
feat: allow applying session settings to queries

‎packages/api/src/clickhouse/__tests__/__snapshots__/renderChartConfig.test.ts.snap‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,17 @@ Array [
473473
]
474474
`;
475475

476+
exports[`renderChartConfig Query settings handles the the query settings 1`] = `
477+
Array [
478+
Object {
479+
"Body": "Oh no! Something went wrong!",
480+
},
481+
Object {
482+
"Body": "This is a test message.",
483+
},
484+
]
485+
`;
486+
476487
exports[`renderChartConfig aggFn numeric agg functions should handle numeric values as strings 1`] = `
477488
Array [
478489
Object {

‎packages/api/src/clickhouse/__tests__/renderChartConfig.test.ts‎

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// TODO: we might want to move this test file to common-utils package
22

3-
import { ChSql } from '@hyperdx/common-utils/dist/clickhouse';
3+
import { ChSql, chSql } from '@hyperdx/common-utils/dist/clickhouse';
44
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
55
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
66
import { renderChartConfig } from '@hyperdx/common-utils/dist/core/renderChartConfig';
77
import {
8-
AggregateFunctionSchema,
9-
DerivedColumn,
108
MetricsDataType,
9+
QuerySettings,
1110
} from '@hyperdx/common-utils/dist/types';
1211
import _ from 'lodash';
1312
import ms from 'ms';
@@ -36,6 +35,13 @@ const TEST_METRIC_TABLES = {
3635
'exponential histogram': DEFAULT_METRICS_TABLE.EXPONENTIAL_HISTOGRAM,
3736
};
3837

38+
const querySettings: QuerySettings = [
39+
{ setting: 'optimize_read_in_order', value: '0' },
40+
{ setting: 'cast_keep_nullable', value: '1' },
41+
{ setting: 'count_distinct_implementation', value: 'uniqCombined64' },
42+
{ setting: 'async_insert_busy_timeout_min_ms', value: '20000' },
43+
];
44+
3945
describe('renderChartConfig', () => {
4046
const server = getServer();
4147

@@ -152,6 +158,7 @@ describe('renderChartConfig', () => {
152158
timestampValueExpression: 'ts',
153159
},
154160
metadata,
161+
querySettings,
155162
);
156163
const res = await queryData(query);
157164
expect(res).toMatchSnapshot();
@@ -190,6 +197,7 @@ describe('renderChartConfig', () => {
190197
timestampValueExpression: 'ts',
191198
},
192199
metadata,
200+
querySettings,
193201
);
194202
const res = await queryData(query);
195203
expect(res).toMatchSnapshot();
@@ -226,16 +234,10 @@ describe('renderChartConfig', () => {
226234
connection: connection.id,
227235
},
228236
metadata,
237+
querySettings,
229238
);
230239

231-
const resp = await clickhouseClient
232-
.query<'JSON'>({
233-
query: query.sql,
234-
query_params: query.params,
235-
format: 'JSON',
236-
})
237-
.then(res => res.json() as any);
238-
expect(resp.data).toMatchSnapshot();
240+
expect(await queryData(query)).toMatchSnapshot();
239241
});
240242

241243
it('simple select + group by query logs', async () => {
@@ -272,6 +274,7 @@ describe('renderChartConfig', () => {
272274
groupBy: 'ServiceName',
273275
},
274276
metadata,
277+
querySettings,
275278
);
276279
expect(await queryData(query)).toMatchSnapshot();
277280
});
@@ -342,6 +345,7 @@ describe('renderChartConfig', () => {
342345
connection: connection.id,
343346
},
344347
metadata,
348+
querySettings,
345349
);
346350
expect(await queryData(avgQuery)).toMatchSnapshot();
347351
const maxQuery = await renderChartConfig(
@@ -363,6 +367,7 @@ describe('renderChartConfig', () => {
363367
connection: connection.id,
364368
},
365369
metadata,
370+
querySettings,
366371
);
367372
expect(await queryData(maxQuery)).toMatchSnapshot();
368373
const sumQuery = await renderChartConfig(
@@ -384,6 +389,7 @@ describe('renderChartConfig', () => {
384389
connection: connection.id,
385390
},
386391
metadata,
392+
querySettings,
387393
);
388394
expect(await queryData(sumQuery)).toMatchSnapshot();
389395
});
@@ -409,6 +415,7 @@ describe('renderChartConfig', () => {
409415
connection: connection.id,
410416
},
411417
metadata,
418+
querySettings,
412419
);
413420
expect(await queryData(query)).toMatchSnapshot();
414421
});
@@ -434,6 +441,7 @@ describe('renderChartConfig', () => {
434441
connection: connection.id,
435442
},
436443
metadata,
444+
querySettings,
437445
);
438446
expect(await queryData(query)).toMatchSnapshot();
439447
});
@@ -459,6 +467,7 @@ describe('renderChartConfig', () => {
459467
connection: connection.id,
460468
},
461469
metadata,
470+
querySettings,
462471
);
463472
expect(await queryData(query)).toMatchSnapshot();
464473
});
@@ -485,6 +494,7 @@ describe('renderChartConfig', () => {
485494
connection: connection.id,
486495
},
487496
metadata,
497+
querySettings,
488498
);
489499
expect(await queryData(query)).toMatchSnapshot();
490500
});
@@ -657,6 +667,7 @@ describe('renderChartConfig', () => {
657667
connection: connection.id,
658668
},
659669
metadata,
670+
querySettings,
660671
);
661672
expect(await queryData(query)).toMatchSnapshot();
662673
});
@@ -681,6 +692,7 @@ describe('renderChartConfig', () => {
681692
connection: connection.id,
682693
},
683694
metadata,
695+
querySettings,
684696
);
685697
expect(await queryData(query)).toMatchSnapshot();
686698
});
@@ -705,6 +717,7 @@ describe('renderChartConfig', () => {
705717
connection: connection.id,
706718
},
707719
metadata,
720+
querySettings,
708721
);
709722
expect(await queryData(query)).toMatchSnapshot();
710723
});
@@ -749,6 +762,7 @@ describe('renderChartConfig', () => {
749762
connection: connection.id,
750763
},
751764
metadata,
765+
querySettings,
752766
);
753767
expect(await queryData(minQuery)).toMatchSnapshot('minSum');
754768

@@ -771,6 +785,7 @@ describe('renderChartConfig', () => {
771785
connection: connection.id,
772786
},
773787
metadata,
788+
querySettings,
774789
);
775790
expect(await queryData(maxQuery)).toMatchSnapshot('maxSum');
776791
});
@@ -1151,6 +1166,7 @@ describe('renderChartConfig', () => {
11511166
connection: connection.id,
11521167
},
11531168
metadata,
1169+
querySettings,
11541170
);
11551171
const res = await queryData(query);
11561172
expect(res).toMatchSnapshot();
@@ -1200,6 +1216,7 @@ describe('renderChartConfig', () => {
12001216
connection: connection.id,
12011217
},
12021218
metadata,
1219+
querySettings,
12031220
);
12041221
const res = await queryData(query);
12051222
expect(res).toMatchSnapshot();
@@ -1250,6 +1267,7 @@ describe('renderChartConfig', () => {
12501267
connection: connection.id,
12511268
},
12521269
metadata,
1270+
querySettings,
12531271
);
12541272
const res = await queryData(query);
12551273
expect(res).toMatchSnapshot();
@@ -1294,6 +1312,7 @@ describe('renderChartConfig', () => {
12941312
connection: connection.id,
12951313
},
12961314
metadata,
1315+
querySettings,
12971316
);
12981317
const res = await queryData(query);
12991318
expect(res).toMatchSnapshot();
@@ -1320,6 +1339,7 @@ describe('renderChartConfig', () => {
13201339
connection: connection.id,
13211340
},
13221341
metadata,
1342+
querySettings,
13231343
);
13241344
const res = await queryData(query);
13251345
expect(res).toMatchSnapshot();
@@ -1347,6 +1367,7 @@ describe('renderChartConfig', () => {
13471367
connection: connection.id,
13481368
},
13491369
metadata,
1370+
querySettings,
13501371
);
13511372
const res = await queryData(query);
13521373
expect(res).toMatchSnapshot();
@@ -1374,6 +1395,7 @@ describe('renderChartConfig', () => {
13741395
connection: connection.id,
13751396
},
13761397
metadata,
1398+
querySettings,
13771399
);
13781400
const res = await queryData(query);
13791401
expect(res).toMatchSnapshot();
@@ -1444,6 +1466,7 @@ describe('renderChartConfig', () => {
14441466
connection: connection.id,
14451467
},
14461468
metadata,
1469+
querySettings,
14471470
);
14481471

14491472
const res = await queryData(query);
@@ -1480,6 +1503,7 @@ describe('renderChartConfig', () => {
14801503
connection: connection.id,
14811504
},
14821505
metadata,
1506+
querySettings,
14831507
);
14841508

14851509
const res = await queryData(query);
@@ -1509,6 +1533,7 @@ describe('renderChartConfig', () => {
15091533
connection: connection.id,
15101534
},
15111535
metadata,
1536+
querySettings,
15121537
);
15131538

15141539
const res = await queryData(query);
@@ -1520,4 +1545,43 @@ describe('renderChartConfig', () => {
15201545
expect(query.sql).not.toMatch(/MetricName IN /);
15211546
});
15221547
});
1548+
1549+
describe('Query settings', () => {
1550+
it('handles the the query settings', async () => {
1551+
const now = new Date('2023-11-16T22:12:00.000Z');
1552+
1553+
await bulkInsertLogs([
1554+
{
1555+
ServiceName: 'api',
1556+
Timestamp: now,
1557+
SeverityText: 'error',
1558+
Body: 'Oh no! Something went wrong!',
1559+
},
1560+
{
1561+
ServiceName: 'api',
1562+
Timestamp: now,
1563+
SeverityText: 'info',
1564+
Body: 'This is a test message.',
1565+
},
1566+
]);
1567+
1568+
const query = await renderChartConfig(
1569+
{
1570+
select: [{ valueExpression: 'Body' }],
1571+
from: logSource.from,
1572+
where: '',
1573+
timestampValueExpression: 'Timestamp',
1574+
connection: connection.id,
1575+
settings: chSql`max_result_rows = 1`,
1576+
},
1577+
metadata,
1578+
[...querySettings, { setting: 'result_overflow_mode', value: 'break' }],
1579+
);
1580+
1581+
const res = await queryData(query);
1582+
// ensures `result_overflow_mode = break` is applied, otherwise query would error.
1583+
expect(res).toHaveLength(2);
1584+
expect(res).toMatchSnapshot();
1585+
});
1586+
});
15231587
});

‎packages/api/src/models/source.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ export const Source = mongoose.model<ISource>(
8686
},
8787
default: undefined,
8888
},
89+
90+
querySettings: {
91+
type: [
92+
{
93+
setting: { type: String, required: true },
94+
value: { type: String, required: true },
95+
},
96+
],
97+
default: undefined,
98+
maxlength: 10,
99+
},
89100
},
90101
{
91102
toJSON: { virtuals: true },

‎packages/api/src/routers/api/ai.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ router.post(
274274
const keyValues = await metadata.getKeyValues({
275275
chartConfig: cc,
276276
keys: keysToFetch.map(f => f.key),
277+
source,
277278
});
278279

279280
const anthropic = createAnthropic({

‎packages/api/src/routers/external-api/v2/charts.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ router.post(
588588
const result = await clickhouseClient.queryChartConfig({
589589
config: chartConfig,
590590
metadata,
591+
querySettings: source.querySettings,
591592
});
592593

593594
return {

‎packages/api/src/tasks/checkAlerts/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ export const processAlert = async (
448448
const checksData = await clickhouseClient.queryChartConfig({
449449
config: optimizedChartConfig,
450450
metadata,
451+
querySettings: source.querySettings,
451452
});
452453

453454
logger.info(

‎packages/api/src/tasks/checkAlerts/template.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,11 @@ ${targetTemplate}`;
602602

603603
let truncatedResults = '';
604604
try {
605-
const query = await renderChartConfig(chartConfig, metadata);
605+
const query = await renderChartConfig(
606+
chartConfig,
607+
metadata,
608+
source.querySettings,
609+
);
606610
const raw = await clickhouseClient
607611
.query<'CSV'>({
608612
query: query.sql,

‎packages/api/src/tasks/pingPongTask.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import logger from '@/utils/logger';
44
export default class PingPongTask implements HdxTask<PingTaskArgs> {
55
constructor(private args: PingTaskArgs) {}
66

7-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
87
async execute(): Promise<void> {
98
logger.info(`
109
O .

‎packages/app/src/TeamPage.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useCallback, useState } from 'react';
22
import Head from 'next/head';
33
import { CopyToClipboard } from 'react-copy-to-clipboard';
4-
import { SubmitHandler, useForm, useWatch } from 'react-hook-form';
4+
import { SubmitHandler, useForm } from 'react-hook-form';
55
import { DEFAULT_METADATA_MAX_ROWS_TO_READ } from '@hyperdx/common-utils/dist/core/metadata';
6-
import { TeamClickHouseSettings } from '@hyperdx/common-utils/dist/types';
6+
import { type TeamClickHouseSettings } from '@hyperdx/common-utils/dist/types';
77
import {
88
Box,
99
Button,

0 commit comments

Comments
 (0)