ydb-embedded-ui 1.10.1 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.10.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.1...v1.10.2) (2022-08-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * convert bytes on decimal scale ([db9b0a7](https://github.com/ydb-platform/ydb-embedded-ui/commit/db9b0a71fc5334f5a40992cc6abc0688782ad5d2))
9
+ * display HDD instead of ROT as pdisk type ([bd9e5ba](https://github.com/ydb-platform/ydb-embedded-ui/commit/bd9e5ba4e594cb3a1f6a964f619f9824e083ae7c))
10
+ * **InfoViewer:** accept default value formatter ([e03d8cc](https://github.com/ydb-platform/ydb-embedded-ui/commit/e03d8cc5de76e4ac00b05586ae6f6522a9708fb0))
11
+ * **InfoViewer:** allow longer labels ([89060a3](https://github.com/ydb-platform/ydb-embedded-ui/commit/89060a381858b5beaa3c3cf3402c13c917705676))
12
+ * **Overview:** display table r/o replicas ([6dbe0b4](https://github.com/ydb-platform/ydb-embedded-ui/commit/6dbe0b45fc5e3867f9d6141d270c15508a693e35))
13
+ * **Overview:** format & group table info in overview ([1a35cfc](https://github.com/ydb-platform/ydb-embedded-ui/commit/1a35cfcd2075454c4a1f1fc4961a4b3106b6d225))
14
+ * **QueryEditor:** save chosen run action ([b0fb436](https://github.com/ydb-platform/ydb-embedded-ui/commit/b0fb43651e0c6d1dc5d6a25f92716703402b556d))
15
+ * use current i18n lang for numeral formatting ([5d58fcf](https://github.com/ydb-platform/ydb-embedded-ui/commit/5d58fcffde21924f3cbe6c28946c7a9f755a8490))
16
+
3
17
  ## [1.10.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.0...v1.10.1) (2022-08-10)
4
18
 
5
19
 
@@ -10,13 +10,16 @@ const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
10
10
  ]);
11
11
 
12
12
  const formatItem = createInfoFormatter<TIndexDescription>({
13
- Type: (value) => value?.substring(10), // trims EIndexType prefix
14
- State: (value) => value?.substring(11), // trims EIndexState prefix
15
- KeyColumnNames: (value) => value?.join(', '),
16
- DataColumnNames: (value) => value?.join(', '),
17
- }, {
18
- KeyColumnNames: 'Columns',
19
- DataColumnNames: 'Includes',
13
+ values: {
14
+ Type: (value) => value?.substring(10), // trims EIndexType prefix
15
+ State: (value) => value?.substring(11), // trims EIndexState prefix
16
+ KeyColumnNames: (value) => value?.join(', '),
17
+ DataColumnNames: (value) => value?.join(', '),
18
+ },
19
+ labels: {
20
+ KeyColumnNames: 'Columns',
21
+ DataColumnNames: 'Includes',
22
+ },
20
23
  });
21
24
 
22
25
  interface IndexInfoViewerProps {
@@ -27,11 +27,10 @@
27
27
 
28
28
  &__label {
29
29
  display: flex;
30
- flex: 1 1 auto;
30
+ flex: 0 1 auto;
31
31
  align-items: baseline;
32
32
 
33
33
  min-width: 200px;
34
- max-width: 200px;
35
34
 
36
35
  white-space: nowrap;
37
36
 
@@ -2,7 +2,7 @@ type LabelMap<T> = {
2
2
  [label in keyof T]?: string;
3
3
  }
4
4
 
5
- type FieldMappers<T> = {
5
+ type ValueFormatters<T> = {
6
6
  [label in keyof T]?: (value: T[label]) => string | undefined;
7
7
  }
8
8
 
@@ -13,20 +13,28 @@ function formatLabel<Shape>(label: keyof Shape, map: LabelMap<Shape>) {
13
13
  function formatValue<Shape, Key extends keyof Shape>(
14
14
  label: Key,
15
15
  value: Shape[Key],
16
- mappers: FieldMappers<Shape>,
16
+ formatters: ValueFormatters<Shape>,
17
+ defaultFormatter?: (value: Shape[Key]) => string | undefined,
17
18
  ) {
18
- const mapper = mappers[label];
19
- const mappedValue = mapper ? mapper(value) : value;
19
+ const formatter = formatters[label] || defaultFormatter;
20
+ const formattedValue = formatter ? formatter(value) : value;
20
21
 
21
- return String(mappedValue ?? '');
22
+ return String(formattedValue ?? '');
22
23
  }
23
24
 
24
- export function createInfoFormatter<Shape extends Record<string, any>>(
25
- fieldMappers?: FieldMappers<Shape>,
26
- labelMap?: LabelMap<Shape>,
27
- ) {
25
+ interface CreateInfoFormatterOptions<Shape> {
26
+ values?: ValueFormatters<Shape>,
27
+ labels?: LabelMap<Shape>,
28
+ defaultValueFormatter?: (value: Shape[keyof Shape]) => string | undefined,
29
+ }
30
+
31
+ export function createInfoFormatter<Shape extends Record<string, any>>({
32
+ values: valueFormatters,
33
+ labels: labelMap,
34
+ defaultValueFormatter,
35
+ }: CreateInfoFormatterOptions<Shape>) {
28
36
  return <Key extends keyof Shape>(label: Key, value: Shape[Key]) => ({
29
37
  label: formatLabel(label, labelMap || {}),
30
- value: formatValue(label, value, fieldMappers || {}),
38
+ value: formatValue(label, value, valueFormatters || {}, defaultValueFormatter),
31
39
  });
32
40
  }
@@ -60,20 +60,21 @@ function DetailedOverview(props: DetailedOverviewProps) {
60
60
  const isTenant = tenantName === currentSchemaPath;
61
61
  return (
62
62
  <div className={b()}>
63
- <div className={b('section')}>
64
- {!isTenant && (
65
- <Overview type={type} tenantName={tenantName} />
66
- )}
67
- {isTenant && <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo}/>}
68
- </div>
69
- {isTenant && (
70
- <div className={b('section')}>
71
- <Healthcheck
72
- tenant={tenantName}
73
- preview={true}
74
- showMoreHandler={openModalHandler}
75
- />
76
- </div>
63
+ {isTenant ? (
64
+ <>
65
+ <div className={b('section')}>
66
+ <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
67
+ </div>
68
+ <div className={b('section')}>
69
+ <Healthcheck
70
+ tenant={tenantName}
71
+ preview={true}
72
+ showMoreHandler={openModalHandler}
73
+ />
74
+ </div>
75
+ </>
76
+ ) : (
77
+ <Overview type={type} tenantName={tenantName} />
77
78
  )}
78
79
  </div>
79
80
  );
@@ -33,6 +33,7 @@ import {
33
33
  DEFAULT_SIZE_RESULT_PANE_KEY,
34
34
  DEFAULT_TABLE_SETTINGS,
35
35
  SAVED_QUERIES_KEY,
36
+ QUERY_INITIAL_RUN_ACTION_KEY,
36
37
  } from '../../../utils/constants';
37
38
  import {prepareQueryResponse} from '../../../utils/index';
38
39
 
@@ -538,7 +539,13 @@ function QueryEditor(props) {
538
539
  };
539
540
 
540
541
  const renderControls = () => {
541
- const {executeQuery, explainQuery, savedQueries, selectRunAction} = props;
542
+ const {
543
+ executeQuery,
544
+ explainQuery,
545
+ savedQueries,
546
+ selectRunAction,
547
+ setSettingValue,
548
+ } = props;
542
549
  const {runAction} = executeQuery;
543
550
  const runIsDisabled = !executeQuery.input || executeQuery.loading;
544
551
  const runText = _.find(RUN_ACTIONS, {value: runAction}).content;
@@ -546,7 +553,10 @@ function QueryEditor(props) {
546
553
  const menuItems = RUN_ACTIONS.map((action) => {
547
554
  return {
548
555
  text: action.content,
549
- action: () => selectRunAction(action.value),
556
+ action: () => {
557
+ selectRunAction(action.value);
558
+ setSettingValue(QUERY_INITIAL_RUN_ACTION_KEY, action.value);
559
+ },
550
560
  };
551
561
  });
552
562
 
@@ -3,63 +3,185 @@ import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import './SchemaInfoViewer.scss';
5
5
 
6
- import {formatCPU, formatBytes} from '../../../../utils';
6
+ import {formatCPU, formatBytes, formatNumber, formatBps} from '../../../../utils';
7
7
 
8
- import InfoViewer from '../../../../components/InfoViewer/InfoViewer';
8
+ import {InfoViewer, createInfoFormatter} from '../../../../components/InfoViewer';
9
9
 
10
10
  const b = cn('schema-info-viewer');
11
11
 
12
+ const formatTabletMetricsItem = createInfoFormatter({
13
+ values: {
14
+ CPU: formatCPU,
15
+ Memory: formatBytes,
16
+ Storage: formatBytes,
17
+ Network: formatBps,
18
+ ReadThroughput: formatBps,
19
+ WriteThroughput: formatBps,
20
+ },
21
+ defaultValueFormatter: formatNumber,
22
+ });
23
+
24
+ const formatFollowerGroupItem = createInfoFormatter({
25
+ values: {
26
+ FollowerCount: formatNumber,
27
+ },
28
+ });
29
+
30
+ const formatPartitionConfigItem = createInfoFormatter({
31
+ values: {
32
+ FollowerCount: formatNumber,
33
+ CrossDataCenterFollowerCount: formatNumber,
34
+ },
35
+ });
36
+
37
+ const formatTableStatsItem = createInfoFormatter({
38
+ values: {
39
+ DataSize: formatBytes,
40
+ IndexSize: formatBytes,
41
+ LastAccessTime: (value) => value > 0 ? new Date(Number(value)).toUTCString() : 'N/A',
42
+ LastUpdateTime: (value) => value > 0 ? new Date(Number(value)).toUTCString() : 'N/A',
43
+ },
44
+ defaultValueFormatter: formatNumber,
45
+ });
46
+
47
+ const formatTableStats = (fields) => Object.entries(fields)
48
+ .map(([label, value]) => formatTableStatsItem(label, value))
49
+ .filter(({value}) => Boolean(value));
50
+
12
51
  class SchemaInfoViewer extends React.Component {
13
52
  static propTypes = {
14
53
  data: PropTypes.object.isRequired,
15
54
  };
16
- formatTabletMetricsValue = (key, value) => {
17
- if (key === 'CPU') {
18
- return formatCPU(value);
19
- } else if (key === 'Memory' || key === 'Storage') {
20
- return formatBytes(value);
21
- } else {
22
- return value;
55
+
56
+ renderItem(itemData, title) {
57
+ if (!Array.isArray(itemData) || !itemData.length) {
58
+ return null;
23
59
  }
24
- };
60
+
61
+ return (
62
+ <div className={b('item')}>
63
+ <InfoViewer
64
+ title={title}
65
+ info={itemData}
66
+ />
67
+ </div>
68
+ );
69
+ }
70
+
71
+ renderContent(data) {
72
+ const {PathDescription = {}} = data;
73
+ const {TableStats = {}, TabletMetrics = {}, Table: {PartitionConfig = {}} = {}} = PathDescription;
74
+ const {
75
+ PartCount,
76
+ RowCount,
77
+ DataSize,
78
+ IndexSize,
79
+
80
+ LastAccessTime,
81
+ LastUpdateTime,
82
+
83
+ ImmediateTxCompleted,
84
+ PlannedTxCompleted,
85
+ TxRejectedByOverload,
86
+ TxRejectedBySpace,
87
+ TxCompleteLagMsec,
88
+ InFlightTxCount,
89
+
90
+ RowUpdates,
91
+ RowDeletes,
92
+ RowReads,
93
+ RangeReads,
94
+ RangeReadRows,
95
+
96
+ ...restTableStats
97
+ } = TableStats;
98
+ const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
99
+
100
+ const tableStatsInfo = [
101
+ formatTableStats({
102
+ PartCount,
103
+ RowCount,
104
+ DataSize,
105
+ IndexSize,
106
+ }),
107
+ formatTableStats({
108
+ LastAccessTime,
109
+ LastUpdateTime,
110
+ }),
111
+ formatTableStats({
112
+ ImmediateTxCompleted,
113
+ PlannedTxCompleted,
114
+ TxRejectedByOverload,
115
+ TxRejectedBySpace,
116
+ TxCompleteLagMsec,
117
+ InFlightTxCount,
118
+ }),
119
+ formatTableStats({
120
+ RowUpdates,
121
+ RowDeletes,
122
+ RowReads,
123
+ RangeReads,
124
+ RangeReadRows,
125
+ }),
126
+ formatTableStats(restTableStats),
127
+ ];
128
+
129
+ const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
130
+ formatTabletMetricsItem(key, TabletMetrics[key])
131
+ );
132
+
133
+ const partitionConfigInfo = [];
134
+
135
+ if (Array.isArray(FollowerGroups) && FollowerGroups.length > 0) {
136
+ partitionConfigInfo.push(...Object.keys(FollowerGroups[0]).map((key) =>
137
+ formatFollowerGroupItem(key, FollowerGroups[0][key])
138
+ ));
139
+ } else if (FollowerCount !== undefined) {
140
+ partitionConfigInfo.push(
141
+ formatPartitionConfigItem('FollowerCount', FollowerCount)
142
+ );
143
+ } else if (CrossDataCenterFollowerCount !== undefined) {
144
+ partitionConfigInfo.push(
145
+ formatPartitionConfigItem('CrossDataCenterFollowerCount', CrossDataCenterFollowerCount)
146
+ );
147
+ }
148
+
149
+ if ([
150
+ tabletMetricsInfo,
151
+ partitionConfigInfo,
152
+ tableStatsInfo.flat(),
153
+ ].flat().length === 0) {
154
+ return (
155
+ <div className={b('item')}>Empty</div>
156
+ );
157
+ }
158
+
159
+ return (
160
+ <div className={b('row')}>
161
+ {tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
162
+ <div className={b('col')}>
163
+ {this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
164
+ {this.renderItem(partitionConfigInfo, 'Partition Config')}
165
+ </div>
166
+ ) : null}
167
+ <div className={b('col')}>
168
+ {tableStatsInfo.map((info, index) => (
169
+ <React.Fragment key={index}>
170
+ {this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
171
+ </React.Fragment>
172
+ ))}
173
+ </div>
174
+ </div>
175
+ );
176
+ }
177
+
25
178
  render() {
26
179
  const {data} = this.props;
27
180
 
28
181
  if (data) {
29
- const {PathDescription = {}} = data;
30
- const {TableStats = {}, TabletMetrics = {}} = PathDescription;
31
- const {PartCount, ...restTableStats} = TableStats;
32
-
33
- const priorityInfo = [{
34
- label: 'PartCount',
35
- value: PartCount,
36
- }].filter(({value}) => value !== undefined);
37
-
38
- const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
39
- label: key,
40
- value: TableStats[key].toString(),
41
- }));
42
-
43
- const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) => ({
44
- label: key,
45
- value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
46
- }));
47
-
48
- const generalInfo = [
49
- ...priorityInfo,
50
- ...tabletMetricsInfo,
51
- ...tableStatsInfo,
52
- ];
53
-
54
182
  return (
55
183
  <div className={b()}>
56
- <div className={b('item')}>
57
- {generalInfo.length ? (
58
- <InfoViewer info={generalInfo}></InfoViewer>
59
- ) : (
60
- <div>Empty</div>
61
- )}
62
- </div>
184
+ {this.renderContent(data)}
63
185
  </div>
64
186
  );
65
187
  } else {
@@ -1,6 +1,24 @@
1
1
  .schema-info-viewer {
2
2
  overflow: auto;
3
3
 
4
+ &__row {
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ justify-content: flex-start;
8
+ align-items: flex-start;
9
+ }
10
+
11
+ &__col {
12
+ display: flex;
13
+ flex-direction: column;
14
+ justify-content: flex-start;
15
+ align-items: flex-start;
16
+
17
+ & + & {
18
+ margin-left: 50px;
19
+ }
20
+ }
21
+
4
22
  &__item {
5
23
  margin-bottom: 20px;
6
24
 
@@ -83,7 +83,6 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
83
83
  path,
84
84
  enums: true,
85
85
  backup: false,
86
- partition_config: false,
87
86
  partition_stats: false,
88
87
  partitioning_info: false,
89
88
  },
@@ -1,7 +1,8 @@
1
1
  import {createRequestActionTypes, createApiRequest} from '../utils';
2
2
  import '../../services/api';
3
3
  import {getValueFromLS, parseJson} from '../../utils/utils';
4
- import {QUERIES_HISTORY_KEY} from '../../utils/constants';
4
+ import {QUERIES_HISTORY_KEY, QUERY_INITIAL_RUN_ACTION_KEY} from '../../utils/constants';
5
+ import {readSavedSettingsValue} from './settings';
5
6
 
6
7
  const MAXIMUM_QUERIES_IN_HISTORY = 20;
7
8
 
@@ -39,7 +40,7 @@ const initialState = {
39
40
  ? MAXIMUM_QUERIES_IN_HISTORY - 1
40
41
  : queriesHistoryInitial.length - 1,
41
42
  },
42
- runAction: RUN_ACTIONS_VALUES.script,
43
+ runAction: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY, RUN_ACTIONS_VALUES.script),
43
44
  monacoHotKey: null,
44
45
  };
45
46
 
@@ -1,4 +1,11 @@
1
- import {ALL, defaultUserSettings, SAVED_QUERIES_KEY, THEME_KEY, TENANT_INITIAL_TAB_KEY} from '../../utils/constants';
1
+ import {
2
+ defaultUserSettings,
3
+ ALL,
4
+ SAVED_QUERIES_KEY,
5
+ THEME_KEY,
6
+ TENANT_INITIAL_TAB_KEY,
7
+ QUERY_INITIAL_RUN_ACTION_KEY,
8
+ } from '../../utils/constants';
2
9
  import '../../services/api';
3
10
  import {getValueFromLS} from '../../utils/utils';
4
11
 
@@ -7,24 +14,24 @@ const SET_SETTING_VALUE = 'settings/SET_VALUE';
7
14
 
8
15
  const userSettings = window.userSettings || {};
9
16
  const systemSettings = window.systemSettings || {};
10
- const theme = window.web_version
11
- ? userSettings.theme || 'light'
12
- : getValueFromLS(THEME_KEY, 'light');
13
- const savedQueries = window.web_version
14
- ? userSettings[SAVED_QUERIES_KEY]
15
- : getValueFromLS(SAVED_QUERIES_KEY, '[]');
16
- const savedTenantGeneralTab = window.web_version
17
- ? userSettings[TENANT_INITIAL_TAB_KEY]
18
- : getValueFromLS(TENANT_INITIAL_TAB_KEY);
17
+
18
+ export function readSavedSettingsValue(key, defaultValue) {
19
+ const savedValue = window.web_version
20
+ ? userSettings[key]
21
+ : getValueFromLS(key);
22
+
23
+ return savedValue ?? defaultValue;
24
+ }
19
25
 
20
26
  export const initialState = {
21
27
  problemFilter: ALL,
22
28
  userSettings: {
23
29
  ...defaultUserSettings,
24
30
  ...userSettings,
25
- theme,
26
- [SAVED_QUERIES_KEY]: savedQueries,
27
- [TENANT_INITIAL_TAB_KEY]: savedTenantGeneralTab,
31
+ theme: readSavedSettingsValue(THEME_KEY, 'light'),
32
+ [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
33
+ [TENANT_INITIAL_TAB_KEY]: readSavedSettingsValue(TENANT_INITIAL_TAB_KEY),
34
+ [QUERY_INITIAL_RUN_ACTION_KEY]: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY),
28
35
  },
29
36
  systemSettings,
30
37
  };
@@ -16,7 +16,7 @@ export interface TEvDescribeSchemeResult {
16
16
  PathOwnerId?: string;
17
17
  }
18
18
 
19
- enum EStatus {
19
+ enum EStatus {
20
20
  StatusSuccess = 'StatusSuccess',
21
21
  StatusAccepted = 'StatusAccepted',
22
22
  StatusPathDoesNotExist = 'StatusPathDoesNotExist',
@@ -47,8 +47,8 @@ interface TPathDescription {
47
47
  Children?: TDirEntry[];
48
48
 
49
49
  // for table
50
- Table?: unknown;
51
- TableStats?: unknown;
50
+ Table?: TTableDescription;
51
+ TableStats?: TTableStats;
52
52
  TabletMetrics?: unknown;
53
53
  TablePartitions?: unknown[];
54
54
 
@@ -82,6 +82,119 @@ interface TDirEntry {
82
82
  Version?: TPathVersion;
83
83
  }
84
84
 
85
+ // incomplete
86
+ export interface TTableDescription {
87
+ PartitionConfig?: TPartitionConfig;
88
+ }
89
+
90
+ // incomplete
91
+ export interface TPartitionConfig {
92
+ /** uint64 */
93
+ FollowerCount?: string;
94
+ /**
95
+ * uint32
96
+ * @deprecated use FollowerGroups
97
+ */
98
+ CrossDataCenterFollowerCount?: string;
99
+ /** 0 or 1 items */
100
+ FollowerGroups?: TFollowerGroup[];
101
+ }
102
+
103
+ export interface TFollowerGroup {
104
+ /** uint32 */
105
+ FollowerCount?: string;
106
+ AllowLeaderPromotion?: boolean;
107
+ AllowClientRead?: boolean;
108
+ /** uint32[] */
109
+ AllowedNodeIDs?: string[];
110
+ /**
111
+ * uint32[]
112
+ * @deprecated use AllowedDataCenters
113
+ */
114
+ AllowedDataCenterNumIDs?: string[];
115
+ RequireAllDataCenters?: boolean;
116
+ LocalNodeOnly?: boolean;
117
+ RequireDifferentNodes?: boolean;
118
+ FollowerCountPerDataCenter?: boolean; // multiplies FollowerCount by number of DataCenters
119
+ AllowedDataCenters?: string[];
120
+ }
121
+
122
+ interface TTableStats {
123
+ /** uint64 */
124
+ DataSize?: string;
125
+ /** uint64 */
126
+ RowCount?: string;
127
+ /** uint64 */
128
+ IndexSize?: string;
129
+ /** uint64 */
130
+ InMemSize?: string;
131
+
132
+ /**
133
+ * uint64
134
+ * unix time in millisec
135
+ */
136
+ LastAccessTime?: string;
137
+ /**
138
+ * uint64
139
+ * unix time in millisec
140
+ */
141
+ LastUpdateTime?: string;
142
+
143
+ RowCountHistogram?: THistogram;
144
+ DataSizeHistogram?: THistogram;
145
+
146
+ /** uint64 */
147
+ ImmediateTxCompleted?: string;
148
+ /** uint64 */
149
+ PlannedTxCompleted?: string;
150
+ /** uint64 */
151
+ TxRejectedByOverload?: string;
152
+ /** uint64 */
153
+ TxRejectedBySpace?: string;
154
+ /** uint64 */
155
+ TxCompleteLagMsec?: string;
156
+ /** uint64 */
157
+ InFlightTxCount?: string;
158
+
159
+ /** uint64 */
160
+ RowUpdates?: string;
161
+ /** uint64 */
162
+ RowDeletes?: string;
163
+ /** uint64 */
164
+ RowReads?: string;
165
+ /** uint64 */
166
+ RangeReads?: string;
167
+ /** uint64 */
168
+ RangeReadRows?: string;
169
+
170
+ /** uint64 */
171
+ PartCount?: string;
172
+
173
+ KeyAccessSample?: THistogram;
174
+
175
+ /** uint64 */
176
+ SearchHeight?: string;
177
+
178
+ /**
179
+ * uint64
180
+ * seconds since epoch
181
+ */
182
+ LastFullCompactionTs?: string;
183
+
184
+ // i.e. this shard lent to other shards
185
+ HasLoanedParts?: boolean;
186
+ }
187
+
188
+ interface THistogram {
189
+ Buckets?: THistogramBucket[];
190
+ }
191
+
192
+ interface THistogramBucket {
193
+ Key?: string;
194
+ /** uint64 */
195
+ Value?: string;
196
+ }
197
+
85
198
  export interface TIndexDescription {
86
199
  Name?: string;
87
200
  /** uint64 */
@@ -111,7 +224,7 @@ export enum EPathType {
111
224
 
112
225
  EPathTypeSubDomain = 'EPathTypeSubDomain',
113
226
 
114
- EPathTypeTableIndex = 'EPathTypeTableIndex',
227
+ EPathTypeTableIndex = 'EPathTypeTableIndex',
115
228
  EPathTypeExtSubDomain = 'EPathTypeExtSubDomain',
116
229
 
117
230
  EPathTypeColumnStore = 'EPathTypeColumnStore',
@@ -6,6 +6,9 @@ export const GROUP_AUTO_RELOAD_INTERVAL = 10 * SECOND;
6
6
  export const PDISK_AUTO_RELOAD_INTERVAL = 10 * SECOND;
7
7
  export const VDISK_AUTO_RELOAD_INTERVAL = 10 * SECOND;
8
8
  export const AUTO_RELOAD_INTERVAL = 10 * SECOND;
9
+ // by agreement, display all byte values in decimal scale
10
+ // values in data are always in bytes, never in higher units,
11
+ // therefore there is no issue arbitrary converting them in UI
9
12
  export const MEGABYTE = 1_000_000;
10
13
  export const GIGABYTE = 1_000_000_000;
11
14
  export const TERABYTE = 1_000_000_000_000;
@@ -139,3 +142,4 @@ export const DEFAULT_TABLE_SETTINGS = {
139
142
  };
140
143
 
141
144
  export const TENANT_INITIAL_TAB_KEY = 'saved_tenant_initial_tab';
145
+ export const QUERY_INITIAL_RUN_ACTION_KEY = 'query_initial_run_action';
@@ -1,16 +1,20 @@
1
1
  import numeral from 'numeral';
2
2
  import _ from 'lodash';
3
3
 
4
+ import {i18n} from './i18n';
4
5
  import {MEGABYTE, TERABYTE, DAY_IN_SECONDS, GIGABYTE} from './constants';
5
6
 
6
7
  import locales from 'numeral/locales'; // eslint-disable-line no-unused-vars
7
- numeral.locale('ru');
8
- numeral.localeData().delimiters.decimal = '.';
8
+
9
+ numeral.locale(i18n.lang);
9
10
 
10
11
  export const formatBytes = (bytes) => {
11
- return numeral(bytes).format('0 ib').replace('i', '');
12
+ // by agreement, display byte values in decimal scale
13
+ return numeral(bytes).format('0 b');
12
14
  };
13
15
 
16
+ export const formatBps = (bytes) => formatBytes(bytes) + '/s';
17
+
14
18
  export const formatBytesToGigabyte = (bytes) => {
15
19
  return `${Math.floor(bytes / GIGABYTE)} GB`;
16
20
  };
@@ -28,7 +28,7 @@ export const parseBitField = <T extends Record<string, number>>(
28
28
  };
29
29
 
30
30
  export enum IPDiskType {
31
- ROT = 'ROT',
31
+ HDD = 'HDD', // ROT (Rotation?) = HDD
32
32
  SSD = 'SSD',
33
33
  MVME = 'NVME',
34
34
  }
@@ -67,7 +67,7 @@ export const getPDiskType = (data: TPDiskStateInfo): IPDiskType | undefined => {
67
67
  return IPDiskType.MVME;
68
68
  }
69
69
  } else if (categoryBitField.typeExt === '0') {
70
- return IPDiskType.ROT;
70
+ return IPDiskType.HDD;
71
71
  }
72
72
 
73
73
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "1.10.1",
3
+ "version": "1.10.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],