Skip to content

Commit 3991c56

Browse files
feat: show negative uptime for nodes when disconnected (#1740)
1 parent bc8acee commit 3991c56

File tree

10 files changed

+84
-29
lines changed

10 files changed

+84
-29
lines changed

src/components/TooltipsContent/TabletTooltipContent/TabletTooltipContent.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type {TTabletStateInfo} from '../../../types/api/tablet';
2-
import {calcUptime} from '../../../utils/dataFormatters/dataFormatters';
2+
import {getUptimeFromDateFormatted} from '../../../utils/dataFormatters/dataFormatters';
33
import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer';
44

55
const formatTablet = createInfoFormatter<TTabletStateInfo>({
66
values: {
7-
ChangeTime: (value) => calcUptime(value),
7+
ChangeTime: (value) => getUptimeFromDateFormatted(value),
88
},
99
labels: {
1010
TabletId: 'Tablet',

src/components/nodesColumns/columns.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export function getUptimeColumn<T extends {StartTime?: string; Uptime?: string}>
110110
sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
111111
render: ({row}) => row.Uptime,
112112
align: DataTable.RIGHT,
113-
width: 110,
113+
width: 120,
114114
};
115115
}
116116

src/components/nodesColumns/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export const NODES_COLUMNS_TO_DATA_FIELDS: Record<NodesColumnId, NodesRequiredFi
177177
DC: ['DC'],
178178
Rack: ['Rack'],
179179
Version: ['Version'],
180-
Uptime: ['Uptime'],
180+
Uptime: ['Uptime', 'DisconnectTime'],
181181
Memory: ['Memory', 'MemoryDetailed'],
182182
RAM: ['Memory'],
183183
Pools: ['CPU'],

src/containers/Tablet/components/TabletInfo/TabletInfo.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {selectIsUserAllowedToMakeChanges} from '../../../../store/reducers/authe
1010
import {ETabletState} from '../../../../types/api/tablet';
1111
import type {TTabletStateInfo} from '../../../../types/api/tablet';
1212
import {cn} from '../../../../utils/cn';
13-
import {calcUptime} from '../../../../utils/dataFormatters/dataFormatters';
13+
import {getUptimeFromDateFormatted} from '../../../../utils/dataFormatters/dataFormatters';
1414
import {createTabletDeveloperUIHref} from '../../../../utils/developerUI/developerUI';
1515
import {useTypedSelector} from '../../../../utils/hooks';
1616
import {getDefaultNodePath} from '../../../Node/NodePages';
@@ -70,7 +70,10 @@ export const TabletInfo = ({tablet}: TabletInfoProps) => {
7070
tabletInfo.push({label: tabletInfoKeyset('field_state'), value: <TabletState state={State} />});
7171

7272
if (hasUptime) {
73-
tabletInfo.push({label: tabletInfoKeyset('field_uptime'), value: calcUptime(ChangeTime)});
73+
tabletInfo.push({
74+
label: tabletInfoKeyset('field_uptime'),
75+
value: getUptimeFromDateFormatted(ChangeTime),
76+
});
7477
}
7578

7679
tabletInfo.push(

src/containers/Tablet/components/TabletTable/TabletTable.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {InternalLink} from '../../../../components/InternalLink/InternalLink';
66
import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
77
import {TabletState} from '../../../../components/TabletState/TabletState';
88
import type {ITabletPreparedHistoryItem} from '../../../../types/store/tablet';
9-
import {calcUptime} from '../../../../utils/dataFormatters/dataFormatters';
9+
import {getUptimeFromDateFormatted} from '../../../../utils/dataFormatters/dataFormatters';
1010
import {getDefaultNodePath} from '../../../Node/NodePages';
1111

1212
const TABLET_COLUMNS_WIDTH_LS_KEY = 'tabletTableColumnsWidth';
@@ -21,7 +21,7 @@ const columns: Column<ITabletPreparedHistoryItem>[] = [
2121
name: 'Change time',
2222
align: DataTable.RIGHT,
2323
sortable: false,
24-
render: ({row}) => calcUptime(row.changeTime),
24+
render: ({row}) => getUptimeFromDateFormatted(row.changeTime),
2525
},
2626
{
2727
name: 'State',

src/containers/Tablets/TabletsTable.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {tabletApi} from '../../store/reducers/tablet';
1414
import {ETabletState} from '../../types/api/tablet';
1515
import type {TTabletStateInfo} from '../../types/api/tablet';
1616
import {DEFAULT_TABLE_SETTINGS, EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
17-
import {calcUptime} from '../../utils/dataFormatters/dataFormatters';
17+
import {getUptimeFromDateFormatted} from '../../utils/dataFormatters/dataFormatters';
1818
import {useTypedSelector} from '../../utils/hooks';
1919
import {getDefaultNodePath} from '../Node/NodePages';
2020

@@ -97,7 +97,7 @@ function getColumns({database}: {database?: string}) {
9797
return i18n('Uptime');
9898
},
9999
render: ({row}) => {
100-
return calcUptime(row.ChangeTime);
100+
return getUptimeFromDateFormatted(row.ChangeTime);
101101
},
102102
sortAccessor: (row) => -Number(row.ChangeTime),
103103
align: 'right',

src/store/reducers/nodes/selectors.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {HOUR_IN_SECONDS} from '../../../utils/constants';
2-
import {calcUptimeInSeconds} from '../../../utils/dataFormatters/dataFormatters';
2+
import {calcTimeDiffInSec} from '../../../utils/dataFormatters/dataFormatters';
33
import {NodesUptimeFilterValues} from '../../../utils/nodes';
44

55
// ==== Filters ====
@@ -12,6 +12,6 @@ export const filterNodesByUptime = <T extends {StartTime?: string}>(
1212
return nodesList;
1313
}
1414
return nodesList.filter(({StartTime}) => {
15-
return !StartTime || calcUptimeInSeconds(StartTime) < HOUR_IN_SECONDS;
15+
return !StartTime || calcTimeDiffInSec(StartTime) < HOUR_IN_SECONDS;
1616
});
1717
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {getDowntimeFromDateFormatted, getUptimeFromDateFormatted} from '../dataFormatters';
2+
3+
describe('getUptimeFromDateFormatted', () => {
4+
it('should calculate and format uptime', () => {
5+
expect(getUptimeFromDateFormatted(3_600_000, 7_200_000)).toBe('1:00:00');
6+
});
7+
it('should return 0 if dateFrom after dateTo', () => {
8+
expect(getUptimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00');
9+
});
10+
});
11+
describe('getDowntimeFromDateFormatted', () => {
12+
it('should calculate and format downtime as -uptime', () => {
13+
expect(getDowntimeFromDateFormatted(3_600_000, 7_200_000)).toBe('-1:00:00');
14+
});
15+
it('should not add sign if downtime is 0', () => {
16+
expect(getDowntimeFromDateFormatted(3_600_000, 3_600_000)).toBe('0:00:00');
17+
});
18+
it('should return 0 if dateFrom after dateTo', () => {
19+
expect(getDowntimeFromDateFormatted(3_600_000, 3_599_000)).toBe('0:00:00');
20+
});
21+
});

src/utils/dataFormatters/dataFormatters.ts

+34-12
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const stringifyVdiskId = (id?: TVDiskID | TVSlotId) => {
4040
return id ? Object.values(id).join('-') : '';
4141
};
4242

43-
export const formatUptime = (seconds: number) => {
43+
export const formatUptimeInSeconds = (seconds: number) => {
4444
const days = Math.floor(seconds / DAY_IN_SECONDS);
4545
const remain = seconds % DAY_IN_SECONDS;
4646

@@ -52,9 +52,41 @@ export const formatUptime = (seconds: number) => {
5252
};
5353

5454
export const formatMsToUptime = (ms?: number) => {
55-
return ms && formatUptime(ms / 1000);
55+
return ms && formatUptimeInSeconds(ms / 1000);
5656
};
5757

58+
export function getUptimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) {
59+
let diff = calcTimeDiffInSec(dateFrom, dateTo);
60+
61+
// Our time and server time could differ a little
62+
// Prevent wrong negative uptime values
63+
diff = diff < 0 ? 0 : diff;
64+
65+
return formatUptimeInSeconds(diff);
66+
}
67+
68+
export function getDowntimeFromDateFormatted(dateFrom?: number | string, dateTo?: number | string) {
69+
let diff = calcTimeDiffInSec(dateFrom, dateTo);
70+
71+
// Our time and server time could differ a little
72+
// Prevent wrong negative uptime values
73+
diff = diff < 0 ? 0 : diff;
74+
75+
const formattedUptime = formatUptimeInSeconds(diff);
76+
77+
// Do not add sign to 0 values to prevent -0:00:00 uptime
78+
return diff === 0 ? formattedUptime : '-' + formattedUptime;
79+
}
80+
81+
export function calcTimeDiffInSec(
82+
dateFrom?: number | string,
83+
dateTo: number | string = new Date().getTime(),
84+
) {
85+
const diffMs = Number(dateTo) - Number(dateFrom);
86+
87+
return diffMs / 1000;
88+
}
89+
5890
export function formatStorageValues(
5991
value?: number,
6092
total?: number,
@@ -175,16 +207,6 @@ export const formatTimestamp = (value?: string | number, defaultValue = '') => {
175207
return formattedData ?? defaultValue;
176208
};
177209

178-
export const calcUptimeInSeconds = (milliseconds: number | string) => {
179-
const currentDate = new Date();
180-
const diff = currentDate.getTime() - Number(milliseconds);
181-
return diff <= 0 ? 0 : diff / 1000;
182-
};
183-
184-
export const calcUptime = (milliseconds?: number | string) => {
185-
return formatUptime(calcUptimeInSeconds(Number(milliseconds)));
186-
};
187-
188210
export function getStringifiedData(value: unknown) {
189211
if (value === undefined) {
190212
return '';

src/utils/nodes.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import type {TNodeInfo} from '../types/api/nodesList';
88
import type {NodeHostsMap} from '../types/store/nodesList';
99

1010
import {HOUR_IN_SECONDS} from './constants';
11-
import {calcUptime} from './dataFormatters/dataFormatters';
11+
import {
12+
getDowntimeFromDateFormatted,
13+
getUptimeFromDateFormatted,
14+
} from './dataFormatters/dataFormatters';
1215

1316
import {valueIsDefined} from '.';
1417

@@ -63,15 +66,21 @@ export interface PreparedNodeSystemState extends TSystemStateInfo {
6366
SharedCacheUsed?: number;
6467
}
6568

66-
export const prepareNodeSystemState = (
69+
export function prepareNodeSystemState(
6770
systemState: TSystemStateInfo = {},
68-
): PreparedNodeSystemState => {
71+
): PreparedNodeSystemState {
6972
// There is no Rack in Location field for din nodes
7073
const Rack = systemState.Location?.Rack || systemState.Rack;
7174
const DC = systemState.Location?.DataCenter || systemState.DataCenter;
7275
const TenantName = systemState?.Tenants?.[0];
7376

74-
const Uptime = calcUptime(systemState.StartTime);
77+
let Uptime: string;
78+
79+
if (systemState.DisconnectTime) {
80+
Uptime = getDowntimeFromDateFormatted(systemState.DisconnectTime);
81+
} else {
82+
Uptime = getUptimeFromDateFormatted(systemState.StartTime);
83+
}
7584

7685
const LoadAveragePercents = calculateLoadAveragePercents(systemState);
7786

@@ -91,7 +100,7 @@ export const prepareNodeSystemState = (
91100
SharedCacheLimit,
92101
SharedCacheUsed,
93102
};
94-
};
103+
}
95104

96105
export const getProblemParamValue = (problemFilter: ProblemFilterValue | undefined) => {
97106
return problemFilter === ProblemFilterValues.PROBLEMS;

0 commit comments

Comments
 (0)