@alanszp/queue 7.9.0
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/.gitignore +3 -0
- package/.npmignore +3 -0
- package/LICENSE +21 -0
- package/dist/connectionManager.d.ts +15 -0
- package/dist/connectionManager.js +48 -0
- package/dist/connectionManager.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/queue/createQueue.d.ts +3 -0
- package/dist/queue/createQueue.js +14 -0
- package/dist/queue/createQueue.js.map +1 -0
- package/dist/queue/queue.d.ts +10 -0
- package/dist/queue/queue.js +57 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/queueRepository.d.ts +9 -0
- package/dist/queue/queueRepository.js +23 -0
- package/dist/queue/queueRepository.js.map +1 -0
- package/dist/runner.d.ts +1 -0
- package/dist/runner.js +27 -0
- package/dist/runner.js.map +1 -0
- package/dist/shutdownQueue.d.ts +1 -0
- package/dist/shutdownQueue.js +26 -0
- package/dist/shutdownQueue.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/worker/worker.d.ts +35 -0
- package/dist/worker/worker.js +99 -0
- package/dist/worker/worker.js.map +1 -0
- package/dist/worker/workerRepository.d.ts +24 -0
- package/dist/worker/workerRepository.js +53 -0
- package/dist/worker/workerRepository.js.map +1 -0
- package/package.json +34 -0
- package/src/connectionManager.ts +61 -0
- package/src/index.ts +7 -0
- package/src/queue/createQueue.ts +11 -0
- package/src/queue/queue.ts +55 -0
- package/src/queue/queueRepository.ts +27 -0
- package/src/runner.ts +16 -0
- package/src/shutdownQueue.ts +12 -0
- package/src/types.ts +14 -0
- package/src/worker/worker.ts +134 -0
- package/src/worker/workerRepository.ts +75 -0
- package/tsconfig.json +16 -0
package/.gitignore
ADDED
package/.npmignore
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Alan Szpigiel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ILogger } from "@alanszp/logger";
|
|
2
|
+
import Redis, { RedisOptions } from "ioredis";
|
|
3
|
+
export declare class ConnectionManager {
|
|
4
|
+
private static instance;
|
|
5
|
+
private connection;
|
|
6
|
+
private redisConfiguration;
|
|
7
|
+
private serviceName;
|
|
8
|
+
getLogger: () => ILogger;
|
|
9
|
+
static getInstance(): ConnectionManager;
|
|
10
|
+
private connect;
|
|
11
|
+
setConfiguration(configuration: RedisOptions, service: string, getLogger: () => ILogger): void;
|
|
12
|
+
getServiceName(): string;
|
|
13
|
+
getConnection(): Redis;
|
|
14
|
+
close(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ConnectionManager = void 0;
|
|
7
|
+
const ioredis_1 = __importDefault(require("ioredis"));
|
|
8
|
+
class ConnectionManager {
|
|
9
|
+
static getInstance() {
|
|
10
|
+
if (!ConnectionManager.instance) {
|
|
11
|
+
ConnectionManager.instance = new ConnectionManager();
|
|
12
|
+
}
|
|
13
|
+
return ConnectionManager.instance;
|
|
14
|
+
}
|
|
15
|
+
connect() {
|
|
16
|
+
if (!this.redisConfiguration) {
|
|
17
|
+
throw new Error("Redis configuration required");
|
|
18
|
+
}
|
|
19
|
+
const redis = new ioredis_1.default(this.redisConfiguration);
|
|
20
|
+
redis.on("connect", () => this.getLogger().info("redis.connected"));
|
|
21
|
+
redis.on("ready", () => this.getLogger().info("redis.ready"));
|
|
22
|
+
redis.on("error", (error) => this.getLogger().error("redis.error", { error }));
|
|
23
|
+
redis.on("close", () => this.getLogger().warn("redis.closed"));
|
|
24
|
+
redis.on("reconnecting", () => this.getLogger().info("redis.reconnecting"));
|
|
25
|
+
redis.on("end", () => this.getLogger().info("redis.ended"));
|
|
26
|
+
redis.on("wait", () => this.getLogger().info("redis.waiting"));
|
|
27
|
+
return redis;
|
|
28
|
+
}
|
|
29
|
+
setConfiguration(configuration, service, getLogger) {
|
|
30
|
+
this.redisConfiguration = configuration;
|
|
31
|
+
this.serviceName = service;
|
|
32
|
+
this.getLogger = getLogger;
|
|
33
|
+
}
|
|
34
|
+
getServiceName() {
|
|
35
|
+
return this.serviceName;
|
|
36
|
+
}
|
|
37
|
+
getConnection() {
|
|
38
|
+
if (!this.connection) {
|
|
39
|
+
this.connection = this.connect();
|
|
40
|
+
}
|
|
41
|
+
return this.connection;
|
|
42
|
+
}
|
|
43
|
+
close() {
|
|
44
|
+
this.connection.disconnect();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.ConnectionManager = ConnectionManager;
|
|
48
|
+
//# sourceMappingURL=connectionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectionManager.js","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":";;;;;;AACA,sDAA8C;AAE9C,MAAa,iBAAiB;IAWrB,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE;YAC/B,iBAAiB,CAAC,QAAQ,GAAG,IAAI,iBAAiB,EAAE,CAAC;SACtD;QAED,OAAO,iBAAiB,CAAC,QAAQ,CAAC;IACpC,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;SACjD;QAED,MAAM,KAAK,GAAG,IAAI,iBAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACjD,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACpE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACtF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC5E,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;QAE/D,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,gBAAgB,CAAC,aAA2B,EAAE,OAAe,EAAE,SAAwB;QAC5F,IAAI,CAAC,kBAAkB,GAAG,aAAa,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAEM,cAAc;QACnB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAEM,aAAa;QAClB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YACpB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;SAClC;QAED,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC;CACF;AAzDD,8CAyDC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./queue/createQueue"), exports);
|
|
14
|
+
__exportStar(require("./connectionManager"), exports);
|
|
15
|
+
__exportStar(require("./shutdownQueue"), exports);
|
|
16
|
+
__exportStar(require("./types"), exports);
|
|
17
|
+
__exportStar(require("./runner"), exports);
|
|
18
|
+
__exportStar(require("./worker/worker"), exports);
|
|
19
|
+
__exportStar(require("./worker/workerRepository"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sDAAoC;AACpC,sDAAoC;AACpC,kDAAgC;AAChC,0CAAwB;AACxB,2CAAyB;AACzB,kDAAgC;AAChC,4DAA0C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createQueue = void 0;
|
|
4
|
+
const queue_1 = require("./queue");
|
|
5
|
+
const queueRepository_1 = require("./queueRepository");
|
|
6
|
+
const connectionManager_1 = require("../connectionManager");
|
|
7
|
+
function createQueue(name, queueOptions) {
|
|
8
|
+
const connection = connectionManager_1.ConnectionManager.getInstance().getConnection();
|
|
9
|
+
const queue = new queue_1.Queue(connection, name, connectionManager_1.ConnectionManager.getInstance().getServiceName(), queueOptions);
|
|
10
|
+
queueRepository_1.QueueRepository.Instance.registerQueue(queue);
|
|
11
|
+
return queue;
|
|
12
|
+
}
|
|
13
|
+
exports.createQueue = createQueue;
|
|
14
|
+
//# sourceMappingURL=createQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createQueue.js","sourceRoot":"","sources":["../../src/queue/createQueue.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAChC,uDAAoD;AACpD,4DAAyD;AAGzD,SAAgB,WAAW,CAAoB,IAAY,EAAE,YAA2B;IACtF,MAAM,UAAU,GAAG,qCAAiB,CAAC,WAAW,EAAE,CAAC,aAAa,EAAE,CAAC;IACnE,MAAM,KAAK,GAAG,IAAI,aAAK,CAAU,UAAU,EAAE,IAAI,EAAE,qCAAiB,CAAC,WAAW,EAAE,CAAC,cAAc,EAAE,EAAE,YAAY,CAAC,CAAC;IACnH,iCAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AALD,kCAKC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConnectionOptions, JobData, QueueOptions } from "../types";
|
|
2
|
+
export declare class Queue<JobType = JobData> {
|
|
3
|
+
private _queue;
|
|
4
|
+
private name;
|
|
5
|
+
constructor(connection: ConnectionOptions, name: string, prefix: string, queueOptions?: QueueOptions);
|
|
6
|
+
publishJob(job: JobType): Promise<void>;
|
|
7
|
+
publishBulkJob(jobDatas: JobType[]): Promise<void>;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
private get queue();
|
|
10
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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.Queue = void 0;
|
|
13
|
+
const lodash_1 = require("lodash");
|
|
14
|
+
const types_1 = require("../types");
|
|
15
|
+
const BULL_PREFIX = "b";
|
|
16
|
+
const DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS = 60 * 60 * 24 * 30;
|
|
17
|
+
const DEFAULT_COMPLETED_JOB_MAX_COUNT = 500;
|
|
18
|
+
class Queue {
|
|
19
|
+
constructor(connection, name, prefix, queueOptions) {
|
|
20
|
+
this.name = name;
|
|
21
|
+
this._queue = new types_1.RawQueue(name, Object.assign(Object.assign({}, (0, lodash_1.merge)({
|
|
22
|
+
defaultJobOptions: {
|
|
23
|
+
removeOnComplete: {
|
|
24
|
+
age: DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS,
|
|
25
|
+
count: DEFAULT_COMPLETED_JOB_MAX_COUNT,
|
|
26
|
+
},
|
|
27
|
+
attempts: 3,
|
|
28
|
+
backoff: {
|
|
29
|
+
type: "exponential",
|
|
30
|
+
delay: 3000,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
connection,
|
|
34
|
+
}, queueOptions || {})), { prefix: `{${prefix}}:${BULL_PREFIX}` }));
|
|
35
|
+
}
|
|
36
|
+
publishJob(job) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
yield this.queue.add(this.name, job);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
publishBulkJob(jobDatas) {
|
|
42
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const jobs = jobDatas.map((data) => ({ name: this.name, data }));
|
|
44
|
+
yield this.queue.addBulk(jobs);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
close() {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
yield this.queue.close();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
get queue() {
|
|
53
|
+
return this._queue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.Queue = Queue;
|
|
57
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/queue/queue.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mCAA+B;AAC/B,oCAA8E;AAE9E,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAM,wCAAwC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACnE,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAE5C,MAAa,KAAK;IAKhB,YAAY,UAA6B,EAAE,IAAY,EAAE,MAAc,EAAE,YAA2B;QAClG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,IAAI,CAAC,MAAM,GAAG,IAAI,gBAAQ,CAAU,IAAI,kCACnC,IAAA,cAAK,EACN;YACE,iBAAiB,EAAE;gBACjB,gBAAgB,EAAE;oBAChB,GAAG,EAAE,wCAAwC;oBAC7C,KAAK,EAAE,+BAA+B;iBACvC;gBACD,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE;oBACP,IAAI,EAAE,aAAa;oBACnB,KAAK,EAAE,IAAI;iBACZ;aACF;YACD,UAAU;SACX,EACD,YAAY,IAAI,EAAE,CACnB,GACE,EAAE,MAAM,EAAE,IAAI,MAAM,KAAK,WAAW,EAAE,EAAE,EAC3C,CAAC;IACL,CAAC;IAEK,UAAU,CAAC,GAAY;;YAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvC,CAAC;KAAA;IAEK,cAAc,CAAC,QAAmB;;YACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACjE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;KAAA;IAEK,KAAK;;YACT,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC;KAAA;IAED,IAAY,KAAK;QACf,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AA9CD,sBA8CC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Queue } from "./queue";
|
|
2
|
+
export declare class QueueRepository {
|
|
3
|
+
private _queues;
|
|
4
|
+
private static _instance;
|
|
5
|
+
private constructor();
|
|
6
|
+
static get Instance(): QueueRepository;
|
|
7
|
+
registerQueue(queue: Queue): void;
|
|
8
|
+
getCloseConnections(): Promise<void>[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueueRepository = void 0;
|
|
4
|
+
class QueueRepository {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._queues = [];
|
|
7
|
+
}
|
|
8
|
+
static get Instance() {
|
|
9
|
+
if (this._instance) {
|
|
10
|
+
return this._instance;
|
|
11
|
+
}
|
|
12
|
+
this._instance = new this();
|
|
13
|
+
return this._instance;
|
|
14
|
+
}
|
|
15
|
+
registerQueue(queue) {
|
|
16
|
+
this._queues.push(queue);
|
|
17
|
+
}
|
|
18
|
+
getCloseConnections() {
|
|
19
|
+
return this._queues.map((q) => q.close());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.QueueRepository = QueueRepository;
|
|
23
|
+
//# sourceMappingURL=queueRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queueRepository.js","sourceRoot":"","sources":["../../src/queue/queueRepository.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAe;IAK1B;QACE,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;IAEM,MAAM,KAAK,QAAQ;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,OAAO,IAAI,CAAC,SAAS,CAAC;SACvB;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAEM,aAAa,CAAC,KAAY;QAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAEM,mBAAmB;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5C,CAAC;CACF;AAxBD,0CAwBC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runWorkers(queueNames: string): Promise<void>;
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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.runWorkers = void 0;
|
|
13
|
+
const connectionManager_1 = require("./connectionManager");
|
|
14
|
+
const workerRepository_1 = require("./worker/workerRepository");
|
|
15
|
+
function runWorkers(queueNames) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
if (queueNames.length === 0) {
|
|
18
|
+
connectionManager_1.ConnectionManager.getInstance().getLogger().warn("worker.env.workers_queues_empty");
|
|
19
|
+
return Promise.resolve();
|
|
20
|
+
}
|
|
21
|
+
const queues = queueNames.replace(/\s/g, "").split(",");
|
|
22
|
+
const workers = workerRepository_1.WorkerRepository.Instance.getWorkersByQueues(queues);
|
|
23
|
+
yield Promise.all(workers.map((worker) => worker.run()));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
exports.runWorkers = runWorkers;
|
|
27
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,2DAAwD;AAExD,gEAA6D;AAE7D,SAAsB,UAAU,CAAC,UAAkB;;QACjD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE;YAC3B,qCAAiB,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YACpF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC1B;QAED,MAAM,MAAM,GAAa,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,mCAAgB,CAAC,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAErE,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;CAAA;AAVD,gCAUC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function shutdownQueue(): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
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.shutdownQueue = void 0;
|
|
13
|
+
const queueRepository_1 = require("./queue/queueRepository");
|
|
14
|
+
const connectionManager_1 = require("./connectionManager");
|
|
15
|
+
const workerRepository_1 = require("./worker/workerRepository");
|
|
16
|
+
function shutdownQueue() {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
yield Promise.all([
|
|
19
|
+
...workerRepository_1.WorkerRepository.Instance.getCloseConnections(),
|
|
20
|
+
...queueRepository_1.QueueRepository.Instance.getCloseConnections(),
|
|
21
|
+
]);
|
|
22
|
+
connectionManager_1.ConnectionManager.getInstance().close();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
exports.shutdownQueue = shutdownQueue;
|
|
26
|
+
//# sourceMappingURL=shutdownQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdownQueue.js","sourceRoot":"","sources":["../src/shutdownQueue.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6DAA0D;AAC1D,2DAAwD;AACxD,gEAA6D;AAE7D,SAAsB,aAAa;;QACjC,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,GAAG,mCAAgB,CAAC,QAAQ,CAAC,mBAAmB,EAAE;YAClD,GAAG,iCAAe,CAAC,QAAQ,CAAC,mBAAmB,EAAE;SAClD,CAAC,CAAC;QAEH,qCAAiB,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;IAC1C,CAAC;CAAA;AAPD,sCAOC"}
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RawQueue = exports.RawWorker = exports.Job = void 0;
|
|
4
|
+
// decoupling from bullmq
|
|
5
|
+
var bullmq_1 = require("bullmq");
|
|
6
|
+
Object.defineProperty(exports, "Job", { enumerable: true, get: function () { return bullmq_1.Job; } });
|
|
7
|
+
Object.defineProperty(exports, "RawWorker", { enumerable: true, get: function () { return bullmq_1.Worker; } });
|
|
8
|
+
Object.defineProperty(exports, "RawQueue", { enumerable: true, get: function () { return bullmq_1.Queue; } });
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAAA,yBAAyB;AACzB,iCAQgB;AALd,6FAAA,GAAG,OAAA;AACH,mGAAA,MAAM,OAAa;AAEnB,kGAAA,KAAK,OAAY"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { JobData, Job, RawWorker, WorkerOptions } from "../types";
|
|
2
|
+
export interface WorkerStatus {
|
|
3
|
+
running: boolean;
|
|
4
|
+
paused: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkerSetup {
|
|
7
|
+
queueName: string;
|
|
8
|
+
workerOptions?: WorkerOptions;
|
|
9
|
+
}
|
|
10
|
+
interface Worker<T = JobData> {
|
|
11
|
+
handleJobFailed?(job: Job<T>, error: Error): Promise<void> | void;
|
|
12
|
+
handleJobCompleted?(job: Job<T>): Promise<void> | void;
|
|
13
|
+
handleJobError?(error: Error): Promise<void> | void;
|
|
14
|
+
}
|
|
15
|
+
declare abstract class Worker<T = JobData> {
|
|
16
|
+
private _worker;
|
|
17
|
+
private _queue;
|
|
18
|
+
private getLogger;
|
|
19
|
+
constructor();
|
|
20
|
+
abstract process(job: Job<T>): Promise<void>;
|
|
21
|
+
abstract setup(): WorkerSetup;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
get status(): WorkerStatus;
|
|
24
|
+
get id(): RawWorker["id"];
|
|
25
|
+
private processJob;
|
|
26
|
+
processFailed(job: Job<T>, error: Error): Promise<void>;
|
|
27
|
+
processCompleted(job: Job<T>): Promise<void>;
|
|
28
|
+
processError(error: Error): Promise<void>;
|
|
29
|
+
run(): Promise<void>;
|
|
30
|
+
protected registerHooks(): void;
|
|
31
|
+
protected get worker(): RawWorker;
|
|
32
|
+
get queueFullName(): string;
|
|
33
|
+
get queueName(): string;
|
|
34
|
+
}
|
|
35
|
+
export { Worker };
|
|
@@ -0,0 +1,99 @@
|
|
|
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.Worker = void 0;
|
|
13
|
+
const connectionManager_1 = require("../connectionManager");
|
|
14
|
+
const types_1 = require("../types");
|
|
15
|
+
const BULL_PREFIX = "b";
|
|
16
|
+
class Worker {
|
|
17
|
+
constructor() {
|
|
18
|
+
const { queueName: name, workerOptions } = this.setup();
|
|
19
|
+
const connectionManager = connectionManager_1.ConnectionManager.getInstance();
|
|
20
|
+
this.getLogger = connectionManager.getLogger;
|
|
21
|
+
const prefix = connectionManager.getServiceName();
|
|
22
|
+
this._queue = { name, prefix, namespace: BULL_PREFIX };
|
|
23
|
+
this._worker = new types_1.RawWorker(name, this.processJob(), Object.assign({ autorun: false, connection: connectionManager.getConnection(), prefix: `{${prefix}}:${BULL_PREFIX}` }, workerOptions));
|
|
24
|
+
this.getLogger().info("worker.ready", { queue: this.queueFullName });
|
|
25
|
+
this.registerHooks();
|
|
26
|
+
}
|
|
27
|
+
close() {
|
|
28
|
+
return this.worker.close();
|
|
29
|
+
}
|
|
30
|
+
get status() {
|
|
31
|
+
return { running: this.worker.isRunning(), paused: this.worker.isPaused() };
|
|
32
|
+
}
|
|
33
|
+
get id() {
|
|
34
|
+
return this.worker.id;
|
|
35
|
+
}
|
|
36
|
+
processJob() {
|
|
37
|
+
return (job) => __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
this.getLogger().info(`worker.process.job_received`, { queue: this.queueFullName, job });
|
|
39
|
+
yield this.process(job);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
processFailed(job, error) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
this.getLogger().warn("worker.job.failed", { queue: this.queueFullName, job, error });
|
|
45
|
+
if (this.handleJobFailed) {
|
|
46
|
+
yield this.handleJobFailed(job, error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
processCompleted(job) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
this.getLogger().info("worker.job.completed", { queue: this.queueFullName, job });
|
|
53
|
+
if (this.handleJobCompleted) {
|
|
54
|
+
yield this.handleJobCompleted(job);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
processError(error) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
this.getLogger().error("worker.job.unhandled_exception", { queue: this.queueFullName, error });
|
|
61
|
+
if (this.handleJobError) {
|
|
62
|
+
yield this.handleJobError(error);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
run() {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
try {
|
|
69
|
+
this.getLogger().info("worker.run.starting", { queue: this.queueFullName });
|
|
70
|
+
yield this.worker.run();
|
|
71
|
+
this.getLogger().info("worker.run.started", { queue: this.queueFullName });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.getLogger().error("worker.run.error", { queue: this.queueFullName, error });
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
registerHooks() {
|
|
80
|
+
// on error: handle unhandled exceptions
|
|
81
|
+
this.worker.on("error", (error) => this.processError(error));
|
|
82
|
+
// on completed: allow to do something else after a job is completed
|
|
83
|
+
this.worker.on("completed", (job) => this.processCompleted(job));
|
|
84
|
+
// on failed: when the process fails with an exception it is possible to listen for the "failed" event
|
|
85
|
+
this.worker.on("failed", (job, error) => this.processFailed(job, error));
|
|
86
|
+
}
|
|
87
|
+
get worker() {
|
|
88
|
+
return this._worker;
|
|
89
|
+
}
|
|
90
|
+
get queueFullName() {
|
|
91
|
+
const { name, prefix, namespace } = this._queue;
|
|
92
|
+
return `{${prefix}}:${namespace}:${name}`;
|
|
93
|
+
}
|
|
94
|
+
get queueName() {
|
|
95
|
+
return this._queue.name;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.Worker = Worker;
|
|
99
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Worker, WorkerStatus } from "./worker";
|
|
2
|
+
export declare type WokerStatusWithId = {
|
|
3
|
+
status: WorkerStatus;
|
|
4
|
+
} & {
|
|
5
|
+
id: string;
|
|
6
|
+
};
|
|
7
|
+
interface WorkerClass {
|
|
8
|
+
new (): Worker;
|
|
9
|
+
}
|
|
10
|
+
export declare class WorkerRepository {
|
|
11
|
+
private _workers;
|
|
12
|
+
private _activeWorkers;
|
|
13
|
+
private static _instance;
|
|
14
|
+
private getLogger;
|
|
15
|
+
private constructor();
|
|
16
|
+
static get Instance(): WorkerRepository;
|
|
17
|
+
registerWorker(queueName: string, workerClass: WorkerClass): WorkerRepository;
|
|
18
|
+
getWorkersByQueues(queueNames: string[]): Worker[];
|
|
19
|
+
getWorkerStatuses(): WokerStatusWithId[];
|
|
20
|
+
getCloseConnections(): Promise<void>[];
|
|
21
|
+
private get workers();
|
|
22
|
+
private get activeWorkers();
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkerRepository = void 0;
|
|
4
|
+
const lodash_1 = require("lodash");
|
|
5
|
+
const connectionManager_1 = require("../connectionManager");
|
|
6
|
+
class WorkerRepository {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._workers = {};
|
|
9
|
+
this._activeWorkers = [];
|
|
10
|
+
this.getLogger = connectionManager_1.ConnectionManager.getInstance().getLogger;
|
|
11
|
+
}
|
|
12
|
+
static get Instance() {
|
|
13
|
+
if (this._instance) {
|
|
14
|
+
return this._instance;
|
|
15
|
+
}
|
|
16
|
+
this._instance = new this();
|
|
17
|
+
return this._instance;
|
|
18
|
+
}
|
|
19
|
+
registerWorker(queueName, workerClass) {
|
|
20
|
+
Object.assign(this.workers, { [queueName]: workerClass });
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
getWorkersByQueues(queueNames) {
|
|
24
|
+
if ((0, lodash_1.isEmpty)(this.workers)) {
|
|
25
|
+
this.getLogger().warn("worker_repository.queue_worker_map_is_empty");
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return (0, lodash_1.compact)(queueNames.map((queueName) => {
|
|
29
|
+
if (this.workers[queueName]) {
|
|
30
|
+
const worker = new this.workers[queueName]();
|
|
31
|
+
// assuming this method is only called in a context for running them
|
|
32
|
+
this._activeWorkers.push(worker);
|
|
33
|
+
return worker;
|
|
34
|
+
}
|
|
35
|
+
this.getLogger().warn("worker_repository.invalid_queue_name", { queueName });
|
|
36
|
+
return null;
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
getWorkerStatuses() {
|
|
40
|
+
return this.activeWorkers.map((worker) => ({ id: worker.id, status: worker.status }));
|
|
41
|
+
}
|
|
42
|
+
getCloseConnections() {
|
|
43
|
+
return this.activeWorkers.map((worker) => worker.close());
|
|
44
|
+
}
|
|
45
|
+
get workers() {
|
|
46
|
+
return this._workers;
|
|
47
|
+
}
|
|
48
|
+
get activeWorkers() {
|
|
49
|
+
return this._activeWorkers;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.WorkerRepository = WorkerRepository;
|
|
53
|
+
//# sourceMappingURL=workerRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerRepository.js","sourceRoot":"","sources":["../../src/worker/workerRepository.ts"],"names":[],"mappings":";;;AACA,mCAA0C;AAC1C,4DAAyD;AASzD,MAAa,gBAAgB;IAS3B;QACE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,qCAAiB,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC;IAC7D,CAAC;IAEM,MAAM,KAAK,QAAQ;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,OAAO,IAAI,CAAC,SAAS,CAAC;SACvB;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,SAAiB,EAAE,WAAwB;QAC/D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB,CAAC,UAAoB;QACrC,IAAI,IAAA,gBAAO,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YACzB,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;YACrE,OAAO,EAAE,CAAC;SACX;QAED,OAAO,IAAA,gBAAO,EACZ,UAAU,CAAC,GAAG,CAAC,CAAC,SAAiB,EAAE,EAAE;YACnC,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;gBAC3B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC7C,oEAAoE;gBACpE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACjC,OAAO,MAAM,CAAC;aACf;YACD,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,sCAAsC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YAC7E,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,iBAAiB;QACf,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxF,CAAC;IAEM,mBAAmB;QACxB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAY,OAAO;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAY,aAAa;QACvB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF;AA/DD,4CA+DC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alanszp/queue",
|
|
3
|
+
"version": "7.9.0",
|
|
4
|
+
"description": "Workers and queues",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"typings": "dist/index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"files": [
|
|
9
|
+
"**/*"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"compile": "rm -rf ./dist && tsc --declaration",
|
|
16
|
+
"compile-watch": "tsc -w",
|
|
17
|
+
"build": "yarn run compile",
|
|
18
|
+
"prepack": "yarn run build",
|
|
19
|
+
"yalc-publish": "yarn run yalc publish"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^15.12.3",
|
|
23
|
+
"ts-node": "^10.0.0",
|
|
24
|
+
"tslint": "^6.1.3",
|
|
25
|
+
"typescript": "^4.3.4"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@alanszp/logger": "^7.0.0",
|
|
29
|
+
"bullmq": "^2.1.2",
|
|
30
|
+
"ioredis": "^5.2.3",
|
|
31
|
+
"lodash": "^4.17.21"
|
|
32
|
+
},
|
|
33
|
+
"gitHead": "3a3685ad54fbde5cea62d5e2cabca96f4bd8faf4"
|
|
34
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ILogger } from "@alanszp/logger";
|
|
2
|
+
import Redis, { RedisOptions } from "ioredis";
|
|
3
|
+
|
|
4
|
+
export class ConnectionManager {
|
|
5
|
+
private static instance: ConnectionManager;
|
|
6
|
+
|
|
7
|
+
private connection: Redis;
|
|
8
|
+
|
|
9
|
+
private redisConfiguration: RedisOptions;
|
|
10
|
+
|
|
11
|
+
private serviceName: string;
|
|
12
|
+
|
|
13
|
+
public getLogger: () => ILogger;
|
|
14
|
+
|
|
15
|
+
public static getInstance(): ConnectionManager {
|
|
16
|
+
if (!ConnectionManager.instance) {
|
|
17
|
+
ConnectionManager.instance = new ConnectionManager();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return ConnectionManager.instance;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private connect(): Redis {
|
|
24
|
+
if (!this.redisConfiguration) {
|
|
25
|
+
throw new Error("Redis configuration required");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const redis = new Redis(this.redisConfiguration);
|
|
29
|
+
redis.on("connect", () => this.getLogger().info("redis.connected"));
|
|
30
|
+
redis.on("ready", () => this.getLogger().info("redis.ready"));
|
|
31
|
+
redis.on("error", (error: Error) => this.getLogger().error("redis.error", { error }));
|
|
32
|
+
redis.on("close", () => this.getLogger().warn("redis.closed"));
|
|
33
|
+
redis.on("reconnecting", () => this.getLogger().info("redis.reconnecting"));
|
|
34
|
+
redis.on("end", () => this.getLogger().info("redis.ended"));
|
|
35
|
+
redis.on("wait", () => this.getLogger().info("redis.waiting"));
|
|
36
|
+
|
|
37
|
+
return redis;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public setConfiguration(configuration: RedisOptions, service: string, getLogger: () => ILogger) {
|
|
41
|
+
this.redisConfiguration = configuration;
|
|
42
|
+
this.serviceName = service;
|
|
43
|
+
this.getLogger = getLogger;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public getServiceName(): string {
|
|
47
|
+
return this.serviceName;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public getConnection(): Redis {
|
|
51
|
+
if (!this.connection) {
|
|
52
|
+
this.connection = this.connect();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return this.connection;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public close(): void {
|
|
59
|
+
this.connection.disconnect();
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Queue } from "./queue";
|
|
2
|
+
import { QueueRepository } from "./queueRepository";
|
|
3
|
+
import { ConnectionManager } from "../connectionManager";
|
|
4
|
+
import { JobData, QueueOptions } from "../types";
|
|
5
|
+
|
|
6
|
+
export function createQueue<JobType = JobData>(name: string, queueOptions?: QueueOptions): Queue {
|
|
7
|
+
const connection = ConnectionManager.getInstance().getConnection();
|
|
8
|
+
const queue = new Queue<JobType>(connection, name, ConnectionManager.getInstance().getServiceName(), queueOptions);
|
|
9
|
+
QueueRepository.Instance.registerQueue(queue);
|
|
10
|
+
return queue;
|
|
11
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { merge } from "lodash";
|
|
2
|
+
import { ConnectionOptions, JobData, QueueOptions, RawQueue } from "../types";
|
|
3
|
+
|
|
4
|
+
const BULL_PREFIX = "b";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS = 60 * 60 * 24 * 30;
|
|
7
|
+
const DEFAULT_COMPLETED_JOB_MAX_COUNT = 500;
|
|
8
|
+
|
|
9
|
+
export class Queue<JobType = JobData> {
|
|
10
|
+
private _queue: RawQueue;
|
|
11
|
+
|
|
12
|
+
private name: string;
|
|
13
|
+
|
|
14
|
+
constructor(connection: ConnectionOptions, name: string, prefix: string, queueOptions?: QueueOptions) {
|
|
15
|
+
this.name = name;
|
|
16
|
+
|
|
17
|
+
this._queue = new RawQueue<JobType>(name, {
|
|
18
|
+
...merge(
|
|
19
|
+
{
|
|
20
|
+
defaultJobOptions: {
|
|
21
|
+
removeOnComplete: {
|
|
22
|
+
age: DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS,
|
|
23
|
+
count: DEFAULT_COMPLETED_JOB_MAX_COUNT,
|
|
24
|
+
},
|
|
25
|
+
attempts: 3,
|
|
26
|
+
backoff: {
|
|
27
|
+
type: "exponential",
|
|
28
|
+
delay: 3000,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
connection,
|
|
32
|
+
},
|
|
33
|
+
queueOptions || {}
|
|
34
|
+
),
|
|
35
|
+
...{ prefix: `{${prefix}}:${BULL_PREFIX}` },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async publishJob(job: JobType): Promise<void> {
|
|
40
|
+
await this.queue.add(this.name, job);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async publishBulkJob(jobDatas: JobType[]): Promise<void> {
|
|
44
|
+
const jobs = jobDatas.map((data) => ({ name: this.name, data }));
|
|
45
|
+
await this.queue.addBulk(jobs);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async close(): Promise<void> {
|
|
49
|
+
await this.queue.close();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private get queue(): RawQueue<JobType> {
|
|
53
|
+
return this._queue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Queue } from "./queue";
|
|
2
|
+
|
|
3
|
+
export class QueueRepository {
|
|
4
|
+
private _queues: Queue[];
|
|
5
|
+
|
|
6
|
+
private static _instance: QueueRepository;
|
|
7
|
+
|
|
8
|
+
private constructor() {
|
|
9
|
+
this._queues = [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static get Instance(): QueueRepository {
|
|
13
|
+
if (this._instance) {
|
|
14
|
+
return this._instance;
|
|
15
|
+
}
|
|
16
|
+
this._instance = new this();
|
|
17
|
+
return this._instance;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public registerQueue(queue: Queue) {
|
|
21
|
+
this._queues.push(queue);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public getCloseConnections(): Promise<void>[] {
|
|
25
|
+
return this._queues.map((q) => q.close());
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/runner.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
import { ConnectionManager } from "./connectionManager";
|
|
3
|
+
import { Worker } from "./worker/worker";
|
|
4
|
+
import { WorkerRepository } from "./worker/workerRepository";
|
|
5
|
+
|
|
6
|
+
export async function runWorkers(queueNames: string): Promise<void> {
|
|
7
|
+
if (queueNames.length === 0) {
|
|
8
|
+
ConnectionManager.getInstance().getLogger().warn("worker.env.workers_queues_empty");
|
|
9
|
+
return Promise.resolve();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const queues: string[] = queueNames.replace(/\s/g, "").split(",");
|
|
13
|
+
const workers = WorkerRepository.Instance.getWorkersByQueues(queues);
|
|
14
|
+
|
|
15
|
+
await Promise.all(workers.map((worker: Worker) => worker.run()));
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { QueueRepository } from "./queue/queueRepository";
|
|
2
|
+
import { ConnectionManager } from "./connectionManager";
|
|
3
|
+
import { WorkerRepository } from "./worker/workerRepository";
|
|
4
|
+
|
|
5
|
+
export async function shutdownQueue(): Promise<void> {
|
|
6
|
+
await Promise.all([
|
|
7
|
+
...WorkerRepository.Instance.getCloseConnections(),
|
|
8
|
+
...QueueRepository.Instance.getCloseConnections(),
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
ConnectionManager.getInstance().close();
|
|
12
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// decoupling from bullmq
|
|
2
|
+
export {
|
|
3
|
+
ConnectionOptions,
|
|
4
|
+
RedisOptions,
|
|
5
|
+
Job,
|
|
6
|
+
Worker as RawWorker,
|
|
7
|
+
WorkerOptions,
|
|
8
|
+
Queue as RawQueue,
|
|
9
|
+
QueueOptions,
|
|
10
|
+
} from "bullmq";
|
|
11
|
+
|
|
12
|
+
// This may seems odd but actual Job data type from bullmq is any 🤷♂️
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export type JobData = any;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ILogger } from "@alanszp/logger";
|
|
2
|
+
import { ConnectionManager } from "../connectionManager";
|
|
3
|
+
import { JobData, Job, RawWorker, WorkerOptions } from "../types";
|
|
4
|
+
|
|
5
|
+
export interface WorkerStatus {
|
|
6
|
+
running: boolean;
|
|
7
|
+
paused: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface WorkerSetup {
|
|
11
|
+
queueName: string;
|
|
12
|
+
workerOptions?: WorkerOptions;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// optional methods
|
|
16
|
+
|
|
17
|
+
interface Worker<T = JobData> {
|
|
18
|
+
handleJobFailed?(job: Job<T>, error: Error): Promise<void> | void;
|
|
19
|
+
handleJobCompleted?(job: Job<T>): Promise<void> | void;
|
|
20
|
+
handleJobError?(error: Error): Promise<void> | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type QueueName = { name: string; prefix: string; namespace: string };
|
|
24
|
+
|
|
25
|
+
const BULL_PREFIX = "b";
|
|
26
|
+
|
|
27
|
+
abstract class Worker<T = JobData> {
|
|
28
|
+
private _worker: RawWorker;
|
|
29
|
+
|
|
30
|
+
private _queue: QueueName;
|
|
31
|
+
|
|
32
|
+
private getLogger: () => ILogger;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
const { queueName: name, workerOptions } = this.setup();
|
|
36
|
+
|
|
37
|
+
const connectionManager = ConnectionManager.getInstance();
|
|
38
|
+
this.getLogger = connectionManager.getLogger;
|
|
39
|
+
|
|
40
|
+
const prefix = connectionManager.getServiceName();
|
|
41
|
+
this._queue = { name, prefix, namespace: BULL_PREFIX };
|
|
42
|
+
this._worker = new RawWorker<T>(name, this.processJob(), {
|
|
43
|
+
autorun: false,
|
|
44
|
+
connection: connectionManager.getConnection(),
|
|
45
|
+
prefix: `{${prefix}}:${BULL_PREFIX}`,
|
|
46
|
+
...workerOptions,
|
|
47
|
+
});
|
|
48
|
+
this.getLogger().info("worker.ready", { queue: this.queueFullName });
|
|
49
|
+
|
|
50
|
+
this.registerHooks();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle job
|
|
54
|
+
abstract process(job: Job<T>): Promise<void>;
|
|
55
|
+
|
|
56
|
+
abstract setup(): WorkerSetup;
|
|
57
|
+
|
|
58
|
+
public close(): Promise<void> {
|
|
59
|
+
return this.worker.close();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public get status(): WorkerStatus {
|
|
63
|
+
return { running: this.worker.isRunning(), paused: this.worker.isPaused() };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get id(): RawWorker["id"] {
|
|
67
|
+
return this.worker.id;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private processJob(): (job: Job<T>) => Promise<void> {
|
|
71
|
+
return async (job) => {
|
|
72
|
+
this.getLogger().info(`worker.process.job_received`, { queue: this.queueFullName, job });
|
|
73
|
+
await this.process(job);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async processFailed(job: Job<T>, error: Error): Promise<void> {
|
|
78
|
+
this.getLogger().warn("worker.job.failed", { queue: this.queueFullName, job, error });
|
|
79
|
+
if (this.handleJobFailed) {
|
|
80
|
+
await this.handleJobFailed(job, error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async processCompleted(job: Job<T>): Promise<void> {
|
|
85
|
+
this.getLogger().info("worker.job.completed", { queue: this.queueFullName, job });
|
|
86
|
+
if (this.handleJobCompleted) {
|
|
87
|
+
await this.handleJobCompleted(job);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async processError(error: Error): Promise<void> {
|
|
92
|
+
this.getLogger().error("worker.job.unhandled_exception", { queue: this.queueFullName, error });
|
|
93
|
+
if (this.handleJobError) {
|
|
94
|
+
await this.handleJobError(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async run(): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
this.getLogger().info("worker.run.starting", { queue: this.queueFullName });
|
|
101
|
+
|
|
102
|
+
await this.worker.run();
|
|
103
|
+
this.getLogger().info("worker.run.started", { queue: this.queueFullName });
|
|
104
|
+
} catch (error: unknown) {
|
|
105
|
+
this.getLogger().error("worker.run.error", { queue: this.queueFullName, error });
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected registerHooks(): void {
|
|
111
|
+
// on error: handle unhandled exceptions
|
|
112
|
+
this.worker.on("error", (error: Error) => this.processError(error));
|
|
113
|
+
// on completed: allow to do something else after a job is completed
|
|
114
|
+
this.worker.on("completed", (job: Job<T>) => this.processCompleted(job));
|
|
115
|
+
// on failed: when the process fails with an exception it is possible to listen for the "failed" event
|
|
116
|
+
this.worker.on("failed", (job: Job<T>, error: Error) => this.processFailed(job, error));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected get worker(): RawWorker {
|
|
120
|
+
return this._worker;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get queueFullName(): string {
|
|
124
|
+
const { name, prefix, namespace } = this._queue;
|
|
125
|
+
return `{${prefix}}:${namespace}:${name}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get queueName(): string {
|
|
129
|
+
return this._queue.name;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// this export is required for merging optional methods interface into abstract class
|
|
134
|
+
export { Worker };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ILogger } from "@alanszp/logger";
|
|
2
|
+
import { compact, isEmpty } from "lodash";
|
|
3
|
+
import { ConnectionManager } from "../connectionManager";
|
|
4
|
+
import { Worker, WorkerStatus } from "./worker";
|
|
5
|
+
|
|
6
|
+
export type WokerStatusWithId = { status: WorkerStatus } & { id: string };
|
|
7
|
+
interface WorkerClass {
|
|
8
|
+
new (): Worker;
|
|
9
|
+
}
|
|
10
|
+
type QueueWorkerMap = Record<string, WorkerClass>;
|
|
11
|
+
|
|
12
|
+
export class WorkerRepository {
|
|
13
|
+
private _workers: QueueWorkerMap;
|
|
14
|
+
|
|
15
|
+
private _activeWorkers: Worker[];
|
|
16
|
+
|
|
17
|
+
private static _instance: WorkerRepository;
|
|
18
|
+
|
|
19
|
+
private getLogger: () => ILogger;
|
|
20
|
+
|
|
21
|
+
private constructor() {
|
|
22
|
+
this._workers = {};
|
|
23
|
+
this._activeWorkers = [];
|
|
24
|
+
this.getLogger = ConnectionManager.getInstance().getLogger;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static get Instance(): WorkerRepository {
|
|
28
|
+
if (this._instance) {
|
|
29
|
+
return this._instance;
|
|
30
|
+
}
|
|
31
|
+
this._instance = new this();
|
|
32
|
+
return this._instance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public registerWorker(queueName: string, workerClass: WorkerClass): WorkerRepository {
|
|
36
|
+
Object.assign(this.workers, { [queueName]: workerClass });
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getWorkersByQueues(queueNames: string[]): Worker[] {
|
|
41
|
+
if (isEmpty(this.workers)) {
|
|
42
|
+
this.getLogger().warn("worker_repository.queue_worker_map_is_empty");
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return compact(
|
|
47
|
+
queueNames.map((queueName: string) => {
|
|
48
|
+
if (this.workers[queueName]) {
|
|
49
|
+
const worker = new this.workers[queueName]();
|
|
50
|
+
// assuming this method is only called in a context for running them
|
|
51
|
+
this._activeWorkers.push(worker);
|
|
52
|
+
return worker;
|
|
53
|
+
}
|
|
54
|
+
this.getLogger().warn("worker_repository.invalid_queue_name", { queueName });
|
|
55
|
+
return null;
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getWorkerStatuses(): WokerStatusWithId[] {
|
|
61
|
+
return this.activeWorkers.map((worker) => ({ id: worker.id, status: worker.status }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public getCloseConnections(): Promise<void>[] {
|
|
65
|
+
return this.activeWorkers.map((worker) => worker.close());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private get workers(): QueueWorkerMap {
|
|
69
|
+
return this._workers;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private get activeWorkers(): Worker[] {
|
|
73
|
+
return this._activeWorkers;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDir": "src",
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"target": "es6",
|
|
7
|
+
"types": ["node", "jest"],
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"emitDecoratorMetadata": true,
|
|
12
|
+
"alwaysStrict": true,
|
|
13
|
+
"strictNullChecks": true
|
|
14
|
+
},
|
|
15
|
+
"exclude": ["node_modules"]
|
|
16
|
+
}
|