@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.
Files changed (96) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/driver/BaseDriver.d.ts +36 -0
  3. package/dist/src/driver/BaseDriver.d.ts.map +1 -0
  4. package/dist/src/driver/BaseDriver.js +175 -0
  5. package/dist/src/driver/BaseDriver.js.map +1 -0
  6. package/dist/src/driver/index.d.ts +3 -0
  7. package/dist/src/driver/index.d.ts.map +1 -0
  8. package/dist/src/driver/index.js +15 -0
  9. package/dist/src/driver/index.js.map +1 -0
  10. package/dist/src/driver/utils.d.ts +2 -0
  11. package/dist/src/driver/utils.d.ts.map +1 -0
  12. package/dist/src/driver/utils.js +17 -0
  13. package/dist/src/driver/utils.js.map +1 -0
  14. package/dist/src/index.d.ts +3 -0
  15. package/dist/src/index.d.ts.map +1 -0
  16. package/dist/src/index.js +15 -0
  17. package/dist/src/index.js.map +1 -0
  18. package/dist/src/orchestrator/BaseQueueDriver.d.ts +4 -0
  19. package/dist/src/orchestrator/BaseQueueDriver.d.ts.map +1 -0
  20. package/dist/src/orchestrator/BaseQueueDriver.js +16 -0
  21. package/dist/src/orchestrator/BaseQueueDriver.js.map +1 -0
  22. package/dist/src/orchestrator/ContinueWaitError.d.ts +3 -0
  23. package/dist/src/orchestrator/ContinueWaitError.d.ts.map +1 -0
  24. package/dist/src/orchestrator/ContinueWaitError.js +10 -0
  25. package/dist/src/orchestrator/ContinueWaitError.js.map +1 -0
  26. package/dist/src/orchestrator/LocalCacheDriver.d.ts +8 -0
  27. package/dist/src/orchestrator/LocalCacheDriver.d.ts.map +1 -0
  28. package/dist/src/orchestrator/LocalCacheDriver.js +30 -0
  29. package/dist/src/orchestrator/LocalCacheDriver.js.map +1 -0
  30. package/dist/src/orchestrator/LocalQueueDriver.d.ts +57 -0
  31. package/dist/src/orchestrator/LocalQueueDriver.d.ts.map +1 -0
  32. package/dist/src/orchestrator/LocalQueueDriver.js +230 -0
  33. package/dist/src/orchestrator/LocalQueueDriver.js.map +1 -0
  34. package/dist/src/orchestrator/PreAggregations.d.ts +26 -0
  35. package/dist/src/orchestrator/PreAggregations.d.ts.map +1 -0
  36. package/dist/src/orchestrator/PreAggregations.js +565 -0
  37. package/dist/src/orchestrator/PreAggregations.js.map +1 -0
  38. package/dist/src/orchestrator/QueryCache.d.ts +51 -0
  39. package/dist/src/orchestrator/QueryCache.d.ts.map +1 -0
  40. package/dist/src/orchestrator/QueryCache.js +293 -0
  41. package/dist/src/orchestrator/QueryCache.js.map +1 -0
  42. package/dist/src/orchestrator/QueryOrchestrator.d.ts +27 -0
  43. package/dist/src/orchestrator/QueryOrchestrator.d.ts.map +1 -0
  44. package/dist/src/orchestrator/QueryOrchestrator.js +79 -0
  45. package/dist/src/orchestrator/QueryOrchestrator.js.map +1 -0
  46. package/dist/src/orchestrator/QueryQueue.d.ts +36 -0
  47. package/dist/src/orchestrator/QueryQueue.d.ts.map +1 -0
  48. package/dist/src/orchestrator/QueryQueue.js +351 -0
  49. package/dist/src/orchestrator/QueryQueue.js.map +1 -0
  50. package/dist/src/orchestrator/RedisCacheDriver.d.ts +12 -0
  51. package/dist/src/orchestrator/RedisCacheDriver.d.ts.map +1 -0
  52. package/dist/src/orchestrator/RedisCacheDriver.js +50 -0
  53. package/dist/src/orchestrator/RedisCacheDriver.js.map +1 -0
  54. package/dist/src/orchestrator/RedisFactory.d.ts +3 -0
  55. package/dist/src/orchestrator/RedisFactory.d.ts.map +1 -0
  56. package/dist/src/orchestrator/RedisFactory.js +45 -0
  57. package/dist/src/orchestrator/RedisFactory.js.map +1 -0
  58. package/dist/src/orchestrator/RedisPool.d.ts +10 -0
  59. package/dist/src/orchestrator/RedisPool.d.ts.map +1 -0
  60. package/dist/src/orchestrator/RedisPool.js +57 -0
  61. package/dist/src/orchestrator/RedisPool.js.map +1 -0
  62. package/dist/src/orchestrator/RedisQueueDriver.d.ts +47 -0
  63. package/dist/src/orchestrator/RedisQueueDriver.d.ts.map +1 -0
  64. package/dist/src/orchestrator/RedisQueueDriver.js +253 -0
  65. package/dist/src/orchestrator/RedisQueueDriver.js.map +1 -0
  66. package/dist/src/orchestrator/TimeoutError.d.ts +4 -0
  67. package/dist/src/orchestrator/TimeoutError.d.ts.map +1 -0
  68. package/dist/src/orchestrator/TimeoutError.js +7 -0
  69. package/dist/src/orchestrator/TimeoutError.js.map +1 -0
  70. package/dist/src/orchestrator/index.d.ts +14 -0
  71. package/dist/src/orchestrator/index.d.ts.map +1 -0
  72. package/dist/src/orchestrator/index.js +26 -0
  73. package/dist/src/orchestrator/index.js.map +1 -0
  74. package/driver/BaseDriver.js +5 -221
  75. package/driver/README.md +3 -0
  76. package/driver/utils.js +8 -12
  77. package/orchestrator/BaseQueueDriver.js +5 -8
  78. package/orchestrator/ContinueWaitError.js +6 -5
  79. package/orchestrator/LocalCacheDriver.js +5 -29
  80. package/orchestrator/LocalQueueDriver.js +5 -256
  81. package/orchestrator/PreAggregations.js +4 -764
  82. package/orchestrator/QueryCache.js +5 -381
  83. package/orchestrator/QueryOrchestrator.js +5 -100
  84. package/orchestrator/QueryQueue.js +5 -378
  85. package/orchestrator/README.md +3 -0
  86. package/orchestrator/RedisCacheDriver.js +5 -45
  87. package/orchestrator/RedisFactory.js +6 -46
  88. package/orchestrator/RedisPool.js +5 -49
  89. package/orchestrator/RedisQueueDriver.js +5 -283
  90. package/orchestrator/TimeoutError.js +6 -1
  91. package/package.json +29 -11
  92. package/docker-compose.yml +0 -7
  93. package/test/integration/QueryQueueRedis.test.js +0 -5
  94. package/test/unit/PreAggregations.test.js +0 -337
  95. package/test/unit/QueryOrchestrator.test.js +0 -373
  96. package/test/unit/QueryQueue.test.js +0 -247
@@ -1,373 +0,0 @@
1
- /* globals describe, beforeEach, afterEach, test, expect */
2
- const QueryOrchestrator = require('../../orchestrator/QueryOrchestrator');
3
-
4
- class MockDriver {
5
- constructor() {
6
- this.tables = [];
7
- this.tablesReady = [];
8
- this.executedQueries = [];
9
- this.cancelledQueries = [];
10
- }
11
-
12
- query(query) {
13
- this.executedQueries.push(query);
14
- let promise = Promise.resolve([query]);
15
- if (query.match('orders_too_big')) {
16
- promise = promise.then((res) => new Promise(resolve => setTimeout(() => resolve(res), 3000)));
17
- }
18
-
19
- if (query.match('orders_delay')) {
20
- promise = promise.then((res) => new Promise(resolve => setTimeout(() => resolve(res), 800)));
21
- }
22
-
23
- if (query.match(/^SELECT NOW\(\)$/)) {
24
- promise = promise.then(() => new Date().toJSON());
25
- }
26
-
27
- if (this.tablesReady.find(t => query.indexOf(t) !== -1)) {
28
- promise = promise.then(res => res.concat({ tableReady: true }));
29
- }
30
-
31
- promise.cancel = () => {
32
- this.cancelledQueries.push(query);
33
- };
34
- return promise;
35
- }
36
-
37
- async getTablesQuery(schema) {
38
- if (this.tablesQueryDelay) {
39
- await this.delay(this.tablesQueryDelay);
40
- }
41
- return this.tables.map(t => ({ table_name: t.replace(`${schema}.`, '') }));
42
- }
43
-
44
- delay(timeout) {
45
- return new Promise(resolve => setTimeout(() => resolve(), timeout));
46
- }
47
-
48
- async createSchemaIfNotExists(schema) {
49
- this.schema = schema;
50
- return null;
51
- }
52
-
53
- loadPreAggregationIntoTable(preAggregationTableName, loadSql) {
54
- this.tables.push(preAggregationTableName.substring(0, 100));
55
- const promise = this.query(loadSql);
56
- const resPromise = promise.then(() => this.tablesReady.push(preAggregationTableName.substring(0, 100)));
57
- resPromise.cancel = promise.cancel;
58
- return resPromise;
59
- }
60
-
61
- async dropTable(tableName) {
62
- this.tables = this.tables.filter(t => t !== tableName);
63
- return this.query(`DROP TABLE ${tableName}`);
64
- }
65
- }
66
-
67
- describe('QueryOrchestrator', () => {
68
- let mockDriver = null;
69
- const queryOrchestrator = new QueryOrchestrator(
70
- 'TEST',
71
- async () => mockDriver,
72
- (msg, params) => console.log(new Date().toJSON(), msg, params), {
73
- preAggregationsOptions: {
74
- queueOptions: {
75
- executionTimeout: 1
76
- },
77
- usedTablePersistTime: 1
78
- }
79
- }
80
- );
81
-
82
- beforeEach(() => {
83
- mockDriver = new MockDriver();
84
- });
85
-
86
- afterEach(async () => {
87
- await queryOrchestrator.cleanup();
88
- });
89
-
90
- test('basic', async () => {
91
- const query = {
92
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_number_and_count20191101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
93
- values: ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z'],
94
- cacheKeyQueries: {
95
- renewalThreshold: 21600,
96
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
97
- },
98
- preAggregations: [{
99
- preAggregationsSchema: 'stb_pre_aggregations',
100
- tableName: 'stb_pre_aggregations.orders_number_and_count20191101',
101
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_number_and_count20191101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1', ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z']],
102
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
103
- }],
104
- renewQuery: true,
105
- requestId: 'basic'
106
- };
107
- const result = await queryOrchestrator.fetchQuery(query);
108
- console.log(result.data[0]);
109
- expect(result.data[0]).toMatch(/orders_number_and_count20191101_kjypcoio_5yftl5il/);
110
- });
111
-
112
- test('indexes', async () => {
113
- const query = {
114
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_number_and_count20191101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
115
- values: ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z'],
116
- cacheKeyQueries: {
117
- renewalThreshold: 21600,
118
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
119
- },
120
- preAggregations: [{
121
- preAggregationsSchema: 'stb_pre_aggregations',
122
- tableName: 'stb_pre_aggregations.orders_number_and_count20191101',
123
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_number_and_count20191101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1', ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z']],
124
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]],
125
- indexesSql: [{
126
- sql: ['CREATE INDEX orders_number_and_count_week20191101 ON stb_pre_aggregations.orders_number_and_count20191101 ("orders__created_at_week")', []],
127
- indexName: 'orders_number_and_count_week20191101'
128
- }],
129
- }],
130
- renewQuery: true,
131
- requestId: 'indexes'
132
- };
133
- const result = await queryOrchestrator.fetchQuery(query);
134
- console.log(result.data[0]);
135
- expect(result.data[0]).toMatch(/orders_number_and_count20191101_l3kvjcmu_khbemovd/);
136
- expect(mockDriver.executedQueries.join(',')).toMatch(/CREATE INDEX orders_number_and_count_week20191101_l3kvjcmu_khbemovd/);
137
- });
138
-
139
- test('silent truncate', async () => {
140
- const query = {
141
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_number_and_count_and_very_very_very_very_very_very_long20191101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
142
- values: ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z'],
143
- cacheKeyQueries: {
144
- renewalThreshold: 21600,
145
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
146
- },
147
- preAggregations: [{
148
- preAggregationsSchema: 'stb_pre_aggregations',
149
- tableName: 'stb_pre_aggregations.orders_number_and_count_and_very_very_very_very_very_very_long20191101',
150
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_number_and_count_and_very_very_very_very_very_very_long20191101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1', ['2019-11-01T00:00:00Z', '2019-11-30T23:59:59Z']],
151
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]],
152
- }],
153
- renewQuery: true,
154
- requestId: 'silent truncate'
155
- };
156
- let thrown = true;
157
- try {
158
- await queryOrchestrator.fetchQuery(query);
159
- thrown = false;
160
- } catch (e) {
161
- expect(e.message).toMatch(/Pre-aggregation table is not found/);
162
- }
163
- expect(thrown).toBe(true);
164
- });
165
-
166
- test('cancel pre-aggregation', async () => {
167
- const query = {
168
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_number_and_count20181101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
169
- values: ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z'],
170
- cacheKeyQueries: {
171
- renewalThreshold: 21600,
172
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
173
- },
174
- preAggregations: [{
175
- preAggregationsSchema: 'stb_pre_aggregations',
176
- tableName: 'stb_pre_aggregations.orders_number_and_count20181101',
177
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_number_and_count20181101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders_too_big AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1', ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z']],
178
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
179
- }],
180
- renewQuery: true,
181
- requestId: 'cancel pre-aggregation'
182
- };
183
- try {
184
- await queryOrchestrator.fetchQuery(query);
185
- } catch (e) {
186
- expect(e.toString()).toMatch(/timeout/);
187
- }
188
- expect(mockDriver.cancelledQueries[0]).toMatch(/orders_too_big/);
189
- });
190
-
191
- test('save structure versions', async () => {
192
- mockDriver.tables = [];
193
- await queryOrchestrator.fetchQuery({
194
- query: 'SELECT * FROM stb_pre_aggregations.orders',
195
- values: [],
196
- cacheKeyQueries: {
197
- renewalThreshold: 21600,
198
- queries: []
199
- },
200
- preAggregations: [{
201
- preAggregationsSchema: 'stb_pre_aggregations',
202
- tableName: 'stb_pre_aggregations.orders',
203
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders', []],
204
- invalidateKeyQueries: [['SELECT 1', []]]
205
- }],
206
- renewQuery: true,
207
- requestId: 'save structure versions'
208
- });
209
-
210
- await queryOrchestrator.fetchQuery({
211
- query: 'SELECT * FROM stb_pre_aggregations.orders',
212
- values: [],
213
- cacheKeyQueries: {
214
- renewalThreshold: 21600,
215
- queries: []
216
- },
217
- preAggregations: [{
218
- preAggregationsSchema: 'stb_pre_aggregations',
219
- tableName: 'stb_pre_aggregations.orders',
220
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders1', []],
221
- invalidateKeyQueries: [['SELECT 1', []]]
222
- }],
223
- renewQuery: true,
224
- requestId: 'save structure versions'
225
- });
226
-
227
- await new Promise(resolve => setTimeout(() => resolve(), 1000));
228
-
229
- for (let i = 0; i < 5; i++) {
230
- await queryOrchestrator.fetchQuery({
231
- query: 'SELECT * FROM stb_pre_aggregations.orders',
232
- values: [],
233
- cacheKeyQueries: {
234
- renewalThreshold: 21600,
235
- queries: []
236
- },
237
- preAggregations: [{
238
- preAggregationsSchema: 'stb_pre_aggregations',
239
- tableName: 'stb_pre_aggregations.orders',
240
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders', []],
241
- invalidateKeyQueries: [['SELECT 2', []]]
242
- }],
243
- renewQuery: true,
244
- requestId: 'save structure versions'
245
- });
246
- }
247
- expect(mockDriver.tables).toContainEqual(expect.stringMatching(/orders_f5v4jw3p_4eysppzt/));
248
- expect(mockDriver.tables).toContainEqual(expect.stringMatching(/orders_mjooke4_ezlvkhjl/));
249
- });
250
-
251
- test('intermittent empty rollup', async () => {
252
- const firstQuery = queryOrchestrator.fetchQuery({
253
- query: 'SELECT * FROM stb_pre_aggregations.orders_d20181102',
254
- values: [],
255
- cacheKeyQueries: {
256
- renewalThreshold: 21600,
257
- queries: []
258
- },
259
- preAggregations: [{
260
- preAggregationsSchema: 'stb_pre_aggregations',
261
- tableName: 'stb_pre_aggregations.orders_d20181102',
262
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_d20181102 AS SELECT * FROM public.orders_delay', []],
263
- invalidateKeyQueries: [['SELECT 2', []]]
264
- }],
265
- requestId: 'intermittent empty rollup'
266
- });
267
-
268
- queryOrchestrator.fetchQuery({
269
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_d20181101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
270
- values: ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z'],
271
- cacheKeyQueries: {
272
- renewalThreshold: 21600,
273
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
274
- },
275
- preAggregations: [{
276
- preAggregationsSchema: 'stb_pre_aggregations',
277
- tableName: 'stb_pre_aggregations.orders_d20181101',
278
- loadSql: [
279
- 'CREATE TABLE stb_pre_aggregations.orders_d20181101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders_delay AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1',
280
- ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z']
281
- ],
282
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
283
- }],
284
- requestId: 'intermittent empty rollup'
285
- });
286
-
287
- await firstQuery;
288
-
289
- const res = await queryOrchestrator.fetchQuery({
290
- query: 'SELECT "orders__created_at_week" "orders__created_at_week", sum("orders__count") "orders__count" FROM (SELECT * FROM stb_pre_aggregations.orders_d20181101) as partition_union WHERE ("orders__created_at_week" >= ($1::timestamptz::timestamptz AT TIME ZONE \'UTC\') AND "orders__created_at_week" <= ($2::timestamptz::timestamptz AT TIME ZONE \'UTC\')) GROUP BY 1 ORDER BY 1 ASC LIMIT 10000',
291
- values: ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z'],
292
- cacheKeyQueries: {
293
- renewalThreshold: 21600,
294
- queries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
295
- },
296
- preAggregations: [{
297
- preAggregationsSchema: 'stb_pre_aggregations',
298
- tableName: 'stb_pre_aggregations.orders_d20181101',
299
- loadSql: [
300
- 'CREATE TABLE stb_pre_aggregations.orders_d20181101 AS SELECT\n date_trunc(\'week\', ("orders".created_at::timestamptz AT TIME ZONE \'UTC\')) "orders__created_at_week", count("orders".id) "orders__count", sum("orders".number) "orders__number"\n FROM\n public.orders_delay AS "orders"\n WHERE ("orders".created_at >= $1::timestamptz AND "orders".created_at <= $2::timestamptz) GROUP BY 1',
301
- ['2018-11-01T00:00:00Z', '2018-11-30T23:59:59Z']
302
- ],
303
- invalidateKeyQueries: [['SELECT date_trunc(\'hour\', (NOW()::timestamptz AT TIME ZONE \'UTC\')) as current_hour', []]]
304
- }],
305
- requestId: 'intermittent empty rollup'
306
- });
307
-
308
- console.log(res);
309
-
310
- expect(res.data).toContainEqual(expect.objectContaining({ tableReady: true }));
311
- });
312
-
313
- test('continue serve old tables cache without resetting it', async () => {
314
- mockDriver.tablesQueryDelay = 300;
315
- const requestId = 'continue serve old tables cache without resetting it';
316
- const baseQuery = {
317
- query: 'SELECT * FROM stb_pre_aggregations.orders_d20181103',
318
- values: [],
319
- cacheKeyQueries: {
320
- renewalThreshold: 1,
321
- queries: []
322
- },
323
- preAggregations: [{
324
- preAggregationsSchema: 'stb_pre_aggregations',
325
- tableName: 'stb_pre_aggregations.orders_d20181103',
326
- loadSql: ['CREATE TABLE stb_pre_aggregations.orders_d20181103 AS SELECT * FROM public.orders', []],
327
- invalidateKeyQueries: [['SELECT NOW()', []]],
328
- refreshKeyRenewalThresholds: [0.001]
329
- }]
330
- };
331
-
332
- // create from scratch
333
- await queryOrchestrator.fetchQuery({
334
- ...baseQuery,
335
- requestId: `${requestId}: create from scratch`
336
- });
337
-
338
- // start renew refresh as scheduled refresh does
339
- const refresh = queryOrchestrator.fetchQuery({
340
- ...baseQuery,
341
- renewQuery: true,
342
- requestId: `${requestId}: start refresh`
343
- });
344
-
345
- await mockDriver.delay(100);
346
-
347
- let firstResolve = null;
348
-
349
- console.log('Starting race');
350
-
351
- // If database has a significant delay for pre-aggregations tables fetch we should continue serve rollup cache
352
- // instead of waiting tables fetch query to complete.
353
- await Promise.all([
354
- queryOrchestrator.fetchQuery({
355
- ...baseQuery,
356
- requestId: `${requestId}: race`
357
- }).then(() => {
358
- if (!firstResolve) {
359
- firstResolve = 'query';
360
- }
361
- }),
362
- mockDriver.delay(150).then(() => {
363
- if (!firstResolve) {
364
- firstResolve = 'delay';
365
- }
366
- })
367
- ]);
368
-
369
- await refresh;
370
-
371
- expect(firstResolve).toBe('query');
372
- });
373
- });
@@ -1,247 +0,0 @@
1
- /* globals describe, test, expect, afterAll */
2
- const QueryQueue = require('../../orchestrator/QueryQueue');
3
-
4
- const QueryQueueTest = (name, options) => {
5
- describe(`QueryQueue${name}`, () => {
6
- let delayCount = 0;
7
- const delayFn = (result, delay) => new Promise(resolve => setTimeout(() => resolve(result), delay));
8
- let cancelledQuery;
9
- const queue = new QueryQueue('test_query_queue', {
10
- queryHandlers: {
11
- foo: async (query) => `${query[0]} bar`,
12
- delay: async (query, setCancelHandler) => {
13
- const result = query.result + delayCount;
14
- delayCount += 1;
15
- await setCancelHandler(result);
16
- return delayFn(result, query.delay);
17
- }
18
- },
19
- cancelHandlers: {
20
- delay: (query) => {
21
- console.log(`cancel call: ${JSON.stringify(query)}`);
22
- cancelledQuery = query.queryKey;
23
- }
24
- },
25
- continueWaitTimeout: 1,
26
- executionTimeout: 2,
27
- orphanedTimeout: 2,
28
- concurrency: 1,
29
- ...options
30
- });
31
-
32
- afterAll(async () => {
33
- await options.redisPool.cleanup();
34
- });
35
-
36
- test('gutter', async () => {
37
- const query = ['select * from'];
38
- const result = await queue.executeInQueue('foo', query, query);
39
- expect(result).toBe('select * from bar');
40
- });
41
-
42
- test('instant double wait resolve', async () => {
43
- const results = await Promise.all([
44
- queue.executeInQueue('delay', 'instant', { delay: 400, result: '2' }),
45
- queue.executeInQueue('delay', 'instant', { delay: 400, result: '2' })
46
- ]);
47
- expect(results).toStrictEqual(['20', '20']);
48
- });
49
-
50
- test('priority', async () => {
51
- delayCount = 0;
52
- const result = await Promise.all([
53
- queue.executeInQueue('delay', '11', { delay: 600, result: '1' }, 1),
54
- queue.executeInQueue('delay', '12', { delay: 100, result: '2' }, 0),
55
- queue.executeInQueue('delay', '13', { delay: 100, result: '3' }, 10)
56
- ]);
57
- expect(parseInt(result.find(f => f[0] === '3'), 10) % 10).toBeLessThan(2);
58
- });
59
-
60
- test('timeout', async () => {
61
- delayCount = 0;
62
- const query = ['select * from 2'];
63
- let errorString = '';
64
- for (let i = 0; i < 5; i++) {
65
- try {
66
- await queue.executeInQueue('delay', query, { delay: 3000, result: '1' });
67
- console.log(`Delay ${i}`);
68
- } catch (e) {
69
- if (e.message === 'Continue wait') {
70
- // eslint-disable-next-line no-continue
71
- continue;
72
- }
73
- errorString = e.toString();
74
- break;
75
- }
76
- }
77
- expect(errorString).toEqual(expect.stringContaining('timeout'));
78
- });
79
-
80
- test('stage reporting', async () => {
81
- delayCount = 0;
82
- const resultPromise = queue.executeInQueue('delay', '1', { delay: 200, result: '1' }, 0, { stageQueryKey: '1' });
83
- await delayFn(null, 50);
84
- expect((await queue.getQueryStage('1')).stage).toBe('Executing query');
85
- await resultPromise;
86
- expect(await queue.getQueryStage('1')).toEqual(undefined);
87
- });
88
-
89
- test('priority stage reporting', async () => {
90
- delayCount = 0;
91
- const resultPromise = queue.executeInQueue('delay', '31', { delay: 200, result: '1' }, 20, { stageQueryKey: '12' });
92
- await delayFn(null, 50);
93
- const resultPromise2 = queue.executeInQueue('delay', '32', { delay: 200, result: '1' }, 10, { stageQueryKey: '12' });
94
- await delayFn(null, 50);
95
- expect((await queue.getQueryStage('12', 10)).stage).toBe('#1 in queue');
96
- await resultPromise;
97
- await resultPromise2;
98
- expect(await queue.getQueryStage('12')).toEqual(undefined);
99
- });
100
-
101
- test('negative priority', async () => {
102
- delayCount = 0;
103
- const results = [];
104
- await Promise.all([
105
- queue.executeInQueue('delay', '31', { delay: 400, result: '4' }, -10).then(r => results.push(r)),
106
- queue.executeInQueue('delay', '32', { delay: 100, result: '3' }, -9).then(r => results.push(r)),
107
- queue.executeInQueue('delay', '33', { delay: 100, result: '2' }, -8).then(r => results.push(r)),
108
- queue.executeInQueue('delay', '34', { delay: 100, result: '1' }, -7).then(r => results.push(r))
109
- ]);
110
-
111
- results.splice(0, 1);
112
-
113
- expect(results.map(r => parseInt(r[0], 10) - parseInt(results[0][0], 10))).toEqual([0, 1, 2]);
114
- });
115
-
116
- test('orphaned', async () => {
117
- for (let i = 1; i <= 4; i++) {
118
- await queue.executeInQueue('delay', `11${i}`, { delay: 50, result: `${i}` }, 0);
119
- }
120
- cancelledQuery = null;
121
- delayCount = 0;
122
-
123
- let result = queue.executeInQueue('delay', '111', { delay: 800, result: '1' }, 0);
124
- delayFn(null, 50).then(() => queue.executeInQueue('delay', '112', { delay: 800, result: '2' }, 0)).catch(e => e);
125
- delayFn(null, 60).then(() => queue.executeInQueue('delay', '113', { delay: 500, result: '3' }, 0)).catch(e => e);
126
- delayFn(null, 70).then(() => queue.executeInQueue('delay', '114', { delay: 900, result: '4' }, 0)).catch(e => e);
127
-
128
- expect(await result).toBe('10');
129
- await queue.executeInQueue('delay', '112', { delay: 800, result: '2' }, 0);
130
- result = await queue.executeInQueue('delay', '113', { delay: 900, result: '3' }, 0);
131
- expect(result).toBe('32');
132
- await delayFn(null, 200);
133
- expect(cancelledQuery).toBe('114');
134
- await queue.executeInQueue('delay', '114', { delay: 50, result: '4' }, 0);
135
- });
136
-
137
- test('removed before reconciled', async () => {
138
- const query = ['select * from'];
139
- await queue.processQuery(query);
140
- const result = await queue.executeInQueue('foo', query, query);
141
- expect(result).toBe('select * from bar');
142
- });
143
-
144
- test('queue driver lock obtain race condition', async () => {
145
- const redisClient = await queue.queueDriver.createConnection();
146
- const redisClient2 = await queue.queueDriver.createConnection();
147
- const priority = 10;
148
- const time = new Date().getTime();
149
- const keyScore = time + (10000 - priority) * 1E14;
150
-
151
- // console.log(await redisClient.getQueryAndRemove('race'));
152
- // console.log(await redisClient.getQueryAndRemove('race1'));
153
-
154
- if (redisClient.redisClient) {
155
- await redisClient2.redisClient.setAsync(redisClient.queryProcessingLockKey('race'), '100');
156
- await redisClient.redisClient.watchAsync(redisClient.queryProcessingLockKey('race'));
157
- await redisClient2.redisClient.setAsync(redisClient.queryProcessingLockKey('race'), Math.random());
158
-
159
- const res = await redisClient.redisClient.multi()
160
- .set(redisClient.queryProcessingLockKey('race'), '100')
161
- .set(redisClient.queryProcessingLockKey('race1'), '100')
162
- .execAsync();
163
-
164
- expect(res).toBe(null);
165
- await redisClient.redisClient.delAsync(redisClient.queryProcessingLockKey('race'));
166
- await redisClient.redisClient.delAsync(redisClient.queryProcessingLockKey('race1'));
167
- }
168
-
169
- await queue.reconcileQueue();
170
-
171
- await redisClient.addToQueue(
172
- keyScore, 'race', time, 'handler', ['select'], priority, { stageQueryKey: 'race' }
173
- );
174
-
175
- await redisClient.addToQueue(
176
- keyScore + 100, 'race2', time + 100, 'handler2', ['select2'], priority, { stageQueryKey: 'race2' }
177
- );
178
-
179
- const processingId1 = await redisClient.getNextProcessingId();
180
- const processingId4 = await redisClient.getNextProcessingId();
181
-
182
- await redisClient.freeProcessingLock('race', processingId1, true);
183
- await redisClient.freeProcessingLock('race2', processingId4, true);
184
-
185
- await redisClient2.retrieveForProcessing('race2', await redisClient.getNextProcessingId());
186
-
187
- const processingId = await redisClient.getNextProcessingId();
188
- const retrieve6 = await redisClient.retrieveForProcessing('race', processingId);
189
- console.log(retrieve6);
190
- expect(!!retrieve6[5]).toBe(true);
191
-
192
- console.log(await redisClient.getQueryAndRemove('race'));
193
- console.log(await redisClient.getQueryAndRemove('race2'));
194
-
195
- await queue.queueDriver.release(redisClient);
196
- await queue.queueDriver.release(redisClient2);
197
- });
198
-
199
- test('activated but lock is not acquired', async () => {
200
- const redisClient = await queue.queueDriver.createConnection();
201
- const redisClient2 = await queue.queueDriver.createConnection();
202
- const priority = 10;
203
- const time = new Date().getTime();
204
- const keyScore = time + (10000 - priority) * 1E14;
205
-
206
- await queue.reconcileQueue();
207
-
208
- await redisClient.addToQueue(
209
- keyScore, 'activated1', time, 'handler', ['select'], priority, { stageQueryKey: 'race' }
210
- );
211
-
212
- await redisClient.addToQueue(
213
- keyScore + 100, 'activated2', time + 100, 'handler2', ['select2'], priority, { stageQueryKey: 'race2' }
214
- );
215
-
216
- const processingId1 = await redisClient.getNextProcessingId();
217
- const processingId2 = await redisClient.getNextProcessingId();
218
- const processingId3 = await redisClient.getNextProcessingId();
219
-
220
- const retrieve1 = await redisClient.retrieveForProcessing('activated1', processingId1);
221
- console.log(retrieve1);
222
- const retrieve2 = await redisClient2.retrieveForProcessing('activated2', processingId2);
223
- console.log(retrieve2);
224
- console.log(await redisClient.freeProcessingLock('activated1', processingId1, retrieve1 && retrieve1[2].indexOf('activated1') !== -1));
225
- const retrieve3 = await redisClient.retrieveForProcessing('activated2', processingId3);
226
- console.log(retrieve3);
227
- console.log(await redisClient.freeProcessingLock('activated2', processingId3, retrieve3 && retrieve3[2].indexOf('activated2') !== -1));
228
- console.log(retrieve2[2].indexOf('activated2') !== -1);
229
- console.log(await redisClient2.freeProcessingLock('activated2', processingId2, retrieve2 && retrieve2[2].indexOf('activated2') !== -1));
230
-
231
- const retrieve4 = await redisClient.retrieveForProcessing('activated2', await redisClient.getNextProcessingId());
232
- console.log(retrieve4);
233
- expect(retrieve4[0]).toBe(1);
234
- expect(!!retrieve4[5]).toBe(true);
235
-
236
- console.log(await redisClient.getQueryAndRemove('activated1'));
237
- console.log(await redisClient.getQueryAndRemove('activated2'));
238
-
239
- await queue.queueDriver.release(redisClient);
240
- await queue.queueDriver.release(redisClient2);
241
- });
242
- });
243
- };
244
-
245
- QueryQueueTest('Local');
246
-
247
- module.exports = QueryQueueTest;