From 82d9ec3c6c95679af1a5bdab545adea3cdb7115e Mon Sep 17 00:00:00 2001 From: mufazalov Date: Mon, 9 Dec 2024 12:35:05 +0300 Subject: [PATCH 1/2] feat: show negative uptime for nodes when disconnected --- .../TabletTooltipContent.tsx | 4 +- src/components/nodesColumns/columns.tsx | 2 +- src/components/nodesColumns/constants.ts | 2 +- .../components/TabletInfo/TabletInfo.tsx | 7 +++- .../components/TabletTable/TabletTable.tsx | 4 +- src/containers/Tablets/TabletsTable.tsx | 4 +- src/store/reducers/nodes/selectors.ts | 4 +- .../__test__/formatUptime.test.ts | 10 +++++ src/utils/dataFormatters/dataFormatters.ts | 37 +++++++++++++------ src/utils/nodes.ts | 19 +++++++--- 10 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 src/utils/dataFormatters/__test__/formatUptime.test.ts diff --git a/src/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx b/src/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx index 9ef3569be..d01c80d89 100644 --- a/src/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx +++ b/src/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx @@ -1,10 +1,10 @@ import type {TTabletStateInfo} from '../../../types/api/tablet'; -import {calcUptime} from '../../../utils/dataFormatters/dataFormatters'; +import {getUptimeFromDateFormatted} from '../../../utils/dataFormatters/dataFormatters'; import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer'; const formatTablet = createInfoFormatter({ values: { - ChangeTime: (value) => calcUptime(value), + ChangeTime: (value) => getUptimeFromDateFormatted(value), }, labels: { TabletId: 'Tablet', diff --git a/src/components/nodesColumns/columns.tsx b/src/components/nodesColumns/columns.tsx index f39d55bb8..4803b049a 100644 --- a/src/components/nodesColumns/columns.tsx +++ b/src/components/nodesColumns/columns.tsx @@ -110,7 +110,7 @@ export function getUptimeColumn sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0), render: ({row}) => row.Uptime, align: DataTable.RIGHT, - width: 110, + width: 120, }; } diff --git a/src/components/nodesColumns/constants.ts b/src/components/nodesColumns/constants.ts index de75a77c5..a84f367fa 100644 --- a/src/components/nodesColumns/constants.ts +++ b/src/components/nodesColumns/constants.ts @@ -177,7 +177,7 @@ export const NODES_COLUMNS_TO_DATA_FIELDS: Record { tabletInfo.push({label: tabletInfoKeyset('field_state'), value: }); if (hasUptime) { - tabletInfo.push({label: tabletInfoKeyset('field_uptime'), value: calcUptime(ChangeTime)}); + tabletInfo.push({ + label: tabletInfoKeyset('field_uptime'), + value: getUptimeFromDateFormatted(ChangeTime), + }); } tabletInfo.push( diff --git a/src/containers/Tablet/components/TabletTable/TabletTable.tsx b/src/containers/Tablet/components/TabletTable/TabletTable.tsx index 588e472e9..ebe4b5132 100644 --- a/src/containers/Tablet/components/TabletTable/TabletTable.tsx +++ b/src/containers/Tablet/components/TabletTable/TabletTable.tsx @@ -6,7 +6,7 @@ import {InternalLink} from '../../../../components/InternalLink/InternalLink'; import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable'; import {TabletState} from '../../../../components/TabletState/TabletState'; import type {ITabletPreparedHistoryItem} from '../../../../types/store/tablet'; -import {calcUptime} from '../../../../utils/dataFormatters/dataFormatters'; +import {getUptimeFromDateFormatted} from '../../../../utils/dataFormatters/dataFormatters'; import {getDefaultNodePath} from '../../../Node/NodePages'; const TABLET_COLUMNS_WIDTH_LS_KEY = 'tabletTableColumnsWidth'; @@ -21,7 +21,7 @@ const columns: Column[] = [ name: 'Change time', align: DataTable.RIGHT, sortable: false, - render: ({row}) => calcUptime(row.changeTime), + render: ({row}) => getUptimeFromDateFormatted(row.changeTime), }, { name: 'State', diff --git a/src/containers/Tablets/TabletsTable.tsx b/src/containers/Tablets/TabletsTable.tsx index f1bdcaf25..c97f3df29 100644 --- a/src/containers/Tablets/TabletsTable.tsx +++ b/src/containers/Tablets/TabletsTable.tsx @@ -14,7 +14,7 @@ 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 {calcUptime} from '../../utils/dataFormatters/dataFormatters'; +import {getUptimeFromDateFormatted} from '../../utils/dataFormatters/dataFormatters'; import {useTypedSelector} from '../../utils/hooks'; import {getDefaultNodePath} from '../Node/NodePages'; @@ -97,7 +97,7 @@ function getColumns({database}: {database?: string}) { return i18n('Uptime'); }, render: ({row}) => { - return calcUptime(row.ChangeTime); + return getUptimeFromDateFormatted(row.ChangeTime); }, sortAccessor: (row) => -Number(row.ChangeTime), align: 'right', diff --git a/src/store/reducers/nodes/selectors.ts b/src/store/reducers/nodes/selectors.ts index 61865bd5a..2ec0839f5 100644 --- a/src/store/reducers/nodes/selectors.ts +++ b/src/store/reducers/nodes/selectors.ts @@ -1,5 +1,5 @@ import {HOUR_IN_SECONDS} from '../../../utils/constants'; -import {calcUptimeInSeconds} from '../../../utils/dataFormatters/dataFormatters'; +import {calcTimeDiffInSec} from '../../../utils/dataFormatters/dataFormatters'; import {NodesUptimeFilterValues} from '../../../utils/nodes'; // ==== Filters ==== @@ -12,6 +12,6 @@ export const filterNodesByUptime = ( return nodesList; } return nodesList.filter(({StartTime}) => { - return !StartTime || calcUptimeInSeconds(StartTime) < HOUR_IN_SECONDS; + return !StartTime || calcTimeDiffInSec(StartTime) < HOUR_IN_SECONDS; }); }; diff --git a/src/utils/dataFormatters/__test__/formatUptime.test.ts b/src/utils/dataFormatters/__test__/formatUptime.test.ts new file mode 100644 index 000000000..6668f5114 --- /dev/null +++ b/src/utils/dataFormatters/__test__/formatUptime.test.ts @@ -0,0 +1,10 @@ +import {getUptimeFromDateFormatted} from '../dataFormatters'; + +describe('getUptimeFromDateFormatted', () => { + it('should calculate and format uptime', () => { + expect(getUptimeFromDateFormatted(3_600_000, 7_200_000)).toBe('1:00:00'); + }); + it('should return 0 if dateFrom after dateTo', () => { + expect(getUptimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00'); + }); +}); diff --git a/src/utils/dataFormatters/dataFormatters.ts b/src/utils/dataFormatters/dataFormatters.ts index 24cb335b6..77deb7d94 100644 --- a/src/utils/dataFormatters/dataFormatters.ts +++ b/src/utils/dataFormatters/dataFormatters.ts @@ -40,7 +40,7 @@ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => { return id ? Object.values(id).join('-') : ''; }; -export const formatUptime = (seconds: number) => { +export const formatUptimeInSeconds = (seconds: number) => { const days = Math.floor(seconds / DAY_IN_SECONDS); const remain = seconds % DAY_IN_SECONDS; @@ -52,9 +52,32 @@ export const formatUptime = (seconds: number) => { }; export const formatMsToUptime = (ms?: number) => { - return ms && formatUptime(ms / 1000); + return ms && formatUptimeInSeconds(ms / 1000); }; +export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) { + let diff = calcTimeDiffInSec(dateFrom, dateTo); + + // Our time and server time could differ a little + // Prevent wrong negative uptime values + diff = diff < 0 ? 0 : diff; + + return formatUptimeInSeconds(diff); +} + +export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) { + return '-' + getUptimeFromDateFormatted(dateFrom, dateTo); +} + +export function calcTimeDiffInSec( + dateFrom?: number | string, + dateTo: number | string = new Date().getTime(), +) { + const diffMs = Number(dateTo) - Number(dateFrom); + + return diffMs / 1000; +} + export function formatStorageValues( value?: number, total?: number, @@ -175,16 +198,6 @@ export const formatTimestamp = (value?: string | number, defaultValue = '') => { return formattedData ?? defaultValue; }; -export const calcUptimeInSeconds = (milliseconds: number | string) => { - const currentDate = new Date(); - const diff = currentDate.getTime() - Number(milliseconds); - return diff <= 0 ? 0 : diff / 1000; -}; - -export const calcUptime = (milliseconds?: number | string) => { - return formatUptime(calcUptimeInSeconds(Number(milliseconds))); -}; - export function getStringifiedData(value: unknown) { if (value === undefined) { return ''; diff --git a/src/utils/nodes.ts b/src/utils/nodes.ts index ef57d34b5..25d0eb556 100644 --- a/src/utils/nodes.ts +++ b/src/utils/nodes.ts @@ -8,7 +8,10 @@ import type {TNodeInfo} from '../types/api/nodesList'; import type {NodeHostsMap} from '../types/store/nodesList'; import {HOUR_IN_SECONDS} from './constants'; -import {calcUptime} from './dataFormatters/dataFormatters'; +import { + getDowntimeFromDateFormatted, + getUptimeFromDateFormatted, +} from './dataFormatters/dataFormatters'; import {valueIsDefined} from '.'; @@ -63,15 +66,21 @@ export interface PreparedNodeSystemState extends TSystemStateInfo { SharedCacheUsed?: number; } -export const prepareNodeSystemState = ( +export function prepareNodeSystemState( systemState: TSystemStateInfo = {}, -): PreparedNodeSystemState => { +): PreparedNodeSystemState { // There is no Rack in Location field for din nodes const Rack = systemState.Location?.Rack || systemState.Rack; const DC = systemState.Location?.DataCenter || systemState.DataCenter; const TenantName = systemState?.Tenants?.[0]; - const Uptime = calcUptime(systemState.StartTime); + let Uptime: string; + + if (systemState.DisconnectTime) { + Uptime = getDowntimeFromDateFormatted(systemState.DisconnectTime); + } else { + Uptime = getUptimeFromDateFormatted(systemState.StartTime); + } const LoadAveragePercents = calculateLoadAveragePercents(systemState); @@ -91,7 +100,7 @@ export const prepareNodeSystemState = ( SharedCacheLimit, SharedCacheUsed, }; -}; +} export const getProblemParamValue = (problemFilter: ProblemFilterValue | undefined) => { return problemFilter === ProblemFilterValues.PROBLEMS; From 1c43556b0e9dbdca20bf39024cc8a2f746dbe136 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 10 Dec 2024 13:38:21 +0300 Subject: [PATCH 2/2] fix: do not display -0 downtime, add downtime tests --- .../dataFormatters/__test__/formatUptime.test.ts | 13 ++++++++++++- src/utils/dataFormatters/dataFormatters.ts | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/utils/dataFormatters/__test__/formatUptime.test.ts b/src/utils/dataFormatters/__test__/formatUptime.test.ts index 6668f5114..50b2dd1d4 100644 --- a/src/utils/dataFormatters/__test__/formatUptime.test.ts +++ b/src/utils/dataFormatters/__test__/formatUptime.test.ts @@ -1,4 +1,4 @@ -import {getUptimeFromDateFormatted} from '../dataFormatters'; +import {getDowntimeFromDateFormatted, getUptimeFromDateFormatted} from '../dataFormatters'; describe('getUptimeFromDateFormatted', () => { it('should calculate and format uptime', () => { @@ -8,3 +8,14 @@ describe('getUptimeFromDateFormatted', () => { expect(getUptimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00'); }); }); +describe('getDowntimeFromDateFormatted', () => { + it('should calculate and format downtime as -uptime', () => { + expect(getDowntimeFromDateFormatted(3_600_000, 7_200_000)).toBe('-1:00:00'); + }); + it('should not add sign if downtime is 0', () => { + expect(getDowntimeFromDateFormatted(3_600_000, 3_600_000)).toBe('0:00:00'); + }); + it('should return 0 if dateFrom after dateTo', () => { + expect(getDowntimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00'); + }); +}); diff --git a/src/utils/dataFormatters/dataFormatters.ts b/src/utils/dataFormatters/dataFormatters.ts index 77deb7d94..326d13e68 100644 --- a/src/utils/dataFormatters/dataFormatters.ts +++ b/src/utils/dataFormatters/dataFormatters.ts @@ -66,7 +66,16 @@ export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: } export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) { - return '-' + getUptimeFromDateFormatted(dateFrom, dateTo); + let diff = calcTimeDiffInSec(dateFrom, dateTo); + + // Our time and server time could differ a little + // Prevent wrong negative uptime values + diff = diff < 0 ? 0 : diff; + + const formattedUptime = formatUptimeInSeconds(diff); + + // Do not add sign to 0 values to prevent -0:00:00 uptime + return diff === 0 ? formattedUptime : '-' + formattedUptime; } export function calcTimeDiffInSec(