Skip to content

Commit 6c68264

Browse files
authored
feat: specify resource pools when launching a query (#3198)
1 parent 4349acc commit 6c68264

File tree

16 files changed

+431
-7
lines changed

16 files changed

+431
-7
lines changed

src/containers/Heatmap/Heatmap.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ interface HeatmapProps {
3030
export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
3131
const dispatch = useTypedDispatch();
3232

33-
const itemsContainer = React.createRef<HTMLDivElement>();
33+
const itemsContainer = React.useRef<HTMLDivElement | null>(null);
3434

3535
const [autoRefreshInterval] = useAutoRefreshInterval();
3636

src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
useEventHandler,
3333
useQueryExecutionSettings,
3434
useQueryStreamingSetting,
35+
useResourcePools,
3536
useSetting,
3637
useTypedDispatch,
3738
useTypedSelector,
@@ -88,7 +89,7 @@ export default function QueryEditor({theme, changeUserInput, queriesHistory}: Qu
8889

8990
const isResultLoaded = Boolean(result);
9091

91-
const [querySettings] = useQueryExecutionSettings();
92+
const [querySettings, setQuerySettings] = useQueryExecutionSettings();
9293
const enableTracingLevel = useTracingLevelOptionAvailable();
9394
const [lastQueryExecutionSettings, setLastQueryExecutionSettings] =
9495
useLastQueryExecutionSettings();
@@ -104,6 +105,12 @@ export default function QueryEditor({theme, changeUserInput, queriesHistory}: Qu
104105
SETTING_KEYS.BINARY_DATA_IN_PLAIN_TEXT_DISPLAY,
105106
);
106107

108+
const {
109+
resourcePools,
110+
normalizedResourcePool,
111+
isLoading: isResourcePoolsLoading,
112+
} = useResourcePools(database, querySettings.resourcePool);
113+
107114
const encodeTextWithBase64 = !binaryDataInPlainTextDisplay;
108115

109116
const isStreamingEnabled =
@@ -114,6 +121,28 @@ export default function QueryEditor({theme, changeUserInput, queriesHistory}: Qu
114121
const [sendQuery] = queryApi.useUseSendQueryMutation();
115122
const [streamQuery] = queryApi.useUseStreamQueryMutation();
116123

124+
// Normalize stored resourcePool if it's not available for current database
125+
React.useEffect(() => {
126+
if (isResourcePoolsLoading) {
127+
return;
128+
}
129+
130+
if (querySettings.resourcePool === normalizedResourcePool) {
131+
return;
132+
}
133+
134+
setQuerySettings({
135+
...querySettings,
136+
resourcePool: normalizedResourcePool,
137+
});
138+
}, [
139+
isResourcePoolsLoading,
140+
normalizedResourcePool,
141+
querySettings,
142+
resourcePools.length,
143+
setQuerySettings,
144+
]);
145+
117146
const tableSettings = React.useMemo(() => {
118147
return isStreamingEnabled
119148
? {

src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('getChangedQueryExecutionSettings', () => {
3636
statisticsMode: STATISTICS_MODES.basic,
3737
tracingLevel: TRACING_LEVELS.basic,
3838
pragmas: 'PRAGMA TestPragma;',
39+
resourcePool: DEFAULT_QUERY_SETTINGS.resourcePool,
3940
};
4041
const result = getChangedQueryExecutionSettings(currentSettings, DEFAULT_QUERY_SETTINGS);
4142
expect(result).toEqual([

src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe('getChangedQueryExecutionSettingsDescription', () => {
5858
statisticsMode: STATISTICS_MODES.profile,
5959
tracingLevel: TRACING_LEVELS.diagnostic,
6060
pragmas: 'PRAGMA TestPragma;',
61+
resourcePool: DEFAULT_QUERY_SETTINGS.resourcePool,
6162
};
6263

6364
const result = getChangedQueryExecutionSettingsDescription({

src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ import {cn} from '../../../../utils/cn';
1515
import {
1616
useQueryExecutionSettings,
1717
useQueryStreamingSetting,
18+
useResourcePools,
1819
useSetting,
1920
useTypedDispatch,
2021
useTypedSelector,
2122
} from '../../../../utils/hooks';
22-
import {QUERY_MODES, querySettingsValidationSchema} from '../../../../utils/query';
23+
import type {ResourcePoolValue} from '../../../../utils/query';
24+
import {
25+
QUERY_MODES,
26+
RESOURCE_POOL_NO_OVERRIDE_VALUE,
27+
querySettingsValidationSchema,
28+
} from '../../../../utils/query';
29+
import {useCurrentSchema} from '../../TenantContext';
2330

2431
import {QuerySettingsSelect} from './QuerySettingsSelect';
2532
import {QuerySettingsTimeout} from './QuerySettingsTimeout';
@@ -86,10 +93,44 @@ function QuerySettingsForm({initialValues, onSubmit, onClose}: QuerySettingsForm
8693
const [useShowPlanToSvg] = useSetting<boolean>(SETTING_KEYS.USE_SHOW_PLAN_SVG);
8794
const enableTracingLevel = useTracingLevelOptionAvailable();
8895
const [isQueryStreamingEnabled] = useQueryStreamingSetting();
96+
const {database} = useCurrentSchema();
97+
const {resourcePools, isLoading: isResourcePoolsLoading} = useResourcePools(
98+
database,
99+
initialValues.resourcePool,
100+
);
101+
102+
const resourcePoolOptions = React.useMemo(
103+
() => [
104+
{
105+
value: RESOURCE_POOL_NO_OVERRIDE_VALUE,
106+
content: i18n('form.resource-pool.no-override'),
107+
},
108+
...resourcePools.map((name) => ({
109+
value: name,
110+
content: name,
111+
})),
112+
],
113+
[resourcePools],
114+
);
89115

90116
const timeout = watch('timeout');
117+
const resourcePool = watch('resourcePool');
91118
const queryMode = watch('queryMode');
92119

120+
React.useEffect(() => {
121+
if (isResourcePoolsLoading) {
122+
return;
123+
}
124+
125+
if (!resourcePool || resourcePool === RESOURCE_POOL_NO_OVERRIDE_VALUE) {
126+
return;
127+
}
128+
129+
if (!resourcePools.length || !resourcePools.includes(resourcePool)) {
130+
setValue('resourcePool', RESOURCE_POOL_NO_OVERRIDE_VALUE);
131+
}
132+
}, [isResourcePoolsLoading, resourcePools, resourcePool, setValue]);
133+
93134
return (
94135
<form onSubmit={handleSubmit(onSubmit)}>
95136
<Dialog.Body className={b('dialog-body')}>
@@ -255,6 +296,26 @@ function QuerySettingsForm({initialValues, onSubmit, onClose}: QuerySettingsForm
255296
</div>
256297
</Flex>
257298
)}
299+
<Flex direction="row" alignItems="flex-start" className={b('dialog-row')}>
300+
<label htmlFor="resourcePool" className={b('field-title')}>
301+
{QUERY_SETTINGS_FIELD_SETTINGS.resourcePool.title}
302+
</label>
303+
<div className={b('control-wrapper', {resourcePool: true})}>
304+
<Controller
305+
name="resourcePool"
306+
control={control}
307+
render={({field}) => (
308+
<QuerySettingsSelect<ResourcePoolValue>
309+
id="resourcePool"
310+
setting={field.value ?? RESOURCE_POOL_NO_OVERRIDE_VALUE}
311+
disabled={isResourcePoolsLoading || !resourcePools.length}
312+
onUpdateSetting={(value) => field.onChange(value)}
313+
settingOptions={resourcePoolOptions}
314+
/>
315+
)}
316+
/>
317+
</div>
318+
</Flex>
258319
<Flex direction="row" alignItems="flex-start" className={b('dialog-row')}>
259320
<label htmlFor="pragmas" className={b('field-title')}>
260321
{QUERY_SETTINGS_FIELD_SETTINGS.pragmas.title}

src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsSelect.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
TransactionMode,
99
} from '../../../../types/store/query';
1010
import {cn} from '../../../../utils/cn';
11+
import type {ResourcePoolValue} from '../../../../utils/query';
1112

1213
import i18n from './i18n';
1314

@@ -19,7 +20,7 @@ export const getOptionHeight = () => -1;
1920

2021
const b = cn('ydb-query-settings-select');
2122

22-
type SelectType = QueryMode | TransactionMode | StatisticsMode | TracingLevel;
23+
type SelectType = QueryMode | TransactionMode | StatisticsMode | TracingLevel | ResourcePoolValue;
2324
type QuerySettingSelectOption<T> = SelectOption<T> & {isDefault?: boolean};
2425

2526
interface QuerySettingsSelectProps<T extends SelectType> {

src/containers/Tenant/Query/QuerySettingsDialog/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,7 @@ export const QUERY_SETTINGS_FIELD_SETTINGS = {
157157
pragmas: {
158158
title: formI18n('form.pragmas'),
159159
},
160+
resourcePool: {
161+
title: formI18n('form.resource-pool'),
162+
},
160163
} as const;

src/containers/Tenant/Query/QuerySettingsDialog/i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"form.output.chunk.max.size": "Output chunk max size",
1010
"form.output.chunk.max.size.bytes": "bytes",
1111
"form.pragmas": "Pragmas",
12+
"form.resource-pool": "Resource pool",
13+
"form.resource-pool.no-override": "No pool override",
1214
"button-done": "Save",
1315
"tooltip_plan-to-svg-statistics": "Statistics option is set to \"Full\" due to the enabled \"Execution plan\" experiment.\n To disable it, go to the \"Experiments\" section in the user settings.",
1416
"button-cancel": "Cancel",

src/containers/Tenant/Query/QuerySettingsDialog/i18n/ru.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"form.output.chunk.max.size": "Максимальный размер чанка",
1010
"form.output.chunk.max.size.bytes": "байт",
1111
"form.pragmas": "Прагмы",
12+
"form.resource-pool": "Пул ресурсов",
13+
"form.resource-pool.no-override": "Без переопределения пула",
1214
"tooltip_plan-to-svg-statistics": "Опция статистики установлена в значение \"Full\" из-за включенного эксперимента \"Execution plan\".\n Чтобы отключить его, перейдите в раздел \"Experiments\" в настройках пользователя.",
1315
"button-done": "Готово",
1416
"button-cancel": "Отменить",

src/store/reducers/query/query.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@ import {TracingLevelNumber} from '../../../types/api/query';
55
import type {QueryAction, QueryRequestParams, QuerySettings} from '../../../types/store/query';
66
import type {StreamDataChunk} from '../../../types/store/streaming';
77
import {loadFromSessionStorage, saveToSessionStorage} from '../../../utils';
8-
import {QUERY_EDITOR_CURRENT_QUERY_KEY, QUERY_EDITOR_DIRTY_KEY} from '../../../utils/constants';
9-
import {isQueryErrorResponse} from '../../../utils/query';
8+
import {
9+
QUERY_EDITOR_CURRENT_QUERY_KEY,
10+
QUERY_EDITOR_DIRTY_KEY,
11+
QUERY_TECHNICAL_MARK,
12+
} from '../../../utils/constants';
13+
import {
14+
RESOURCE_POOL_NO_OVERRIDE_VALUE,
15+
isQueryErrorResponse,
16+
parseQueryAPIResponse,
17+
} from '../../../utils/query';
1018
import {isNumeric} from '../../../utils/utils';
1119
import type {RootState} from '../../defaultStore';
1220
import {api} from '../api';
@@ -119,6 +127,15 @@ export const {
119127
selectResultTab,
120128
} = slice.selectors;
121129

130+
const getResourcePoolsQueryText = () => {
131+
return `${QUERY_TECHNICAL_MARK}
132+
SELECT
133+
Name
134+
FROM \`.sys/resource_pools\`
135+
ORDER BY Name
136+
`;
137+
};
138+
122139
interface SendQueryParams extends QueryRequestParams {
123140
actionType?: QueryAction;
124141
queryId: string;
@@ -196,6 +213,11 @@ export const queryApi = api.injectEndpoints({
196213
: undefined,
197214
concurrent_results: DEFAULT_CONCURRENT_RESULTS || undefined,
198215
base64,
216+
resource_pool:
217+
querySettings.resourcePool === RESOURCE_POOL_NO_OVERRIDE_VALUE ||
218+
!querySettings.resourcePool
219+
? undefined
220+
: querySettings.resourcePool,
199221
},
200222
{
201223
signal,
@@ -300,6 +322,11 @@ export const queryApi = api.injectEndpoints({
300322
: undefined,
301323
query_id: queryId,
302324
base64,
325+
resource_pool:
326+
querySettings.resourcePool === RESOURCE_POOL_NO_OVERRIDE_VALUE ||
327+
!querySettings.resourcePool
328+
? undefined
329+
: querySettings.resourcePool,
303330
},
304331
{signal},
305332
);
@@ -366,6 +393,35 @@ export const queryApi = api.injectEndpoints({
366393
}
367394
},
368395
}),
396+
getResourcePools: build.query<string[], {database: string}>({
397+
queryFn: async ({database}, {signal}) => {
398+
try {
399+
const response = await window.api.viewer.sendQuery(
400+
{
401+
query: getResourcePoolsQueryText(),
402+
database,
403+
action: 'execute-query',
404+
internal_call: true,
405+
},
406+
{signal, withRetries: true},
407+
);
408+
409+
if (isQueryErrorResponse(response)) {
410+
return {error: response};
411+
}
412+
413+
const data = parseQueryAPIResponse(response);
414+
const rows = data.resultSets?.[0]?.result || [];
415+
const pools = rows
416+
.map((row) => row && row.Name)
417+
.filter((name): name is string => Boolean(name));
418+
419+
return {data: pools};
420+
} catch (error) {
421+
return {error};
422+
}
423+
},
424+
}),
369425
}),
370426
overrideExisting: 'throw',
371427
});

0 commit comments

Comments
 (0)