Skip to content

Commit a6b68f8

Browse files
authored
fix(query-orchestrator): Reduce number of refresh key queries (#9809)
Cube generates a burst load of queries for refresh keys. We don't use a queue for cube store queries, but we use an in-memory cache inside QueryQueue. A cache miss leads to a problem where we start executing queries without debouncing. Cube executes refresh keys in parallel, which DDoSes Cube Store. It's a quick fix to fix this behavior.
1 parent eb56169 commit a6b68f8

File tree

3 files changed

+31
-26
lines changed

3 files changed

+31
-26
lines changed

packages/cubejs-backend-shared/src/promises.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export const retryWithTimeout = <T>(
265265
);
266266

267267
/**
268-
* High order function that makes to debounce multi async calls to single call at one time
268+
* Creates a debounced version of an asynchronous function.
269269
*/
270270
export const asyncDebounce = <Ret, Arguments>(
271271
fn: (...args: Arguments[]) => Promise<Ret>,

packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import crypto from 'crypto';
22
import csvWriter from 'csv-write-stream';
33
import { LRUCache } from 'lru-cache';
44
import { pipeline } from 'stream';
5-
import { getEnv, MaybeCancelablePromise, streamToArray } from '@cubejs-backend/shared';
5+
import { asyncDebounce, getEnv, MaybeCancelablePromise, streamToArray } from '@cubejs-backend/shared';
66
import { CubeStoreCacheDriver, CubeStoreDriver } from '@cubejs-backend/cubestore-driver';
77
import {
88
BaseDriver,
@@ -34,6 +34,12 @@ export type QueryWithParams = [
3434
options?: QueryOptions
3535
];
3636

37+
export type LoadRefreshKeyOptions = {
38+
requestId?: string;
39+
skipRefreshKeyWaitForRenew?: boolean;
40+
dataSource: string
41+
};
42+
3743
export type Query = {
3844
requestId?: string;
3945
dataSource: string;
@@ -771,32 +777,31 @@ export class QueryCache {
771777
public loadRefreshKeys(
772778
cacheKeyQueries: QueryWithParams[],
773779
expireSecs: number,
774-
options: {
775-
requestId?: string;
776-
skipRefreshKeyWaitForRenew?: boolean;
777-
dataSource: string
778-
}
780+
options: LoadRefreshKeyOptions
779781
) {
780-
return cacheKeyQueries.map((q) => {
781-
const [query, values, queryOptions]: QueryWithParams = Array.isArray(q) ? q : [q, [], {}];
782-
return this.cacheQueryResult(
783-
query,
784-
values,
785-
[query, values],
786-
expireSecs,
787-
{
788-
renewalThreshold: this.options.refreshKeyRenewalThreshold || queryOptions?.renewalThreshold || 2 * 60,
789-
renewalKey: q,
790-
waitForRenew: !options.skipRefreshKeyWaitForRenew,
791-
requestId: options.requestId,
792-
dataSource: options.dataSource,
793-
useInMemory: true,
794-
external: queryOptions?.external,
795-
},
796-
);
797-
});
782+
return cacheKeyQueries.map((q) => this.loadRefreshKey(q, expireSecs, options));
798783
}
799784

785+
public loadRefreshKey = asyncDebounce(async (q: QueryWithParams, expireSecs: number, options: LoadRefreshKeyOptions) => {
786+
const [query, values, queryOptions]: QueryWithParams = Array.isArray(q) ? q : [q, [], {}];
787+
788+
return this.cacheQueryResult(
789+
query,
790+
values,
791+
[query, values],
792+
expireSecs,
793+
{
794+
renewalThreshold: this.options.refreshKeyRenewalThreshold || queryOptions?.renewalThreshold || 2 * 60,
795+
renewalKey: q,
796+
waitForRenew: !options.skipRefreshKeyWaitForRenew,
797+
requestId: options.requestId,
798+
dataSource: options.dataSource,
799+
useInMemory: true,
800+
external: queryOptions?.external,
801+
},
802+
);
803+
});
804+
800805
public withLock = <T = any>(
801806
key: string,
802807
ttl: number,

packages/cubejs-testing/test/smoke-cubesql.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ describe('SQL API', () => {
156156
Authorization: token,
157157
},
158158
body: JSON.stringify({
159-
query: `SELECT orderDate FROM ECommerce LIMIT 0;`,
159+
query: 'SELECT orderDate FROM ECommerce LIMIT 0;',
160160
}),
161161
});
162162

0 commit comments

Comments
 (0)