Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add absolute timestamp to uptime #1768

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/components/CellWithPopover/CellWithPopover.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
.ydb-cell-with-popover {
display: flex;
display: inline-flex;

max-width: 100%;

&_full-width {
display: flex;
}

Comment on lines +2 to +9
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need flex to display progress bars correctly, for other values we need inline-flex for proper align inside table

&__popover {
display: inline-block;
overflow: hidden;
Expand Down
2 changes: 1 addition & 1 deletion src/components/CellWithPopover/CellWithPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function CellWithPopover({
...props
}: CellWithPopoverProps) {
return (
<div className={b({fullWidth}, wrapperClassName)}>
<div className={b({'full-width': fullWidth}, wrapperClassName)}>
<Popover
delayClosing={DELAY_TIMEOUT}
delayOpening={DELAY_TIMEOUT}
Expand Down
6 changes: 5 additions & 1 deletion src/components/FullNodeViewer/FullNodeViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {InfoViewer} from '../InfoViewer/InfoViewer';
import type {InfoViewerItem} from '../InfoViewer/InfoViewer';
import {PoolUsage} from '../PoolUsage/PoolUsage';
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
import {NodeUptime} from '../UptimeViewer/UptimeViewer';

import './FullNodeViewer.scss';

Expand All @@ -30,7 +31,10 @@ export const FullNodeViewer = ({node, className}: FullNodeViewerProps) => {

commonInfo.push(
{label: 'Version', value: node?.Version},
{label: 'Uptime', value: node?.Uptime},
{
label: 'Uptime',
value: <NodeUptime StartTime={node?.StartTime} DisconnectTime={node?.DisconnectTime} />,
},
{label: 'DC', value: node?.DataCenterDescription || node?.DC},
{label: 'Rack', value: node?.Rack},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type {TTabletStateInfo} from '../../../types/api/tablet';
import {getUptimeFromDateFormatted} from '../../../utils/dataFormatters/dataFormatters';
import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer';
import {TabletUptime} from '../../UptimeViewer/UptimeViewer';

const formatTablet = createInfoFormatter<TTabletStateInfo>({
values: {
ChangeTime: (value) => getUptimeFromDateFormatted(value),
ChangeTime: (value) => {
return <TabletUptime ChangeTime={value} />;
},
},
labels: {
TabletId: 'Tablet',
Expand Down
77 changes: 77 additions & 0 deletions src/components/UptimeViewer/UptimeViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {DefinitionList} from '@gravity-ui/uikit';

import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {
formatDateTime,
getDowntimeFromDateFormatted,
getUptimeFromDateFormatted,
} from '../../utils/dataFormatters/dataFormatters';
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';

import i18n from './i18n';

interface NodeUptimeProps {
StartTime?: string;
DisconnectTime?: string;
}

export function NodeUptime({StartTime, DisconnectTime}: NodeUptimeProps) {
let uptime: string | undefined;

if (DisconnectTime) {
uptime = getDowntimeFromDateFormatted(DisconnectTime);
} else {
uptime = getUptimeFromDateFormatted(StartTime);
}

if (!uptime) {
return EMPTY_DATA_PLACEHOLDER;
}
return (
<CellWithPopover
placement={['top', 'auto']}
content={
<DefinitionList responsive>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add Popover if nor StartTime neither DisconnectTime is provided?

Copy link
Member Author

@artemmufazalov artemmufazalov Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uptime won't be calculated in such case (getUptimeFromDateFormatted return undefined for undefined input). I made the code more explicit

{StartTime ? (
<DefinitionList.Item key={'StartTime'} name={i18n('start-time')}>
{formatDateTime(StartTime, {withTimeZone: true})}
</DefinitionList.Item>
) : null}
{DisconnectTime ? (
<DefinitionList.Item key={'DisconnectTime'} name={i18n('disconnect-time')}>
{formatDateTime(DisconnectTime, {withTimeZone: true})}
</DefinitionList.Item>
) : null}
</DefinitionList>
}
>
{uptime}
</CellWithPopover>
);
}

interface TabletUptimeProps {
ChangeTime?: string;
}

export function TabletUptime({ChangeTime}: TabletUptimeProps) {
const uptime = getUptimeFromDateFormatted(ChangeTime);

if (!uptime) {
return EMPTY_DATA_PLACEHOLDER;
}
return (
<CellWithPopover
placement={['top', 'auto']}
content={
<DefinitionList responsive>
<DefinitionList.Item key={'changeTime'} name={i18n('change-time')}>
{formatDateTime(ChangeTime, {withTimeZone: true})}
</DefinitionList.Item>
</DefinitionList>
}
>
{uptime}
</CellWithPopover>
);
}
5 changes: 5 additions & 0 deletions src/components/UptimeViewer/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"start-time": "Start time",
"disconnect-time": "Disconnect time",
"change-time": "Change time"
}
7 changes: 7 additions & 0 deletions src/components/UptimeViewer/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {registerKeysets} from '../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'ydb-uptime-viewer';

export default registerKeysets(COMPONENT, {en});
9 changes: 7 additions & 2 deletions src/components/nodesColumns/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {PoolsGraph} from '../PoolsGraph/PoolsGraph';
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
import {TabletsStatistic} from '../TabletsStatistic';
import {formatPool} from '../TooltipsContent';
import {NodeUptime} from '../UptimeViewer/UptimeViewer';
import {UsageLabel} from '../UsageLabel/UsageLabel';

import {NODES_COLUMNS_IDS, NODES_COLUMNS_TITLES} from './constants';
Expand Down Expand Up @@ -103,12 +104,16 @@ export function getVersionColumn<T extends {Version?: string}>(): Column<T> {
},
};
}
export function getUptimeColumn<T extends {StartTime?: string; Uptime?: string}>(): Column<T> {
export function getUptimeColumn<
T extends {StartTime?: string; DisconnectTime?: string},
>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.Uptime,
header: NODES_COLUMNS_TITLES.Uptime,
sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
render: ({row}) => row.Uptime,
render: ({row}) => {
return <NodeUptime StartTime={row.StartTime} DisconnectTime={row.DisconnectTime} />;
},
align: DataTable.RIGHT,
width: 120,
};
Expand Down
4 changes: 2 additions & 2 deletions src/containers/Tablet/components/TabletInfo/TabletInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import type {InfoViewerItem} from '../../../../components/InfoViewer';
import {InfoViewer} from '../../../../components/InfoViewer';
import {LinkWithIcon} from '../../../../components/LinkWithIcon/LinkWithIcon';
import {TabletState} from '../../../../components/TabletState/TabletState';
import {TabletUptime} from '../../../../components/UptimeViewer/UptimeViewer';
import {getTabletPagePath} from '../../../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../../../store/reducers/authentication/authentication';
import {ETabletState} from '../../../../types/api/tablet';
import type {TTabletStateInfo} from '../../../../types/api/tablet';
import {cn} from '../../../../utils/cn';
import {getUptimeFromDateFormatted} from '../../../../utils/dataFormatters/dataFormatters';
import {createTabletDeveloperUIHref} from '../../../../utils/developerUI/developerUI';
import {useTypedSelector} from '../../../../utils/hooks';
import {getDefaultNodePath} from '../../../Node/NodePages';
Expand Down Expand Up @@ -72,7 +72,7 @@ export const TabletInfo = ({tablet}: TabletInfoProps) => {
if (hasUptime) {
tabletInfo.push({
label: tabletInfoKeyset('field_uptime'),
value: getUptimeFromDateFormatted(ChangeTime),
value: <TabletUptime ChangeTime={ChangeTime} />,
});
}

Expand Down
7 changes: 5 additions & 2 deletions src/containers/Tablet/components/TabletTable/TabletTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {EntityStatus} from '../../../../components/EntityStatus/EntityStatus';
import {InternalLink} from '../../../../components/InternalLink/InternalLink';
import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {TabletState} from '../../../../components/TabletState/TabletState';
import {TabletUptime} from '../../../../components/UptimeViewer/UptimeViewer';
import type {ITabletPreparedHistoryItem} from '../../../../types/store/tablet';
import {getUptimeFromDateFormatted} from '../../../../utils/dataFormatters/dataFormatters';
import {getDefaultNodePath} from '../../../Node/NodePages';

const TABLET_COLUMNS_WIDTH_LS_KEY = 'tabletTableColumnsWidth';
Expand All @@ -21,7 +21,10 @@ const columns: Column<ITabletPreparedHistoryItem>[] = [
name: 'Change time',
align: DataTable.RIGHT,
sortable: false,
render: ({row}) => getUptimeFromDateFormatted(row.changeTime),
render: ({row}) => {
return <TabletUptime ChangeTime={row.changeTime} />;
},
width: 120,
},
{
name: 'State',
Expand Down
5 changes: 3 additions & 2 deletions src/containers/Tablets/TabletsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {ResizeableDataTable} from '../../components/ResizeableDataTable/Resizeab
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
import {TabletNameWrapper} from '../../components/TabletNameWrapper/TabletNameWrapper';
import {TabletState} from '../../components/TabletState/TabletState';
import {TabletUptime} from '../../components/UptimeViewer/UptimeViewer';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {tabletApi} from '../../store/reducers/tablet';
import {ETabletState} from '../../types/api/tablet';
import type {TTabletStateInfo} from '../../types/api/tablet';
import {DEFAULT_TABLE_SETTINGS, EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {getUptimeFromDateFormatted} from '../../utils/dataFormatters/dataFormatters';
import {useTypedSelector} from '../../utils/hooks';
import {getDefaultNodePath} from '../Node/NodePages';

Expand Down Expand Up @@ -97,10 +97,11 @@ function getColumns({database}: {database?: string}) {
return i18n('Uptime');
},
render: ({row}) => {
return getUptimeFromDateFormatted(row.ChangeTime);
return <TabletUptime ChangeTime={row.ChangeTime} />;
},
sortAccessor: (row) => -Number(row.ChangeTime),
align: 'right',
width: 120,
},
{
name: 'Actions',
Expand Down
1 change: 0 additions & 1 deletion src/store/reducers/nodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export interface NodesPreparedEntity extends PreparedNodeSystemState {
Version?: string;

StartTime?: string;
Uptime: string;
DisconnectTime?: string;

MemoryUsed?: string;
Expand Down
3 changes: 1 addition & 2 deletions src/utils/dataFormatters/__test__/formatUptime.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {EMPTY_DATA_PLACEHOLDER} from '../../constants';
import {UNBREAKABLE_GAP} from '../../utils';
import {
formatUptimeInSeconds,
Expand Down Expand Up @@ -65,6 +64,6 @@ describe('formatUptimeInSeconds', () => {
);
});
it('should return empty placeholder on NaN', () => {
expect(formatUptimeInSeconds(Number.NaN)).toBe(EMPTY_DATA_PLACEHOLDER);
expect(formatUptimeInSeconds(Number.NaN)).toBe(undefined);
});
});
12 changes: 8 additions & 4 deletions src/utils/dataFormatters/dataFormatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
getSizeWithSignificantDigits,
} from '../bytesParsers/formatBytes';
import type {BytesSizes} from '../bytesParsers/formatBytes';
import {EMPTY_DATA_PLACEHOLDER, HOUR_IN_SECONDS} from '../constants';
import {HOUR_IN_SECONDS} from '../constants';
import {configuredNumeral} from '../numeral';
import {UNBREAKABLE_GAP, isNumeric} from '../utils';

Expand Down Expand Up @@ -46,7 +46,7 @@ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => {
*/
export function formatUptimeInSeconds(seconds: number) {
if (!isNumeric(seconds)) {
return EMPTY_DATA_PLACEHOLDER;
return undefined;
}

// duration.format() doesn't work well with negative values
Expand Down Expand Up @@ -214,8 +214,12 @@ export const formatCPUWithLabel = (value?: number) => {
return `${localizedCores} ${i18n('format-cpu.cores', {count: cores})}`;
};

export const formatDateTime = (value?: number | string, defaultValue = '') => {
const formattedData = dateTimeParse(Number(value))?.format('YYYY-MM-DD HH:mm');
export const formatDateTime = (
value?: number | string,
{withTimeZone, defaultValue = ''}: {withTimeZone?: boolean; defaultValue?: string} = {},
) => {
const tz = withTimeZone ? ' z' : '';
const formattedData = dateTimeParse(Number(value))?.format(`YYYY-MM-DD HH:mm${tz}`);

return formattedData ?? defaultValue;
};
Expand Down
14 changes: 0 additions & 14 deletions src/utils/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import type {TNodeInfo} from '../types/api/nodesList';
import type {NodeHostsMap} from '../types/store/nodesList';

import {HOUR_IN_SECONDS} from './constants';
import {
getDowntimeFromDateFormatted,
getUptimeFromDateFormatted,
} from './dataFormatters/dataFormatters';

import {valueIsDefined} from '.';

Expand Down Expand Up @@ -60,7 +56,6 @@ export interface PreparedNodeSystemState extends TSystemStateInfo {
Rack?: string;
DC?: string;
LoadAveragePercents?: number[];
Uptime: string;
TenantName?: string;
SharedCacheLimit?: number;
SharedCacheUsed?: number;
Expand All @@ -74,14 +69,6 @@ export function prepareNodeSystemState(
const DC = systemState.Location?.DataCenter || systemState.DataCenter;
const TenantName = systemState?.Tenants?.[0];

let Uptime: PreparedNodeSystemState['Uptime'];

if (systemState.DisconnectTime) {
Uptime = getDowntimeFromDateFormatted(systemState.DisconnectTime);
} else {
Uptime = getUptimeFromDateFormatted(systemState.StartTime);
}

Comment on lines -77 to -84
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more about display, moved it to NodeUptime component

const LoadAveragePercents = calculateLoadAveragePercents(systemState);

// 0 limit means that limit is not set, so it should be undefined
Expand All @@ -94,7 +81,6 @@ export function prepareNodeSystemState(
...systemState,
Rack,
DC,
Uptime,
LoadAveragePercents,
TenantName,
SharedCacheLimit,
Expand Down
Loading