@cubejs-backend/query-orchestrator 0.23.3 → 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 +38 -0
  2. package/README.md +2 -2
  3. package/dist/src/driver/BaseDriver.d.ts +36 -0
  4. package/dist/src/driver/BaseDriver.d.ts.map +1 -0
  5. package/dist/src/driver/BaseDriver.js +175 -0
  6. package/dist/src/driver/BaseDriver.js.map +1 -0
  7. package/dist/src/driver/index.d.ts +3 -0
  8. package/dist/src/driver/index.d.ts.map +1 -0
  9. package/dist/src/driver/index.js +15 -0
  10. package/dist/src/driver/index.js.map +1 -0
  11. package/dist/src/driver/utils.d.ts +2 -0
  12. package/dist/src/driver/utils.d.ts.map +1 -0
  13. package/dist/src/driver/utils.js +17 -0
  14. package/dist/src/driver/utils.js.map +1 -0
  15. package/dist/src/index.d.ts +3 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/index.js +15 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/orchestrator/BaseQueueDriver.d.ts +4 -0
  20. package/dist/src/orchestrator/BaseQueueDriver.d.ts.map +1 -0
  21. package/dist/src/orchestrator/BaseQueueDriver.js +16 -0
  22. package/dist/src/orchestrator/BaseQueueDriver.js.map +1 -0
  23. package/dist/src/orchestrator/ContinueWaitError.d.ts +3 -0
  24. package/dist/src/orchestrator/ContinueWaitError.d.ts.map +1 -0
  25. package/dist/src/orchestrator/ContinueWaitError.js +10 -0
  26. package/dist/src/orchestrator/ContinueWaitError.js.map +1 -0
  27. package/dist/src/orchestrator/LocalCacheDriver.d.ts +8 -0
  28. package/dist/src/orchestrator/LocalCacheDriver.d.ts.map +1 -0
  29. package/dist/src/orchestrator/LocalCacheDriver.js +30 -0
  30. package/dist/src/orchestrator/LocalCacheDriver.js.map +1 -0
  31. package/dist/src/orchestrator/LocalQueueDriver.d.ts +57 -0
  32. package/dist/src/orchestrator/LocalQueueDriver.d.ts.map +1 -0
  33. package/dist/src/orchestrator/LocalQueueDriver.js +230 -0
  34. package/dist/src/orchestrator/LocalQueueDriver.js.map +1 -0
  35. package/dist/src/orchestrator/PreAggregations.d.ts +26 -0
  36. package/dist/src/orchestrator/PreAggregations.d.ts.map +1 -0
  37. package/dist/src/orchestrator/PreAggregations.js +565 -0
  38. package/dist/src/orchestrator/PreAggregations.js.map +1 -0
  39. package/dist/src/orchestrator/QueryCache.d.ts +51 -0
  40. package/dist/src/orchestrator/QueryCache.d.ts.map +1 -0
  41. package/dist/src/orchestrator/QueryCache.js +293 -0
  42. package/dist/src/orchestrator/QueryCache.js.map +1 -0
  43. package/dist/src/orchestrator/QueryOrchestrator.d.ts +27 -0
  44. package/dist/src/orchestrator/QueryOrchestrator.d.ts.map +1 -0
  45. package/dist/src/orchestrator/QueryOrchestrator.js +79 -0
  46. package/dist/src/orchestrator/QueryOrchestrator.js.map +1 -0
  47. package/dist/src/orchestrator/QueryQueue.d.ts +36 -0
  48. package/dist/src/orchestrator/QueryQueue.d.ts.map +1 -0
  49. package/dist/src/orchestrator/QueryQueue.js +351 -0
  50. package/dist/src/orchestrator/QueryQueue.js.map +1 -0
  51. package/dist/src/orchestrator/RedisCacheDriver.d.ts +12 -0
  52. package/dist/src/orchestrator/RedisCacheDriver.d.ts.map +1 -0
  53. package/dist/src/orchestrator/RedisCacheDriver.js +50 -0
  54. package/dist/src/orchestrator/RedisCacheDriver.js.map +1 -0
  55. package/dist/src/orchestrator/RedisFactory.d.ts +3 -0
  56. package/dist/src/orchestrator/RedisFactory.d.ts.map +1 -0
  57. package/dist/src/orchestrator/RedisFactory.js +45 -0
  58. package/dist/src/orchestrator/RedisFactory.js.map +1 -0
  59. package/dist/src/orchestrator/RedisPool.d.ts +10 -0
  60. package/dist/src/orchestrator/RedisPool.d.ts.map +1 -0
  61. package/dist/src/orchestrator/RedisPool.js +57 -0
  62. package/dist/src/orchestrator/RedisPool.js.map +1 -0
  63. package/dist/src/orchestrator/RedisQueueDriver.d.ts +47 -0
  64. package/dist/src/orchestrator/RedisQueueDriver.d.ts.map +1 -0
  65. package/dist/src/orchestrator/RedisQueueDriver.js +253 -0
  66. package/dist/src/orchestrator/RedisQueueDriver.js.map +1 -0
  67. package/dist/src/orchestrator/TimeoutError.d.ts +4 -0
  68. package/dist/src/orchestrator/TimeoutError.d.ts.map +1 -0
  69. package/dist/src/orchestrator/TimeoutError.js +7 -0
  70. package/dist/src/orchestrator/TimeoutError.js.map +1 -0
  71. package/dist/src/orchestrator/index.d.ts +14 -0
  72. package/dist/src/orchestrator/index.d.ts.map +1 -0
  73. package/dist/src/orchestrator/index.js +26 -0
  74. package/dist/src/orchestrator/index.js.map +1 -0
  75. package/driver/BaseDriver.js +5 -221
  76. package/driver/README.md +3 -0
  77. package/driver/utils.js +8 -12
  78. package/orchestrator/BaseQueueDriver.js +5 -8
  79. package/orchestrator/ContinueWaitError.js +6 -5
  80. package/orchestrator/LocalCacheDriver.js +5 -29
  81. package/orchestrator/LocalQueueDriver.js +5 -256
  82. package/orchestrator/PreAggregations.js +4 -764
  83. package/orchestrator/QueryCache.js +5 -381
  84. package/orchestrator/QueryOrchestrator.js +5 -100
  85. package/orchestrator/QueryQueue.js +5 -378
  86. package/orchestrator/README.md +3 -0
  87. package/orchestrator/RedisCacheDriver.js +5 -45
  88. package/orchestrator/RedisFactory.js +6 -46
  89. package/orchestrator/RedisPool.js +5 -49
  90. package/orchestrator/RedisQueueDriver.js +5 -283
  91. package/orchestrator/TimeoutError.js +6 -1
  92. package/package.json +29 -10
  93. package/test/integration/PreAggregations.test.js +0 -337
  94. package/test/integration/QueryQueueRedis.test.js +0 -6
  95. package/test/unit/QueryOrchestrator.test.js +0 -301
  96. package/test/unit/QueryQueue.test.js +0 -249
@@ -1,6 +0,0 @@
1
- /* globals describe, test, expect, afterAll */
2
- const { QueryQueueTest } = require('../unit/QueryQueue.test');
3
- const RedisPool = require('../../orchestrator/RedisPool');
4
-
5
- QueryQueueTest('RedisPool', { cacheAndQueueDriver: 'redis', redisPool: new RedisPool() });
6
- QueryQueueTest('RedisNoPool', { cacheAndQueueDriver: 'redis', redisPool: new RedisPool({ poolMin: 0, poolMax: 0 }) });
@@ -1,301 +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 (this.tablesReady.find(t => query.indexOf(t) !== -1)) {
24
- promise = promise.then(res => res.concat({ tableReady: true }));
25
- }
26
-
27
- promise.cancel = () => {
28
- this.cancelledQueries.push(query);
29
- };
30
- return promise;
31
- }
32
-
33
- async getTablesQuery(schema) {
34
- return this.tables.map(t => ({ table_name: t.replace(`${schema}.`, '') }));
35
- }
36
-
37
- async createSchemaIfNotExists(schema) {
38
- this.schema = schema;
39
- return null;
40
- }
41
-
42
- loadPreAggregationIntoTable(preAggregationTableName, loadSql) {
43
- this.tables.push(preAggregationTableName.substring(0, 100));
44
- const promise = this.query(loadSql);
45
- const resPromise = promise.then(() => this.tablesReady.push(preAggregationTableName.substring(0, 100)));
46
- resPromise.cancel = promise.cancel;
47
- return resPromise;
48
- }
49
-
50
- async dropTable(tableName) {
51
- this.tables = this.tables.filter(t => t !== tableName);
52
- return this.query(`DROP TABLE ${tableName}`);
53
- }
54
- }
55
-
56
- describe('QueryOrchestrator', () => {
57
- let mockDriver = null;
58
- const queryOrchestrator = new QueryOrchestrator(
59
- 'TEST',
60
- async () => mockDriver,
61
- (msg, params) => console.log(msg, params), {
62
- preAggregationsOptions: {
63
- queueOptions: {
64
- executionTimeout: 1
65
- },
66
- usedTablePersistTime: 1
67
- }
68
- }
69
- );
70
-
71
- beforeEach(() => {
72
- mockDriver = new MockDriver();
73
- });
74
-
75
- afterEach(async () => {
76
- await queryOrchestrator.cleanup();
77
- });
78
-
79
- test('basic', async () => {
80
- const query = {
81
- 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",
82
- values: ["2019-11-01T00:00:00Z", "2019-11-30T23:59:59Z"],
83
- cacheKeyQueries: {
84
- renewalThreshold: 21600,
85
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
86
- },
87
- preAggregations: [{
88
- preAggregationsSchema: "stb_pre_aggregations",
89
- tableName: "stb_pre_aggregations.orders_number_and_count20191101",
90
- 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"]],
91
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
92
- }],
93
- renewQuery: true,
94
- requestId: 'basic'
95
- };
96
- const result = await queryOrchestrator.fetchQuery(query);
97
- console.log(result.data[0]);
98
- expect(result.data[0]).toMatch(/orders_number_and_count20191101_kjypcoio_5yftl5il/);
99
- });
100
-
101
- test('indexes', async () => {
102
- const query = {
103
- 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",
104
- values: ["2019-11-01T00:00:00Z", "2019-11-30T23:59:59Z"],
105
- cacheKeyQueries: {
106
- renewalThreshold: 21600,
107
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
108
- },
109
- preAggregations: [{
110
- preAggregationsSchema: "stb_pre_aggregations",
111
- tableName: "stb_pre_aggregations.orders_number_and_count20191101",
112
- 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"]],
113
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]],
114
- indexesSql: [{
115
- sql: ['CREATE INDEX orders_number_and_count_week20191101 ON stb_pre_aggregations.orders_number_and_count20191101 ("orders__created_at_week")', []],
116
- indexName: 'orders_number_and_count_week20191101'
117
- }],
118
- }],
119
- renewQuery: true,
120
- requestId: 'indexes'
121
- };
122
- const result = await queryOrchestrator.fetchQuery(query);
123
- console.log(result.data[0]);
124
- expect(result.data[0]).toMatch(/orders_number_and_count20191101_l3kvjcmu_khbemovd/);
125
- expect(mockDriver.executedQueries.join(',')).toMatch(/CREATE INDEX orders_number_and_count_week20191101_l3kvjcmu_khbemovd/);
126
- });
127
-
128
- test('silent truncate', async () => {
129
- const query = {
130
- 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",
131
- values: ["2019-11-01T00:00:00Z", "2019-11-30T23:59:59Z"],
132
- cacheKeyQueries: {
133
- renewalThreshold: 21600,
134
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
135
- },
136
- preAggregations: [{
137
- preAggregationsSchema: "stb_pre_aggregations",
138
- tableName: "stb_pre_aggregations.orders_number_and_count_and_very_very_very_very_very_very_long20191101",
139
- 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"]],
140
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]],
141
- }],
142
- renewQuery: true,
143
- requestId: 'silent truncate'
144
- };
145
- let thrown = true;
146
- try {
147
- await queryOrchestrator.fetchQuery(query);
148
- thrown = false;
149
- } catch (e) {
150
- expect(e.message).toMatch(/Pre-aggregation table is not found/);
151
- }
152
- expect(thrown).toBe(true);
153
- });
154
-
155
- test('cancel pre-aggregation', async () => {
156
- const query = {
157
- 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",
158
- values: ["2018-11-01T00:00:00Z", "2018-11-30T23:59:59Z"],
159
- cacheKeyQueries: {
160
- renewalThreshold: 21600,
161
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
162
- },
163
- preAggregations: [{
164
- preAggregationsSchema: "stb_pre_aggregations",
165
- tableName: "stb_pre_aggregations.orders_number_and_count20181101",
166
- 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"]],
167
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
168
- }],
169
- renewQuery: true,
170
- requestId: 'cancel pre-aggregation'
171
- };
172
- try {
173
- await queryOrchestrator.fetchQuery(query);
174
- } catch (e) {
175
- expect(e.toString()).toMatch(/timeout/);
176
- }
177
- expect(mockDriver.cancelledQueries[0]).toMatch(/orders_too_big/);
178
- });
179
-
180
- test('save structure versions', async () => {
181
- mockDriver.tables = [];
182
- await queryOrchestrator.fetchQuery({
183
- query: `SELECT * FROM stb_pre_aggregations.orders`,
184
- values: [],
185
- cacheKeyQueries: {
186
- renewalThreshold: 21600,
187
- queries: []
188
- },
189
- preAggregations: [{
190
- preAggregationsSchema: "stb_pre_aggregations",
191
- tableName: "stb_pre_aggregations.orders",
192
- loadSql: ["CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders", []],
193
- invalidateKeyQueries: [["SELECT 1", []]]
194
- }],
195
- renewQuery: true,
196
- requestId: 'save structure versions'
197
- });
198
-
199
- await queryOrchestrator.fetchQuery({
200
- query: `SELECT * FROM stb_pre_aggregations.orders`,
201
- values: [],
202
- cacheKeyQueries: {
203
- renewalThreshold: 21600,
204
- queries: []
205
- },
206
- preAggregations: [{
207
- preAggregationsSchema: "stb_pre_aggregations",
208
- tableName: "stb_pre_aggregations.orders",
209
- loadSql: ["CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders1", []],
210
- invalidateKeyQueries: [["SELECT 1", []]]
211
- }],
212
- renewQuery: true,
213
- requestId: 'save structure versions'
214
- });
215
-
216
- await new Promise(resolve => setTimeout(() => resolve(), 1000));
217
-
218
- for (let i = 0; i < 5; i++) {
219
- await queryOrchestrator.fetchQuery({
220
- query: `SELECT * FROM stb_pre_aggregations.orders`,
221
- values: [],
222
- cacheKeyQueries: {
223
- renewalThreshold: 21600,
224
- queries: []
225
- },
226
- preAggregations: [{
227
- preAggregationsSchema: "stb_pre_aggregations",
228
- tableName: "stb_pre_aggregations.orders",
229
- loadSql: ["CREATE TABLE stb_pre_aggregations.orders AS SELECT * FROM public.orders", []],
230
- invalidateKeyQueries: [["SELECT 2", []]]
231
- }],
232
- renewQuery: true,
233
- requestId: 'save structure versions'
234
- });
235
- }
236
- expect(mockDriver.tables).toContainEqual(expect.stringMatching(/orders_f5v4jw3p_4eysppzt/));
237
- expect(mockDriver.tables).toContainEqual(expect.stringMatching(/orders_mjooke4_ezlvkhjl/));
238
- });
239
-
240
- test('intermittent empty rollup', async () => {
241
- const firstQuery = queryOrchestrator.fetchQuery({
242
- query: `SELECT * FROM stb_pre_aggregations.orders_d20181102`,
243
- values: [],
244
- cacheKeyQueries: {
245
- renewalThreshold: 21600,
246
- queries: []
247
- },
248
- preAggregations: [{
249
- preAggregationsSchema: "stb_pre_aggregations",
250
- tableName: "stb_pre_aggregations.orders_d20181102",
251
- loadSql: ["CREATE TABLE stb_pre_aggregations.orders_d20181102 AS SELECT * FROM public.orders_delay", []],
252
- invalidateKeyQueries: [["SELECT 2", []]]
253
- }],
254
- requestId: 'intermittent empty rollup'
255
- });
256
-
257
- queryOrchestrator.fetchQuery({
258
- 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",
259
- values: ["2018-11-01T00:00:00Z", "2018-11-30T23:59:59Z"],
260
- cacheKeyQueries: {
261
- renewalThreshold: 21600,
262
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
263
- },
264
- preAggregations: [{
265
- preAggregationsSchema: "stb_pre_aggregations",
266
- tableName: "stb_pre_aggregations.orders_d20181101",
267
- loadSql: [
268
- "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",
269
- ["2018-11-01T00:00:00Z", "2018-11-30T23:59:59Z"]
270
- ],
271
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
272
- }],
273
- requestId: 'intermittent empty rollup'
274
- });
275
-
276
- await firstQuery;
277
-
278
- const res = await queryOrchestrator.fetchQuery({
279
- 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",
280
- values: ["2018-11-01T00:00:00Z", "2018-11-30T23:59:59Z"],
281
- cacheKeyQueries: {
282
- renewalThreshold: 21600,
283
- queries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
284
- },
285
- preAggregations: [{
286
- preAggregationsSchema: "stb_pre_aggregations",
287
- tableName: "stb_pre_aggregations.orders_d20181101",
288
- loadSql: [
289
- "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",
290
- ["2018-11-01T00:00:00Z", "2018-11-30T23:59:59Z"]
291
- ],
292
- invalidateKeyQueries: [["SELECT date_trunc('hour', (NOW()::timestamptz AT TIME ZONE 'UTC')) as current_hour", []]]
293
- }],
294
- requestId: 'intermittent empty rollup'
295
- });
296
-
297
- console.log(res);
298
-
299
- expect(res.data).toContainEqual(expect.objectContaining({ tableReady: true }));
300
- });
301
- });
@@ -1,249 +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
-
61
- test('timeout', async () => {
62
- delayCount = 0;
63
- const query = ['select * from 2'];
64
- let errorString = '';
65
- for (let i = 0; i < 5; i++) {
66
- try {
67
- await queue.executeInQueue('delay', query, { delay: 3000, result: '1' });
68
- console.log(`Delay ${i}`);
69
- } catch (e) {
70
- if (e.message === 'Continue wait') {
71
- // eslint-disable-next-line no-continue
72
- continue;
73
- }
74
- errorString = e.toString();
75
- break;
76
- }
77
- }
78
- expect(errorString).toEqual(expect.stringContaining('timeout'));
79
- });
80
-
81
-
82
- test('stage reporting', async () => {
83
- delayCount = 0;
84
- const resultPromise = queue.executeInQueue('delay', '1', { delay: 200, result: '1' }, 0, { stageQueryKey: '1' });
85
- await delayFn(null, 50);
86
- expect((await queue.getQueryStage('1')).stage).toBe('Executing query');
87
- await resultPromise;
88
- expect(await queue.getQueryStage('1')).toEqual(undefined);
89
- });
90
-
91
- test('priority stage reporting', async () => {
92
- delayCount = 0;
93
- const resultPromise = queue.executeInQueue('delay', '31', { delay: 200, result: '1' }, 20, { stageQueryKey: '12' });
94
- await delayFn(null, 50);
95
- const resultPromise2 = queue.executeInQueue('delay', '32', { delay: 200, result: '1' }, 10, { stageQueryKey: '12' });
96
- await delayFn(null, 50);
97
- expect((await queue.getQueryStage('12', 10)).stage).toBe('#1 in queue');
98
- await resultPromise;
99
- await resultPromise2;
100
- expect(await queue.getQueryStage('12')).toEqual(undefined);
101
- });
102
-
103
- test('negative priority', async () => {
104
- delayCount = 0;
105
- const results = [];
106
- await Promise.all([
107
- queue.executeInQueue('delay', '31', { delay: 400, result: '4' }, -10).then(r => results.push(r)),
108
- queue.executeInQueue('delay', '32', { delay: 100, result: '3' }, -9).then(r => results.push(r)),
109
- queue.executeInQueue('delay', '33', { delay: 100, result: '2' }, -8).then(r => results.push(r)),
110
- queue.executeInQueue('delay', '34', { delay: 100, result: '1' }, -7).then(r => results.push(r))
111
- ]);
112
-
113
- results.splice(0, 1);
114
-
115
- expect(results.map(r => parseInt(r[0], 10) - parseInt(results[0][0], 10))).toEqual([0, 1, 2]);
116
- });
117
-
118
- test('orphaned', async () => {
119
- for (let i = 1; i <= 4; i++) {
120
- await queue.executeInQueue('delay', `11${i}`, { delay: 50, result: `${i}` }, 0);
121
- }
122
- cancelledQuery = null;
123
- delayCount = 0;
124
-
125
- let result = queue.executeInQueue('delay', `111`, { delay: 800, result: '1' }, 0);
126
- delayFn(null, 50).then(() => queue.executeInQueue('delay', `112`, { delay: 800, result: '2' }, 0)).catch(e => e);
127
- delayFn(null, 60).then(() => queue.executeInQueue('delay', `113`, { delay: 500, result: '3' }, 0)).catch(e => e);
128
- delayFn(null, 70).then(() => queue.executeInQueue('delay', `114`, { delay: 900, result: '4' }, 0)).catch(e => e);
129
-
130
- expect(await result).toBe('10');
131
- await queue.executeInQueue('delay', `112`, { delay: 800, result: '2' }, 0);
132
- result = await queue.executeInQueue('delay', `113`, { delay: 900, result: '3' }, 0);
133
- expect(result).toBe('32');
134
- await delayFn(null, 200);
135
- expect(cancelledQuery).toBe('114');
136
- await queue.executeInQueue('delay', `114`, { delay: 50, result: '4' }, 0);
137
- });
138
-
139
- test('removed before reconciled', async () => {
140
- const query = ['select * from'];
141
- await queue.processQuery(query);
142
- const result = await queue.executeInQueue('foo', query, query);
143
- expect(result).toBe('select * from bar');
144
- });
145
-
146
- test('queue driver lock obtain race condition', async () => {
147
- const redisClient = await queue.queueDriver.createConnection();
148
- const redisClient2 = await queue.queueDriver.createConnection();
149
- const priority = 10;
150
- const time = new Date().getTime();
151
- const keyScore = time + (10000 - priority) * 1E14;
152
-
153
- // console.log(await redisClient.getQueryAndRemove('race'));
154
- // console.log(await redisClient.getQueryAndRemove('race1'));
155
-
156
- if (redisClient.redisClient) {
157
- await redisClient2.redisClient.setAsync(redisClient.queryProcessingLockKey('race'), '100');
158
- await redisClient.redisClient.watchAsync(redisClient.queryProcessingLockKey('race'));
159
- await redisClient2.redisClient.setAsync(redisClient.queryProcessingLockKey('race'), Math.random());
160
-
161
- const res = await redisClient.redisClient.multi()
162
- .set(redisClient.queryProcessingLockKey('race'), '100')
163
- .set(redisClient.queryProcessingLockKey('race1'), '100')
164
- .execAsync();
165
-
166
- expect(res).toBe(null);
167
- await redisClient.redisClient.delAsync(redisClient.queryProcessingLockKey('race'));
168
- await redisClient.redisClient.delAsync(redisClient.queryProcessingLockKey('race1'));
169
- }
170
-
171
- await queue.reconcileQueue();
172
-
173
- await redisClient.addToQueue(
174
- keyScore, 'race', time, 'handler', ['select'], priority, { stageQueryKey: 'race' }
175
- );
176
-
177
- await redisClient.addToQueue(
178
- keyScore + 100, 'race2', time + 100, 'handler2', ['select2'], priority, { stageQueryKey: 'race2' }
179
- );
180
-
181
- const processingId1 = await redisClient.getNextProcessingId();
182
- const processingId4 = await redisClient.getNextProcessingId();
183
-
184
- await redisClient.freeProcessingLock('race', processingId1, true);
185
- await redisClient.freeProcessingLock('race2', processingId4, true);
186
-
187
- await redisClient2.retrieveForProcessing('race2', await redisClient.getNextProcessingId());
188
-
189
- const processingId = await redisClient.getNextProcessingId();
190
- const retrieve6 = await redisClient.retrieveForProcessing('race', processingId);
191
- console.log(retrieve6);
192
- expect(!!retrieve6[5]).toBe(true);
193
-
194
- console.log(await redisClient.getQueryAndRemove('race'));
195
- console.log(await redisClient.getQueryAndRemove('race2'));
196
-
197
- await queue.queueDriver.release(redisClient);
198
- await queue.queueDriver.release(redisClient2);
199
- });
200
-
201
- test('activated but lock is not acquired', async () => {
202
- const redisClient = await queue.queueDriver.createConnection();
203
- const redisClient2 = await queue.queueDriver.createConnection();
204
- const priority = 10;
205
- const time = new Date().getTime();
206
- const keyScore = time + (10000 - priority) * 1E14;
207
-
208
- await queue.reconcileQueue();
209
-
210
- await redisClient.addToQueue(
211
- keyScore, 'activated1', time, 'handler', ['select'], priority, { stageQueryKey: 'race' }
212
- );
213
-
214
- await redisClient.addToQueue(
215
- keyScore + 100, 'activated2', time + 100, 'handler2', ['select2'], priority, { stageQueryKey: 'race2' }
216
- );
217
-
218
- const processingId1 = await redisClient.getNextProcessingId();
219
- const processingId2 = await redisClient.getNextProcessingId();
220
- const processingId3 = await redisClient.getNextProcessingId();
221
-
222
- const retrieve1 = await redisClient.retrieveForProcessing('activated1', processingId1);
223
- console.log(retrieve1);
224
- const retrieve2 = await redisClient2.retrieveForProcessing('activated2', processingId2);
225
- console.log(retrieve2);
226
- console.log(await redisClient.freeProcessingLock('activated1', processingId1, retrieve1 && retrieve1[2].indexOf('activated1') !== -1));
227
- const retrieve3 = await redisClient.retrieveForProcessing('activated2', processingId3);
228
- console.log(retrieve3);
229
- console.log(await redisClient.freeProcessingLock('activated2', processingId3, retrieve3 && retrieve3[2].indexOf('activated2') !== -1));
230
- console.log(retrieve2[2].indexOf('activated2') !== -1);
231
- console.log(await redisClient2.freeProcessingLock('activated2', processingId2, retrieve2 && retrieve2[2].indexOf('activated2') !== -1));
232
-
233
- const retrieve4 = await redisClient.retrieveForProcessing('activated2', await redisClient.getNextProcessingId());
234
- console.log(retrieve4);
235
- expect(retrieve4[0]).toBe(1);
236
- expect(!!retrieve4[5]).toBe(true);
237
-
238
- console.log(await redisClient.getQueryAndRemove('activated1'));
239
- console.log(await redisClient.getQueryAndRemove('activated2'));
240
-
241
- await queue.queueDriver.release(redisClient);
242
- await queue.queueDriver.release(redisClient2);
243
- });
244
- });
245
- };
246
-
247
- QueryQueueTest('Local');
248
-
249
- module.exports = QueryQueueTest;