[CB] application freezes during loading on heavy resultsets (ORIGINAL FIX) (#3717)

* dbeaver/pro#6731 enables virtualization always

* removes default virtualization prop

* [CB] application freezes during loading on heavy resultsets (OPTIONAL FIXES) (#3715)

* dbeaver/pro#6731 adds some optimizations

* cleanup

* pr fixes

* cleanup

* removes optimizations

* linter fixes
This commit is contained in:
sergeyteleshev
2025-09-10 21:06:54 +08:00
committed by GitHub
parent ab813e21ab
commit d4e2f2365a
8 changed files with 54 additions and 45 deletions
@@ -1,5 +1,20 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { DataGrid as DataGridBase, type ColumnOrColumnGroup, type CellSelectArgs, type DataGridHandle, type ColumnWidth, type ColumnWidths } from 'react-data-grid';
import {
DataGrid as DataGridBase,
type ColumnOrColumnGroup,
type CellSelectArgs,
type DataGridHandle,
type ColumnWidth,
type ColumnWidths,
} from 'react-data-grid';
import { rowRenderer } from './renderers/rowRenderer.js';
import { cellRenderer } from './renderers/cellRenderer.js';
import { DataGridCellHeaderContext, type IDataGridHeaderCellContext } from './DataGridHeaderCellContext.js';
@@ -111,9 +126,12 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
innerGridRef.current?.scrollToCell({ idx: position.colIdx && dndHeaderContext.getDataColIdx(position.colIdx), rowIdx: position.rowIdx });
},
openEditor: (position: ICellPosition) => {
innerGridRef.current?.selectCell({ idx: dndHeaderContext.getDataColIdx(position.colIdx), rowIdx: position.rowIdx }, {
enableEditor: true,
});
innerGridRef.current?.selectCell(
{ idx: dndHeaderContext.getDataColIdx(position.colIdx), rowIdx: position.rowIdx },
{
enableEditor: true,
},
);
},
}));
@@ -126,7 +144,7 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
}
}
let rows = useMemo(
const rows = useMemo(
() =>
new Array<IInnerRow>(rowsCount).fill({ idx: 0 }).map((_, i) => ({
idx: i,
@@ -142,21 +160,16 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
onCellKeyDown?.({ colIdx: dndHeaderContext.getDataColIdx(args.column.idx), rowIdx: args.rowIdx }, event);
}
const isMeasurementRender = columnWidths.size === 0 && columnCount.get() > 0;
if (isMeasurementRender) {
rows = rows.slice(0, 100);
}
return (
<HeaderDnDContext value={dndHeaderContext}>
<DataGridRowContext value={{ rowCount, onScrollToBottom }}>
<DataGridCellContext value={{ cell, cellText, cellElement, cellTooltip, onCellChange }}>
<DataGridCellHeaderContext value={{ headerElement, headerText, getHeaderDnD, columnSortable, onColumnSort, columnSortingState, onHeaderKeyDown }}>
<DataGridCellHeaderContext
value={{ headerElement, headerText, getHeaderDnD, columnSortable, onColumnSort, columnSortingState, onHeaderKeyDown }}
>
<DataGridBase
ref={innerGridRef}
columns={dndHeaderContext.columns}
enableVirtualization={!isMeasurementRender}
rows={rows}
className={className}
headerRowHeight={getHeaderHeight?.()}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -71,14 +71,14 @@ export class LocalizationService extends Bootstrap {
this.localeProviders.push(provider);
}
setSupportedLanguages(locales: ILocale[]) {
setSupportedLanguages(locales: ILocale[]): void {
this.supportedLanguages = locales;
if (this.supportedLanguages.length === 0) {
this.supportedLanguages = [DEFAULT_LOCALE];
}
}
setLanguage(lang: string) {
setLanguage(lang: string): void {
this.language = lang;
}
@@ -142,7 +142,6 @@ export class LocalizationService extends Bootstrap {
name: 'Vietnamese',
nativeName: 'Tiếng Việt',
},
]);
this.addProvider(this.coreProvider.bind(this));
}
@@ -31,7 +31,7 @@ export class ResultSetViewAction extends DatabaseDataAction<any, IDatabaseResult
}
get columnKeys(): IResultSetColumnKey[] {
return this.columns.map(c => ({ index: this.data.columns.indexOf(c) }));
return this.columnsOrder.map(index => ({ index }));
}
get rows(): IResultSetValue[][] {
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -197,7 +197,7 @@ export const TableViewer = observer<TableViewerProps, HTMLDivElement>(
const isStatistics = result?.loadedFully && !result.data;
const resultExist = dataModel.source.hasResult(resultIndex);
const overlay = dataModel.source.results.length > 0 && presentation.dataFormat === dataFormat;
const overlay = getComputed(() => dataModel.source.results.length > 0 && presentation.dataFormat === dataFormat);
const valuePanelDisplayed =
valuePresentation &&
(valuePresentation.dataFormat === undefined || valuePresentation.dataFormat === dataFormat) &&
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -15,9 +15,9 @@ export function navigationTreeDuplicateFilter(navNodeViewService: NavNodeViewSer
const { nodes, duplicates } = navNodeViewService.filterDuplicates(children);
untracked(() => {
navNodeViewService.logDuplicates(node.id, duplicates);
navNodeViewService.logDuplicates(node.id, Array.from(duplicates));
});
return nodes;
return Array.from(nodes);
};
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -13,8 +13,8 @@ import { NotificationService } from '@cloudbeaver/core-events';
import type { INavNodeFolderTransform, NavNodeFolderTransformFn, NavNodeTransformView } from './IFolderTransform.js';
export interface INodeDuplicateList {
nodes: string[];
duplicates: string[];
nodes: Set<string>;
duplicates: Set<string>;
}
export interface INodeLimitedList {
@@ -59,10 +59,10 @@ export class NavNodeViewService {
const { nodes, duplicates } = this.filterDuplicates(children);
untracked(() => {
this.logDuplicates(nodeId, duplicates);
this.logDuplicates(nodeId, Array.from(duplicates));
});
return nodes;
return Array.from(nodes);
},
});
}
@@ -80,25 +80,24 @@ export class NavNodeViewService {
}
filterDuplicates(nodes: string[]): INodeDuplicateList {
const seen = new Set<string>();
const duplicatesSet = new Set<string>();
const nextChildren: string[] = [];
const duplicates: string[] = [];
for (const child of nodes) {
const isDuplicate = duplicates.includes(child);
if (nextChildren.includes(child) || isDuplicate) {
if (!isDuplicate) {
duplicates.push(child);
nextChildren.splice(nextChildren.indexOf(child), 1);
}
if (seen.has(child)) {
duplicatesSet.add(child);
} else {
seen.add(child);
nextChildren.push(child);
}
}
const uniqueChildren = new Set(nextChildren.filter(child => !duplicatesSet.has(child)));
return {
nodes: nextChildren,
duplicates,
nodes: uniqueChildren,
duplicates: duplicatesSet,
};
}
@@ -35,12 +35,10 @@ export const VirtualFolderPanel: NavNodeTransformViewComponent = observer(functi
const allData = dbObjectLoader.resource.get(pagination.allPages).filter(isDefined);
const { nodes, duplicates } = navNodeViewService.filterDuplicates(allData.map(node => node?.id) || []);
const objects = allData.filter(
object => object && nodes.includes(object.id) && navNodeInfoResource.get(object.id)?.nodeType === nodeType,
) as DBObject[];
const objects = allData.filter(object => object && nodes.has(object.id) && navNodeInfoResource.get(object.id)?.nodeType === nodeType) as DBObject[];
useEffect(() => {
navNodeViewService.logDuplicates(nodeId, duplicates);
navNodeViewService.logDuplicates(nodeId, Array.from(duplicates));
});
return (
@@ -38,15 +38,15 @@ export const ObjectPropertyTable = observer<ObjectPropertyTableProps>(function O
const allData = dbObjectLoader.resource.get(pagination.allPages).filter(isDefined);
const { nodes, duplicates } = navNodeViewService.filterDuplicates(allData.map(node => node?.id) || []);
const objects = allData.filter(node => nodes.includes(node.id)) as DBObject[];
const objects = allData.filter(node => nodes.has(node.id)) as DBObject[];
useEffect(() => {
navNodeViewService.logDuplicates(objectId, duplicates);
navNodeViewService.logDuplicates(objectId, Array.from(duplicates));
});
return (
<>
{nodes.length === 0 ? (
{nodes.size === 0 ? (
<TextPlaceholder>{translate('plugin_object_viewer_table_no_items')}</TextPlaceholder>
) : (
<div className={s(styles, { box: true }, className)}>