Skip to content

Commit

Permalink
CB-5004 Do not spam server while typing query in Sql Editor (#2615)
Browse files Browse the repository at this point in the history
* CB-5004 adds debounce for sql editor script set and auto complete

* CB-5004 adds correct debounce time for parse script + parses script before its execution

* CB-5004 disable script buttons only for empty sql editor or script being executing

* CB-5004 fix: executes script parsing before get sub query

* CB-5004 reverts getHintProposals throttling

* CB-5004 fix: force launch sql editor script actions and removes delay from format script button

---------

Co-authored-by: Alexey <[email protected]>
Co-authored-by: Daria Marutkina <[email protected]>
Co-authored-by: mr-anton-t <[email protected]>
  • Loading branch information
4 people authored May 27, 2024
1 parent 965d96c commit 1133112
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 17 deletions.
20 changes: 18 additions & 2 deletions webapp/packages/core-utils/src/debounce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { debounce } from './debounce';
import { debounce, debounceAsync } from './debounce';

// https://jestjs.io/docs/timer-mocks
// Tell Jest to mock all timeout functions
Expand All @@ -23,6 +23,22 @@ describe('Debounce', () => {
// Fast-forward time
jest.runAllTimers();

expect(func).toBeCalledTimes(1);
expect(func).toHaveBeenCalledTimes(1);
});
});

describe('DebounceAsync', () => {
test('function should be executed just once', async () => {
const func = jest.fn(() => Promise.resolve(true));
const debouncedFunction = debounceAsync(func, 1000);

debouncedFunction();
debouncedFunction();
debouncedFunction();

// Fast-forward time
jest.runAllTimers();

expect(func).toHaveBeenCalledTimes(1);
});
});
24 changes: 24 additions & 0 deletions webapp/packages/core-utils/src/debounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,27 @@ export function debounce<T extends (...args: any[]) => any>(func: T, delay: numb
}, delay);
};
}

export function debounceAsync<T extends (...args: any[]) => Promise<any>>(func: T, delay: number): T {
let timeoutId: NodeJS.Timeout | null;

return function (this: any, ...args: Parameters<T>): Promise<ReturnType<T>> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;

return new Promise((resolve, reject) => {
if (timeoutId) {
clearTimeout(timeoutId);
}

timeoutId = setTimeout(async () => {
try {
const result = await func.apply(context, args);
resolve(result);
} catch (error) {
reject(error);
}
}, delay);
});
} as T;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export interface ISQLEditorData {
readonly cursorSegment: ISQLScriptSegment | undefined;
readonly readonly: boolean;
readonly editing: boolean;
readonly isLineScriptEmpty: boolean;
readonly isScriptEmpty: boolean;
readonly isDisabled: boolean;
readonly isIncomingChanges: boolean;
Expand All @@ -42,7 +41,7 @@ export interface ISQLEditorData {
/** displays if last getHintProposals call ended with limit */
readonly hintsLimitIsMet: boolean;

updateParserScriptsThrottle(): Promise<void>;
updateParserScriptsDebounced(): Promise<void>;
setScript(query: string): void;
init(): void;
destruct(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const SQLEditorActions = observer<Props>(function SQLEditorActions({ data
const styles = useS(style);
const translate = useTranslate();
const isActiveSegmentMode = getComputed(() => data.activeSegmentMode.activeSegmentMode);
const disabled = getComputed(() => data.isLineScriptEmpty || data.isDisabled);
const disabled = getComputed(() => data.isScriptEmpty || data.isDisabled);
const isQuery = data.dataSource?.hasFeature(ESqlDataSourceFeatures.query);
const isExecutable = data.dataSource?.hasFeature(ESqlDataSourceFeatures.executable);

Expand Down
29 changes: 17 additions & 12 deletions webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dial
import { NotificationService } from '@cloudbeaver/core-events';
import { SyncExecutor } from '@cloudbeaver/core-executor';
import type { SqlCompletionProposal, SqlDialectInfo, SqlScriptInfoFragment } from '@cloudbeaver/core-sdk';
import { createLastPromiseGetter, LastPromiseGetter, throttleAsync } from '@cloudbeaver/core-utils';
import { createLastPromiseGetter, debounceAsync, LastPromiseGetter, throttleAsync } from '@cloudbeaver/core-utils';

import type { ISqlEditorTabState } from '../ISqlEditorTabState';
import { ESqlDataSourceFeatures } from '../SqlDataSource/ESqlDataSourceFeatures';
Expand Down Expand Up @@ -104,12 +104,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
return this.dataSource?.isEditing() ?? false;
},

get isLineScriptEmpty(): boolean {
return !this.activeSegment?.query;
},

get isScriptEmpty(): boolean {
return this.value === '' || this.parser.scripts.length === 0;
return this.value === '';
},

get isDisabled(): boolean {
Expand Down Expand Up @@ -166,7 +162,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
untracked(() => {
this.sqlDialectInfoService.loadSqlDialectInfo(key).then(async () => {
try {
await this.updateParserScriptsThrottle();
await this.updateParserScriptsDebounced();
} catch {}
});
});
Expand Down Expand Up @@ -205,13 +201,14 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
this.hintsLimitIsMet = hints.length >= MAX_HINTS_LIMIT;

return hints;
}, 1000 / 3),
}, 300),

async formatScript(): Promise<void> {
if (this.isDisabled || this.isScriptEmpty || !this.dataSource?.executionContext) {
return;
}

await this.updateParserScripts();
const query = this.value;
const script = this.getExecutingQuery(false);

Expand All @@ -238,6 +235,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
if (!isQuery || !isExecutable) {
return;
}

await this.updateParserScripts();
const query = this.getSubQuery();

try {
Expand Down Expand Up @@ -269,6 +268,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
if (!isQuery || !isExecutable) {
return;
}

await this.updateParserScripts();
const query = this.getSubQuery();

try {
Expand All @@ -286,6 +287,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
return;
}

await this.updateParserScripts();
const query = this.getSubQuery();

try {
Expand Down Expand Up @@ -362,9 +364,9 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
this.dataSource?.setScript(query);
},

updateParserScriptsThrottle: throttleAsync(async function updateParserScriptsThrottle() {
updateParserScriptsDebounced: debounceAsync(async function updateParserScriptsThrottle() {
await data.updateParserScripts();
}, 1000 / 2),
}, 2000),

async updateParserScripts() {
if (!this.dataSource?.hasFeature(ESqlDataSourceFeatures.script)) {
Expand Down Expand Up @@ -402,7 +404,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
passEmpty?: boolean,
passDisabled?: boolean,
): Promise<T | undefined> {
if (!segment || (this.isDisabled && !passDisabled) || (!passEmpty && this.isLineScriptEmpty)) {
if (!segment || (this.isDisabled && !passDisabled) || (!passEmpty && this.isScriptEmpty)) {
return;
}

Expand Down Expand Up @@ -433,6 +435,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
const projectId = this.dataSource?.executionContext?.projectId;
const connectionId = this.dataSource?.executionContext?.connectionId;

await data.updateParserScripts();

if (!projectId || !connectionId || this.cursor.begin !== this.cursor.end) {
return this.getSubQuery();
}
Expand Down Expand Up @@ -469,6 +473,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
},
}),
{
getHintProposals: action.bound,
formatScript: action.bound,
executeQuery: action.bound,
executeQueryNewTab: action.bound,
Expand Down Expand Up @@ -507,7 +512,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
// ensure that cursor is in script boundaries
data.setCursor(data.cursor.begin, data.cursor.end);
data.parser.setScript(script);
data.updateParserScriptsThrottle().catch(() => {});
data.updateParserScriptsDebounced().catch(() => {});
data.onUpdate.execute();
},
],
Expand Down

0 comments on commit 1133112

Please sign in to comment.