Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions src/containers/Tenant/Diagnostics/Diagnostics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {StorageWrapper} from '../../Storage/StorageWrapper';
import {Tablets} from '../../Tablets';

import Describe from './Describe/Describe';
import HotKeys from './HotKeys/HotKeys';
import {HotKeys} from './HotKeys/HotKeys';
import Network from './Network/Network';
import {Partitions} from './Partitions/Partitions';
import {Consumers} from './Consumers';
Expand Down Expand Up @@ -145,7 +145,7 @@ function Diagnostics(props: DiagnosticsProps) {
return <Describe tenant={tenantNameString} type={type} />;
}
case TENANT_DIAGNOSTICS_TABS_IDS.hotKeys: {
return <HotKeys type={type} />;
return <HotKeys path={currentSchemaPath} />;
}
case TENANT_DIAGNOSTICS_TABS_IDS.graph: {
return <Heatmap path={currentSchemaPath} />;
Expand Down
3 changes: 2 additions & 1 deletion src/containers/Tenant/Diagnostics/DiagnosticsPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const DATABASE_PAGES = [
];

export const TABLE_PAGES = [overview, topShards, nodes, graph, tablets, hotKeys, describe];
export const COLUMN_TABLE_PAGES = [overview, topShards, nodes, graph, tablets, describe];

export const DIR_PAGES = [overview, topShards, nodes, describe];

Expand All @@ -96,7 +97,7 @@ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
[EPathType.EPathTypeColumnStore]: DATABASE_PAGES,

[EPathType.EPathTypeTable]: TABLE_PAGES,
[EPathType.EPathTypeColumnTable]: TABLE_PAGES,
[EPathType.EPathTypeColumnTable]: COLUMN_TABLE_PAGES,

[EPathType.EPathTypeDir]: DIR_PAGES,
[EPathType.EPathTypeTableIndex]: DIR_PAGES,
Expand Down
157 changes: 0 additions & 157 deletions src/containers/Tenant/Diagnostics/HotKeys/HotKeys.js

This file was deleted.

34 changes: 1 addition & 33 deletions src/containers/Tenant/Diagnostics/HotKeys/HotKeys.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
@import '../../../../styles/mixins.scss';

.hot-keys {
display: flex;
overflow: auto;
flex-grow: 1;
flex-direction: column;
align-items: flex-start;

max-height: 100%;

background-color: var(--g-color-base-background);

.ydb-hot-keys {
&__table-content {
overflow: auto;

Expand All @@ -19,28 +9,6 @@
@include freeze-nth-column(1);
}

&__header {
position: sticky;
z-index: 2;
top: 0px;
left: 0;

width: 100%;
padding: 10px 0;

background-color: var(--g-color-base-background);
}
&__loader {
display: flex;
justify-content: center;
align-items: center;

width: 100%;
height: 100%;
}
&__stub {
margin: 10px;
}
&__primary-key-column {
display: flex;
gap: 5px;
Expand Down
143 changes: 143 additions & 0 deletions src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {useEffect, useMemo, useRef} from 'react';
import {useDispatch} from 'react-redux';
import DataTable, {type Column} from '@gravity-ui/react-data-table';

import type {HotKey} from '../../../../types/api/hotkeys';
import type {IResponseError} from '../../../../types/api/error';
import {Icon} from '../../../../components/Icon';
import {ResponseError} from '../../../../components/Errors/ResponseError';
import {useTypedSelector} from '../../../../utils/hooks';
import {cn} from '../../../../utils/cn';
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
import {
setHotKeysData,
setHotKeysDataWasNotLoaded,
setHotKeysError,
setHotKeysLoading,
} from '../../../../store/reducers/hotKeys/hotKeys';

import './HotKeys.scss';
import i18n from './i18n';

const b = cn('ydb-hot-keys');

const tableColumnsIds = {
accessSample: 'accessSample',
keyValues: 'keyValues',
} as const;

const getHotKeysColumns = (keyColumnsIds: string[] = []): Column<HotKey>[] => {
const keysColumns: Column<HotKey>[] = keyColumnsIds.map((col, index) => ({
name: col,
header: (
<div className={b('primary-key-column')}>
<Icon name="key" viewBox="0 0 12 7" width={12} height={7} />
{col}
</div>
),
render: ({row}) => row.keyValues[index],
align: DataTable.RIGHT,
sortable: false,
}));

return [
{
name: tableColumnsIds.accessSample,
header: 'Samples',
render: ({row}) => row.accessSample,
align: DataTable.RIGHT,
sortable: false,
},
...keysColumns,
];
};

interface HotKeysProps {
path: string;
}

export function HotKeys({path}: HotKeysProps) {
const dispatch = useDispatch();

const collectSamplesTimerRef = useRef<ReturnType<typeof setTimeout>>();

const {loading, wasLoaded, data, error} = useTypedSelector((state) => state.hotKeys);
const {loading: schemaLoading, data: schemaData} = useTypedSelector((state) => state.schema);

const keyColumnsIds = schemaData[path]?.PathDescription?.Table?.KeyColumnNames;

const tableColumns = useMemo(() => {
return getHotKeysColumns(keyColumnsIds);
}, [keyColumnsIds]);

useEffect(() => {
const fetchHotkeys = async (enableSampling: boolean) => {
// Set hotkeys error, but not data, since data is set conditionally
try {
const response = await window.api.getHotKeys(path, enableSampling);
return response;
} catch (err) {
dispatch(setHotKeysError(err as IResponseError));
return undefined;
}
};

const fetchData = async () => {
// If there is previous pending request for samples, cancel it
if (collectSamplesTimerRef.current !== undefined) {
window.clearInterval(collectSamplesTimerRef.current);
}

dispatch(setHotKeysDataWasNotLoaded());
dispatch(setHotKeysLoading());
Comment on lines +91 to +92
Copy link
Member Author

Choose a reason for hiding this comment

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

Although there is no autorefresh for hotkeys tab, I made separate actions to keep flow similar to other components


// Send request that will trigger hot keys sampling (enable_sampling = true)
const initialResponse = await fetchHotkeys(true);

// If there are hotkeys in the initial request (hotkeys was collected before)
// we could just use colleted samples (collected hotkeys are stored only for 30 seconds)
if (initialResponse && initialResponse.hotkeys) {
dispatch(setHotKeysData(initialResponse));
} else if (initialResponse) {
// Else wait for 5 seconds, while hot keys are being collected
// And request these samples (enable_sampling = false)
const timer = setTimeout(async () => {
const responseWithSamples = await fetchHotkeys(false);
if (responseWithSamples) {
dispatch(setHotKeysData(responseWithSamples));
}
}, 5000);
collectSamplesTimerRef.current = timer;
}
};
fetchData();
}, [dispatch, path]);

// It takes a while to collect hot keys. Display explicit status message, while collecting
if ((loading && !wasLoaded) || schemaLoading) {
return <div>{i18n('hot-keys-collecting')}</div>;
}

if (error) {
return <ResponseError error={error} />;
}

if (!data) {
return <div>{i18n('no-data')}</div>;
}

return (
<div className={b('table-content')}>
<DataTable
columns={tableColumns}
data={data}
settings={DEFAULT_TABLE_SETTINGS}
theme="yandex-cloud"
initialSortOrder={{
columnId: tableColumnsIds.accessSample,
order: DataTable.DESCENDING,
}}
/>
</div>
);
}
4 changes: 4 additions & 0 deletions src/containers/Tenant/Diagnostics/HotKeys/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"hot-keys-collecting": "Please wait a little while we are collecting hot keys samples...",
"no-data": "No information about hot keys"
}
9 changes: 9 additions & 0 deletions src/containers/Tenant/Diagnostics/HotKeys/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {i18n, Lang} from '../../../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'ydb-hot-keys';

i18n.registerKeyset(Lang.En, COMPONENT, en);

export default i18n.keyset(COMPONENT);
Loading