ydb-embedded-ui 4.30.0 → 4.31.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/README.md +2 -0
  2. package/dist/components/MetricChart/MetricChart.scss +34 -0
  3. package/dist/components/MetricChart/MetricChart.tsx +198 -0
  4. package/dist/components/MetricChart/convertReponse.ts +33 -0
  5. package/dist/components/MetricChart/getChartData.ts +20 -0
  6. package/dist/components/MetricChart/getDefaultDataFormatter.ts +45 -0
  7. package/dist/components/MetricChart/index.ts +2 -0
  8. package/dist/components/MetricChart/reducer.ts +86 -0
  9. package/dist/components/MetricChart/types.ts +32 -0
  10. package/dist/components/TimeFrameSelector/TimeFrameSelector.scss +5 -0
  11. package/dist/components/TimeFrameSelector/TimeFrameSelector.tsx +33 -0
  12. package/dist/containers/App/Content.js +16 -12
  13. package/dist/containers/Tenant/Diagnostics/TenantOverview/DefaultDashboard.tsx +50 -0
  14. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +13 -4
  15. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/CpuDashboard.tsx +18 -0
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +2 -0
  17. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantDashboard/TenantDashboard.scss +14 -0
  18. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantDashboard/TenantDashboard.tsx +71 -0
  19. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/MemoryDashboard.tsx +21 -0
  20. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TenantMemory.tsx +7 -1
  21. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +2 -1
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/StorageDashboard.tsx +21 -0
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +2 -0
  24. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -1
  25. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -1
  26. package/dist/containers/UserSettings/i18n/en.json +4 -1
  27. package/dist/containers/UserSettings/i18n/ru.json +4 -1
  28. package/dist/containers/UserSettings/settings.ts +12 -1
  29. package/dist/services/api.ts +18 -0
  30. package/dist/services/settings.ts +2 -0
  31. package/dist/store/reducers/tenant/tenant.ts +6 -1
  32. package/dist/types/api/render.ts +34 -0
  33. package/dist/utils/cn.ts +3 -0
  34. package/dist/utils/constants.ts +4 -0
  35. package/dist/utils/timeParsers/formatDuration.ts +10 -0
  36. package/dist/utils/timeframes.ts +10 -0
  37. package/dist/utils/versions/getVersionsColors.ts +1 -0
  38. package/package.json +3 -1
  39. package/CHANGELOG.md +0 -1559
@@ -0,0 +1,71 @@
1
+ import {StringParam, useQueryParam} from 'use-query-params';
2
+
3
+ import {cn} from '../../../../../utils/cn';
4
+ import type {TimeFrame} from '../../../../../utils/timeframes';
5
+ import {useSetting, useTypedSelector} from '../../../../../utils/hooks';
6
+ import {DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY} from '../../../../../utils/constants';
7
+ import {TimeFrameSelector} from '../../../../../components/TimeFrameSelector/TimeFrameSelector';
8
+ import {
9
+ type ChartOptions,
10
+ MetricChart,
11
+ type MetricDescription,
12
+ } from '../../../../../components/MetricChart';
13
+
14
+ import './TenantDashboard.scss';
15
+
16
+ const CHART_WIDTH = 428;
17
+ const CHART_WIDTH_FULL = 872;
18
+
19
+ const b = cn('ydb-tenant-dashboard');
20
+
21
+ export interface ChartConfig {
22
+ metrics: MetricDescription[];
23
+ title: string;
24
+ options?: ChartOptions;
25
+ }
26
+
27
+ interface TenantDashboardProps {
28
+ charts: ChartConfig[];
29
+ }
30
+
31
+ export const TenantDashboard = ({charts}: TenantDashboardProps) => {
32
+ const [timeFrame = '1h', setTimeframe] = useQueryParam('timeframe', StringParam);
33
+
34
+ const {autorefresh} = useTypedSelector((state) => state.schema);
35
+
36
+ const [chartsEnabled] = useSetting(DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY);
37
+
38
+ if (!chartsEnabled) {
39
+ return null;
40
+ }
41
+
42
+ // If there is only one chart, display it with full width
43
+ const chartWidth = charts.length === 1 ? CHART_WIDTH_FULL : CHART_WIDTH;
44
+ const chartHeight = CHART_WIDTH / 1.5;
45
+
46
+ const renderContent = () => {
47
+ return charts.map((chartConfig) => {
48
+ return (
49
+ <MetricChart
50
+ key={chartConfig.metrics.map(({target}) => target).join('&')}
51
+ title={chartConfig.title}
52
+ metrics={chartConfig.metrics}
53
+ timeFrame={timeFrame as TimeFrame}
54
+ chartOptions={chartConfig.options}
55
+ autorefresh={autorefresh}
56
+ width={chartWidth}
57
+ height={chartHeight}
58
+ />
59
+ );
60
+ });
61
+ };
62
+
63
+ return (
64
+ <div className={b(null)}>
65
+ <div className={b('controls')}>
66
+ <TimeFrameSelector value={timeFrame as TimeFrame} onChange={setTimeframe} />
67
+ </div>
68
+ <div className={b('charts')}>{renderContent()}</div>
69
+ </div>
70
+ );
71
+ };
@@ -0,0 +1,21 @@
1
+ import {type ChartConfig, TenantDashboard} from '../TenantDashboard/TenantDashboard';
2
+ import i18n from '../i18n';
3
+
4
+ const memoryDashboardConfig: ChartConfig[] = [
5
+ {
6
+ title: i18n('charts.memory-usage'),
7
+ metrics: [
8
+ {
9
+ target: 'resources.memory.used_bytes',
10
+ title: i18n('charts.memory-usage'),
11
+ },
12
+ ],
13
+ options: {
14
+ dataType: 'size',
15
+ },
16
+ },
17
+ ];
18
+
19
+ export const MemoryDashboard = () => {
20
+ return <TenantDashboard charts={memoryDashboardConfig} />;
21
+ };
@@ -1,3 +1,4 @@
1
+ import {MemoryDashboard} from './MemoryDashboard';
1
2
  import {TopNodesByMemory} from './TopNodesByMemory';
2
3
 
3
4
  interface TenantMemoryProps {
@@ -5,5 +6,10 @@ interface TenantMemoryProps {
5
6
  }
6
7
 
7
8
  export function TenantMemory({path}: TenantMemoryProps) {
8
- return <TopNodesByMemory path={path} />;
9
+ return (
10
+ <>
11
+ <MemoryDashboard />
12
+ <TopNodesByMemory path={path} />
13
+ </>
14
+ );
9
15
  }
@@ -17,6 +17,7 @@ import {HealthcheckDetails} from './Healthcheck/HealthcheckDetails';
17
17
  import {MetricsCards, type TenantMetrics} from './MetricsCards/MetricsCards';
18
18
  import {TenantStorage} from './TenantStorage/TenantStorage';
19
19
  import {TenantMemory} from './TenantMemory/TenantMemory';
20
+ import {DefaultDashboard} from './DefaultDashboard';
20
21
  import {useHealthcheck} from './useHealthcheck';
21
22
 
22
23
  import './TenantOverview.scss';
@@ -140,7 +141,7 @@ export function TenantOverview({
140
141
  );
141
142
  }
142
143
  default: {
143
- return undefined;
144
+ return <DefaultDashboard />;
144
145
  }
145
146
  }
146
147
  };
@@ -0,0 +1,21 @@
1
+ import {type ChartConfig, TenantDashboard} from '../TenantDashboard/TenantDashboard';
2
+ import i18n from '../i18n';
3
+
4
+ const storageDashboardConfig: ChartConfig[] = [
5
+ {
6
+ title: i18n('charts.storage-usage'),
7
+ metrics: [
8
+ {
9
+ target: 'resources.storage.used_bytes',
10
+ title: i18n('charts.storage-usage'),
11
+ },
12
+ ],
13
+ options: {
14
+ dataType: 'size',
15
+ },
16
+ },
17
+ ];
18
+
19
+ export const StorageDashboard = () => {
20
+ return <TenantDashboard charts={storageDashboardConfig} />;
21
+ };
@@ -7,6 +7,7 @@ import {getSizeWithSignificantDigits} from '../../../../../utils/bytesParsers';
7
7
 
8
8
  import '../TenantOverview.scss';
9
9
 
10
+ import {StorageDashboard} from './StorageDashboard';
10
11
  import {TopTables} from './TopTables';
11
12
  import {TopGroups} from './TopGroups';
12
13
 
@@ -62,6 +63,7 @@ export function TenantStorage({tenantName, metrics}: TenantStorageProps) {
62
63
  ];
63
64
  return (
64
65
  <>
66
+ <StorageDashboard />
65
67
  <InfoViewer className={b('storage-info')} title="Storage details" info={info} />
66
68
  <TopTables path={tenantName} />
67
69
  <TopGroups tenant={tenantName} />
@@ -23,5 +23,11 @@
23
23
  "by-load": "by load",
24
24
  "by-memory": "by memory",
25
25
  "by-usage": "by usage",
26
- "by-size": "by size"
26
+ "by-size": "by size",
27
+
28
+ "charts.queries-per-second": "Queries per second",
29
+ "charts.transaction-latency": "Transactions latencies {{percentile}}",
30
+ "charts.cpu-usage": "CPU usage",
31
+ "charts.storage-usage": "Storage usage",
32
+ "charts.memory-usage": "Memory usage"
27
33
  }
@@ -23,5 +23,11 @@
23
23
  "by-load": "по нагрузке",
24
24
  "by-memory": "по памяти",
25
25
  "by-usage": "по потреблению",
26
- "by-size": "по размеру"
26
+ "by-size": "по размеру",
27
+
28
+ "charts.queries-per-second": "Количество запросов в секунду",
29
+ "charts.transaction-latency": "Задержка транзакций {{percentile}}",
30
+ "charts.cpu-usage": "Использование CPU",
31
+ "charts.storage-usage": "Использование хранилища",
32
+ "charts.memory-usage": "Использование памяти"
27
33
  }
@@ -23,5 +23,8 @@
23
23
  "settings.useVirtualTables.popover": "It will increase performance, but could work unstable",
24
24
 
25
25
  "settings.queryUseMultiSchema.title": "Allow queries with multiple result sets",
26
- "settings.queryUseMultiSchema.popover": "Use 'multi' schema for queries that enables queries with multiple result sets. Returns nothing on versions 23-3 and older"
26
+ "settings.queryUseMultiSchema.popover": "Use 'multi' schema for queries that enables queries with multiple result sets. Returns nothing on versions 23-3 and older",
27
+
28
+ "settings.displayChartsInDbDiagnostics.title": "Display charts in database diagnostics",
29
+ "settings.displayChartsInDbDiagnostics.popover": "Incorrect data may be displayed (shows data only for the database related to the current node), does not work well on static nodes"
27
30
  }
@@ -23,5 +23,8 @@
23
23
  "settings.useVirtualTables.popover": "Это улучшит производительность, но может работать нестабильно",
24
24
 
25
25
  "settings.queryUseMultiSchema.title": "Разрешить запросы с несколькими результатами",
26
- "settings.queryUseMultiSchema.popover": "Использовать для запросов схему 'multi', которая позволяет выполнять запросы с несколькими результатами. На версиях 23-3 и старше результат не возвращается вообще"
26
+ "settings.queryUseMultiSchema.popover": "Использовать для запросов схему 'multi', которая позволяет выполнять запросы с несколькими результатами. На версиях 23-3 и старше результат не возвращается вообще",
27
+
28
+ "settings.displayChartsInDbDiagnostics.title": "Показывать графики в диагностике базы данных",
29
+ "settings.displayChartsInDbDiagnostics.popover": "Могут отображаться неправильные данные (показывает данные только для базы, относящейся к текущей ноде), плохо работает на статических нодах"
27
30
  }
@@ -10,6 +10,7 @@ import {
10
10
  USE_BACKEND_PARAMS_FOR_TABLES_KEY,
11
11
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
12
12
  QUERY_USE_MULTI_SCHEMA_KEY,
13
+ DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
13
14
  } from '../../utils/constants';
14
15
  import {Lang, defaultLang} from '../../utils/i18n';
15
16
 
@@ -94,6 +95,11 @@ export const queryUseMultiSchemaSetting: SettingProps = {
94
95
  title: i18n('settings.queryUseMultiSchema.title'),
95
96
  helpPopoverContent: i18n('settings.queryUseMultiSchema.popover'),
96
97
  };
98
+ export const displayChartsInDbDiagnosticsSetting: SettingProps = {
99
+ settingKey: DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
100
+ title: i18n('settings.displayChartsInDbDiagnostics.title'),
101
+ helpPopoverContent: i18n('settings.displayChartsInDbDiagnostics.popover'),
102
+ };
97
103
 
98
104
  export const appearanceSection: SettingsSection = {
99
105
  id: 'appearanceSection',
@@ -103,7 +109,12 @@ export const appearanceSection: SettingsSection = {
103
109
  export const experimentsSection: SettingsSection = {
104
110
  id: 'experimentsSection',
105
111
  title: i18n('section.experiments'),
106
- settings: [useNodesEndpointSetting, useVirtualTables, queryUseMultiSchemaSetting],
112
+ settings: [
113
+ useNodesEndpointSetting,
114
+ useVirtualTables,
115
+ queryUseMultiSchemaSetting,
116
+ displayChartsInDbDiagnosticsSetting,
117
+ ],
107
118
  };
108
119
 
109
120
  export const generalPage: SettingsPage = {
@@ -28,6 +28,7 @@ import type {DescribeTopicResult} from '../types/api/topic';
28
28
  import type {TEvPDiskStateResponse} from '../types/api/pdisk';
29
29
  import type {TEvVDiskStateResponse} from '../types/api/vdisk';
30
30
  import type {TUserToken} from '../types/api/whoami';
31
+ import type {JsonRenderRequestParams, JsonRenderResponse} from '../types/api/render';
31
32
  import type {QuerySyntax} from '../types/store/query';
32
33
  import type {ComputeApiRequestParams, NodesApiRequestParams} from '../store/reducers/nodes/types';
33
34
  import type {StorageApiRequestParams} from '../store/reducers/storage/types';
@@ -377,7 +378,24 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
377
378
  path_id: tenantId?.PathId,
378
379
  });
379
380
  }
381
+ getChartData(
382
+ {target, from, until, maxDataPoints}: JsonRenderRequestParams,
383
+ {concurrentId}: AxiosOptions = {},
384
+ ) {
385
+ const requestString = `${target}&from=${from}&until=${until}&maxDataPoints=${maxDataPoints}&format=json`;
380
386
 
387
+ return this.post<JsonRenderResponse>(
388
+ this.getPath('/viewer/json/render'),
389
+ requestString,
390
+ {},
391
+ {
392
+ concurrentId,
393
+ headers: {
394
+ 'Content-Type': 'application/x-www-form-urlencoded',
395
+ },
396
+ },
397
+ );
398
+ }
381
399
  /** @deprecated use localStorage instead */
382
400
  postSetting(settingsApi: string, name: string, value: string) {
383
401
  return this.request({
@@ -3,6 +3,7 @@ import {TENANT_PAGES_IDS} from '../store/reducers/tenant/constants';
3
3
  import {
4
4
  ASIDE_HEADER_COMPACT_KEY,
5
5
  CLUSTER_INFO_HIDDEN_KEY,
6
+ DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY,
6
7
  INVERTED_DISKS_KEY,
7
8
  LANGUAGE_KEY,
8
9
  LAST_USED_QUERY_ACTION_KEY,
@@ -37,6 +38,7 @@ export const DEFAULT_USER_SETTINGS: SettingsObject = {
37
38
  [PARTITIONS_HIDDEN_COLUMNS_KEY]: [],
38
39
  [CLUSTER_INFO_HIDDEN_KEY]: true,
39
40
  [USE_BACKEND_PARAMS_FOR_TABLES_KEY]: false,
41
+ [DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY]: false,
40
42
  };
41
43
 
42
44
  class SettingsManager {
@@ -24,7 +24,12 @@ const SET_METRICS_TAB = 'tenant/SET_METRICS_TAB';
24
24
  const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
25
25
  const SET_DATA_WAS_NOT_LOADED = 'tenant/SET_DATA_WAS_NOT_LOADED';
26
26
 
27
- const initialState = {loading: false, wasLoaded: false};
27
+ // Tenant diagnostics tab content was requested twice,
28
+ // because requests were sent before state was set as loading and after tenant data is fully loaded
29
+ // So tenant data is considered loading from the start, there is no attempt to load tab content
30
+ // TODO: try fix with 'display: none' for tenant diagnostics tab content while tenant data loading,
31
+ // but with parallel (not sequent) data requests
32
+ const initialState = {loading: true, wasLoaded: false};
28
33
 
29
34
  const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState, action) => {
30
35
  switch (action.type) {
@@ -0,0 +1,34 @@
1
+ /**
2
+ * endpoint: /viewer/json/render
3
+ *
4
+ * source: https://github.com/ydb-platform/ydb/blob/main/ydb/core/viewer/json_render.h
5
+ */
6
+
7
+ export type JsonRenderResponse =
8
+ | {
9
+ error?: string;
10
+ status?: string;
11
+ }
12
+ | MetricData[];
13
+
14
+ export interface MetricData {
15
+ datapoints: MetricDatapoint[];
16
+ target: string;
17
+ title: string;
18
+ tags: MetricTags;
19
+ }
20
+
21
+ interface MetricTags {
22
+ name: string;
23
+ }
24
+
25
+ /** [metric value - null or double, timestamp - seconds] */
26
+ export type MetricDatapoint = [null | number, number];
27
+
28
+ export interface JsonRenderRequestParams {
29
+ /** metrics names in format "target=queries.latencies.p50&target=queries.latencies.p75&target=queries.latencies.p90" */
30
+ target: string;
31
+ from: number;
32
+ until: number;
33
+ maxDataPoints: number;
34
+ }
@@ -0,0 +1,3 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ export {cn};
@@ -71,6 +71,8 @@ export const COLORS_PRIORITY = {
71
71
 
72
72
  export const TENANT_OVERVIEW_TABLES_LIMIT = 5;
73
73
 
74
+ export const EMPTY_DATA_PLACEHOLDER = '—';
75
+
74
76
  // ==== Titles ====
75
77
  export const DEVELOPER_UI_TITLE = 'Developer UI';
76
78
  export const CLUSTER_DEFAULT_TITLE = 'Cluster';
@@ -125,3 +127,5 @@ export const USE_BACKEND_PARAMS_FOR_TABLES_KEY = 'useBackendParamsForTables';
125
127
 
126
128
  // Enable schema that supports multiple resultsets
127
129
  export const QUERY_USE_MULTI_SCHEMA_KEY = 'queryUseMultiSchema';
130
+
131
+ export const DISPLAY_CHARTS_IN_DB_DIAGNOSTICS_KEY = 'displayChartsInDbDiagnostics';
@@ -1,4 +1,5 @@
1
1
  import {DAY_IN_SECONDS, HOUR_IN_SECONDS} from '../constants';
2
+ import {formatNumber} from '../dataFormatters/dataFormatters';
2
3
 
3
4
  import i18n from './i18n';
4
5
 
@@ -6,6 +7,8 @@ import i18n from './i18n';
6
7
  * Process time difference in ms and returns formated time.
7
8
  * By default only two major values are returned (days & hours, hours & minutes, minutes & seconds, etc.).
8
9
  * It can be altered with valuesCount arg
10
+ *
11
+ * value - duration in ms
9
12
  */
10
13
  export const formatDurationToShortTimeFormat = (value: number, valuesCount: 1 | 2 = 2) => {
11
14
  const ms = value % 1000;
@@ -62,3 +65,10 @@ export const formatDurationToShortTimeFormat = (value: number, valuesCount: 1 |
62
65
 
63
66
  return i18n('ms', duration);
64
67
  };
68
+
69
+ /**
70
+ * Parse ms duration to string
71
+ */
72
+ export const formatToMs = (value: number) => {
73
+ return i18n('ms', {ms: formatNumber(value)});
74
+ };
@@ -0,0 +1,10 @@
1
+ import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from './constants';
2
+
3
+ export const TIMEFRAMES = {
4
+ '30m': 30 * MINUTE_IN_SECONDS,
5
+ '1h': HOUR_IN_SECONDS,
6
+ '1d': DAY_IN_SECONDS,
7
+ '1w': 7 * DAY_IN_SECONDS,
8
+ } as const;
9
+
10
+ export type TimeFrame = keyof typeof TIMEFRAMES;
@@ -9,6 +9,7 @@ export const hashCode = (s: string) => {
9
9
  }, 0);
10
10
  };
11
11
 
12
+ // TODO: colors used in charts as well, need to move to constants
12
13
  // 11 distinct colors from https://mokole.com/palette.html
13
14
  export const COLORS = [
14
15
  '#008000', // green
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.30.0",
3
+ "version": "4.31.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@gravity-ui/axios-wrapper": "^1.3.0",
13
+ "@gravity-ui/chartkit": "^4.15.0",
13
14
  "@gravity-ui/components": "^2.9.1",
14
15
  "@gravity-ui/date-utils": "^1.1.1",
15
16
  "@gravity-ui/i18n": "^1.0.0",
@@ -45,6 +46,7 @@
45
46
  "reselect": "4.1.6",
46
47
  "sass": "1.32.8",
47
48
  "url": "^0.11.0",
49
+ "use-query-params": "^2.2.1",
48
50
  "web-vitals": "1.1.2",
49
51
  "ydb-ui-components": "^3.6.0"
50
52
  },