@yrpri/api 9.0.193 → 9.0.195

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/models/audio.cjs CHANGED
@@ -5,9 +5,24 @@ const _ = require('lodash');
5
5
  const queue = require('../services/workers/queue.cjs');
6
6
  let bullAudioQueue;
7
7
  if (process.env.USE_YOUR_PRIORITIES_ENCODER) {
8
- const Queue = require('bull');
9
- const redisUrl = process.env.REDIS_URL ? process.env.REDIS_URL : undefined;
10
- bullAudioQueue = new Queue('AudioEncoding', redisUrl);
8
+ const { Queue } = require('bullmq');
9
+ let redisUrl = process.env.REDIS_URL ? process.env.REDIS_URL : "redis://localhost:6379";
10
+ if (redisUrl.startsWith("redis://h:")) {
11
+ redisUrl = redisUrl.replace("redis://h:", "redis://:");
12
+ }
13
+ const parsedRedisUrl = new URL(redisUrl);
14
+ const redisConnection = {
15
+ host: parsedRedisUrl.hostname,
16
+ port: parseInt(parsedRedisUrl.port) || 6379,
17
+ password: parsedRedisUrl.password || undefined,
18
+ username: parsedRedisUrl.username || undefined,
19
+ };
20
+ if (redisUrl.startsWith("rediss://")) {
21
+ redisConnection.tls = {
22
+ rejectUnauthorized: false,
23
+ };
24
+ }
25
+ bullAudioQueue = new Queue('AudioEncoding', { connection: redisConnection });
11
26
  }
12
27
  module.exports = (sequelize, DataTypes) => {
13
28
  const Audio = sequelize.define("Audio", {
@@ -393,7 +408,7 @@ module.exports = (sequelize, DataTypes) => {
393
408
  jobPackage = _.merge(jobPackage, {
394
409
  acBackgroundJobId: jobId,
395
410
  });
396
- await bullAudioQueue.add(jobPackage);
411
+ await bullAudioQueue.add('audio-encoding', jobPackage);
397
412
  callback(null, { Job: { Id: jobId } });
398
413
  }
399
414
  });
package/models/video.cjs CHANGED
@@ -6,9 +6,24 @@ const _ = require("lodash");
6
6
  const queue = require("../services/workers/queue.cjs");
7
7
  let bullVideoQueue;
8
8
  if (process.env.USE_YOUR_PRIORITIES_ENCODER) {
9
- const Queue = require("bull");
10
- const redisUrl = process.env.REDIS_URL ? process.env.REDIS_URL : undefined;
11
- bullVideoQueue = new Queue("VideoEncoding", redisUrl);
9
+ const { Queue } = require("bullmq");
10
+ let redisUrl = process.env.REDIS_URL ? process.env.REDIS_URL : "redis://localhost:6379";
11
+ if (redisUrl.startsWith("redis://h:")) {
12
+ redisUrl = redisUrl.replace("redis://h:", "redis://:");
13
+ }
14
+ const parsedRedisUrl = new URL(redisUrl);
15
+ const redisConnection = {
16
+ host: parsedRedisUrl.hostname,
17
+ port: parseInt(parsedRedisUrl.port) || 6379,
18
+ password: parsedRedisUrl.password || undefined,
19
+ username: parsedRedisUrl.username || undefined,
20
+ };
21
+ if (redisUrl.startsWith("rediss://")) {
22
+ redisConnection.tls = {
23
+ rejectUnauthorized: false,
24
+ };
25
+ }
26
+ bullVideoQueue = new Queue("VideoEncoding", { connection: redisConnection });
12
27
  }
13
28
  module.exports = (sequelize, DataTypes) => {
14
29
  const Video = sequelize.define("Video", {
@@ -1077,7 +1092,7 @@ module.exports = (sequelize, DataTypes) => {
1077
1092
  jobPackage = _.merge(jobPackage, {
1078
1093
  acBackgroundJobId: jobId,
1079
1094
  });
1080
- await bullVideoQueue.add(jobPackage);
1095
+ await bullVideoQueue.add('video-encoding', jobPackage);
1081
1096
  callback(null, { Job: { Id: jobId } });
1082
1097
  }
1083
1098
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yrpri/api",
3
- "version": "9.0.193",
3
+ "version": "9.0.195",
4
4
  "license": "MIT",
5
5
  "author": "Robert Bjarnason & Citizens Foundation",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "axios": "^1.9.0",
33
33
  "bcrypt": "^5.1.1",
34
34
  "body-parser": "^2.2.0",
35
- "bull": "^4.16.5",
35
+ "bullmq": "^5.31.0",
36
36
  "bunyan": "^1.8.15",
37
37
  "bunyan-prettystream": "git+https://github.com/rbjarnason/node-bunyan-prettystream.git",
38
38
  "cheerio": "^1.0.0",
@@ -5,39 +5,80 @@ var airbrake = null;
5
5
  if (process.env.AIRBRAKE_PROJECT_ID) {
6
6
  airbrake = require('../utils/airbrake.cjs');
7
7
  }
8
- const BullQueue = require('bull');
8
+ const { Queue, Worker } = require('bullmq');
9
9
  let redisUrl = process.env.REDIS_URL ? process.env.REDIS_URL : "redis://localhost:6379";
10
10
  if (redisUrl.startsWith("redis://h:")) {
11
11
  redisUrl = redisUrl.replace("redis://h:", "redis://:");
12
12
  }
13
13
  const EventEmitter = require('events');
14
14
  EventEmitter.defaultMaxListeners = 100;
15
- const redissOptions = {
16
- tls: {
17
- rejectUnauthorized: false,
18
- },
15
+ // Parse redis URL to get connection options
16
+ const parsedRedisUrl = new URL(redisUrl);
17
+ const redisConnection = {
18
+ host: parsedRedisUrl.hostname,
19
+ port: parseInt(parsedRedisUrl.port) || 6379,
20
+ password: parsedRedisUrl.password || undefined,
21
+ username: parsedRedisUrl.username || undefined,
19
22
  };
20
- log.info("Starting app access to Bull Queue", { redis_url: redisUrl });
23
+ // Add TLS options for rediss:// URLs
24
+ if (redisUrl.startsWith("rediss://")) {
25
+ redisConnection.tls = {
26
+ rejectUnauthorized: false,
27
+ };
28
+ }
29
+ log.info("Starting app access to BullMQ Queue", { redis_url: redisUrl });
30
+ MAIN_WORKER_QUEUE_NAME = process.env.MAIN_WORKER_QUEUE_NAME || 'mainYpQueue';
21
31
  class YpQueue {
22
32
  constructor() {
23
33
  log.info("Create YpQueue");
24
- this.createQueues();
34
+ this.processors = new Map(); // Store processors per job name
35
+ this.workers = new Map(); // Store workers per job name
36
+ this.queues = new Map(); // Store queues per job name
37
+ this.queuePrefix = MAIN_WORKER_QUEUE_NAME;
25
38
  }
26
- get defaultQueueOptions() {
39
+ get defaultJobOptions() {
27
40
  return {
28
- redis: redisUrl.includes("rediss://") ? redissOptions : undefined,
29
- defaultJobOptions: {
30
- attempts: 1,
31
- removeOnComplete: true,
32
- removeOnFail: true
33
- }
41
+ attempts: 1,
42
+ removeOnComplete: true,
43
+ removeOnFail: true
44
+ };
45
+ }
46
+ // Wrap callback-style processor to work with BullMQ's promise-based API
47
+ wrapProcessor(processor) {
48
+ return (job) => {
49
+ return new Promise((resolve, reject) => {
50
+ // Call the processor with job and a done callback
51
+ processor(job, (error, result) => {
52
+ if (error) {
53
+ reject(error);
54
+ }
55
+ else {
56
+ resolve(result);
57
+ }
58
+ });
59
+ });
34
60
  };
35
61
  }
36
62
  process(name, concurrency, processor) {
37
- this.mainQueue.process(name, concurrency, processor);
63
+ if (this.workers.has(name)) {
64
+ log.warn('Processor already registered, skipping re-registration', { name });
65
+ return;
66
+ }
67
+ const wrappedProcessor = this.wrapProcessor(processor);
68
+ this.processors.set(name, wrappedProcessor);
69
+ const queue = this.getQueue(name);
70
+ log.info('Registered processor', { name, concurrency, queue: queue.name });
71
+ const worker = new Worker(queue.name, wrappedProcessor, {
72
+ connection: redisConnection,
73
+ concurrency,
74
+ name
75
+ });
76
+ this.workers.set(name, worker);
77
+ this.setupWorkerEvents(worker);
38
78
  }
39
79
  add(name, workPackage, priority, options) {
40
- const jobOptions = options || {};
80
+ const queue = this.getQueue(name);
81
+ const jobOptions = { ...this.defaultJobOptions, ...(options || {}) };
41
82
  let priorityNumber = 1000;
42
83
  switch (priority) {
43
84
  case 'now':
@@ -57,32 +98,51 @@ class YpQueue {
57
98
  break;
58
99
  }
59
100
  jobOptions.priority = priorityNumber;
60
- this.mainQueue.add(name, workPackage, jobOptions);
101
+ queue.add(name, workPackage, jobOptions);
102
+ }
103
+ getQueue(name) {
104
+ if (this.queues.has(name)) {
105
+ return this.queues.get(name);
106
+ }
107
+ const queueName = `${this.queuePrefix}-${name}`;
108
+ const queue = new Queue(queueName, {
109
+ connection: redisConnection,
110
+ defaultJobOptions: this.defaultJobOptions
111
+ });
112
+ queue.on('error', (error) => {
113
+ log.error('Queue Error', { error });
114
+ if (airbrake) {
115
+ if (!(error instanceof Error)) {
116
+ error = new Error(error);
117
+ }
118
+ airbrake.notify(error).then((airbrakeErr) => {
119
+ if (airbrakeErr.error) {
120
+ log.error("AirBrake Error", { context: 'airbrake', err: airbrakeErr.error, errorStatus: 500 });
121
+ }
122
+ });
123
+ }
124
+ });
125
+ this.queues.set(name, queue);
126
+ return queue;
61
127
  }
62
- createQueues() {
63
- this.mainQueue = new BullQueue('mainYpQueue', redisUrl, this.defaultQueueOptions);
64
- this.mainQueue.on('active', function (job) {
128
+ setupWorkerEvents(worker) {
129
+ worker.on('active', (job) => {
65
130
  log.info('JQ', { id: job.id, name: job.name });
66
- }).on('completed', function (job, result) {
131
+ });
132
+ worker.on('completed', (job) => {
67
133
  log.info('JC', { id: job.id, name: job.name });
68
- }).on('failed', function (job, error) {
134
+ });
135
+ worker.on('failed', (job, error) => {
69
136
  log.error('Job Failed', { id: job ? job.id : null, name: job ? job.name : null, data: job ? job.data : null, error });
70
- }).on('resumed', function (job) {
71
- log.info('Job Removed', { id: job.id });
72
- }).on('waiting', function (jobId) {
73
- log.info('Job Waiting', { id: jobId });
74
- }).on('stalled', function (job) {
75
- log.info('Job Stalled', { id: job.id, name: job.name, data: job.data, });
76
- }).on('progress', function (job, process) {
77
- log.info('Job Progress', { id: job.id, process });
78
- }).on('paused', function () {
79
- log.info('Queue Paused');
80
- }).on('cleaned', function (jobs, type) {
81
- log.info('Job Cleaned', { jobs, type });
82
- }).on('drained', function () {
83
- log.info('Queue Drained');
84
- }).on('error', function (error) {
85
- log.error('Job Error', { error });
137
+ });
138
+ worker.on('stalled', (jobId) => {
139
+ log.info('Job Stalled', { id: jobId });
140
+ });
141
+ worker.on('progress', (job, progress) => {
142
+ log.info('Job Progress', { id: job.id, progress });
143
+ });
144
+ worker.on('error', (error) => {
145
+ log.error('Worker Error', { error });
86
146
  if (airbrake) {
87
147
  if (!(error instanceof Error)) {
88
148
  error = new Error(error);
@@ -1,21 +1,18 @@
1
1
  declare const _exports: YpQueue;
2
2
  export = _exports;
3
3
  declare class YpQueue {
4
- get defaultQueueOptions(): {
5
- redis: {
6
- tls: {
7
- rejectUnauthorized: boolean;
8
- };
9
- } | undefined;
10
- defaultJobOptions: {
11
- attempts: number;
12
- removeOnComplete: boolean;
13
- removeOnFail: boolean;
14
- };
4
+ processors: Map<any, any>;
5
+ workers: Map<any, any>;
6
+ queues: Map<any, any>;
7
+ queuePrefix: any;
8
+ get defaultJobOptions(): {
9
+ attempts: number;
10
+ removeOnComplete: boolean;
11
+ removeOnFail: boolean;
15
12
  };
13
+ wrapProcessor(processor: any): (job: any) => Promise<any>;
16
14
  process(name: any, concurrency: any, processor: any): void;
17
15
  add(name: any, workPackage: any, priority: any, options: any): void;
18
- createQueues(): void;
19
- mainQueue: BullQueue.Queue<any> | undefined;
16
+ getQueue(name: any): any;
17
+ setupWorkerEvents(worker: any): void;
20
18
  }
21
- import BullQueue = require("bull");