Skip to content

Commit 5654ed0

Browse files
authored
feat: serverless database view (#2836)
1 parent 3069209 commit 5654ed0

File tree

17 files changed

+544
-173
lines changed

17 files changed

+544
-173
lines changed

src/containers/Tenant/Diagnostics/Diagnostics.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {Partitions} from './Partitions/Partitions';
3838
import {TopQueries} from './TopQueries';
3939
import {TopShards} from './TopShards';
4040
import {TopicData} from './TopicData/TopicData';
41+
import i18n from './i18n';
4142

4243
import './Diagnostics.scss';
4344

@@ -60,7 +61,7 @@ function Diagnostics(props: DiagnosticsProps) {
6061

6162
const tenantName = isDatabaseEntityType(type) ? path : database;
6263

63-
const {controlPlane} = useTenantBaseInfo(isDatabaseEntityType(type) ? path : '');
64+
const {controlPlane, databaseType} = useTenantBaseInfo(isDatabaseEntityType(type) ? path : '');
6465

6566
const hasFeatureFlags = useFeatureFlagsAvailable();
6667
const hasTopicData = useTopicDataAvailable();
@@ -72,6 +73,7 @@ function Diagnostics(props: DiagnosticsProps) {
7273
hasBackups: typeof uiFactory.renderBackups === 'function' && Boolean(controlPlane),
7374
hasConfigs: isViewerUser,
7475
hasAccess: uiFactory.hasAccess,
76+
databaseType,
7577
});
7678
let activeTab = pages.find((el) => el.id === diagnosticsTab);
7779
if (!activeTab) {
@@ -176,7 +178,7 @@ function Diagnostics(props: DiagnosticsProps) {
176178
});
177179
}
178180
default: {
179-
return <div>No data...</div>;
181+
return <div>{i18n('no-data')}</div>;
180182
}
181183
}
182184
};
@@ -187,10 +189,10 @@ function Diagnostics(props: DiagnosticsProps) {
187189
<TabProvider value={activeTab?.id}>
188190
<TabList size="l">
189191
{pages.map(({id, title}) => {
190-
const path = getDiagnosticsPageLink(id);
192+
const linkPath = getDiagnosticsPageLink(id);
191193
return (
192194
<Tab key={id} value={id}>
193-
<InternalLink to={path} as="tab">
195+
<InternalLink to={linkPath} as="tab">
194196
{title}
195197
</InternalLink>
196198
</Tab>

src/containers/Tenant/Diagnostics/DiagnosticsPages.ts

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {StringParam, useQueryParams} from 'use-query-params';
55
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
66
import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types';
77
import {EPathSubType, EPathType} from '../../../types/api/schema';
8+
import type {ETenantType} from '../../../types/api/tenant';
89
import type {TenantQuery} from '../TenantPages';
910
import {TenantTabsGroups, getTenantPath} from '../TenantPages';
1011
import {isDatabaseEntityType, isTopicEntityType} from '../utils/schema';
@@ -14,6 +15,16 @@ type Page = {
1415
title: string;
1516
};
1617

18+
interface GetPagesOptions {
19+
hasFeatureFlags?: boolean;
20+
hasTopicData?: boolean;
21+
isTopLevel?: boolean;
22+
hasBackups?: boolean;
23+
hasConfigs?: boolean;
24+
hasAccess?: boolean;
25+
databaseType?: ETenantType;
26+
}
27+
1728
const overview = {
1829
id: TENANT_DIAGNOSTICS_TABS_IDS.overview,
1930
title: 'Info',
@@ -118,6 +129,16 @@ const DATABASE_PAGES = [
118129
backups,
119130
];
120131

132+
const SERVERLESS_DATABASE_PAGES = [
133+
overview,
134+
topQueries,
135+
topShards,
136+
tablets,
137+
describe,
138+
configs,
139+
operations,
140+
];
141+
121142
const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe, access];
122143
const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, tablets, describe, access];
123144

@@ -168,41 +189,52 @@ const pathSubTypeToPages: Record<EPathSubType, Page[] | undefined> = {
168189
[EPathSubType.EPathSubTypeEmpty]: undefined,
169190
};
170191

171-
export const getPagesByType = (
172-
type?: EPathType,
173-
subType?: EPathSubType,
174-
options?: {
175-
hasFeatureFlags?: boolean;
176-
hasTopicData?: boolean;
177-
isTopLevel?: boolean;
178-
hasBackups?: boolean;
179-
hasConfigs?: boolean;
180-
hasAccess?: boolean;
181-
},
182-
) => {
192+
function computeInitialPages(type?: EPathType, subType?: EPathSubType) {
183193
const subTypePages = subType ? pathSubTypeToPages[subType] : undefined;
184194
const typePages = type ? pathTypeToPages[type] : undefined;
185-
let pages = subTypePages || typePages || DIR_PAGES;
195+
return subTypePages || typePages || DIR_PAGES;
196+
}
197+
198+
function getDatabasePages(databaseType?: ETenantType) {
199+
return databaseType === 'Serverless' ? SERVERLESS_DATABASE_PAGES : DATABASE_PAGES;
200+
}
201+
202+
function applyFilters(pages: Page[], type?: EPathType, options: GetPagesOptions = {}) {
203+
let result = pages;
186204

187-
if (isTopicEntityType(type) && !options?.hasTopicData) {
188-
pages = pages?.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData);
205+
if (isTopicEntityType(type) && !options.hasTopicData) {
206+
result = result.filter((p) => p.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData);
189207
}
190-
if (isDatabaseEntityType(type) || options?.isTopLevel) {
191-
pages = DATABASE_PAGES;
192-
if (!options?.hasFeatureFlags) {
193-
pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
194-
}
208+
209+
const removals: TenantDiagnosticsTab[] = [];
210+
if (!options.hasBackups) {
211+
removals.push(TENANT_DIAGNOSTICS_TABS_IDS.backups);
195212
}
196-
if (!options?.hasBackups) {
197-
pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.backups);
213+
if (!options.hasConfigs) {
214+
removals.push(TENANT_DIAGNOSTICS_TABS_IDS.configs);
198215
}
199-
if (!options?.hasConfigs) {
200-
pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
216+
if (!options.hasAccess) {
217+
removals.push(TENANT_DIAGNOSTICS_TABS_IDS.access);
201218
}
202-
if (!options?.hasAccess) {
203-
pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.access);
219+
220+
return result.filter((p) => !removals.includes(p.id));
221+
}
222+
223+
export const getPagesByType = (
224+
type?: EPathType,
225+
subType?: EPathSubType,
226+
options?: GetPagesOptions,
227+
) => {
228+
const base = computeInitialPages(type, subType);
229+
const dbContext = isDatabaseEntityType(type) || options?.isTopLevel;
230+
const seeded = dbContext ? getDatabasePages(options?.databaseType) : base;
231+
232+
let withFlags = seeded;
233+
if (!options?.hasFeatureFlags) {
234+
withFlags = seeded.filter((p) => p.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
204235
}
205-
return pages;
236+
237+
return applyFilters(withFlags, type, options);
206238
};
207239

208240
export const useDiagnosticsPageLinkGetter = () => {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import {Link} from 'react-router-dom';
2+
3+
import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
4+
import type {TenantMetricsTab} from '../../../../../store/reducers/tenant/types';
5+
import type {ETenantType} from '../../../../../types/api/tenant';
6+
import {cn} from '../../../../../utils/cn';
7+
import {
8+
formatCoresLegend,
9+
formatStorageLegend,
10+
} from '../../../../../utils/metrics/formatMetricLegend';
11+
import {TabCard} from '../TabCard/TabCard';
12+
import i18n from '../i18n';
13+
14+
const b = cn('tenant-metrics-tabs');
15+
16+
interface CommonMetricsTabsProps {
17+
activeTab: TenantMetricsTab;
18+
tabLinks: Record<TenantMetricsTab, string>;
19+
cpu: {totalUsed: number; totalLimit: number};
20+
storage: {totalUsed: number; totalLimit: number};
21+
storageGroupsCount?: number;
22+
databaseType?: ETenantType;
23+
}
24+
25+
export function CommonMetricsTabs({
26+
activeTab,
27+
tabLinks,
28+
cpu,
29+
storage,
30+
storageGroupsCount,
31+
databaseType,
32+
}: CommonMetricsTabsProps) {
33+
const isServerless = databaseType === 'Serverless';
34+
35+
return (
36+
<>
37+
<div
38+
className={b('link-container', {
39+
active: activeTab === TENANT_METRICS_TABS_IDS.cpu,
40+
})}
41+
>
42+
<Link to={tabLinks.cpu} className={b('link')}>
43+
<TabCard
44+
text={i18n('context_cpu-load')}
45+
value={cpu.totalUsed}
46+
limit={cpu.totalLimit}
47+
legendFormatter={formatCoresLegend}
48+
active={activeTab === TENANT_METRICS_TABS_IDS.cpu}
49+
helpText={i18n('context_cpu-description')}
50+
databaseType={databaseType}
51+
subtitle={isServerless ? i18n('context_serverless-autoscaled') : undefined}
52+
/>
53+
</Link>
54+
</div>
55+
<div
56+
className={b('link-container', {
57+
active: activeTab === TENANT_METRICS_TABS_IDS.storage,
58+
})}
59+
>
60+
<Link to={tabLinks.storage} className={b('link')}>
61+
<TabCard
62+
text={
63+
storageGroupsCount === undefined || isServerless
64+
? i18n('cards.storage-label')
65+
: i18n('context_storage-groups', {count: storageGroupsCount})
66+
}
67+
value={storage.totalUsed}
68+
limit={storage.totalLimit}
69+
legendFormatter={formatStorageLegend}
70+
active={activeTab === TENANT_METRICS_TABS_IDS.storage}
71+
helpText={i18n('context_storage-description')}
72+
databaseType={databaseType}
73+
subtitle={
74+
isServerless && storage.totalLimit
75+
? i18n('context_serverless-storage-subtitle', {
76+
groups: String(storageGroupsCount ?? 0),
77+
legend: formatStorageLegend({
78+
value: storage.totalUsed,
79+
capacity: storage.totalLimit,
80+
}),
81+
})
82+
: undefined
83+
}
84+
/>
85+
</Link>
86+
</div>
87+
</>
88+
);
89+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {Link} from 'react-router-dom';
2+
3+
import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
4+
import type {TenantMetricsTab} from '../../../../../store/reducers/tenant/types';
5+
import {cn} from '../../../../../utils/cn';
6+
import {
7+
formatSpeedLegend,
8+
formatStorageLegend,
9+
} from '../../../../../utils/metrics/formatMetricLegend';
10+
import {TabCard} from '../TabCard/TabCard';
11+
import i18n from '../i18n';
12+
13+
const b = cn('tenant-metrics-tabs');
14+
15+
interface DedicatedMetricsTabsProps {
16+
activeTab: TenantMetricsTab;
17+
tabLinks: Record<TenantMetricsTab, string>;
18+
memory: {totalUsed: number; totalLimit: number};
19+
network: {totalUsed: number; totalLimit: number} | null;
20+
showNetwork: boolean;
21+
}
22+
23+
export function DedicatedMetricsTabs({
24+
activeTab,
25+
tabLinks,
26+
memory,
27+
network,
28+
showNetwork,
29+
}: DedicatedMetricsTabsProps) {
30+
return (
31+
<>
32+
<div
33+
className={b('link-container', {
34+
active: activeTab === TENANT_METRICS_TABS_IDS.memory,
35+
})}
36+
>
37+
<Link to={tabLinks.memory} className={b('link')}>
38+
<TabCard
39+
text={i18n('context_memory-used')}
40+
value={memory.totalUsed}
41+
limit={memory.totalLimit}
42+
legendFormatter={formatStorageLegend}
43+
active={activeTab === TENANT_METRICS_TABS_IDS.memory}
44+
helpText={i18n('context_memory-description')}
45+
databaseType="Dedicated"
46+
/>
47+
</Link>
48+
</div>
49+
{showNetwork && network && (
50+
<div
51+
className={b('link-container', {
52+
active: activeTab === TENANT_METRICS_TABS_IDS.network,
53+
})}
54+
>
55+
<Link to={tabLinks.network} className={b('link')}>
56+
<TabCard
57+
text={i18n('context_network-usage')}
58+
value={network.totalUsed}
59+
limit={network.totalLimit}
60+
legendFormatter={formatSpeedLegend}
61+
active={activeTab === TENANT_METRICS_TABS_IDS.network}
62+
helpText={i18n('context_network-description')}
63+
databaseType="Dedicated"
64+
/>
65+
</Link>
66+
</div>
67+
)}
68+
</>
69+
);
70+
}

src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,13 @@
224224
padding-right: 0;
225225
}
226226
}
227+
228+
.tenant-metrics-tabs_serverless {
229+
.tenant-metrics-tabs__link-container_placeholder {
230+
pointer-events: none;
231+
232+
.tenant-tab-card__card-container {
233+
opacity: 0;
234+
}
235+
}
236+
}

0 commit comments

Comments
 (0)