ti2 1.0.124 → 1.0.126

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/worker/queue.js CHANGED
@@ -1,12 +1,17 @@
1
1
  const Queue = require('bull');
2
+ const crypto = require('crypto');
2
3
  const R = require('ramda');
3
4
  const Redis = require('ioredis');
4
- const sqldb = require('../models');
5
- const { env: { redisHost, redisPort } } = process;
5
+
6
6
  const REDIS_URL = process.env.REDIS_URL || 'redis://redis:6379';
7
7
  const itemsTTL = 3 * 60 * 60; // 3 hours
8
8
 
9
- const queue = new Queue('work', `${REDIS_URL}/0`);
9
+ const queue = new Queue('work', `${REDIS_URL}/0`, {
10
+ settings: {
11
+ lockDuration: 120e3,
12
+ lockRenewTime: 15e3,
13
+ },
14
+ });
10
15
  const redisResults = new Redis(`${REDIS_URL}/1`);
11
16
 
12
17
  const getPending = async () => {
@@ -40,71 +45,60 @@ const addJob = async (payload, paramsParam) => {
40
45
  removeOnComplete: true,
41
46
  ...params,
42
47
  });
43
- // For repeat jobs, we need to store the full repeat key
44
- const id = job.opts.repeat ? job.opts.jobId : job.id;
45
- return id;
48
+ // Repeat jobs use an occurrence ID containing Bull's repeat-definition hash.
49
+ // Persisting it lets removeJob identify one exact definition without matching by cron.
50
+ return job.id;
46
51
  };
47
52
 
48
53
  const saveResult = async ({ id, resultValue }) => {
49
54
  await redisResults.set(id, JSON.stringify(resultValue), 'EX', itemsTTL);
50
55
  };
51
56
 
52
- const removeJob = async (jobId) => {
53
- // For repeat jobs, the ID is in the format 'repeat:jobId:timestamp'
57
+ const removeJob = async jobId => {
58
+ if (!jobId) return;
59
+ // Bull occurrence IDs use the format 'repeat:<repeat-definition-hash>:<timestamp>'.
54
60
  const jobIdParts = jobId.split(':');
55
61
  const isRepeatJob = jobIdParts[0] === 'repeat';
56
62
 
57
63
  if (isRepeatJob) {
58
- // Get all repeatable jobs
59
- const repeatableJobs = await queue.getRepeatableJobs();
60
-
61
- // Find the job with matching cron pattern
62
- const cronJob = await sqldb.CronJobs.findOne({
63
- where: {
64
- bullJobId: jobId,
65
- },
66
- });
67
-
68
- if (cronJob) {
69
- // First remove all repeatable jobs with matching cron pattern
70
- for (const repeatableJob of repeatableJobs) {
71
- if (repeatableJob.cron === cronJob.cron) {
72
- await queue.removeRepeatableByKey(repeatableJob.key);
73
- }
74
- }
75
-
76
- // Then remove all jobs with this pattern
77
- const jobs = await queue.getJobs(['active', 'wait', 'delayed']);
78
- for (const job of jobs) {
79
- if (job.opts.repeat && job.opts.jobId === jobId) {
80
- await job.remove();
81
- }
82
- }
83
-
84
- // Wait for the queue to process the removals
85
- await new Promise(resolve => setTimeout(resolve, 500));
86
-
87
- // Clean up any remaining jobs
88
- const remainingJobs = await queue.getJobs(['active', 'wait', 'delayed']);
89
- for (const job of remainingJobs) {
90
- if (job.opts.repeat && job.opts.jobId === jobId) {
91
- await job.remove();
92
- }
64
+ const job = await queue.getJob(jobId);
65
+ let repeatKey = R.path(['opts', 'repeat', 'key'], job);
66
+ if (!repeatKey) {
67
+ // Bull v4 does not expose occurrence-to-definition lookup. Recreate its
68
+ // internal hash only for legacy rows whose scheduled occurrence has expired.
69
+ const repeatHash = jobIdParts[1];
70
+ const repeatableJobs = await queue.getRepeatableJobs();
71
+ const repeatable = repeatableJobs.find(candidate => {
72
+ const candidateJobId = candidate.id ? `${candidate.id}:` : ':';
73
+ const namespace = crypto.createHash('md5').update(candidate.key).digest('hex');
74
+ const candidateHash = crypto.createHash('md5')
75
+ .update(`${candidate.name}${candidateJobId}${namespace}`)
76
+ .digest('hex');
77
+ return candidateHash === repeatHash;
78
+ });
79
+ repeatKey = repeatable && repeatable.key;
80
+ }
81
+ if (repeatKey) await queue.removeRepeatableByKey(repeatKey);
82
+ if (job) {
83
+ try {
84
+ await job.remove();
85
+ } catch (error) {
86
+ if (error.message && !error.message.includes('not found')) throw error;
93
87
  }
94
-
95
- await redisResults.del(jobId);
96
- return;
97
88
  }
98
-
99
- // If we couldn't find the repeatable job, just delete from the database
100
- // This can happen if the job was already removed from Bull but still exists in our database
89
+ await redisResults.del(jobId);
101
90
  return;
102
91
  }
103
92
 
104
93
  // If not a repeatable job, try to remove as a regular job
105
94
  const job = await queue.getJob(jobId);
106
95
  if (!job) {
107
- throw new Error('Job not found');
96
+ // Older CronJobs rows stored opts.jobId instead of the repeat occurrence ID.
97
+ const repeatableJobs = await queue.getRepeatableJobs() || [];
98
+ const repeatable = repeatableJobs.find(candidate => candidate.id === jobId);
99
+ if (repeatable) await queue.removeRepeatableByKey(repeatable.key);
100
+ await redisResults.del(jobId);
101
+ return;
108
102
  }
109
103
  await job.remove();
110
104
  await redisResults.del(jobId);