@budibase/backend-core 3.2.0 → 3.2.1
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/dist/index.js +1986 -1348
- package/dist/index.js.map +4 -4
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +4 -4
- package/dist/plugins.js.meta.json +1 -1
- package/dist/src/queue/inMemoryQueue.d.ts +0 -2
- package/dist/src/queue/inMemoryQueue.js +1 -11
- package/dist/src/queue/inMemoryQueue.js.map +1 -1
- package/dist/src/queue/queue.js +6 -0
- package/dist/src/queue/queue.js.map +1 -1
- package/dist/tests/core/utilities/index.d.ts +1 -0
- package/dist/tests/core/utilities/index.js +2 -1
- package/dist/tests/core/utilities/index.js.map +1 -1
- package/dist/tests/core/utilities/queue.d.ts +2 -0
- package/dist/tests/core/utilities/queue.js +21 -0
- package/dist/tests/core/utilities/queue.js.map +1 -0
- package/dist/tests/core/utilities/testContainerUtils.d.ts +2 -0
- package/dist/tests/core/utilities/testContainerUtils.js +63 -0
- package/dist/tests/core/utilities/testContainerUtils.js.map +1 -1
- package/dist/tests/core/utilities/utils/index.d.ts +1 -0
- package/dist/tests/core/utilities/utils/index.js +2 -1
- package/dist/tests/core/utilities/utils/index.js.map +1 -1
- package/dist/tests/core/utilities/utils/queue.d.ts +3 -0
- package/dist/tests/core/utilities/utils/queue.js +37 -0
- package/dist/tests/core/utilities/utils/queue.js.map +1 -0
- package/package.json +4 -4
- package/src/cache/tests/docWritethrough.spec.ts +26 -24
- package/src/queue/inMemoryQueue.ts +2 -12
- package/src/queue/queue.ts +10 -3
- package/tests/core/utilities/index.ts +1 -0
- package/tests/core/utilities/queue.ts +9 -0
- package/tests/core/utilities/testContainerUtils.ts +57 -0
- package/tests/core/utilities/utils/index.ts +1 -0
- package/tests/core/utilities/utils/queue.ts +27 -0
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.getContainerByImage = getContainerByImage;
|
|
4
13
|
exports.getContainerById = getContainerById;
|
|
5
14
|
exports.getExposedV4Ports = getExposedV4Ports;
|
|
6
15
|
exports.getExposedV4Port = getExposedV4Port;
|
|
7
16
|
exports.setupEnv = setupEnv;
|
|
17
|
+
exports.startContainer = startContainer;
|
|
8
18
|
const child_process_1 = require("child_process");
|
|
19
|
+
const lodash_1 = require("lodash");
|
|
9
20
|
const IPV4_PORT_REGEX = new RegExp(`0\\.0\\.0\\.0:(\\d+)->(\\d+)/tcp`, "g");
|
|
10
21
|
function getTestcontainers() {
|
|
11
22
|
// We use --format json to make sure the output is nice and machine-readable,
|
|
@@ -76,4 +87,56 @@ function setupEnv(...envs) {
|
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
}
|
|
90
|
+
function startContainer(container) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
const imageName = container.imageName.string;
|
|
93
|
+
let key = imageName;
|
|
94
|
+
if (imageName.includes("@sha256")) {
|
|
95
|
+
key = imageName.split("@")[0];
|
|
96
|
+
}
|
|
97
|
+
key = key.replace(/\//g, "-").replace(/:/g, "-");
|
|
98
|
+
container = container
|
|
99
|
+
.withReuse()
|
|
100
|
+
.withLabels({ "com.budibase": "true" })
|
|
101
|
+
.withName(`${key}_testcontainer`);
|
|
102
|
+
let startedContainer = undefined;
|
|
103
|
+
let lastError = undefined;
|
|
104
|
+
for (let i = 0; i < 10; i++) {
|
|
105
|
+
try {
|
|
106
|
+
// container.start() is not an idempotent operation, calling `start`
|
|
107
|
+
// modifies the internal state of a GenericContainer instance such that
|
|
108
|
+
// the hash it uses to determine reuse changes. We need to clone the
|
|
109
|
+
// container before calling start to ensure that we're using the same
|
|
110
|
+
// reuse hash every time.
|
|
111
|
+
const containerCopy = (0, lodash_1.cloneDeep)(container);
|
|
112
|
+
startedContainer = yield containerCopy.start();
|
|
113
|
+
lastError = undefined;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
lastError = e;
|
|
118
|
+
yield new Promise(resolve => setTimeout(resolve, 1000));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!startedContainer) {
|
|
122
|
+
if (lastError) {
|
|
123
|
+
throw lastError;
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`failed to start container: ${imageName}`);
|
|
126
|
+
}
|
|
127
|
+
const info = getContainerById(startedContainer.getId());
|
|
128
|
+
if (!info) {
|
|
129
|
+
throw new Error("Container not found");
|
|
130
|
+
}
|
|
131
|
+
// Some Docker runtimes, when you expose a port, will bind it to both
|
|
132
|
+
// 127.0.0.1 and ::1, so ipv4 and ipv6. The port spaces of ipv4 and ipv6
|
|
133
|
+
// addresses are not shared, and testcontainers will sometimes give you back
|
|
134
|
+
// the ipv6 port. There's no way to know that this has happened, and if you
|
|
135
|
+
// try to then connect to `localhost:port` you may attempt to bind to the v4
|
|
136
|
+
// address which could be unbound or even an entirely different container. For
|
|
137
|
+
// that reason, we don't use testcontainers' `getExposedPort` function,
|
|
138
|
+
// preferring instead our own method that guaranteed v4 ports.
|
|
139
|
+
return getExposedV4Ports(info);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
79
142
|
//# sourceMappingURL=testContainerUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testContainerUtils.js","sourceRoot":"","sources":["../../../../tests/core/utilities/testContainerUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"testContainerUtils.js","sourceRoot":"","sources":["../../../../tests/core/utilities/testContainerUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.time = void 0;
|
|
26
|
+
exports.queue = exports.time = void 0;
|
|
27
27
|
exports.time = __importStar(require("./time"));
|
|
28
|
+
exports.queue = __importStar(require("./queue"));
|
|
28
29
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../tests/core/utilities/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAA8B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../tests/core/utilities/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAA8B;AAC9B,iDAAgC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.useRealQueues = useRealQueues;
|
|
13
|
+
exports.processMessages = processMessages;
|
|
14
|
+
const testcontainers_1 = require("testcontainers");
|
|
15
|
+
const testContainerUtils_1 = require("../testContainerUtils");
|
|
16
|
+
function useRealQueues() {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
var _a;
|
|
19
|
+
const ports = yield (0, testContainerUtils_1.startContainer)(new testcontainers_1.GenericContainer("redis")
|
|
20
|
+
.withExposedPorts(6379)
|
|
21
|
+
.withWaitStrategy(testcontainers_1.Wait.forSuccessfulCommand(`redis-cli`).withStartupTimeout(10000)));
|
|
22
|
+
const port = (_a = ports.find(x => x.container === 6379)) === null || _a === void 0 ? void 0 : _a.host;
|
|
23
|
+
if (!port) {
|
|
24
|
+
throw new Error("Redis port not found");
|
|
25
|
+
}
|
|
26
|
+
process.env.BULL_TEST_REDIS_PORT = port.toString();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function processMessages(queue) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
do {
|
|
32
|
+
yield queue.whenCurrentJobsFinished();
|
|
33
|
+
} while (yield queue.count());
|
|
34
|
+
yield queue.whenCurrentJobsFinished();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../../../tests/core/utilities/utils/queue.ts"],"names":[],"mappings":";;;;;;;;;;;AAIA,sCAcC;AAED,0CAMC;AAzBD,mDAAuD;AACvD,8DAAsD;AAEtD,SAAsB,aAAa;;;QACjC,MAAM,KAAK,GAAG,MAAM,IAAA,mCAAc,EAChC,IAAI,iCAAgB,CAAC,OAAO,CAAC;aAC1B,gBAAgB,CAAC,IAAI,CAAC;aACtB,gBAAgB,CACf,qBAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CACjE,CACJ,CAAA;QAED,MAAM,IAAI,GAAG,MAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,0CAAE,IAAI,CAAA;QACxD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;QACzC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;IACpD,CAAC;CAAA;AAED,SAAsB,eAAe,CAAC,KAAY;;QAChD,GAAG,CAAC;YACF,MAAM,KAAK,CAAC,uBAAuB,EAAE,CAAA;QACvC,CAAC,QAAQ,MAAM,KAAK,CAAC,KAAK,EAAE,EAAC;QAE7B,MAAM,KAAK,CAAC,uBAAuB,EAAE,CAAA;IACvC,CAAC;CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/backend-core",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Budibase backend core libraries used in server and worker",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@budibase/nano": "10.1.5",
|
|
25
25
|
"@budibase/pouchdb-replication-stream": "1.2.11",
|
|
26
|
-
"@budibase/shared-core": "3.2.
|
|
27
|
-
"@budibase/types": "3.2.
|
|
26
|
+
"@budibase/shared-core": "3.2.1",
|
|
27
|
+
"@budibase/types": "3.2.1",
|
|
28
28
|
"aws-cloudfront-sign": "3.0.2",
|
|
29
29
|
"aws-sdk": "2.1030.0",
|
|
30
30
|
"bcrypt": "5.1.0",
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "46f5cd0a5d1c053ce787de66825dce0c03d5e559"
|
|
99
99
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import tk from "timekeeper"
|
|
2
2
|
|
|
3
3
|
import _ from "lodash"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DBTestConfiguration,
|
|
6
|
+
generator,
|
|
7
|
+
structures,
|
|
8
|
+
utils,
|
|
9
|
+
} from "../../../tests"
|
|
5
10
|
import { getDB } from "../../db"
|
|
6
11
|
|
|
7
12
|
import {
|
|
@@ -10,15 +15,14 @@ import {
|
|
|
10
15
|
init,
|
|
11
16
|
} from "../docWritethrough"
|
|
12
17
|
|
|
13
|
-
import InMemoryQueue from "../../queue/inMemoryQueue"
|
|
14
|
-
|
|
15
18
|
const initialTime = Date.now()
|
|
16
19
|
|
|
17
20
|
async function waitForQueueCompletion() {
|
|
18
|
-
|
|
19
|
-
await queue.waitForCompletion()
|
|
21
|
+
await utils.queue.processMessages(DocWritethroughProcessor.queue)
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
beforeAll(() => utils.queue.useRealQueues())
|
|
25
|
+
|
|
22
26
|
describe("docWritethrough", () => {
|
|
23
27
|
beforeAll(() => {
|
|
24
28
|
init()
|
|
@@ -67,7 +71,7 @@ describe("docWritethrough", () => {
|
|
|
67
71
|
const patch3 = generatePatchObject(3)
|
|
68
72
|
await docWritethrough.patch(patch3)
|
|
69
73
|
|
|
70
|
-
expect(await db.
|
|
74
|
+
expect(await db.tryGet(documentId)).toEqual({
|
|
71
75
|
_id: documentId,
|
|
72
76
|
...patch1,
|
|
73
77
|
...patch2,
|
|
@@ -92,7 +96,7 @@ describe("docWritethrough", () => {
|
|
|
92
96
|
|
|
93
97
|
await waitForQueueCompletion()
|
|
94
98
|
|
|
95
|
-
expect(await db.
|
|
99
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
96
100
|
expect.objectContaining({
|
|
97
101
|
_id: documentId,
|
|
98
102
|
...patch1,
|
|
@@ -117,7 +121,7 @@ describe("docWritethrough", () => {
|
|
|
117
121
|
await waitForQueueCompletion()
|
|
118
122
|
|
|
119
123
|
expect(date1).not.toEqual(date2)
|
|
120
|
-
expect(await db.
|
|
124
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
121
125
|
expect.objectContaining({
|
|
122
126
|
createdAt: date1.toISOString(),
|
|
123
127
|
updatedAt: date2.toISOString(),
|
|
@@ -135,7 +139,7 @@ describe("docWritethrough", () => {
|
|
|
135
139
|
await docWritethrough.patch(patch2)
|
|
136
140
|
|
|
137
141
|
const keyToOverride = _.sample(Object.keys(patch1))!
|
|
138
|
-
expect(await db.
|
|
142
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
139
143
|
expect.objectContaining({
|
|
140
144
|
[keyToOverride]: patch1[keyToOverride],
|
|
141
145
|
})
|
|
@@ -150,7 +154,7 @@ describe("docWritethrough", () => {
|
|
|
150
154
|
await docWritethrough.patch(patch3)
|
|
151
155
|
await waitForQueueCompletion()
|
|
152
156
|
|
|
153
|
-
expect(await db.
|
|
157
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
154
158
|
expect.objectContaining({
|
|
155
159
|
...patch1,
|
|
156
160
|
...patch2,
|
|
@@ -180,14 +184,14 @@ describe("docWritethrough", () => {
|
|
|
180
184
|
await secondDocWritethrough.patch(doc2Patch2)
|
|
181
185
|
await waitForQueueCompletion()
|
|
182
186
|
|
|
183
|
-
expect(await db.
|
|
187
|
+
expect(await db.tryGet(docWritethrough.docId)).toEqual(
|
|
184
188
|
expect.objectContaining({
|
|
185
189
|
...doc1Patch,
|
|
186
190
|
...doc1Patch2,
|
|
187
191
|
})
|
|
188
192
|
)
|
|
189
193
|
|
|
190
|
-
expect(await db.
|
|
194
|
+
expect(await db.tryGet(secondDocWritethrough.docId)).toEqual(
|
|
191
195
|
expect.objectContaining({
|
|
192
196
|
...doc2Patch,
|
|
193
197
|
...doc2Patch2,
|
|
@@ -203,7 +207,7 @@ describe("docWritethrough", () => {
|
|
|
203
207
|
await docWritethrough.patch(initialPatch)
|
|
204
208
|
await waitForQueueCompletion()
|
|
205
209
|
|
|
206
|
-
expect(await db.
|
|
210
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
207
211
|
expect.objectContaining(initialPatch)
|
|
208
212
|
)
|
|
209
213
|
|
|
@@ -214,10 +218,10 @@ describe("docWritethrough", () => {
|
|
|
214
218
|
await docWritethrough.patch(extraPatch)
|
|
215
219
|
await waitForQueueCompletion()
|
|
216
220
|
|
|
217
|
-
expect(await db.
|
|
221
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
218
222
|
expect.objectContaining(extraPatch)
|
|
219
223
|
)
|
|
220
|
-
expect(await db.
|
|
224
|
+
expect(await db.tryGet(documentId)).not.toEqual(
|
|
221
225
|
expect.objectContaining(initialPatch)
|
|
222
226
|
)
|
|
223
227
|
})
|
|
@@ -242,7 +246,7 @@ describe("docWritethrough", () => {
|
|
|
242
246
|
expect(queueMessageSpy).toHaveBeenCalledTimes(5)
|
|
243
247
|
|
|
244
248
|
await waitForQueueCompletion()
|
|
245
|
-
expect(await db.
|
|
249
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
246
250
|
expect.objectContaining(patches)
|
|
247
251
|
)
|
|
248
252
|
|
|
@@ -250,7 +254,7 @@ describe("docWritethrough", () => {
|
|
|
250
254
|
expect(queueMessageSpy).toHaveBeenCalledTimes(45)
|
|
251
255
|
|
|
252
256
|
await waitForQueueCompletion()
|
|
253
|
-
expect(await db.
|
|
257
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
254
258
|
expect.objectContaining(patches)
|
|
255
259
|
)
|
|
256
260
|
|
|
@@ -258,20 +262,18 @@ describe("docWritethrough", () => {
|
|
|
258
262
|
expect(queueMessageSpy).toHaveBeenCalledTimes(55)
|
|
259
263
|
|
|
260
264
|
await waitForQueueCompletion()
|
|
261
|
-
expect(await db.
|
|
265
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
262
266
|
expect.objectContaining(patches)
|
|
263
267
|
)
|
|
264
268
|
})
|
|
265
269
|
})
|
|
266
270
|
|
|
267
|
-
|
|
268
|
-
// eslint-disable-next-line jest/no-disabled-tests
|
|
269
|
-
it.skip("patches will execute in order", async () => {
|
|
271
|
+
it("patches will execute in order", async () => {
|
|
270
272
|
let incrementalValue = 0
|
|
271
273
|
const keyToOverride = generator.word()
|
|
272
274
|
async function incrementalPatches(count: number) {
|
|
273
275
|
for (let i = 0; i < count; i++) {
|
|
274
|
-
await docWritethrough.patch({ [keyToOverride]: incrementalValue
|
|
276
|
+
await docWritethrough.patch({ [keyToOverride]: ++incrementalValue })
|
|
275
277
|
}
|
|
276
278
|
}
|
|
277
279
|
|
|
@@ -279,13 +281,13 @@ describe("docWritethrough", () => {
|
|
|
279
281
|
await incrementalPatches(5)
|
|
280
282
|
|
|
281
283
|
await waitForQueueCompletion()
|
|
282
|
-
expect(await db.
|
|
284
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
283
285
|
expect.objectContaining({ [keyToOverride]: 5 })
|
|
284
286
|
)
|
|
285
287
|
|
|
286
288
|
await incrementalPatches(40)
|
|
287
289
|
await waitForQueueCompletion()
|
|
288
|
-
expect(await db.
|
|
290
|
+
expect(await db.tryGet(documentId)).toEqual(
|
|
289
291
|
expect.objectContaining({ [keyToOverride]: 45 })
|
|
290
292
|
)
|
|
291
293
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import events from "events"
|
|
2
|
-
import { newid
|
|
2
|
+
import { newid } from "../utils"
|
|
3
3
|
import { Queue, QueueOptions, JobOptions } from "./queue"
|
|
4
4
|
|
|
5
5
|
interface JobMessage {
|
|
@@ -141,7 +141,7 @@ class InMemoryQueue implements Partial<Queue> {
|
|
|
141
141
|
} else {
|
|
142
142
|
pushMessage()
|
|
143
143
|
}
|
|
144
|
-
return {} as any
|
|
144
|
+
return { id: jobId } as any
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
@@ -184,16 +184,6 @@ class InMemoryQueue implements Partial<Queue> {
|
|
|
184
184
|
// do nothing
|
|
185
185
|
return this as any
|
|
186
186
|
}
|
|
187
|
-
|
|
188
|
-
async waitForCompletion() {
|
|
189
|
-
do {
|
|
190
|
-
await timeout(50)
|
|
191
|
-
} while (this.hasRunningJobs())
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
hasRunningJobs() {
|
|
195
|
-
return this._addCount > this._runCount
|
|
196
|
-
}
|
|
197
187
|
}
|
|
198
188
|
|
|
199
189
|
export default InMemoryQueue
|
package/src/queue/queue.ts
CHANGED
|
@@ -15,7 +15,7 @@ const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs()
|
|
|
15
15
|
const QUEUE_LOCK_RENEW_INTERNAL_MS = Duration.fromSeconds(30).toMs()
|
|
16
16
|
// cleanup the queue every 60 seconds
|
|
17
17
|
const CLEANUP_PERIOD_MS = Duration.fromSeconds(60).toMs()
|
|
18
|
-
let QUEUES: BullQueue.Queue[]
|
|
18
|
+
let QUEUES: BullQueue.Queue[] = []
|
|
19
19
|
let cleanupInterval: NodeJS.Timeout
|
|
20
20
|
|
|
21
21
|
async function cleanup() {
|
|
@@ -45,11 +45,18 @@ export function createQueue<T>(
|
|
|
45
45
|
if (opts.jobOptions) {
|
|
46
46
|
queueConfig.defaultJobOptions = opts.jobOptions
|
|
47
47
|
}
|
|
48
|
-
let queue:
|
|
48
|
+
let queue: BullQueue.Queue<T>
|
|
49
49
|
if (!env.isTest()) {
|
|
50
50
|
queue = new BullQueue(jobQueue, queueConfig)
|
|
51
|
+
} else if (
|
|
52
|
+
process.env.BULL_TEST_REDIS_PORT &&
|
|
53
|
+
!isNaN(+process.env.BULL_TEST_REDIS_PORT)
|
|
54
|
+
) {
|
|
55
|
+
queue = new BullQueue(jobQueue, {
|
|
56
|
+
redis: { host: "localhost", port: +process.env.BULL_TEST_REDIS_PORT },
|
|
57
|
+
})
|
|
51
58
|
} else {
|
|
52
|
-
queue = new InMemoryQueue(jobQueue, queueConfig)
|
|
59
|
+
queue = new InMemoryQueue(jobQueue, queueConfig) as any
|
|
53
60
|
}
|
|
54
61
|
addListeners(queue, jobQueue, opts?.removeStalledCb)
|
|
55
62
|
QUEUES.push(queue)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execSync } from "child_process"
|
|
2
|
+
import { cloneDeep } from "lodash"
|
|
3
|
+
import { GenericContainer, StartedTestContainer } from "testcontainers"
|
|
2
4
|
|
|
3
5
|
const IPV4_PORT_REGEX = new RegExp(`0\\.0\\.0\\.0:(\\d+)->(\\d+)/tcp`, "g")
|
|
4
6
|
|
|
@@ -106,3 +108,58 @@ export function setupEnv(...envs: any[]) {
|
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
}
|
|
111
|
+
|
|
112
|
+
export async function startContainer(container: GenericContainer) {
|
|
113
|
+
const imageName = (container as any).imageName.string as string
|
|
114
|
+
let key: string = imageName
|
|
115
|
+
if (imageName.includes("@sha256")) {
|
|
116
|
+
key = imageName.split("@")[0]
|
|
117
|
+
}
|
|
118
|
+
key = key.replace(/\//g, "-").replace(/:/g, "-")
|
|
119
|
+
|
|
120
|
+
container = container
|
|
121
|
+
.withReuse()
|
|
122
|
+
.withLabels({ "com.budibase": "true" })
|
|
123
|
+
.withName(`${key}_testcontainer`)
|
|
124
|
+
|
|
125
|
+
let startedContainer: StartedTestContainer | undefined = undefined
|
|
126
|
+
let lastError = undefined
|
|
127
|
+
for (let i = 0; i < 10; i++) {
|
|
128
|
+
try {
|
|
129
|
+
// container.start() is not an idempotent operation, calling `start`
|
|
130
|
+
// modifies the internal state of a GenericContainer instance such that
|
|
131
|
+
// the hash it uses to determine reuse changes. We need to clone the
|
|
132
|
+
// container before calling start to ensure that we're using the same
|
|
133
|
+
// reuse hash every time.
|
|
134
|
+
const containerCopy = cloneDeep(container)
|
|
135
|
+
startedContainer = await containerCopy.start()
|
|
136
|
+
lastError = undefined
|
|
137
|
+
break
|
|
138
|
+
} catch (e: any) {
|
|
139
|
+
lastError = e
|
|
140
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!startedContainer) {
|
|
145
|
+
if (lastError) {
|
|
146
|
+
throw lastError
|
|
147
|
+
}
|
|
148
|
+
throw new Error(`failed to start container: ${imageName}`)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const info = getContainerById(startedContainer.getId())
|
|
152
|
+
if (!info) {
|
|
153
|
+
throw new Error("Container not found")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Some Docker runtimes, when you expose a port, will bind it to both
|
|
157
|
+
// 127.0.0.1 and ::1, so ipv4 and ipv6. The port spaces of ipv4 and ipv6
|
|
158
|
+
// addresses are not shared, and testcontainers will sometimes give you back
|
|
159
|
+
// the ipv6 port. There's no way to know that this has happened, and if you
|
|
160
|
+
// try to then connect to `localhost:port` you may attempt to bind to the v4
|
|
161
|
+
// address which could be unbound or even an entirely different container. For
|
|
162
|
+
// that reason, we don't use testcontainers' `getExposedPort` function,
|
|
163
|
+
// preferring instead our own method that guaranteed v4 ports.
|
|
164
|
+
return getExposedV4Ports(info)
|
|
165
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Queue } from "bull"
|
|
2
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
3
|
+
import { startContainer } from "../testContainerUtils"
|
|
4
|
+
|
|
5
|
+
export async function useRealQueues() {
|
|
6
|
+
const ports = await startContainer(
|
|
7
|
+
new GenericContainer("redis")
|
|
8
|
+
.withExposedPorts(6379)
|
|
9
|
+
.withWaitStrategy(
|
|
10
|
+
Wait.forSuccessfulCommand(`redis-cli`).withStartupTimeout(10000)
|
|
11
|
+
)
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const port = ports.find(x => x.container === 6379)?.host
|
|
15
|
+
if (!port) {
|
|
16
|
+
throw new Error("Redis port not found")
|
|
17
|
+
}
|
|
18
|
+
process.env.BULL_TEST_REDIS_PORT = port.toString()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function processMessages(queue: Queue) {
|
|
22
|
+
do {
|
|
23
|
+
await queue.whenCurrentJobsFinished()
|
|
24
|
+
} while (await queue.count())
|
|
25
|
+
|
|
26
|
+
await queue.whenCurrentJobsFinished()
|
|
27
|
+
}
|