@cubejs-backend/query-orchestrator 0.24.0 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/src/driver/BaseDriver.d.ts +36 -0
- package/dist/src/driver/BaseDriver.d.ts.map +1 -0
- package/dist/src/driver/BaseDriver.js +175 -0
- package/dist/src/driver/BaseDriver.js.map +1 -0
- package/dist/src/driver/index.d.ts +3 -0
- package/dist/src/driver/index.d.ts.map +1 -0
- package/dist/src/driver/index.js +15 -0
- package/dist/src/driver/index.js.map +1 -0
- package/dist/src/driver/utils.d.ts +2 -0
- package/dist/src/driver/utils.d.ts.map +1 -0
- package/dist/src/driver/utils.js +17 -0
- package/dist/src/driver/utils.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +15 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/orchestrator/BaseQueueDriver.d.ts +4 -0
- package/dist/src/orchestrator/BaseQueueDriver.d.ts.map +1 -0
- package/dist/src/orchestrator/BaseQueueDriver.js +16 -0
- package/dist/src/orchestrator/BaseQueueDriver.js.map +1 -0
- package/dist/src/orchestrator/ContinueWaitError.d.ts +3 -0
- package/dist/src/orchestrator/ContinueWaitError.d.ts.map +1 -0
- package/dist/src/orchestrator/ContinueWaitError.js +10 -0
- package/dist/src/orchestrator/ContinueWaitError.js.map +1 -0
- package/dist/src/orchestrator/LocalCacheDriver.d.ts +8 -0
- package/dist/src/orchestrator/LocalCacheDriver.d.ts.map +1 -0
- package/dist/src/orchestrator/LocalCacheDriver.js +30 -0
- package/dist/src/orchestrator/LocalCacheDriver.js.map +1 -0
- package/dist/src/orchestrator/LocalQueueDriver.d.ts +57 -0
- package/dist/src/orchestrator/LocalQueueDriver.d.ts.map +1 -0
- package/dist/src/orchestrator/LocalQueueDriver.js +230 -0
- package/dist/src/orchestrator/LocalQueueDriver.js.map +1 -0
- package/dist/src/orchestrator/PreAggregations.d.ts +26 -0
- package/dist/src/orchestrator/PreAggregations.d.ts.map +1 -0
- package/dist/src/orchestrator/PreAggregations.js +565 -0
- package/dist/src/orchestrator/PreAggregations.js.map +1 -0
- package/dist/src/orchestrator/QueryCache.d.ts +51 -0
- package/dist/src/orchestrator/QueryCache.d.ts.map +1 -0
- package/dist/src/orchestrator/QueryCache.js +293 -0
- package/dist/src/orchestrator/QueryCache.js.map +1 -0
- package/dist/src/orchestrator/QueryOrchestrator.d.ts +27 -0
- package/dist/src/orchestrator/QueryOrchestrator.d.ts.map +1 -0
- package/dist/src/orchestrator/QueryOrchestrator.js +79 -0
- package/dist/src/orchestrator/QueryOrchestrator.js.map +1 -0
- package/dist/src/orchestrator/QueryQueue.d.ts +36 -0
- package/dist/src/orchestrator/QueryQueue.d.ts.map +1 -0
- package/dist/src/orchestrator/QueryQueue.js +351 -0
- package/dist/src/orchestrator/QueryQueue.js.map +1 -0
- package/dist/src/orchestrator/RedisCacheDriver.d.ts +12 -0
- package/dist/src/orchestrator/RedisCacheDriver.d.ts.map +1 -0
- package/dist/src/orchestrator/RedisCacheDriver.js +50 -0
- package/dist/src/orchestrator/RedisCacheDriver.js.map +1 -0
- package/dist/src/orchestrator/RedisFactory.d.ts +3 -0
- package/dist/src/orchestrator/RedisFactory.d.ts.map +1 -0
- package/dist/src/orchestrator/RedisFactory.js +45 -0
- package/dist/src/orchestrator/RedisFactory.js.map +1 -0
- package/dist/src/orchestrator/RedisPool.d.ts +10 -0
- package/dist/src/orchestrator/RedisPool.d.ts.map +1 -0
- package/dist/src/orchestrator/RedisPool.js +57 -0
- package/dist/src/orchestrator/RedisPool.js.map +1 -0
- package/dist/src/orchestrator/RedisQueueDriver.d.ts +47 -0
- package/dist/src/orchestrator/RedisQueueDriver.d.ts.map +1 -0
- package/dist/src/orchestrator/RedisQueueDriver.js +253 -0
- package/dist/src/orchestrator/RedisQueueDriver.js.map +1 -0
- package/dist/src/orchestrator/TimeoutError.d.ts +4 -0
- package/dist/src/orchestrator/TimeoutError.d.ts.map +1 -0
- package/dist/src/orchestrator/TimeoutError.js +7 -0
- package/dist/src/orchestrator/TimeoutError.js.map +1 -0
- package/dist/src/orchestrator/index.d.ts +14 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -0
- package/dist/src/orchestrator/index.js +26 -0
- package/dist/src/orchestrator/index.js.map +1 -0
- package/driver/BaseDriver.js +5 -221
- package/driver/README.md +3 -0
- package/driver/utils.js +8 -12
- package/orchestrator/BaseQueueDriver.js +5 -8
- package/orchestrator/ContinueWaitError.js +6 -5
- package/orchestrator/LocalCacheDriver.js +5 -29
- package/orchestrator/LocalQueueDriver.js +5 -256
- package/orchestrator/PreAggregations.js +4 -764
- package/orchestrator/QueryCache.js +5 -381
- package/orchestrator/QueryOrchestrator.js +5 -100
- package/orchestrator/QueryQueue.js +5 -378
- package/orchestrator/README.md +3 -0
- package/orchestrator/RedisCacheDriver.js +5 -45
- package/orchestrator/RedisFactory.js +6 -46
- package/orchestrator/RedisPool.js +5 -49
- package/orchestrator/RedisQueueDriver.js +5 -283
- package/orchestrator/TimeoutError.js +6 -1
- package/package.json +29 -11
- package/docker-compose.yml +0 -7
- package/test/integration/QueryQueueRedis.test.js +0 -5
- package/test/unit/PreAggregations.test.js +0 -337
- package/test/unit/QueryOrchestrator.test.js +0 -373
- package/test/unit/QueryQueue.test.js +0 -247
|
@@ -1,384 +1,8 @@
|
|
|
1
|
-
const
|
|
2
|
-
const QueryQueue = require('./QueryQueue');
|
|
3
|
-
const ContinueWaitError = require('./ContinueWaitError');
|
|
4
|
-
const RedisCacheDriver = require('./RedisCacheDriver');
|
|
5
|
-
const LocalCacheDriver = require('./LocalCacheDriver');
|
|
1
|
+
const { QueryCache } = require('../dist/src/orchestrator/QueryCache');
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.driverFactory = clientFactory;
|
|
12
|
-
this.externalDriverFactory = options.externalDriverFactory;
|
|
13
|
-
this.logger = logger;
|
|
14
|
-
this.cacheDriver = options.cacheAndQueueDriver === 'redis' ?
|
|
15
|
-
new RedisCacheDriver({ pool: options.redisPool }) :
|
|
16
|
-
new LocalCacheDriver();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async cachedQueryResult(queryBody, preAggregationsTablesToTempTables) {
|
|
20
|
-
const replacePreAggregationTableNames = (queryAndParams) => QueryCache.replacePreAggregationTableNames(
|
|
21
|
-
queryAndParams, preAggregationsTablesToTempTables
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const query = replacePreAggregationTableNames(queryBody.query);
|
|
25
|
-
let queuePriority = 10;
|
|
26
|
-
if (Number.isInteger(queryBody.queuePriority)) {
|
|
27
|
-
// eslint-disable-next-line prefer-destructuring
|
|
28
|
-
queuePriority = queryBody.queuePriority;
|
|
29
|
-
}
|
|
30
|
-
const forceNoCache = queryBody.forceNoCache || false;
|
|
31
|
-
const { values } = queryBody;
|
|
32
|
-
const cacheKeyQueries =
|
|
33
|
-
(
|
|
34
|
-
queryBody.cacheKeyQueries && queryBody.cacheKeyQueries.queries ||
|
|
35
|
-
queryBody.cacheKeyQueries ||
|
|
36
|
-
[]
|
|
37
|
-
).map(replacePreAggregationTableNames);
|
|
38
|
-
|
|
39
|
-
const renewalThreshold = queryBody.cacheKeyQueries && queryBody.cacheKeyQueries.renewalThreshold;
|
|
40
|
-
const refreshKeyRenewalThresholds = queryBody.cacheKeyQueries &&
|
|
41
|
-
queryBody.cacheKeyQueries.refreshKeyRenewalThresholds;
|
|
42
|
-
|
|
43
|
-
const expireSecs = queryBody.expireSecs || 24 * 3600;
|
|
44
|
-
|
|
45
|
-
if (!cacheKeyQueries) {
|
|
46
|
-
return {
|
|
47
|
-
data: await this.queryWithRetryAndRelease(query, values, {
|
|
48
|
-
external: queryBody.external,
|
|
49
|
-
requestId: queryBody.requestId
|
|
50
|
-
})
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const cacheKey = QueryCache.queryCacheKey(queryBody);
|
|
54
|
-
|
|
55
|
-
if (queryBody.renewQuery) {
|
|
56
|
-
this.logger('Requested renew', { cacheKey, requestId: queryBody.requestId });
|
|
57
|
-
return this.renewQuery(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, {
|
|
58
|
-
external: queryBody.external,
|
|
59
|
-
requestId: queryBody.requestId,
|
|
60
|
-
refreshKeyRenewalThresholds
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!this.options.backgroundRenew) {
|
|
65
|
-
const resultPromise = this.renewQuery(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, {
|
|
66
|
-
external: queryBody.external,
|
|
67
|
-
requestId: queryBody.requestId,
|
|
68
|
-
refreshKeyRenewalThresholds,
|
|
69
|
-
skipRefreshKeyWaitForRenew: true
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
this.startRenewCycle(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, {
|
|
73
|
-
external: queryBody.external,
|
|
74
|
-
requestId: queryBody.requestId,
|
|
75
|
-
refreshKeyRenewalThresholds
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
return resultPromise;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
this.logger('Background fetch', { cacheKey, requestId: queryBody.requestId });
|
|
82
|
-
|
|
83
|
-
const mainPromise = this.cacheQueryResult(
|
|
84
|
-
query, values,
|
|
85
|
-
cacheKey,
|
|
86
|
-
expireSecs,
|
|
87
|
-
{
|
|
88
|
-
priority: queuePriority,
|
|
89
|
-
forceNoCache,
|
|
90
|
-
external: queryBody.external,
|
|
91
|
-
requestId: queryBody.requestId
|
|
92
|
-
}
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
if (!forceNoCache) {
|
|
96
|
-
this.startRenewCycle(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, {
|
|
97
|
-
external: queryBody.external,
|
|
98
|
-
requestId: queryBody.requestId,
|
|
99
|
-
refreshKeyRenewalThresholds
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
data: await mainPromise,
|
|
105
|
-
lastRefreshTime: await this.lastRefreshTime(cacheKey)
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
static queryCacheKey(queryBody) {
|
|
110
|
-
return [queryBody.query, queryBody.values, (queryBody.preAggregations || []).map(p => p.loadSql)];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
static replaceAll(replaceThis, withThis, inThis) {
|
|
114
|
-
withThis = withThis.replace(/\$/g, '$$$$');
|
|
115
|
-
return inThis.replace(
|
|
116
|
-
new RegExp(replaceThis.replace(/([/,!\\^${}[\]().*+?|<>\-&])/g, '\\$&'), 'g'),
|
|
117
|
-
withThis
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static replacePreAggregationTableNames(queryAndParams, preAggregationsTablesToTempTables) {
|
|
122
|
-
const [keyQuery, params] = Array.isArray(queryAndParams) ? queryAndParams : [queryAndParams, []];
|
|
123
|
-
const replacedKeqQuery = preAggregationsTablesToTempTables.reduce(
|
|
124
|
-
(query, [tableName, { targetTableName }]) => QueryCache.replaceAll(tableName, targetTableName, query),
|
|
125
|
-
keyQuery
|
|
126
|
-
);
|
|
127
|
-
return Array.isArray(queryAndParams) ? [replacedKeqQuery, params] : replacedKeqQuery;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
queryWithRetryAndRelease(query, values, {
|
|
131
|
-
priority, cacheKey, external, requestId
|
|
132
|
-
}) {
|
|
133
|
-
const queue = external ? this.getExternalQueue() : this.getQueue();
|
|
134
|
-
return queue.executeInQueue('query', cacheKey, {
|
|
135
|
-
queryKey: cacheKey, query, values, requestId
|
|
136
|
-
}, priority, {
|
|
137
|
-
stageQueryKey: cacheKey,
|
|
138
|
-
requestId
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
getQueue() {
|
|
143
|
-
if (!this.queue) {
|
|
144
|
-
this.queue = QueryCache.createQueue(
|
|
145
|
-
`SQL_QUERY_${this.redisPrefix}`,
|
|
146
|
-
this.driverFactory,
|
|
147
|
-
(client, q) => {
|
|
148
|
-
this.logger('Executing SQL', {
|
|
149
|
-
...q
|
|
150
|
-
});
|
|
151
|
-
return client.query(q.query, q.values, q);
|
|
152
|
-
}, {
|
|
153
|
-
logger: this.logger,
|
|
154
|
-
cacheAndQueueDriver: this.options.cacheAndQueueDriver,
|
|
155
|
-
redisPool: this.options.redisPool,
|
|
156
|
-
...this.options.queueOptions
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
return this.queue;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
getExternalQueue() {
|
|
164
|
-
if (!this.externalQueue) {
|
|
165
|
-
this.externalQueue = QueryCache.createQueue(
|
|
166
|
-
`SQL_QUERY_EXT_${this.redisPrefix}`,
|
|
167
|
-
this.externalDriverFactory,
|
|
168
|
-
(client, q) => {
|
|
169
|
-
this.logger('Executing SQL', {
|
|
170
|
-
...q
|
|
171
|
-
});
|
|
172
|
-
return client.query(q.query, q.values, q);
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
logger: this.logger,
|
|
176
|
-
cacheAndQueueDriver: this.options.cacheAndQueueDriver,
|
|
177
|
-
redisPool: this.options.redisPool,
|
|
178
|
-
...this.options.externalQueueOptions
|
|
179
|
-
}
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
return this.externalQueue;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
static createQueue(redisPrefix, clientFactory, executeFn, options) {
|
|
186
|
-
options = options || {};
|
|
187
|
-
const queue = new QueryQueue(redisPrefix, {
|
|
188
|
-
queryHandlers: {
|
|
189
|
-
query: async (q, setCancelHandle) => {
|
|
190
|
-
const client = await clientFactory();
|
|
191
|
-
const resultPromise = executeFn(client, q);
|
|
192
|
-
let handle;
|
|
193
|
-
if (resultPromise.cancel) {
|
|
194
|
-
queue.cancelHandlerCounter += 1;
|
|
195
|
-
handle = queue.cancelHandlerCounter;
|
|
196
|
-
queue.handles[handle] = resultPromise;
|
|
197
|
-
await setCancelHandle(handle);
|
|
198
|
-
}
|
|
199
|
-
const result = await resultPromise;
|
|
200
|
-
if (handle) {
|
|
201
|
-
delete queue.handles[handle];
|
|
202
|
-
}
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
cancelHandlers: {
|
|
207
|
-
query: async (q) => {
|
|
208
|
-
if (q.cancelHandler && queue.handles[q.cancelHandler]) {
|
|
209
|
-
await queue.handles[q.cancelHandler].cancel();
|
|
210
|
-
delete queue.handles[q.cancelHandler];
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
logger: (msg, params) => options.logger(msg, params),
|
|
215
|
-
...options
|
|
216
|
-
});
|
|
217
|
-
queue.cancelHandlerCounter = 0;
|
|
218
|
-
queue.handles = {};
|
|
219
|
-
return queue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
startRenewCycle(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, options) {
|
|
223
|
-
this.renewQuery(
|
|
224
|
-
query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, options
|
|
225
|
-
).catch(e => {
|
|
226
|
-
if (!(e instanceof ContinueWaitError)) {
|
|
227
|
-
this.logger('Error while renew cycle', {
|
|
228
|
-
query, query_values: values, error: e.stack || e, requestId: options.requestId
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
renewQuery(query, values, cacheKeyQueries, expireSecs, cacheKey, renewalThreshold, options) {
|
|
235
|
-
options = options || {};
|
|
236
|
-
return Promise.all(
|
|
237
|
-
cacheKeyQueries.map((q, i) => this.cacheQueryResult(
|
|
238
|
-
Array.isArray(q) ? q[0] : q,
|
|
239
|
-
Array.isArray(q) ? q[1] : [],
|
|
240
|
-
q,
|
|
241
|
-
expireSecs,
|
|
242
|
-
{
|
|
243
|
-
renewalThreshold:
|
|
244
|
-
this.options.refreshKeyRenewalThreshold ||
|
|
245
|
-
(options.refreshKeyRenewalThresholds || [])[i] ||
|
|
246
|
-
2 * 60,
|
|
247
|
-
renewalKey: q,
|
|
248
|
-
waitForRenew: !options.skipRefreshKeyWaitForRenew,
|
|
249
|
-
requestId: options.requestId
|
|
250
|
-
}
|
|
251
|
-
))
|
|
252
|
-
)
|
|
253
|
-
.catch(e => {
|
|
254
|
-
if (e instanceof ContinueWaitError) {
|
|
255
|
-
throw e;
|
|
256
|
-
}
|
|
257
|
-
this.logger('Error fetching cache key queries', { error: e.stack || e, requestId: options.requestId });
|
|
258
|
-
return [];
|
|
259
|
-
})
|
|
260
|
-
.then(async cacheKeyQueryResults => (
|
|
261
|
-
{
|
|
262
|
-
data: await this.cacheQueryResult(
|
|
263
|
-
query, values,
|
|
264
|
-
cacheKey,
|
|
265
|
-
expireSecs,
|
|
266
|
-
{
|
|
267
|
-
renewalThreshold: renewalThreshold || 6 * 60 * 60,
|
|
268
|
-
renewalKey: cacheKeyQueryResults && [
|
|
269
|
-
cacheKeyQueries, cacheKeyQueryResults, this.queryRedisKey([query, values])
|
|
270
|
-
],
|
|
271
|
-
waitForRenew: true,
|
|
272
|
-
external: options.external,
|
|
273
|
-
requestId: options.requestId
|
|
274
|
-
}
|
|
275
|
-
),
|
|
276
|
-
refreshKeyValues: cacheKeyQueryResults,
|
|
277
|
-
lastRefreshTime: await this.lastRefreshTime(cacheKey)
|
|
278
|
-
}
|
|
279
|
-
));
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
cacheQueryResult(query, values, cacheKey, expiration, options) {
|
|
283
|
-
options = options || {};
|
|
284
|
-
const { renewalThreshold } = options;
|
|
285
|
-
const renewalKey = options.renewalKey && this.queryRedisKey(options.renewalKey);
|
|
286
|
-
const redisKey = this.queryRedisKey(cacheKey);
|
|
287
|
-
const fetchNew = () => (
|
|
288
|
-
this.queryWithRetryAndRelease(query, values, {
|
|
289
|
-
priority: options.priority, cacheKey, external: options.external, requestId: options.requestId
|
|
290
|
-
}).then(res => {
|
|
291
|
-
const result = {
|
|
292
|
-
time: (new Date()).getTime(),
|
|
293
|
-
result: res,
|
|
294
|
-
renewalKey
|
|
295
|
-
};
|
|
296
|
-
return this.cacheDriver.set(redisKey, result, expiration)
|
|
297
|
-
.then(() => {
|
|
298
|
-
this.logger('Renewed', { cacheKey, requestId: options.requestId });
|
|
299
|
-
return res;
|
|
300
|
-
});
|
|
301
|
-
}).catch(e => {
|
|
302
|
-
if (!(e instanceof ContinueWaitError)) {
|
|
303
|
-
this.logger('Dropping Cache', { cacheKey, error: e.stack || e, requestId: options.requestId });
|
|
304
|
-
this.cacheDriver.remove(redisKey)
|
|
305
|
-
.catch(err => this.logger('Error removing key', {
|
|
306
|
-
cacheKey,
|
|
307
|
-
error: err.stack || err,
|
|
308
|
-
requestId: options.requestId
|
|
309
|
-
}));
|
|
310
|
-
}
|
|
311
|
-
throw e;
|
|
312
|
-
})
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
if (options.forceNoCache) {
|
|
316
|
-
this.logger('Force no cache for', { cacheKey, requestId: options.requestId });
|
|
317
|
-
return fetchNew();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return this.cacheDriver.get(redisKey).then(res => {
|
|
321
|
-
if (res) {
|
|
322
|
-
const parsedResult = res;
|
|
323
|
-
const renewedAgo = (new Date()).getTime() - parsedResult.time;
|
|
324
|
-
this.logger('Found cache entry', {
|
|
325
|
-
cacheKey,
|
|
326
|
-
time: parsedResult.time,
|
|
327
|
-
renewedAgo,
|
|
328
|
-
renewalKey: parsedResult.renewalKey,
|
|
329
|
-
newRenewalKey: renewalKey,
|
|
330
|
-
renewalThreshold,
|
|
331
|
-
requestId: options.requestId
|
|
332
|
-
});
|
|
333
|
-
if (
|
|
334
|
-
renewalKey && (
|
|
335
|
-
!renewalThreshold ||
|
|
336
|
-
!parsedResult.time ||
|
|
337
|
-
renewedAgo > renewalThreshold * 1000 ||
|
|
338
|
-
parsedResult.renewalKey !== renewalKey
|
|
339
|
-
)
|
|
340
|
-
) {
|
|
341
|
-
if (options.waitForRenew) {
|
|
342
|
-
this.logger('Waiting for renew', { cacheKey, renewalThreshold, requestId: options.requestId });
|
|
343
|
-
return fetchNew();
|
|
344
|
-
} else {
|
|
345
|
-
this.logger('Renewing existing key', { cacheKey, renewalThreshold, requestId: options.requestId });
|
|
346
|
-
fetchNew().catch(e => {
|
|
347
|
-
if (!(e instanceof ContinueWaitError)) {
|
|
348
|
-
this.logger('Error renewing', { cacheKey, error: e.stack || e, requestId: options.requestId });
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
this.logger('Using cache for', { cacheKey, requestId: options.requestId });
|
|
354
|
-
return parsedResult.result;
|
|
355
|
-
} else {
|
|
356
|
-
this.logger('Missing cache for', { cacheKey, requestId: options.requestId });
|
|
357
|
-
return fetchNew();
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
async lastRefreshTime(cacheKey) {
|
|
363
|
-
const cachedValue = await this.cacheDriver.get(this.queryRedisKey(cacheKey));
|
|
364
|
-
return cachedValue && new Date(cachedValue.time);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async resultFromCacheIfExists(queryBody) {
|
|
368
|
-
const cacheKey = QueryCache.queryCacheKey(queryBody);
|
|
369
|
-
const cachedValue = await this.cacheDriver.get(this.queryRedisKey(cacheKey));
|
|
370
|
-
if (cachedValue) {
|
|
371
|
-
return {
|
|
372
|
-
data: cachedValue.result,
|
|
373
|
-
lastRefreshTime: new Date(cachedValue.time)
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
return null;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
queryRedisKey(cacheKey) {
|
|
380
|
-
return `SQL_QUERY_RESULT_${this.redisPrefix}_${crypto.createHash('md5').update(JSON.stringify(cacheKey)).digest('hex')}`;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
3
|
+
process.emitWarning(
|
|
4
|
+
'Using absolute import with @cubejs-backend/query-orchestrator is deprecated',
|
|
5
|
+
'DeprecationWarning'
|
|
6
|
+
);
|
|
383
7
|
|
|
384
8
|
module.exports = QueryCache;
|
|
@@ -1,103 +1,8 @@
|
|
|
1
|
-
const
|
|
2
|
-
const QueryCache = require('./QueryCache');
|
|
3
|
-
const PreAggregations = require('./PreAggregations');
|
|
4
|
-
const RedisPool = require('./RedisPool');
|
|
1
|
+
const { QueryOrchestrator } = require('../dist/src/orchestrator/QueryOrchestrator');
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.driverFactory = driverFactory;
|
|
11
|
-
this.logger = logger;
|
|
12
|
-
const { externalDriverFactory } = options;
|
|
13
|
-
const cacheAndQueueDriver = options.cacheAndQueueDriver || process.env.CUBEJS_CACHE_AND_QUEUE_DRIVER || (
|
|
14
|
-
process.env.NODE_ENV === 'production' || process.env.REDIS_URL ? 'redis' : 'memory'
|
|
15
|
-
);
|
|
16
|
-
if (cacheAndQueueDriver !== 'redis' && cacheAndQueueDriver !== 'memory') {
|
|
17
|
-
throw new Error('Only \'redis\' or \'memory\' are supported for cacheAndQueueDriver option');
|
|
18
|
-
}
|
|
19
|
-
const redisPool = cacheAndQueueDriver === 'redis' ? new RedisPool() : undefined;
|
|
20
|
-
|
|
21
|
-
this.redisPool = redisPool;
|
|
22
|
-
this.queryCache = new QueryCache(
|
|
23
|
-
this.redisPrefix, this.driverFactory, this.logger, {
|
|
24
|
-
externalDriverFactory,
|
|
25
|
-
cacheAndQueueDriver,
|
|
26
|
-
redisPool,
|
|
27
|
-
...options.queryCacheOptions,
|
|
28
|
-
}
|
|
29
|
-
);
|
|
30
|
-
this.preAggregations = new PreAggregations(
|
|
31
|
-
this.redisPrefix, this.driverFactory, this.logger, this.queryCache, {
|
|
32
|
-
externalDriverFactory,
|
|
33
|
-
cacheAndQueueDriver,
|
|
34
|
-
redisPool,
|
|
35
|
-
...options.preAggregationsOptions
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
this.rollupOnlyMode = options.rollupOnlyMode;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async fetchQuery(queryBody) {
|
|
42
|
-
return this.preAggregations.loadAllPreAggregationsIfNeeded(queryBody)
|
|
43
|
-
.then(async preAggregationsTablesToTempTables => {
|
|
44
|
-
const usedPreAggregations = R.fromPairs(preAggregationsTablesToTempTables);
|
|
45
|
-
if (this.rollupOnlyMode && Object.keys(usedPreAggregations).length === 0) {
|
|
46
|
-
throw new Error('No pre-aggregation exists for that query');
|
|
47
|
-
}
|
|
48
|
-
if (!queryBody.query) {
|
|
49
|
-
return {
|
|
50
|
-
usedPreAggregations
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const result = await this.queryCache.cachedQueryResult(
|
|
54
|
-
queryBody, preAggregationsTablesToTempTables
|
|
55
|
-
);
|
|
56
|
-
return {
|
|
57
|
-
...result,
|
|
58
|
-
usedPreAggregations
|
|
59
|
-
};
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async queryStage(queryBody) {
|
|
64
|
-
const queue = this.preAggregations.getQueue();
|
|
65
|
-
const preAggregationsQueryStageState = await queue.fetchQueryStageState();
|
|
66
|
-
const pendingPreAggregationIndex =
|
|
67
|
-
(await Promise.all(
|
|
68
|
-
(queryBody.preAggregations || [])
|
|
69
|
-
.map(p => queue.getQueryStage(
|
|
70
|
-
PreAggregations.preAggregationQueryCacheKey(p), 10, preAggregationsQueryStageState
|
|
71
|
-
))
|
|
72
|
-
)).findIndex(p => !!p);
|
|
73
|
-
if (pendingPreAggregationIndex === -1) {
|
|
74
|
-
return this.queryCache.getQueue().getQueryStage(QueryCache.queryCacheKey(queryBody));
|
|
75
|
-
}
|
|
76
|
-
const preAggregation = queryBody.preAggregations[pendingPreAggregationIndex];
|
|
77
|
-
const preAggregationStage = await queue.getQueryStage(
|
|
78
|
-
PreAggregations.preAggregationQueryCacheKey(preAggregation), undefined, preAggregationsQueryStageState
|
|
79
|
-
);
|
|
80
|
-
if (!preAggregationStage) {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
const stageMessage =
|
|
84
|
-
`Building pre-aggregation ${pendingPreAggregationIndex + 1}/${queryBody.preAggregations.length}`;
|
|
85
|
-
if (preAggregationStage.stage.indexOf('queue') !== -1) {
|
|
86
|
-
return { ...preAggregationStage, stage: `${stageMessage}: ${preAggregationStage.stage}` };
|
|
87
|
-
} else {
|
|
88
|
-
return { ...preAggregationStage, stage: stageMessage };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
resultFromCacheIfExists(queryBody) {
|
|
93
|
-
return this.queryCache.resultFromCacheIfExists(queryBody);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async cleanup() {
|
|
97
|
-
if (this.redisPool) {
|
|
98
|
-
await this.redisPool.cleanup();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
3
|
+
process.emitWarning(
|
|
4
|
+
'Using absolute import with @cubejs-backend/query-orchestrator is deprecated',
|
|
5
|
+
'DeprecationWarning'
|
|
6
|
+
);
|
|
102
7
|
|
|
103
8
|
module.exports = QueryOrchestrator;
|