@deenruv/job-queue-plugin 1.0.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.
Files changed (48) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +94 -0
  3. package/package/bullmq/bullmq-job-queue-strategy.d.ts +39 -0
  4. package/package/bullmq/bullmq-job-queue-strategy.js +362 -0
  5. package/package/bullmq/bullmq-job-queue-strategy.js.map +1 -0
  6. package/package/bullmq/constants.d.ts +4 -0
  7. package/package/bullmq/constants.js +16 -0
  8. package/package/bullmq/constants.js.map +1 -0
  9. package/package/bullmq/index.d.ts +2 -0
  10. package/package/bullmq/index.js +27 -0
  11. package/package/bullmq/index.js.map +1 -0
  12. package/package/bullmq/plugin.d.ts +133 -0
  13. package/package/bullmq/plugin.js +171 -0
  14. package/package/bullmq/plugin.js.map +1 -0
  15. package/package/bullmq/redis-health-check-strategy.d.ts +6 -0
  16. package/package/bullmq/redis-health-check-strategy.js +15 -0
  17. package/package/bullmq/redis-health-check-strategy.js.map +1 -0
  18. package/package/bullmq/redis-health-indicator.d.ts +8 -0
  19. package/package/bullmq/redis-health-indicator.js +80 -0
  20. package/package/bullmq/redis-health-indicator.js.map +1 -0
  21. package/package/bullmq/redis-job-buffer-storage-strategy.d.ts +16 -0
  22. package/package/bullmq/redis-job-buffer-storage-strategy.js +82 -0
  23. package/package/bullmq/redis-job-buffer-storage-strategy.js.map +1 -0
  24. package/package/bullmq/scripts/get-jobs-by-type.d.ts +10 -0
  25. package/package/bullmq/scripts/get-jobs-by-type.js +114 -0
  26. package/package/bullmq/scripts/get-jobs-by-type.js.map +1 -0
  27. package/package/bullmq/types.d.ts +101 -0
  28. package/package/bullmq/types.js +3 -0
  29. package/package/bullmq/types.js.map +1 -0
  30. package/package/index.d.ts +4 -0
  31. package/package/index.js +8 -0
  32. package/package/index.js.map +1 -0
  33. package/package/pub-sub/constants.d.ts +2 -0
  34. package/package/pub-sub/constants.js +6 -0
  35. package/package/pub-sub/constants.js.map +1 -0
  36. package/package/pub-sub/index.d.ts +2 -0
  37. package/package/pub-sub/index.js +19 -0
  38. package/package/pub-sub/index.js.map +1 -0
  39. package/package/pub-sub/options.d.ts +13 -0
  40. package/package/pub-sub/options.js +3 -0
  41. package/package/pub-sub/options.js.map +1 -0
  42. package/package/pub-sub/plugin.d.ts +6 -0
  43. package/package/pub-sub/plugin.js +36 -0
  44. package/package/pub-sub/plugin.js.map +1 -0
  45. package/package/pub-sub/pub-sub-job-queue-strategy.d.ts +16 -0
  46. package/package/pub-sub/pub-sub-job-queue-strategy.js +123 -0
  47. package/package/pub-sub/pub-sub-job-queue-strategy.js.map +1 -0
  48. package/package.json +42 -0
@@ -0,0 +1,133 @@
1
+ import { BullMQPluginOptions } from "./types";
2
+ /**
3
+ * @description
4
+ * This plugin is a drop-in replacement of the DefaultJobQueuePlugin, which implements a push-based
5
+ * job queue strategy built on top of the popular [BullMQ](https://github.com/taskforcesh/bullmq) library.
6
+ *
7
+ * ## Advantages over the DefaultJobQueuePlugin
8
+ *
9
+ * The advantage of this approach is that jobs are stored in Redis rather than in the database. For more complex
10
+ * applications with many job queues and/or multiple worker instances, this can massively reduce the load on the
11
+ * DB server. The reason for this is that the DefaultJobQueuePlugin uses polling to check for new jobs. By default
12
+ * it will poll every 200ms. A typical Deenruv instance uses at least 3 queues (handling emails, collections, search index),
13
+ * so even with a single worker instance this results in 15 queries per second to the DB constantly. Adding more
14
+ * custom queues and multiple worker instances can easily result in 50 or 100 queries per second. At this point
15
+ * performance may be impacted.
16
+ *
17
+ * Using this plugin, no polling is needed, as BullMQ will _push_ jobs to the worker(s) as and when they are added
18
+ * to the queue. This results in significantly more scalable performance characteristics, as well as lower latency
19
+ * in processing jobs.
20
+ *
21
+ * ## Installation
22
+ *
23
+ * `yarn add \@deenruv/job-queue-plugin bullmq`
24
+ *
25
+ * or
26
+ *
27
+ * `npm install \@deenruv/job-queue-plugin bullmq`
28
+ *
29
+ * **Note:** The v1.x version of this plugin is designed to work with bullmq v1.x, etc.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { BullMQJobQueuePlugin } from '\@deenruv/job-queue-plugin/package/bullmq';
34
+ *
35
+ * const config: DeenruvConfig = {
36
+ * // Add an instance of the plugin to the plugins array
37
+ * plugins: [
38
+ * // DefaultJobQueuePlugin should be removed from the plugins array
39
+ * BullMQJobQueuePlugin.init({
40
+ * connection: {
41
+ * port: 6379
42
+ * }
43
+ * }),
44
+ * ],
45
+ * };
46
+ * ```
47
+ *
48
+ * ### Running Redis locally
49
+ *
50
+ * To develop with this plugin, you'll need an instance of Redis to connect to. Here's a docker-compose config
51
+ * that will set up [Redis](https://redis.io/) as well as [Redis Commander](https://github.com/joeferner/redis-commander),
52
+ * which is a web-based UI for interacting with Redis:
53
+ *
54
+ * ```YAML
55
+ * version: "3"
56
+ * services:
57
+ * redis:
58
+ * image: bitnami/redis:6.2
59
+ * hostname: redis
60
+ * container_name: redis
61
+ * environment:
62
+ * - ALLOW_EMPTY_PASSWORD=yes
63
+ * ports:
64
+ * - "6379:6379"
65
+ * redis-commander:
66
+ * container_name: redis-commander
67
+ * hostname: redis-commander
68
+ * image: rediscommander/redis-commander:latest
69
+ * environment:
70
+ * - REDIS_HOSTS=local:redis:6379
71
+ * ports:
72
+ * - "8085:8081"
73
+ * ```
74
+ *
75
+ * ## Concurrency
76
+ *
77
+ * The default concurrency of a single worker is 3, i.e. up to 3 jobs will be processed at the same time.
78
+ * You can change the concurrency in the `workerOptions` passed to the `init()` method:
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const config: DeenruvConfig = {
83
+ * plugins: [
84
+ * BullMQJobQueuePlugin.init({
85
+ * workerOptions: {
86
+ * concurrency: 10,
87
+ * },
88
+ * }),
89
+ * ],
90
+ * };
91
+ * ```
92
+ *
93
+ * ## Removing old jobs
94
+ *
95
+ * By default, BullMQ will keep completed jobs in the `completed` set and failed jobs in the `failed` set. Over time,
96
+ * these sets can grow very large. Since Deenruv v2.1, the default behaviour is to remove jobs from these sets after
97
+ * 30 days or after a maximum of 5,000 completed or failed jobs.
98
+ *
99
+ * This can be configured using the `removeOnComplete` and `removeOnFail` options:
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const config: DeenruvConfig = {
104
+ * plugins: [
105
+ * BullMQJobQueuePlugin.init({
106
+ * workerOptions: {
107
+ * removeOnComplete: {
108
+ * count: 500,
109
+ * },
110
+ * removeOnFail: {
111
+ * age: 60 * 60 * 24 * 7, // 7 days
112
+ * count: 1000,
113
+ * },
114
+ * }
115
+ * }),
116
+ * ],
117
+ * };
118
+ * ```
119
+ *
120
+ * The `count` option specifies the maximum number of jobs to keep in the set, while the `age` option specifies the
121
+ * maximum age of a job in seconds. If both options are specified, then the jobs kept will be the ones that satisfy
122
+ * both properties.
123
+ *
124
+ * @docsCategory core plugins/JobQueuePlugin
125
+ */
126
+ export declare class BullMQJobQueuePlugin {
127
+ static options: BullMQPluginOptions;
128
+ /**
129
+ * @description
130
+ * Configures the plugin.
131
+ */
132
+ static init(options: BullMQPluginOptions): typeof BullMQJobQueuePlugin;
133
+ }
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.BullMQJobQueuePlugin = void 0;
10
+ const core_1 = require("@deenruv/core");
11
+ const bullmq_job_queue_strategy_1 = require("./bullmq-job-queue-strategy");
12
+ const constants_1 = require("./constants");
13
+ const redis_health_check_strategy_1 = require("./redis-health-check-strategy");
14
+ const redis_health_indicator_1 = require("./redis-health-indicator");
15
+ const redis_job_buffer_storage_strategy_1 = require("./redis-job-buffer-storage-strategy");
16
+ /**
17
+ * @description
18
+ * This plugin is a drop-in replacement of the DefaultJobQueuePlugin, which implements a push-based
19
+ * job queue strategy built on top of the popular [BullMQ](https://github.com/taskforcesh/bullmq) library.
20
+ *
21
+ * ## Advantages over the DefaultJobQueuePlugin
22
+ *
23
+ * The advantage of this approach is that jobs are stored in Redis rather than in the database. For more complex
24
+ * applications with many job queues and/or multiple worker instances, this can massively reduce the load on the
25
+ * DB server. The reason for this is that the DefaultJobQueuePlugin uses polling to check for new jobs. By default
26
+ * it will poll every 200ms. A typical Deenruv instance uses at least 3 queues (handling emails, collections, search index),
27
+ * so even with a single worker instance this results in 15 queries per second to the DB constantly. Adding more
28
+ * custom queues and multiple worker instances can easily result in 50 or 100 queries per second. At this point
29
+ * performance may be impacted.
30
+ *
31
+ * Using this plugin, no polling is needed, as BullMQ will _push_ jobs to the worker(s) as and when they are added
32
+ * to the queue. This results in significantly more scalable performance characteristics, as well as lower latency
33
+ * in processing jobs.
34
+ *
35
+ * ## Installation
36
+ *
37
+ * `yarn add \@deenruv/job-queue-plugin bullmq`
38
+ *
39
+ * or
40
+ *
41
+ * `npm install \@deenruv/job-queue-plugin bullmq`
42
+ *
43
+ * **Note:** The v1.x version of this plugin is designed to work with bullmq v1.x, etc.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { BullMQJobQueuePlugin } from '\@deenruv/job-queue-plugin/package/bullmq';
48
+ *
49
+ * const config: DeenruvConfig = {
50
+ * // Add an instance of the plugin to the plugins array
51
+ * plugins: [
52
+ * // DefaultJobQueuePlugin should be removed from the plugins array
53
+ * BullMQJobQueuePlugin.init({
54
+ * connection: {
55
+ * port: 6379
56
+ * }
57
+ * }),
58
+ * ],
59
+ * };
60
+ * ```
61
+ *
62
+ * ### Running Redis locally
63
+ *
64
+ * To develop with this plugin, you'll need an instance of Redis to connect to. Here's a docker-compose config
65
+ * that will set up [Redis](https://redis.io/) as well as [Redis Commander](https://github.com/joeferner/redis-commander),
66
+ * which is a web-based UI for interacting with Redis:
67
+ *
68
+ * ```YAML
69
+ * version: "3"
70
+ * services:
71
+ * redis:
72
+ * image: bitnami/redis:6.2
73
+ * hostname: redis
74
+ * container_name: redis
75
+ * environment:
76
+ * - ALLOW_EMPTY_PASSWORD=yes
77
+ * ports:
78
+ * - "6379:6379"
79
+ * redis-commander:
80
+ * container_name: redis-commander
81
+ * hostname: redis-commander
82
+ * image: rediscommander/redis-commander:latest
83
+ * environment:
84
+ * - REDIS_HOSTS=local:redis:6379
85
+ * ports:
86
+ * - "8085:8081"
87
+ * ```
88
+ *
89
+ * ## Concurrency
90
+ *
91
+ * The default concurrency of a single worker is 3, i.e. up to 3 jobs will be processed at the same time.
92
+ * You can change the concurrency in the `workerOptions` passed to the `init()` method:
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const config: DeenruvConfig = {
97
+ * plugins: [
98
+ * BullMQJobQueuePlugin.init({
99
+ * workerOptions: {
100
+ * concurrency: 10,
101
+ * },
102
+ * }),
103
+ * ],
104
+ * };
105
+ * ```
106
+ *
107
+ * ## Removing old jobs
108
+ *
109
+ * By default, BullMQ will keep completed jobs in the `completed` set and failed jobs in the `failed` set. Over time,
110
+ * these sets can grow very large. Since Deenruv v2.1, the default behaviour is to remove jobs from these sets after
111
+ * 30 days or after a maximum of 5,000 completed or failed jobs.
112
+ *
113
+ * This can be configured using the `removeOnComplete` and `removeOnFail` options:
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const config: DeenruvConfig = {
118
+ * plugins: [
119
+ * BullMQJobQueuePlugin.init({
120
+ * workerOptions: {
121
+ * removeOnComplete: {
122
+ * count: 500,
123
+ * },
124
+ * removeOnFail: {
125
+ * age: 60 * 60 * 24 * 7, // 7 days
126
+ * count: 1000,
127
+ * },
128
+ * }
129
+ * }),
130
+ * ],
131
+ * };
132
+ * ```
133
+ *
134
+ * The `count` option specifies the maximum number of jobs to keep in the set, while the `age` option specifies the
135
+ * maximum age of a job in seconds. If both options are specified, then the jobs kept will be the ones that satisfy
136
+ * both properties.
137
+ *
138
+ * @docsCategory core plugins/JobQueuePlugin
139
+ */
140
+ let BullMQJobQueuePlugin = class BullMQJobQueuePlugin {
141
+ /**
142
+ * @description
143
+ * Configures the plugin.
144
+ */
145
+ static init(options) {
146
+ this.options = options;
147
+ return this;
148
+ }
149
+ };
150
+ exports.BullMQJobQueuePlugin = BullMQJobQueuePlugin;
151
+ exports.BullMQJobQueuePlugin = BullMQJobQueuePlugin = __decorate([
152
+ (0, core_1.DeenruvPlugin)({
153
+ imports: [core_1.PluginCommonModule],
154
+ configuration: (config) => {
155
+ config.jobQueueOptions.jobQueueStrategy = new bullmq_job_queue_strategy_1.BullMQJobQueueStrategy();
156
+ config.jobQueueOptions.jobBufferStorageStrategy =
157
+ new redis_job_buffer_storage_strategy_1.RedisJobBufferStorageStrategy();
158
+ config.systemOptions.healthChecks.push(new redis_health_check_strategy_1.RedisHealthCheckStrategy());
159
+ return config;
160
+ },
161
+ providers: [
162
+ {
163
+ provide: constants_1.BULLMQ_PLUGIN_OPTIONS,
164
+ useFactory: () => BullMQJobQueuePlugin.options,
165
+ },
166
+ redis_health_indicator_1.RedisHealthIndicator,
167
+ ],
168
+ compatibility: "^0.0.0",
169
+ })
170
+ ], BullMQJobQueuePlugin);
171
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/bullmq/plugin.ts"],"names":[],"mappings":";;;;;;;;;AAAA,wCAAkE;AAElE,2EAAqE;AACrE,2CAAoD;AACpD,+EAAyE;AACzE,qEAAgE;AAChE,2FAAoF;AAGpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2HG;AAmBI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAG/B;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,OAA4B;QACtC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAXY,oDAAoB;+BAApB,oBAAoB;IAlBhC,IAAA,oBAAa,EAAC;QACb,OAAO,EAAE,CAAC,yBAAkB,CAAC;QAC7B,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE;YACxB,MAAM,CAAC,eAAe,CAAC,gBAAgB,GAAG,IAAI,kDAAsB,EAAE,CAAC;YACvE,MAAM,CAAC,eAAe,CAAC,wBAAwB;gBAC7C,IAAI,iEAA6B,EAAE,CAAC;YACtC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,sDAAwB,EAAE,CAAC,CAAC;YACvE,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,iCAAqB;gBAC9B,UAAU,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,OAAO;aAC/C;YACD,6CAAoB;SACrB;QACD,aAAa,EAAE,QAAQ;KACxB,CAAC;GACW,oBAAoB,CAWhC"}
@@ -0,0 +1,6 @@
1
+ import { HealthIndicatorFunction } from "@nestjs/terminus";
2
+ import { HealthCheckStrategy, Injector } from "@deenruv/core";
3
+ export declare class RedisHealthCheckStrategy implements HealthCheckStrategy {
4
+ init(injector: Injector): void;
5
+ getHealthIndicator(): HealthIndicatorFunction;
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisHealthCheckStrategy = void 0;
4
+ const redis_health_indicator_1 = require("./redis-health-indicator");
5
+ let indicator;
6
+ class RedisHealthCheckStrategy {
7
+ init(injector) {
8
+ indicator = injector.get(redis_health_indicator_1.RedisHealthIndicator);
9
+ }
10
+ getHealthIndicator() {
11
+ return () => indicator.isHealthy("redis (job queue)");
12
+ }
13
+ }
14
+ exports.RedisHealthCheckStrategy = RedisHealthCheckStrategy;
15
+ //# sourceMappingURL=redis-health-check-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-health-check-strategy.js","sourceRoot":"","sources":["../../src/bullmq/redis-health-check-strategy.ts"],"names":[],"mappings":";;;AAGA,qEAAgE;AAEhE,IAAI,SAA+B,CAAC;AAEpC,MAAa,wBAAwB;IACnC,IAAI,CAAC,QAAkB;QACrB,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,6CAAoB,CAAC,CAAC;IACjD,CAAC;IACD,kBAAkB;QAChB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACxD,CAAC;CACF;AAPD,4DAOC"}
@@ -0,0 +1,8 @@
1
+ import { HealthIndicator, HealthIndicatorResult } from "@nestjs/terminus";
2
+ import { BullMQPluginOptions } from "./types";
3
+ export declare class RedisHealthIndicator extends HealthIndicator {
4
+ private options;
5
+ private timeoutTimer;
6
+ constructor(options: BullMQPluginOptions);
7
+ isHealthy(key: string, timeoutMs?: number): Promise<HealthIndicatorResult>;
8
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RedisHealthIndicator = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const terminus_1 = require("@nestjs/terminus");
18
+ const core_1 = require("@deenruv/core");
19
+ const bullmq_1 = require("bullmq");
20
+ const constants_1 = require("./constants");
21
+ let RedisHealthIndicator = class RedisHealthIndicator extends terminus_1.HealthIndicator {
22
+ constructor(options) {
23
+ super();
24
+ this.options = options;
25
+ }
26
+ async isHealthy(key, timeoutMs = 5000) {
27
+ if (!this.options.connection) {
28
+ throw new Error("Redis connection options are not defined");
29
+ }
30
+ const connection = new bullmq_1.RedisConnection(this.options.connection);
31
+ const pingResult = await new Promise(async (resolve, reject) => {
32
+ try {
33
+ connection.on("error", (err) => {
34
+ core_1.Logger.error(`Redis health check error: ${JSON.stringify(err.message)}`, constants_1.loggerCtx, err.stack);
35
+ resolve(err);
36
+ });
37
+ if (this.timeoutTimer) {
38
+ clearTimeout(this.timeoutTimer);
39
+ }
40
+ const timeout = new Promise((_resolve) => (this.timeoutTimer = setTimeout(_resolve, timeoutMs)));
41
+ const client = await Promise.race([connection.client, timeout]);
42
+ clearTimeout(this.timeoutTimer);
43
+ if (!client) {
44
+ resolve("timeout");
45
+ return;
46
+ }
47
+ void client.ping((err, res) => {
48
+ if (err) {
49
+ resolve(err);
50
+ }
51
+ else {
52
+ resolve(res);
53
+ }
54
+ });
55
+ }
56
+ catch (e) {
57
+ resolve(e);
58
+ }
59
+ });
60
+ try {
61
+ await connection.close();
62
+ // await connection.disconnect();
63
+ }
64
+ catch (e) {
65
+ core_1.Logger.error(`Redis health check error closing connection: ${JSON.stringify(e.message)}`, constants_1.loggerCtx, e.stack);
66
+ }
67
+ const result = this.getStatus(key, pingResult === "PONG");
68
+ if (pingResult === "PONG") {
69
+ return result;
70
+ }
71
+ throw new terminus_1.HealthCheckError("Redis failed", result);
72
+ }
73
+ };
74
+ exports.RedisHealthIndicator = RedisHealthIndicator;
75
+ exports.RedisHealthIndicator = RedisHealthIndicator = __decorate([
76
+ (0, common_1.Injectable)(),
77
+ __param(0, (0, common_1.Inject)(constants_1.BULLMQ_PLUGIN_OPTIONS)),
78
+ __metadata("design:paramtypes", [Object])
79
+ ], RedisHealthIndicator);
80
+ //# sourceMappingURL=redis-health-indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-health-indicator.js","sourceRoot":"","sources":["../../src/bullmq/redis-health-indicator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { Injector, Job, JobBufferStorageStrategy } from "@deenruv/core";
2
+ export declare class RedisJobBufferStorageStrategy implements JobBufferStorageStrategy {
3
+ private redis;
4
+ init(injector: Injector): void;
5
+ add(bufferId: string, job: Job<any>): Promise<Job<any>>;
6
+ bufferSize(bufferIds?: string[]): Promise<{
7
+ [bufferId: string]: number;
8
+ }>;
9
+ flush(bufferIds?: string[]): Promise<{
10
+ [bufferId: string]: Job[];
11
+ }>;
12
+ private keyName;
13
+ private toJobConfigString;
14
+ private toJob;
15
+ private getAllBufferIds;
16
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisJobBufferStorageStrategy = void 0;
4
+ const core_1 = require("@deenruv/core");
5
+ const ioredis_1 = require("ioredis");
6
+ const constants_1 = require("./constants");
7
+ const BUFFER_LIST_PREFIX = "deenruv-job-buffer";
8
+ class RedisJobBufferStorageStrategy {
9
+ init(injector) {
10
+ const options = injector.get(constants_1.BULLMQ_PLUGIN_OPTIONS);
11
+ if (options.connection instanceof ioredis_1.Redis) {
12
+ this.redis = options.connection;
13
+ }
14
+ else if (options.connection instanceof ioredis_1.Cluster) {
15
+ this.redis = options.connection;
16
+ }
17
+ else {
18
+ this.redis = new ioredis_1.Redis(options.connection);
19
+ }
20
+ }
21
+ async add(bufferId, job) {
22
+ const result = await this.redis.lpush(this.keyName(bufferId), this.toJobConfigString(job));
23
+ return job;
24
+ }
25
+ async bufferSize(bufferIds) {
26
+ const ids = (bufferIds === null || bufferIds === void 0 ? void 0 : bufferIds.length) ? bufferIds : await this.getAllBufferIds();
27
+ const result = {};
28
+ for (const id of bufferIds || []) {
29
+ const key = this.keyName(id);
30
+ const count = await this.redis.llen(key);
31
+ result[id] = count;
32
+ }
33
+ return result;
34
+ }
35
+ async flush(bufferIds) {
36
+ const ids = (bufferIds === null || bufferIds === void 0 ? void 0 : bufferIds.length) ? bufferIds : await this.getAllBufferIds();
37
+ const result = {};
38
+ for (const id of bufferIds || []) {
39
+ const key = this.keyName(id);
40
+ const items = await this.redis.lrange(key, 0, -1);
41
+ await this.redis.del(key);
42
+ result[id] = items.map((item) => this.toJob(item));
43
+ }
44
+ return result;
45
+ }
46
+ keyName(bufferId) {
47
+ return `${BUFFER_LIST_PREFIX}:${bufferId}`;
48
+ }
49
+ toJobConfigString(job) {
50
+ var _a;
51
+ const jobConfig = Object.assign(Object.assign({}, job), { data: job.data, id: (_a = job.id) !== null && _a !== void 0 ? _a : undefined });
52
+ return JSON.stringify(jobConfig);
53
+ }
54
+ toJob(jobConfigString) {
55
+ try {
56
+ const jobConfig = JSON.parse(jobConfigString);
57
+ return new core_1.Job(jobConfig);
58
+ }
59
+ catch (e) {
60
+ core_1.Logger.error(`Could not parse buffered job:\n${JSON.stringify(e.message)}`, constants_1.loggerCtx, e.stack);
61
+ throw e;
62
+ }
63
+ }
64
+ async getAllBufferIds() {
65
+ const stream = this.redis instanceof ioredis_1.Redis
66
+ ? this.redis.scanStream({
67
+ match: `${BUFFER_LIST_PREFIX}:*`,
68
+ })
69
+ : this.redis.nodes()[0].scanStream({
70
+ match: `${BUFFER_LIST_PREFIX}:*`,
71
+ });
72
+ const keys = await new Promise((resolve, reject) => {
73
+ const allKeys = [];
74
+ stream.on("data", (_keys) => allKeys.push(..._keys));
75
+ stream.on("end", () => resolve(allKeys));
76
+ stream.on("error", (err) => reject(err));
77
+ });
78
+ return keys.map((key) => key.replace(`${BUFFER_LIST_PREFIX}:`, ""));
79
+ }
80
+ }
81
+ exports.RedisJobBufferStorageStrategy = RedisJobBufferStorageStrategy;
82
+ //# sourceMappingURL=redis-job-buffer-storage-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-job-buffer-storage-strategy.js","sourceRoot":"","sources":["../../src/bullmq/redis-job-buffer-storage-strategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { CustomScriptDefinition } from "../types";
2
+ export declare const getJobsByType: CustomScriptDefinition<[
3
+ totalItems: number,
4
+ jobIds: string[]
5
+ ], [
6
+ rangeStart: number,
7
+ rangeEnd: number,
8
+ queueName: string | undefined,
9
+ ...states: string[]
10
+ ]>;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getJobsByType = void 0;
4
+ const script = `--[[
5
+ Get job ids per provided states and filter by name
6
+ Input:
7
+ KEYS[1] 'prefix'
8
+ ARGV[1] start
9
+ ARGV[2] end
10
+ ARGV[3] filterName
11
+ ARGV[4...] types
12
+ ]]
13
+ local rcall = redis.call
14
+ local prefix = KEYS[1]
15
+ local rangeStart = tonumber(ARGV[1])
16
+ local rangeEnd = tonumber(ARGV[2])
17
+ local filterName = ARGV[3]
18
+ local results = {}
19
+
20
+ local targetSets = {}
21
+
22
+ -- Initialize an empty array to hold the sets to unionize. The "completed" and "failed" lists
23
+ -- are sorted sets
24
+ local setsToUnionize = {}
25
+ local typesInUnion = {}
26
+
27
+ -- Initialize an empty array to hold lists to include. The "active" and "wait" lists are
28
+ -- regular lists
29
+ local listsToInclude = {}
30
+
31
+
32
+ -- Iterate through ARGV starting from the first element (ARGV[1]) up to the end
33
+ for i = 4, #ARGV do
34
+ local setKey = prefix .. ARGV[i]
35
+
36
+ -- Check if the setKey is valid (e.g., it exists and is a sorted set)
37
+ local targetExists = redis.call('EXISTS', setKey)
38
+ local listType = redis.call('TYPE', setKey).ok
39
+
40
+ if targetExists == 1 and listType == 'zset' then
41
+ -- Add the valid set to the array
42
+ table.insert(setsToUnionize, setKey)
43
+ table.insert(typesInUnion, ARGV[i])
44
+ end
45
+ if targetExists == 1 and listType == 'list' then
46
+ -- Add the valid set to the array
47
+ table.insert(listsToInclude, setKey)
48
+ table.insert(typesInUnion, ARGV[i])
49
+ end
50
+ end
51
+
52
+ -- Define the destination key for the concatenated sorted set
53
+ local tempSortedSetUnionKey = prefix .. 'union:' .. table.concat(typesInUnion, ':');
54
+
55
+ if #listsToInclude == 0 and #setsToUnionize == 0 then
56
+ return {0, {}}
57
+ end
58
+
59
+ -- Check if there are valid sets to unionize
60
+ if #setsToUnionize > 0 then
61
+ -- Use ZUNIONSTORE to concatenate the valid sorted sets into the destination key
62
+ local numSets = #setsToUnionize
63
+ redis.call('ZUNIONSTORE', tempSortedSetUnionKey, numSets, unpack(setsToUnionize))
64
+ end
65
+
66
+ local originalResults = rcall("ZREVRANGE", tempSortedSetUnionKey, 0, -1)
67
+
68
+
69
+ if #listsToInclude > 0 then
70
+ for _, listKey in ipairs(listsToInclude) do
71
+ local list = rcall("LRANGE", listKey, 0, -1)
72
+ for _, jobId in ipairs(list) do
73
+ table.insert(originalResults, jobId)
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+ -- Define a custom comparison function for sorting in descending order
80
+ local function compareDescending(a, b)
81
+ return tonumber(a) > tonumber(b)
82
+ end
83
+
84
+ -- Sort the table in descending order
85
+ table.sort(originalResults, compareDescending)
86
+
87
+ local filteredResults = {}
88
+ local totalResults = 0
89
+
90
+ for _, job in ipairs(originalResults) do
91
+ local jobName = rcall("HGET", prefix .. job, "name");
92
+ if filterName ~= "" and jobName == filterName then
93
+ if rangeStart <= totalResults and #filteredResults < rangeEnd then
94
+ table.insert(filteredResults, job)
95
+ end
96
+ totalResults = totalResults + 1
97
+ elseif filterName == "" then
98
+ if rangeStart <= totalResults and #filteredResults < rangeEnd then
99
+ table.insert(filteredResults, job)
100
+ end
101
+ totalResults = totalResults + 1
102
+ end
103
+ end
104
+
105
+ rcall("DEL", tempSortedSetUnionKey)
106
+
107
+ return {totalResults, filteredResults}
108
+ `;
109
+ exports.getJobsByType = {
110
+ script,
111
+ numberOfKeys: 1,
112
+ name: "getJobsByType",
113
+ };
114
+ //# sourceMappingURL=get-jobs-by-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-jobs-by-type.js","sourceRoot":"","sources":["../../../src/bullmq/scripts/get-jobs-by-type.ts"],"names":[],"mappings":";;;AAGA,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGd,CAAC;AAEW,QAAA,aAAa,GAQtB;IACF,MAAM;IACN,YAAY,EAAE,CAAC;IACf,IAAI,EAAE,eAAe;CACtB,CAAC"}