Skip to content

Commit

Permalink
feat: add absolute timestamp to uptime
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov committed Dec 18, 2024
1 parent 4ed2fab commit 1b7b4b7
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 33 deletions.
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;
}

&__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>
{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
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);
}

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

0 comments on commit 1b7b4b7

Please sign in to comment.