limit the data size for chart rendering (#14949)

* limit the rows feed to charts

* add telemetry and option to hide

* fix typo

* updates

* comments

* notebook fix
This commit is contained in:
Alan Ren
2021-04-02 19:36:10 -07:00
committed by GitHub
parent 13ad4c9497
commit c2d2cf5a82
11 changed files with 151 additions and 37 deletions

View File

@@ -2,33 +2,34 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/chartView';
import { IPanelView } from 'sql/base/browser/ui/panel/panel';
import { Insight } from './insight';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import { ICellValue, VisualizationOptions } from 'sql/workbench/services/query/common/query';
import { ChartOptions, IChartOption, ControlType } from './chartOptions';
import { Extensions, IInsightRegistry, IInsightData } from 'sql/platform/dashboard/browser/insightRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import * as DOM from 'vs/base/browser/dom';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { CreateInsightAction, CopyAction, SaveImageAction, IChartActionContext, ConfigureChartAction } from 'sql/workbench/contrib/charts/browser/actions';
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { IInsightOptions, ChartType, InsightType } from 'sql/workbench/contrib/charts/common/interfaces';
import { IPanelView } from 'sql/base/browser/ui/panel/panel';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { ITaskbarContent, Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { Extensions, IInsightData, IInsightRegistry } from 'sql/platform/dashboard/browser/insightRegistry';
import { ChartState } from 'sql/workbench/common/editor/query/chartState';
import { ConfigureChartAction, CopyAction, CreateInsightAction, IChartActionContext, SaveImageAction } from 'sql/workbench/contrib/charts/browser/actions';
import { getChartMaxRowCount } from 'sql/workbench/contrib/charts/browser/utils';
import { ChartType, IInsightOptions, InsightType } from 'sql/workbench/contrib/charts/common/interfaces';
import { ICellValue, VisualizationOptions } from 'sql/workbench/services/query/common/query';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import * as DOM from 'vs/base/browser/dom';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { isUndefinedOrNull } from 'vs/base/common/types';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Event, Emitter } from 'vs/base/common/event';
import { Registry } from 'vs/platform/registry/common/platform';
import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ChartOptions, ControlType, IChartOption } from './chartOptions';
import { Insight } from './insight';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
@@ -89,7 +90,8 @@ export class ChartView extends Disposable implements IPanelView {
@IContextViewService private _contextViewService: IContextViewService,
@IThemeService private _themeService: IThemeService,
@IInstantiationService private _instantiationService: IInstantiationService,
@INotificationService private readonly _notificationService: INotificationService
@INotificationService private readonly _notificationService: INotificationService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
super();
this.taskbarContainer = DOM.$('div.taskbar-container');
@@ -195,7 +197,7 @@ export class ChartView extends Disposable implements IPanelView {
public chart(dataId: { batchId: number, resultId: number }) {
this.state.dataId = dataId;
this._currentData = dataId;
this.shouldGraph();
this.fetchData();
}
layout(dimension: DOM.Dimension): void {
@@ -209,7 +211,7 @@ export class ChartView extends Disposable implements IPanelView {
public set queryRunner(runner: QueryRunner) {
this._queryRunner = runner;
this.shouldGraph();
this.fetchData();
}
public setData(rows: ICellValue[][], columns: string[]): void {
@@ -228,7 +230,7 @@ export class ChartView extends Disposable implements IPanelView {
}
}
private shouldGraph() {
private fetchData(): void {
// Check if we have the necessary information
if (this._currentData && this._queryRunner) {
// check if we are being asked to graph something that is available
@@ -236,7 +238,7 @@ export class ChartView extends Disposable implements IPanelView {
if (batch) {
let summary = batch.resultSetSummaries[this._currentData.resultId];
if (summary) {
this._queryRunner.getQueryRows(0, summary.rowCount, this._currentData.batchId, this._currentData.resultId).then(d => {
this._queryRunner.getQueryRows(0, Math.min(getChartMaxRowCount(this._configurationService), summary.rowCount), this._currentData.batchId, this._currentData.resultId).then(d => {
let rows = d.rows;
let columns = summary.columnInfo.map(c => c.columnName);
this.setData(rows, columns);

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Extensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const chartsConfiguration: IConfigurationNode = {
id: 'builtinCharts',
type: 'object',
title: nls.localize('builtinChartsConfigurationTitle', "Built-in Charts"),
properties: {
'builtinCharts.maxRowCount': {
type: 'number',
default: 300,
description: nls.localize('builtinCharts.maxRowCountDescription', "The maximum number of rows for charts to display. Warning: increasing this may impact performance.")
}
}
};
configurationRegistry.registerConfiguration(chartsConfiguration);

View File

@@ -46,3 +46,7 @@ export interface IInsightCtor {
new <Services extends BrandedService[]>(container: HTMLElement, options: IInsightOptions, ...services: Services): IInsight;
readonly types: Array<InsightType | ChartType>;
}
export interface IChartsConfiguration {
readonly maxRowCount: number;
}

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChartsConfiguration } from 'sql/workbench/contrib/charts/browser/interfaces';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
/**
* Gets the max allowed row count for chart rendering.
*/
export function getChartMaxRowCount(configurationService: IConfigurationService): number {
return configurationService.getValue<IChartsConfiguration>('builtinCharts').maxRowCount;
}
/**
* Show a toast notification about the max row count for chart has exceeded.
*/
export function notifyMaxRowCountExceeded(storageService: IStorageService, notificationService: INotificationService, configurationService: IConfigurationService): void {
const storageKey = 'charts/ignoreMaxRowCountExceededNotification';
if (!storageService.getBoolean(storageKey, StorageScope.GLOBAL, false)) {
notificationService.prompt(Severity.Info,
nls.localize('charts.maxAllowedRowsExceeded', "Maximum row count for built-in charts has been exceeded, only the first {0} rows are used. To configure the value, you can open user settings and search for: 'builtinCharts.maxRowCount'.", getChartMaxRowCount(configurationService)),
[{
label: nls.localize('charts.neverShowAgain', "Don't Show Again"),
isSecondary: true,
run: () => {
storageService.store(storageKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}]);
}
}

View File

@@ -11,6 +11,7 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
suite('Chart View', () => {
test('initializes without error', () => {
@@ -40,6 +41,7 @@ function createChartView(isQueryEditorChart: boolean): ChartView {
const themeService = new TestThemeService();
const instantiationService = new TestInstantiationService();
const notificationService = new TestNotificationService();
const configurationService = new TestConfigurationService();
instantiationService.stub(IThemeService, themeService);
return new ChartView(isQueryEditorChart, contextViewService, themeService, instantiationService, notificationService);
return new ChartView(isQueryEditorChart, contextViewService, themeService, instantiationService, notificationService, configurationService);
}