@yrpri/api 9.0.193 → 9.0.194
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 +19 -4
- package/models/video.cjs +19 -4
- package/package.json +2 -2
- package/services/workers/queue.cjs +97 -38
- package/services/workers/queue.d.cts +11 -14
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('
|
|
9
|
-
|
|
10
|
-
|
|
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("
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
3
|
+
"version": "9.0.194",
|
|
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
|
-
"
|
|
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,79 @@ var airbrake = null;
|
|
|
5
5
|
if (process.env.AIRBRAKE_PROJECT_ID) {
|
|
6
6
|
airbrake = require('../utils/airbrake.cjs');
|
|
7
7
|
}
|
|
8
|
-
const
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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 });
|
|
21
30
|
class YpQueue {
|
|
22
31
|
constructor() {
|
|
23
32
|
log.info("Create YpQueue");
|
|
24
|
-
this.
|
|
33
|
+
this.processors = new Map(); // Store processors per job name
|
|
34
|
+
this.workers = new Map(); // Store workers per job name
|
|
35
|
+
this.queues = new Map(); // Store queues per job name
|
|
36
|
+
this.queuePrefix = 'mainYpQueue';
|
|
25
37
|
}
|
|
26
|
-
get
|
|
38
|
+
get defaultJobOptions() {
|
|
27
39
|
return {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
attempts: 1,
|
|
41
|
+
removeOnComplete: true,
|
|
42
|
+
removeOnFail: true
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Wrap callback-style processor to work with BullMQ's promise-based API
|
|
46
|
+
wrapProcessor(processor) {
|
|
47
|
+
return (job) => {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
// Call the processor with job and a done callback
|
|
50
|
+
processor(job, (error, result) => {
|
|
51
|
+
if (error) {
|
|
52
|
+
reject(error);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
resolve(result);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
34
59
|
};
|
|
35
60
|
}
|
|
36
61
|
process(name, concurrency, processor) {
|
|
37
|
-
this.
|
|
62
|
+
if (this.workers.has(name)) {
|
|
63
|
+
log.warn('Processor already registered, skipping re-registration', { name });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const wrappedProcessor = this.wrapProcessor(processor);
|
|
67
|
+
this.processors.set(name, wrappedProcessor);
|
|
68
|
+
const queue = this.getQueue(name);
|
|
69
|
+
log.info('Registered processor', { name, concurrency, queue: queue.name });
|
|
70
|
+
const worker = new Worker(queue.name, wrappedProcessor, {
|
|
71
|
+
connection: redisConnection,
|
|
72
|
+
concurrency,
|
|
73
|
+
name
|
|
74
|
+
});
|
|
75
|
+
this.workers.set(name, worker);
|
|
76
|
+
this.setupWorkerEvents(worker);
|
|
38
77
|
}
|
|
39
78
|
add(name, workPackage, priority, options) {
|
|
40
|
-
const
|
|
79
|
+
const queue = this.getQueue(name);
|
|
80
|
+
const jobOptions = { ...this.defaultJobOptions, ...(options || {}) };
|
|
41
81
|
let priorityNumber = 1000;
|
|
42
82
|
switch (priority) {
|
|
43
83
|
case 'now':
|
|
@@ -57,32 +97,51 @@ class YpQueue {
|
|
|
57
97
|
break;
|
|
58
98
|
}
|
|
59
99
|
jobOptions.priority = priorityNumber;
|
|
60
|
-
|
|
100
|
+
queue.add(name, workPackage, jobOptions);
|
|
101
|
+
}
|
|
102
|
+
getQueue(name) {
|
|
103
|
+
if (this.queues.has(name)) {
|
|
104
|
+
return this.queues.get(name);
|
|
105
|
+
}
|
|
106
|
+
const queueName = `${this.queuePrefix}-${name}`;
|
|
107
|
+
const queue = new Queue(queueName, {
|
|
108
|
+
connection: redisConnection,
|
|
109
|
+
defaultJobOptions: this.defaultJobOptions
|
|
110
|
+
});
|
|
111
|
+
queue.on('error', (error) => {
|
|
112
|
+
log.error('Queue Error', { error });
|
|
113
|
+
if (airbrake) {
|
|
114
|
+
if (!(error instanceof Error)) {
|
|
115
|
+
error = new Error(error);
|
|
116
|
+
}
|
|
117
|
+
airbrake.notify(error).then((airbrakeErr) => {
|
|
118
|
+
if (airbrakeErr.error) {
|
|
119
|
+
log.error("AirBrake Error", { context: 'airbrake', err: airbrakeErr.error, errorStatus: 500 });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
this.queues.set(name, queue);
|
|
125
|
+
return queue;
|
|
61
126
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.mainQueue.on('active', function (job) {
|
|
127
|
+
setupWorkerEvents(worker) {
|
|
128
|
+
worker.on('active', (job) => {
|
|
65
129
|
log.info('JQ', { id: job.id, name: job.name });
|
|
66
|
-
})
|
|
130
|
+
});
|
|
131
|
+
worker.on('completed', (job) => {
|
|
67
132
|
log.info('JC', { id: job.id, name: job.name });
|
|
68
|
-
})
|
|
133
|
+
});
|
|
134
|
+
worker.on('failed', (job, error) => {
|
|
69
135
|
log.error('Job Failed', { id: job ? job.id : null, name: job ? job.name : null, data: job ? job.data : null, error });
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
log.info('Job
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
|
|
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 });
|
|
136
|
+
});
|
|
137
|
+
worker.on('stalled', (jobId) => {
|
|
138
|
+
log.info('Job Stalled', { id: jobId });
|
|
139
|
+
});
|
|
140
|
+
worker.on('progress', (job, progress) => {
|
|
141
|
+
log.info('Job Progress', { id: job.id, progress });
|
|
142
|
+
});
|
|
143
|
+
worker.on('error', (error) => {
|
|
144
|
+
log.error('Worker Error', { error });
|
|
86
145
|
if (airbrake) {
|
|
87
146
|
if (!(error instanceof Error)) {
|
|
88
147
|
error = new Error(error);
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
declare const _exports: YpQueue;
|
|
2
2
|
export = _exports;
|
|
3
3
|
declare class YpQueue {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
removeOnComplete: boolean;
|
|
13
|
-
removeOnFail: boolean;
|
|
14
|
-
};
|
|
4
|
+
processors: Map<any, any>;
|
|
5
|
+
workers: Map<any, any>;
|
|
6
|
+
queues: Map<any, any>;
|
|
7
|
+
queuePrefix: string;
|
|
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
|
-
|
|
19
|
-
|
|
16
|
+
getQueue(name: any): any;
|
|
17
|
+
setupWorkerEvents(worker: any): void;
|
|
20
18
|
}
|
|
21
|
-
import BullQueue = require("bull");
|