ydb-embedded-ui 4.19.2 → 4.20.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +24 -0
- package/dist/components/LinkToSchemaObject/LinkToSchemaObject.tsx +20 -0
- package/dist/components/ProgressViewer/ProgressViewer.tsx +2 -1
- package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +3 -2
- package/dist/components/UsageLabel/UsageLabel.scss +6 -0
- package/dist/components/UsageLabel/UsageLabel.tsx +22 -0
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +13 -8
- package/dist/containers/AsideNavigation/i18n/en.json +13 -0
- package/dist/containers/AsideNavigation/i18n/index.ts +11 -0
- package/dist/containers/AsideNavigation/i18n/ru.json +13 -0
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +74 -68
- package/dist/containers/Node/NodeStructure/Vdisk.tsx +9 -33
- package/dist/containers/Nodes/Nodes.tsx +10 -2
- package/dist/containers/Nodes/getNodesColumns.tsx +206 -122
- package/dist/containers/Storage/Storage.tsx +9 -2
- package/dist/containers/Storage/utils/index.ts +1 -22
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -3
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +0 -1
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +4 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +1 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx +8 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx +11 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx +0 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +3 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +21 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx +53 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +53 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +85 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx +53 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TenantMemory.tsx +9 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx +55 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +40 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +35 -19
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx +53 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +15 -7
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +3 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +3 -3
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +0 -2
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +13 -61
- package/dist/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx +82 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +18 -97
- package/dist/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx +138 -0
- package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.tsx +2 -4
- package/dist/containers/Tenant/Query/ExecuteResult/{ExecuteResult.js → ExecuteResult.tsx} +51 -31
- package/dist/containers/Tenant/Query/Issues/Issues.tsx +4 -6
- package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +1 -1
- package/dist/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx +1 -1
- package/dist/routes.ts +6 -0
- package/dist/store/reducers/{executeTopQueries.ts → executeTopQueries/executeTopQueries.ts} +23 -75
- package/dist/{types/store/executeTopQueries.ts → store/reducers/executeTopQueries/types.ts} +3 -7
- package/dist/store/reducers/executeTopQueries/utils.ts +36 -0
- package/dist/store/reducers/index.ts +12 -2
- package/dist/store/reducers/nodes/types.ts +1 -0
- package/dist/store/reducers/nodes/utils.ts +16 -6
- package/dist/store/reducers/{shardsWorkload.ts → shardsWorkload/shardsWorkload.ts} +5 -11
- package/dist/{types/store/shardsWorkload.ts → store/reducers/shardsWorkload/types.ts} +3 -7
- package/dist/store/reducers/storage/storage.ts +1 -1
- package/dist/store/reducers/tenantOverview/topNodesByCpu/topNodesByCpu.ts +87 -0
- package/dist/store/reducers/tenantOverview/topNodesByCpu/types.ts +29 -0
- package/dist/store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad.ts +87 -0
- package/dist/store/reducers/tenantOverview/topNodesByLoad/types.ts +29 -0
- package/dist/store/reducers/tenantOverview/topNodesByMemory/topNodesByMemory.ts +87 -0
- package/dist/store/reducers/tenantOverview/topNodesByMemory/types.ts +29 -0
- package/dist/store/reducers/tenantOverview/topQueries/tenantOverviewTopQueries.ts +93 -0
- package/dist/store/reducers/tenantOverview/topQueries/types.ts +14 -0
- package/dist/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts +103 -0
- package/dist/store/reducers/tenantOverview/topShards/types.ts +14 -0
- package/dist/store/reducers/tenantOverview/topShards/utils.ts +3 -0
- package/dist/styles/mixins.scss +4 -0
- package/dist/types/additionalProps.ts +3 -1
- package/dist/types/api/compute.ts +1 -1
- package/dist/types/react-json-inspector.d.ts +21 -0
- package/dist/utils/diagnostics.ts +11 -0
- package/dist/utils/generateEvaluator.ts +21 -0
- package/package.json +1 -1
@@ -1,5 +1,5 @@
|
|
1
|
-
import React, {useEffect, useState} from 'react';
|
2
|
-
import {useDispatch
|
1
|
+
import React, {type ReactNode, useEffect, useState} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
import JSONTree from 'react-json-inspector';
|
5
5
|
|
@@ -11,13 +11,15 @@ import EnableFullscreenButton from '../../../../components/EnableFullscreenButto
|
|
11
11
|
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
12
12
|
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
|
13
13
|
|
14
|
+
import type {ValueOf} from '../../../../types/common';
|
15
|
+
import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
|
14
16
|
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
15
|
-
|
16
17
|
import {prepareQueryError} from '../../../../utils/query';
|
18
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
17
19
|
|
18
20
|
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
19
21
|
|
20
|
-
import ResultIssues from '../Issues/Issues';
|
22
|
+
import {ResultIssues} from '../Issues/Issues';
|
21
23
|
import {QueryDuration} from '../QueryDuration/QueryDuration';
|
22
24
|
|
23
25
|
import './ExecuteResult.scss';
|
@@ -27,31 +29,51 @@ const b = cn('ydb-query-execute-result');
|
|
27
29
|
const resultOptionsIds = {
|
28
30
|
result: 'result',
|
29
31
|
stats: 'stats',
|
30
|
-
};
|
32
|
+
} as const;
|
33
|
+
|
34
|
+
type SectionID = ValueOf<typeof resultOptionsIds>;
|
31
35
|
|
32
36
|
const resultOptions = [
|
33
37
|
{value: resultOptionsIds.result, content: 'Result'},
|
34
38
|
{value: resultOptionsIds.stats, content: 'Stats'},
|
35
39
|
];
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
interface ExecuteResultProps {
|
42
|
+
textResults: string;
|
43
|
+
result: ReactNode;
|
44
|
+
stats: IQueryResult['stats'] | undefined;
|
45
|
+
error: string | QueryErrorResponse | undefined;
|
46
|
+
copyDisabled?: boolean;
|
47
|
+
isResultsCollapsed?: boolean;
|
48
|
+
onCollapseResults: VoidFunction;
|
49
|
+
onExpandResults: VoidFunction;
|
50
|
+
}
|
51
|
+
|
52
|
+
export function ExecuteResult({
|
53
|
+
textResults,
|
54
|
+
result,
|
55
|
+
stats,
|
56
|
+
error,
|
57
|
+
copyDisabled,
|
58
|
+
isResultsCollapsed,
|
59
|
+
onCollapseResults,
|
60
|
+
onExpandResults,
|
61
|
+
}: ExecuteResultProps) {
|
62
|
+
const [activeSection, setActiveSection] = useState<SectionID>(resultOptionsIds.result);
|
63
|
+
const isFullscreen = useTypedSelector((state) => state.fullscreen);
|
40
64
|
const dispatch = useDispatch();
|
41
65
|
|
42
66
|
useEffect(() => {
|
43
67
|
return () => {
|
44
68
|
dispatch(disableFullscreen());
|
45
69
|
};
|
46
|
-
}, []);
|
70
|
+
}, [dispatch]);
|
47
71
|
|
48
|
-
const onSelectSection = (value) => {
|
49
|
-
setActiveSection(value);
|
72
|
+
const onSelectSection = (value: string) => {
|
73
|
+
setActiveSection(value as SectionID);
|
50
74
|
};
|
51
75
|
|
52
76
|
const renderClipboardButton = () => {
|
53
|
-
const {textResults, copyDisabled} = props;
|
54
|
-
|
55
77
|
return (
|
56
78
|
<CopyToClipboard
|
57
79
|
text={textResults}
|
@@ -65,7 +87,7 @@ export function ExecuteResult(props) {
|
|
65
87
|
const renderStats = () => {
|
66
88
|
const content = (
|
67
89
|
<JSONTree
|
68
|
-
data={
|
90
|
+
data={stats}
|
69
91
|
isExpanded={() => true}
|
70
92
|
className={b('inspector')}
|
71
93
|
searchOptions={{
|
@@ -86,8 +108,6 @@ export function ExecuteResult(props) {
|
|
86
108
|
};
|
87
109
|
|
88
110
|
const renderResult = () => {
|
89
|
-
const {result} = props;
|
90
|
-
|
91
111
|
return (
|
92
112
|
<React.Fragment>
|
93
113
|
{result}
|
@@ -101,11 +121,11 @@ export function ExecuteResult(props) {
|
|
101
121
|
};
|
102
122
|
|
103
123
|
const renderIssues = () => {
|
104
|
-
|
105
|
-
|
106
|
-
|
124
|
+
if (!error) {
|
125
|
+
return null;
|
126
|
+
}
|
107
127
|
|
108
|
-
if (
|
128
|
+
if (typeof error === 'object' && error.data?.issues && Array.isArray(error.data.issues)) {
|
109
129
|
return (
|
110
130
|
<React.Fragment>
|
111
131
|
<ResultIssues data={error.data} />
|
@@ -120,20 +140,20 @@ export function ExecuteResult(props) {
|
|
120
140
|
);
|
121
141
|
}
|
122
142
|
|
123
|
-
|
124
|
-
|
125
|
-
}
|
143
|
+
const parsedError = typeof error === 'string' ? error : prepareQueryError(error);
|
144
|
+
|
145
|
+
return <div className={b('error')}>{parsedError}</div>;
|
126
146
|
};
|
127
147
|
|
128
148
|
return (
|
129
149
|
<React.Fragment>
|
130
150
|
<div className={b('controls')}>
|
131
151
|
<div className={b('controls-right')}>
|
132
|
-
<QueryExecutionStatus error={
|
152
|
+
<QueryExecutionStatus error={error} />
|
133
153
|
|
134
|
-
{
|
154
|
+
{stats && !error && (
|
135
155
|
<React.Fragment>
|
136
|
-
<QueryDuration duration={
|
156
|
+
<QueryDuration duration={stats?.DurationUs} />
|
137
157
|
<Divider />
|
138
158
|
<RadioButton
|
139
159
|
options={resultOptions}
|
@@ -147,16 +167,16 @@ export function ExecuteResult(props) {
|
|
147
167
|
{renderClipboardButton()}
|
148
168
|
<EnableFullscreenButton />
|
149
169
|
<PaneVisibilityToggleButtons
|
150
|
-
onCollapse={
|
151
|
-
onExpand={
|
152
|
-
isCollapsed={
|
170
|
+
onCollapse={onCollapseResults}
|
171
|
+
onExpand={onExpandResults}
|
172
|
+
isCollapsed={isResultsCollapsed}
|
153
173
|
initialDirection="bottom"
|
154
174
|
/>
|
155
175
|
</div>
|
156
176
|
</div>
|
157
177
|
<div className={b('result')}>
|
158
|
-
{activeSection === resultOptionsIds.result && !
|
159
|
-
{activeSection === resultOptionsIds.stats && !
|
178
|
+
{activeSection === resultOptionsIds.result && !error && renderResult()}
|
179
|
+
{activeSection === resultOptionsIds.stats && !error && renderStats()}
|
160
180
|
{renderIssues()}
|
161
181
|
</div>
|
162
182
|
</React.Fragment>
|
@@ -21,10 +21,9 @@ const blockIssue = cn('kv-issue');
|
|
21
21
|
|
22
22
|
interface ResultIssuesProps {
|
23
23
|
data: ErrorResponse | string;
|
24
|
-
className: string;
|
25
24
|
}
|
26
25
|
|
27
|
-
export
|
26
|
+
export function ResultIssues({data}: ResultIssuesProps) {
|
28
27
|
const [showIssues, setShowIssues] = React.useState(false);
|
29
28
|
|
30
29
|
const issues = typeof data === 'string' ? undefined : data?.issues;
|
@@ -59,22 +58,21 @@ export default function ResultIssues({data, className}: ResultIssuesProps) {
|
|
59
58
|
</Button>
|
60
59
|
)}
|
61
60
|
</div>
|
62
|
-
{hasIssues && showIssues && <Issues issues={issues}
|
61
|
+
{hasIssues && showIssues && <Issues issues={issues} />}
|
63
62
|
</div>
|
64
63
|
);
|
65
64
|
}
|
66
65
|
|
67
66
|
interface IssuesProps {
|
68
|
-
className?: string;
|
69
67
|
issues: IssueMessage[] | null | undefined;
|
70
68
|
}
|
71
|
-
export function Issues({issues
|
69
|
+
export function Issues({issues}: IssuesProps) {
|
72
70
|
const mostSevereIssue = issues?.reduce((result, issue) => {
|
73
71
|
const severity = issue.severity ?? 10;
|
74
72
|
return Math.min(result, severity);
|
75
73
|
}, 10);
|
76
74
|
return (
|
77
|
-
<div className={blockIssues(null
|
75
|
+
<div className={blockIssues(null)}>
|
78
76
|
{issues?.map((issue, index) => (
|
79
77
|
<Issue key={index} issue={issue} expanded={issue === mostSevereIssue} />
|
80
78
|
))}
|
@@ -67,7 +67,7 @@ export function paneVisibilityToggleReducerCreator(isPaneCollapsedKey: string) {
|
|
67
67
|
interface ToggleButtonProps {
|
68
68
|
onCollapse: VoidFunction;
|
69
69
|
onExpand: VoidFunction;
|
70
|
-
isCollapsed
|
70
|
+
isCollapsed?: boolean;
|
71
71
|
initialDirection?: 'right' | 'left' | 'top' | 'bottom';
|
72
72
|
className?: string;
|
73
73
|
}
|
package/dist/routes.ts
CHANGED
@@ -49,4 +49,10 @@ export function createHref(
|
|
49
49
|
return `${compile(route)(params)}${search}`;
|
50
50
|
}
|
51
51
|
|
52
|
+
// embedded version could be located in some folder (e.g. host/some_folder/app_router_path)
|
53
|
+
// window.location has the full pathname, while location from router ignores path to project
|
54
|
+
// this navigation assumes page reloading
|
55
|
+
export const createExternalUILink = (query = {}) =>
|
56
|
+
createHref(window.location.pathname, undefined, query);
|
57
|
+
|
52
58
|
export default routes;
|
@@ -1,19 +1,14 @@
|
|
1
1
|
import type {AnyAction, Reducer} from 'redux';
|
2
2
|
import type {ThunkAction} from 'redux-thunk';
|
3
3
|
|
4
|
-
import '
|
5
|
-
import {
|
6
|
-
|
7
|
-
ITopQueriesFilters,
|
8
|
-
ITopQueriesState,
|
9
|
-
} from '../../types/store/executeTopQueries';
|
10
|
-
import {IQueryResult} from '../../types/store/query';
|
4
|
+
import '../../../services/api';
|
5
|
+
import type {IQueryResult} from '../../../types/store/query';
|
6
|
+
import {parseQueryAPIExecuteResponse} from '../../../utils/query';
|
11
7
|
|
12
|
-
import {
|
13
|
-
|
14
|
-
import {
|
15
|
-
|
16
|
-
import type {RootState} from '.';
|
8
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
9
|
+
import type {RootState} from '..';
|
10
|
+
import type {ITopQueriesAction, ITopQueriesFilters, ITopQueriesState} from './types';
|
11
|
+
import {getFiltersConditions} from './utils';
|
17
12
|
|
18
13
|
export const FETCH_TOP_QUERIES = createRequestActionTypes('top-queries', 'FETCH_TOP_QUERIES');
|
19
14
|
const SET_TOP_QUERIES_STATE = 'top-queries/SET_TOP_QUERIES_STATE';
|
@@ -25,41 +20,6 @@ const initialState = {
|
|
25
20
|
filters: {},
|
26
21
|
};
|
27
22
|
|
28
|
-
const getMaxIntervalSubquery = (path: string) => `(
|
29
|
-
SELECT
|
30
|
-
MAX(IntervalEnd)
|
31
|
-
FROM \`${path}/.sys/top_queries_by_cpu_time_one_hour\`
|
32
|
-
)`;
|
33
|
-
|
34
|
-
function getFiltersConditions(path: string, filters?: ITopQueriesFilters) {
|
35
|
-
const conditions: string[] = [];
|
36
|
-
|
37
|
-
if (filters?.from && filters?.to && filters.from > filters.to) {
|
38
|
-
throw new Error('Invalid date range');
|
39
|
-
}
|
40
|
-
|
41
|
-
if (filters?.from) {
|
42
|
-
// matching `from` & `to` is an edge case
|
43
|
-
// other cases should not include the starting point, since intervals are stored using the ending time
|
44
|
-
const gt = filters.to === filters.from ? '>=' : '>';
|
45
|
-
conditions.push(`IntervalEnd ${gt} Timestamp('${new Date(filters.from).toISOString()}')`);
|
46
|
-
}
|
47
|
-
|
48
|
-
if (filters?.to) {
|
49
|
-
conditions.push(`IntervalEnd <= Timestamp('${new Date(filters.to).toISOString()}')`);
|
50
|
-
}
|
51
|
-
|
52
|
-
if (!filters?.from && !filters?.to) {
|
53
|
-
conditions.push(`IntervalEnd IN ${getMaxIntervalSubquery(path)}`);
|
54
|
-
}
|
55
|
-
|
56
|
-
if (filters?.text) {
|
57
|
-
conditions.push(`QueryText ILIKE '%${filters.text}%'`);
|
58
|
-
}
|
59
|
-
|
60
|
-
return conditions.join(' AND ');
|
61
|
-
}
|
62
|
-
|
63
23
|
const getQueryText = (path: string, filters?: ITopQueriesFilters) => {
|
64
24
|
const filterConditions = getFiltersConditions(path, filters);
|
65
25
|
return `
|
@@ -128,34 +88,22 @@ type FetchTopQueries = (params: {
|
|
128
88
|
filters?: ITopQueriesFilters;
|
129
89
|
}) => ThunkAction<Promise<IQueryResult | undefined>, RootState, unknown, AnyAction>;
|
130
90
|
|
131
|
-
export const fetchTopQueries: FetchTopQueries =
|
132
|
-
({
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
actions: FETCH_TOP_QUERIES,
|
148
|
-
dataHandler: parseQueryAPIExecuteResponse,
|
149
|
-
})(dispatch, getState);
|
150
|
-
} catch (error) {
|
151
|
-
dispatch({
|
152
|
-
type: FETCH_TOP_QUERIES.FAILURE,
|
153
|
-
error,
|
154
|
-
});
|
155
|
-
|
156
|
-
throw error;
|
157
|
-
}
|
158
|
-
};
|
91
|
+
export const fetchTopQueries: FetchTopQueries = ({database, filters}) =>
|
92
|
+
createApiRequest({
|
93
|
+
request: window.api.sendQuery(
|
94
|
+
{
|
95
|
+
schema: 'modern',
|
96
|
+
query: getQueryText(database, filters),
|
97
|
+
database,
|
98
|
+
action: 'execute-scan',
|
99
|
+
},
|
100
|
+
{
|
101
|
+
concurrentId: 'executeTopQueries',
|
102
|
+
},
|
103
|
+
),
|
104
|
+
actions: FETCH_TOP_QUERIES,
|
105
|
+
dataHandler: parseQueryAPIExecuteResponse,
|
106
|
+
});
|
159
107
|
|
160
108
|
export function setTopQueriesState(state: Partial<ITopQueriesState>) {
|
161
109
|
return {
|
@@ -1,10 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
|
4
|
-
setTopQueriesFilters,
|
5
|
-
} from '../../store/reducers/executeTopQueries';
|
6
|
-
import type {ApiRequestAction} from '../../store/utils';
|
7
|
-
import type {IQueryResult, QueryErrorResponse} from './query';
|
1
|
+
import {FETCH_TOP_QUERIES, setTopQueriesState, setTopQueriesFilters} from './executeTopQueries';
|
2
|
+
import type {ApiRequestAction} from '../../utils';
|
3
|
+
import type {IQueryResult, QueryErrorResponse} from '../../../types/store/query';
|
8
4
|
|
9
5
|
export interface ITopQueriesFilters {
|
10
6
|
/** ms from epoch */
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import {ITopQueriesFilters} from './types';
|
2
|
+
|
3
|
+
const getMaxIntervalSubquery = (path: string) => `(
|
4
|
+
SELECT
|
5
|
+
MAX(IntervalEnd)
|
6
|
+
FROM \`${path}/.sys/top_queries_by_cpu_time_one_hour\`
|
7
|
+
)`;
|
8
|
+
|
9
|
+
export function getFiltersConditions(path: string, filters?: ITopQueriesFilters) {
|
10
|
+
const conditions: string[] = [];
|
11
|
+
|
12
|
+
if (filters?.from && filters?.to && filters.from > filters.to) {
|
13
|
+
throw new Error('Invalid date range');
|
14
|
+
}
|
15
|
+
|
16
|
+
if (filters?.from) {
|
17
|
+
// matching `from` & `to` is an edge case
|
18
|
+
// other cases should not include the starting point, since intervals are stored using the ending time
|
19
|
+
const gt = filters.to === filters.from ? '>=' : '>';
|
20
|
+
conditions.push(`IntervalEnd ${gt} Timestamp('${new Date(filters.from).toISOString()}')`);
|
21
|
+
}
|
22
|
+
|
23
|
+
if (filters?.to) {
|
24
|
+
conditions.push(`IntervalEnd <= Timestamp('${new Date(filters.to).toISOString()}')`);
|
25
|
+
}
|
26
|
+
|
27
|
+
if (!filters?.from && !filters?.to) {
|
28
|
+
conditions.push(`IntervalEnd IN ${getMaxIntervalSubquery(path)}`);
|
29
|
+
}
|
30
|
+
|
31
|
+
if (filters?.text) {
|
32
|
+
conditions.push(`QueryText ILIKE '%${filters.text}%'`);
|
33
|
+
}
|
34
|
+
|
35
|
+
return conditions.join(' AND ');
|
36
|
+
}
|
@@ -1,6 +1,9 @@
|
|
1
1
|
import {combineReducers} from 'redux';
|
2
2
|
|
3
3
|
import nodes from './nodes/nodes';
|
4
|
+
import {topNodesByLoad} from './tenantOverview/topNodesByLoad/topNodesByLoad';
|
5
|
+
import {topNodesByCpu} from './tenantOverview/topNodesByCpu/topNodesByCpu';
|
6
|
+
import {topNodesByMemory} from './tenantOverview/topNodesByMemory/topNodesByMemory';
|
4
7
|
import cluster from './cluster/cluster';
|
5
8
|
import clusterNodes from './clusterNodes/clusterNodes';
|
6
9
|
import tenant from './tenant/tenant';
|
@@ -26,10 +29,12 @@ import preview from './preview';
|
|
26
29
|
import nodesList from './nodesList';
|
27
30
|
import describe from './describe';
|
28
31
|
import schemaAcl from './schemaAcl/schemaAcl';
|
29
|
-
import executeTopQueries from './executeTopQueries';
|
32
|
+
import executeTopQueries from './executeTopQueries/executeTopQueries';
|
33
|
+
import {tenantOverviewTopQueries} from './tenantOverview/topQueries/tenantOverviewTopQueries';
|
30
34
|
import executeTopTables from './tenantOverview/executeTopTables/executeTopTables';
|
31
35
|
import healthcheckInfo from './healthcheckInfo';
|
32
|
-
import shardsWorkload from './shardsWorkload';
|
36
|
+
import shardsWorkload from './shardsWorkload/shardsWorkload';
|
37
|
+
import {tenantOverviewTopShards} from './tenantOverview/topShards/tenantOverviewTopShards';
|
33
38
|
import hotKeys from './hotKeys';
|
34
39
|
import olapStats from './olapStats';
|
35
40
|
import authentication from './authentication/authentication';
|
@@ -41,6 +46,9 @@ import singleClusterMode from './singleClusterMode';
|
|
41
46
|
export const rootReducer = {
|
42
47
|
singleClusterMode,
|
43
48
|
nodes,
|
49
|
+
topNodesByLoad,
|
50
|
+
topNodesByCpu,
|
51
|
+
topNodesByMemory,
|
44
52
|
cluster,
|
45
53
|
clusterNodes,
|
46
54
|
tenant,
|
@@ -69,8 +77,10 @@ export const rootReducer = {
|
|
69
77
|
schemaAcl,
|
70
78
|
executeTopQueries,
|
71
79
|
executeTopTables,
|
80
|
+
tenantOverviewTopQueries,
|
72
81
|
healthcheckInfo,
|
73
82
|
shardsWorkload,
|
83
|
+
tenantOverviewTopShards,
|
74
84
|
hotKeys,
|
75
85
|
authentication,
|
76
86
|
header,
|
@@ -1,6 +1,7 @@
|
|
1
|
-
import type {TComputeInfo, TComputeNodeInfo} from '../../../types/api/compute';
|
1
|
+
import type {TComputeInfo, TComputeNodeInfo, TComputeTenantInfo} from '../../../types/api/compute';
|
2
2
|
import type {TNodesInfo} from '../../../types/api/nodes';
|
3
3
|
import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
|
4
|
+
import {generateEvaluator} from '../../../utils/generateEvaluator';
|
4
5
|
|
5
6
|
import type {NodesHandledResponse, NodesPreparedEntity} from './types';
|
6
7
|
|
@@ -14,23 +15,30 @@ const prepareComputeNode = (node: TComputeNodeInfo, tenantName?: string) => {
|
|
14
15
|
};
|
15
16
|
};
|
16
17
|
|
17
|
-
export const
|
18
|
+
export const prepareComputeNodes = (nodes?: TComputeNodeInfo[], tenants?: TComputeTenantInfo[]) => {
|
18
19
|
const preparedNodes: NodesPreparedEntity[] = [];
|
19
20
|
|
20
21
|
// First try to parse v2 response in case backend supports it
|
21
22
|
// Else parse v1 response
|
22
|
-
|
23
|
-
|
23
|
+
|
24
|
+
if (nodes) {
|
25
|
+
nodes.forEach((node) => {
|
24
26
|
preparedNodes.push(prepareComputeNode(node));
|
25
27
|
});
|
26
|
-
} else if (
|
27
|
-
for (const tenant of
|
28
|
+
} else if (tenants) {
|
29
|
+
for (const tenant of tenants) {
|
28
30
|
tenant.Nodes?.forEach((node) => {
|
29
31
|
preparedNodes.push(prepareComputeNode(node, tenant.Name));
|
30
32
|
});
|
31
33
|
}
|
32
34
|
}
|
33
35
|
|
36
|
+
return preparedNodes;
|
37
|
+
};
|
38
|
+
|
39
|
+
export const prepareComputeNodesData = (data: TComputeInfo): NodesHandledResponse => {
|
40
|
+
const preparedNodes = prepareComputeNodes(data.Nodes, data.Tenants);
|
41
|
+
|
34
42
|
return {
|
35
43
|
Nodes: preparedNodes,
|
36
44
|
TotalNodes: Number(data.TotalNodes) || preparedNodes.length,
|
@@ -57,3 +65,5 @@ export const prepareNodesData = (data: TNodesInfo): NodesHandledResponse => {
|
|
57
65
|
FoundNodes: Number(data.FoundNodes),
|
58
66
|
};
|
59
67
|
};
|
68
|
+
|
69
|
+
export const getLoadSeverityForNode = generateEvaluator(60, 80, ['success', 'warning', 'danger']);
|
@@ -1,16 +1,10 @@
|
|
1
1
|
import type {Reducer} from 'redux';
|
2
2
|
|
3
|
-
import '
|
4
|
-
import
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
} from '../../types/store/shardsWorkload';
|
9
|
-
import {EShardsWorkloadMode} from '../../types/store/shardsWorkload';
|
10
|
-
|
11
|
-
import {parseQueryAPIExecuteResponse} from '../../utils/query';
|
12
|
-
|
13
|
-
import {createRequestActionTypes, createApiRequest} from '../utils';
|
3
|
+
import '../../../services/api';
|
4
|
+
import {parseQueryAPIExecuteResponse} from '../../../utils/query';
|
5
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
6
|
+
import type {IShardsWorkloadAction, IShardsWorkloadFilters, IShardsWorkloadState} from './types';
|
7
|
+
import {EShardsWorkloadMode} from './types';
|
14
8
|
|
15
9
|
export const SEND_SHARD_QUERY = createRequestActionTypes('query', 'SEND_SHARD_QUERY');
|
16
10
|
const SET_SHARD_STATE = 'query/SET_SHARD_STATE';
|
@@ -1,10 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
|
4
|
-
setShardsQueryFilters,
|
5
|
-
} from '../../store/reducers/shardsWorkload';
|
6
|
-
import type {ApiRequestAction} from '../../store/utils';
|
7
|
-
import type {IQueryResult, QueryErrorResponse} from './query';
|
1
|
+
import {SEND_SHARD_QUERY, setShardsState, setShardsQueryFilters} from './shardsWorkload';
|
2
|
+
import type {ApiRequestAction} from '../../utils';
|
3
|
+
import type {IQueryResult, QueryErrorResponse} from '../../../types/store/query';
|
8
4
|
|
9
5
|
export enum EShardsWorkloadMode {
|
10
6
|
Immediate = 'immediate',
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import type {Reducer} from 'redux';
|
2
|
+
|
3
|
+
import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
|
4
|
+
import {createApiRequest, createRequestActionTypes} from '../../../utils';
|
5
|
+
import {prepareNodesData} from '../../nodes/utils';
|
6
|
+
import type {NodesApiRequestParams} from '../../nodes/types';
|
7
|
+
import type {TopNodesByCpuAction, TopNodesByCpuState, TopPoolsStateSlice} from './types';
|
8
|
+
|
9
|
+
export const FETCH_TOP_NODES_BY_CPU = createRequestActionTypes(
|
10
|
+
'topNodesByCpu',
|
11
|
+
'FETCH_TOP_NODES_BY_CPU',
|
12
|
+
);
|
13
|
+
const SET_DATA_WAS_NOT_LOADED = 'topNodesByCpu/SET_DATA_WAS_NOT_LOADED';
|
14
|
+
|
15
|
+
const initialState = {
|
16
|
+
loading: false,
|
17
|
+
wasLoaded: false,
|
18
|
+
};
|
19
|
+
|
20
|
+
export const topNodesByCpu: Reducer<TopNodesByCpuState, TopNodesByCpuAction> = (
|
21
|
+
state = initialState,
|
22
|
+
action,
|
23
|
+
) => {
|
24
|
+
switch (action.type) {
|
25
|
+
case FETCH_TOP_NODES_BY_CPU.REQUEST: {
|
26
|
+
return {
|
27
|
+
...state,
|
28
|
+
loading: true,
|
29
|
+
};
|
30
|
+
}
|
31
|
+
case FETCH_TOP_NODES_BY_CPU.SUCCESS: {
|
32
|
+
return {
|
33
|
+
...state,
|
34
|
+
data: action.data?.Nodes,
|
35
|
+
loading: false,
|
36
|
+
wasLoaded: true,
|
37
|
+
error: undefined,
|
38
|
+
};
|
39
|
+
}
|
40
|
+
case FETCH_TOP_NODES_BY_CPU.FAILURE: {
|
41
|
+
if (action.error?.isCancelled) {
|
42
|
+
return state;
|
43
|
+
}
|
44
|
+
|
45
|
+
return {
|
46
|
+
...state,
|
47
|
+
error: action.error,
|
48
|
+
loading: false,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
case SET_DATA_WAS_NOT_LOADED: {
|
52
|
+
return {
|
53
|
+
...state,
|
54
|
+
wasLoaded: false,
|
55
|
+
};
|
56
|
+
}
|
57
|
+
default:
|
58
|
+
return state;
|
59
|
+
}
|
60
|
+
};
|
61
|
+
|
62
|
+
const concurrentId = 'getTopNodeByCpu';
|
63
|
+
|
64
|
+
export function getTopNodesByCpu({
|
65
|
+
type = 'any',
|
66
|
+
sortOrder = -1,
|
67
|
+
sortValue = 'CPU',
|
68
|
+
limit = TENANT_OVERVIEW_TABLES_LIMIT,
|
69
|
+
...params
|
70
|
+
}: NodesApiRequestParams) {
|
71
|
+
return createApiRequest({
|
72
|
+
request: window.api.getNodes(
|
73
|
+
{type, sortOrder, sortValue, limit, ...params},
|
74
|
+
{concurrentId},
|
75
|
+
),
|
76
|
+
actions: FETCH_TOP_NODES_BY_CPU,
|
77
|
+
dataHandler: prepareNodesData,
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
export const selectTopNodesByCpu = (state: TopPoolsStateSlice) => state.topNodesByCpu.data;
|
82
|
+
|
83
|
+
export const setDataWasNotLoaded = () => {
|
84
|
+
return {
|
85
|
+
type: SET_DATA_WAS_NOT_LOADED,
|
86
|
+
} as const;
|
87
|
+
};
|