Skip to content

Commit b49e184

Browse files
committed
feat: Show refreshKey values in Playground
1 parent d952aff commit b49e184

File tree

4 files changed

+110
-37
lines changed

4 files changed

+110
-37
lines changed

packages/cubejs-api-gateway/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ class ApiGateway {
322322
res({
323323
query: normalizedQuery,
324324
data: transformData(aliasToMemberNameMap, flattenAnnotation, response.data),
325+
lastRefreshTime: response.lastRefreshTime && response.lastRefreshTime.toISOString(),
326+
refreshKeyValues: process.env.NODE_ENV === 'production' ? undefined : response.refreshKeyValues,
325327
annotation
326328
});
327329
} catch (e) {

packages/cubejs-playground/src/ChartContainer.jsx

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* global navigator */
22
import React from 'react';
33
import {
4-
Card, Button, Menu, Dropdown, Icon, notification, Modal
4+
Card, Button, Menu, Dropdown, Icon, notification, Modal, Table
55
} from 'antd';
66
import { getParameters } from 'codesandbox-import-utils/lib/api/define';
77
import { fetch } from 'whatwg-fetch';
@@ -233,6 +233,18 @@ class ChartContainer extends React.Component {
233233
>
234234
SQL
235235
</Button>
236+
<Button
237+
onClick={() => {
238+
playgroundAction('Show Cache');
239+
this.setState({ showCode: showCode === 'cache' ? null : 'cache' });
240+
}}
241+
icon="sync"
242+
size="small"
243+
type={showCode === 'cache' ? 'primary' : 'default'}
244+
disabled={!!frameworkItem.docsLink}
245+
>
246+
Cache
247+
</Button>
236248
<Button
237249
icon="code-sandbox"
238250
size="small"
@@ -282,6 +294,51 @@ class ChartContainer extends React.Component {
282294
render={({ sqlQuery }) => <PrismCode code={sqlQuery && sqlFormatter.format(sqlQuery.sql())} />}
283295
/>
284296
);
297+
} else if (showCode === 'cache') {
298+
return (
299+
<QueryRenderer
300+
loadSql
301+
query={{ ...query, renewQuery: true }}
302+
cubejsApi={cubejsApi}
303+
render={
304+
({ sqlQuery, resultSet: rs }) => (
305+
<div>
306+
<h3>
307+
Last Refresh Time:
308+
{rs && rs.loadResponse.lastRefreshTime}
309+
</h3>
310+
<Table
311+
loading={!sqlQuery}
312+
pagination={false}
313+
columns={[
314+
{
315+
title: 'Refresh Key SQL',
316+
key: 'refreshKey',
317+
render: (text, record) => <PrismCode code={sqlFormatter.format(record[0])} />,
318+
},
319+
{
320+
title: 'Value',
321+
key: 'value',
322+
render: (text, record) => (
323+
<PrismCode
324+
code={
325+
rs && rs.loadResponse.refreshKeyValues
326+
&& JSON.stringify(rs.loadResponse.refreshKeyValues[
327+
sqlQuery.sqlQuery.sql.cacheKeyQueries.queries.indexOf(record)
328+
], null, 2)
329+
}
330+
/>
331+
),
332+
}
333+
]}
334+
dataSource={
335+
sqlQuery && sqlQuery.sqlQuery.sql && sqlQuery.sqlQuery.sql.cacheKeyQueries.queries
336+
}
337+
/>
338+
</div>
339+
)}
340+
/>
341+
);
285342
}
286343
return render({ resultSet, error, sandboxId });
287344
};

packages/cubejs-query-orchestrator/orchestrator/QueryCache.js

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@ class QueryCache {
1616
new LocalCacheDriver();
1717
}
1818

19-
cachedQueryResult (queryBody, preAggregationsTablesToTempTables) {
20-
const replacePreAggregationTableNames = (queryAndParams) =>
21-
QueryCache.replacePreAggregationTableNames(queryAndParams, preAggregationsTablesToTempTables);
19+
async cachedQueryResult(queryBody, preAggregationsTablesToTempTables) {
20+
const replacePreAggregationTableNames = (queryAndParams) => QueryCache.replacePreAggregationTableNames(
21+
queryAndParams, preAggregationsTablesToTempTables
22+
);
2223

2324
const query = replacePreAggregationTableNames(queryBody.query);
2425
let queuePriority = 10;
2526
if (Number.isInteger(queryBody.queuePriority)) {
27+
// eslint-disable-next-line prefer-destructuring
2628
queuePriority = queryBody.queuePriority;
2729
}
2830
const forceNoCache = queryBody.forceNoCache || false;
29-
const values = queryBody.values;
31+
const { values } = queryBody;
3032
const cacheKeyQueries =
3133
(
3234
queryBody.cacheKeyQueries && queryBody.cacheKeyQueries.queries ||
@@ -39,7 +41,7 @@ class QueryCache {
3941
const expireSecs = queryBody.expireSecs || 24 * 3600;
4042

4143
if (!cacheKeyQueries) {
42-
return this.queryWithRetryAndRelease(query, values, queryBody.external);
44+
return { data: await this.queryWithRetryAndRelease(query, values, queryBody.external) };
4345
}
4446
const cacheKey = QueryCache.queryCacheKey(queryBody);
4547

@@ -67,22 +69,25 @@ class QueryCache {
6769
});
6870
}
6971

70-
return mainPromise;
72+
return {
73+
data: await mainPromise,
74+
lastRefreshTime: await this.lastRefreshTime(cacheKey)
75+
};
7176
}
7277

7378
static queryCacheKey(queryBody) {
7479
return [queryBody.query, queryBody.values, (queryBody.preAggregations || []).map(p => p.loadSql)];
7580
}
7681

7782
static replaceAll(replaceThis, withThis, inThis) {
78-
withThis = withThis.replace(/\$/g,"$$$$");
83+
withThis = withThis.replace(/\$/g, "$$$$");
7984
return inThis.replace(
80-
new RegExp(replaceThis.replace(/([/,!\\^${}[\]().*+?|<>\-&])/g,"\\$&"),"g"),
85+
new RegExp(replaceThis.replace(/([/,!\\^${}[\]().*+?|<>\-&])/g, "\\$&"), "g"),
8186
withThis
8287
);
8388
}
8489

85-
static replacePreAggregationTableNames (queryAndParams, preAggregationsTablesToTempTables) {
90+
static replacePreAggregationTableNames(queryAndParams, preAggregationsTablesToTempTables) {
8691
const [keyQuery, params] = Array.isArray(queryAndParams) ? queryAndParams : [queryAndParams, []];
8792
const replacedKeqQuery = preAggregationsTablesToTempTables.reduce(
8893
(query, [tableName, tempTable]) => QueryCache.replaceAll(tableName, tempTable, query),
@@ -169,9 +174,9 @@ class QueryCache {
169174
query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, options
170175
).catch(e => {
171176
if (!(e instanceof ContinueWaitError)) {
172-
this.logger('Error while renew cycle', { query, query_values: values, error: e.stack || e })
177+
this.logger('Error while renew cycle', { query, query_values: values, error: e.stack || e });
173178
}
174-
})
179+
});
175180
}
176181

177182
renewQuery(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, options) {
@@ -192,30 +197,34 @@ class QueryCache {
192197
this.logger('Error fetching cache key queries', { error: e.stack || e });
193198
return [];
194199
})
195-
.then(cacheKeyQueryResults => {
196-
return this.cacheQueryResult(
197-
query, values,
198-
cacheKey,
199-
expireSecs,
200-
{
201-
renewalThreshold: renewalThreshold || 6 * 60 * 60,
202-
renewalKey: cacheKeyQueryResults && [
203-
cacheKeyQueries, cacheKeyQueryResults, this.queryRedisKey([query, values])
204-
],
205-
waitForRenew: true,
206-
external: options.external
207-
}
208-
);
209-
});
200+
.then(async cacheKeyQueryResults => (
201+
{
202+
data: await this.cacheQueryResult(
203+
query, values,
204+
cacheKey,
205+
expireSecs,
206+
{
207+
renewalThreshold: renewalThreshold || 6 * 60 * 60,
208+
renewalKey: cacheKeyQueryResults && [
209+
cacheKeyQueries, cacheKeyQueryResults, this.queryRedisKey([query, values])
210+
],
211+
waitForRenew: true,
212+
external: options.external
213+
}
214+
),
215+
refreshKeyValues: cacheKeyQueryResults,
216+
lastRefreshTime: await this.lastRefreshTime(cacheKey)
217+
}
218+
));
210219
}
211220

212221
cacheQueryResult(query, values, cacheKey, expiration, options) {
213222
options = options || {};
214-
const renewalThreshold = options.renewalThreshold;
223+
const { renewalThreshold } = options;
215224
const renewalKey = options.renewalKey && this.queryRedisKey(options.renewalKey);
216225
const redisKey = this.queryRedisKey(cacheKey);
217-
const fetchNew = () => {
218-
return this.queryWithRetryAndRelease(query, values, options.priority, cacheKey, options.external).then(res => {
226+
const fetchNew = () => (
227+
this.queryWithRetryAndRelease(query, values, options.priority, cacheKey, options.external).then(res => {
219228
const result = {
220229
time: (new Date()).getTime(),
221230
result: res,
@@ -224,17 +233,17 @@ class QueryCache {
224233
return this.cacheDriver.set(redisKey, result, expiration)
225234
.then(() => {
226235
this.logger('Renewed', { cacheKey });
227-
return res
236+
return res;
228237
});
229238
}).catch(e => {
230239
if (!(e instanceof ContinueWaitError)) {
231240
this.logger('Dropping Cache', { cacheKey, error: e.stack || e });
232241
this.cacheDriver.remove(redisKey)
233-
.catch(e => this.logger('Error removing key', { cacheKey, error: e.stack || e }));
242+
.catch(err => this.logger('Error removing key', { cacheKey, error: err.stack || err }));
234243
}
235244
throw e;
236-
});
237-
};
245+
})
246+
);
238247

239248
if (options.forceNoCache) {
240249
this.logger('Force no cache for', { cacheKey });
@@ -268,7 +277,7 @@ class QueryCache {
268277
this.logger('Renewing existing key', { cacheKey, renewalThreshold });
269278
fetchNew().catch(e => {
270279
if (!(e instanceof ContinueWaitError)) {
271-
this.logger('Error renewing', {cacheKey, error: e.stack || e})
280+
this.logger('Error renewing', { cacheKey, error: e.stack || e });
272281
}
273282
});
274283
}
@@ -282,8 +291,13 @@ class QueryCache {
282291
});
283292
}
284293

294+
async lastRefreshTime(cacheKey) {
295+
const cachedValue = await this.cacheDriver.get(this.queryRedisKey(cacheKey));
296+
return cachedValue && new Date(cachedValue.time);
297+
}
298+
285299
queryRedisKey(cacheKey) {
286-
return `SQL_QUERY_RESULT_${this.redisPrefix}_${crypto.createHash('md5').update(JSON.stringify(cacheKey)).digest("hex")}`
300+
return `SQL_QUERY_RESULT_${this.redisPrefix}_${crypto.createHash('md5').update(JSON.stringify(cacheKey)).digest("hex")}`;
287301
}
288302
}
289303

packages/cubejs-server-core/core/OrchestratorApi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class OrchestratorApi {
3232
params: query.values
3333
});
3434

35-
return { data };
35+
return data;
3636
} catch (err) {
3737
if ((err instanceof pt.TimeoutError || err instanceof ContinueWaitError)) {
3838
this.logger('Continue wait', {

0 commit comments

Comments
 (0)