You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
7.3 KiB

1 month ago
'use strict';
const _ = require('lodash');
const Job = require('./job');
const scripts = require('./scripts');
module.exports = function(Queue) {
Queue.prototype.getJob = async function(jobId) {
await this.isReady();
return Job.fromId(this, jobId);
};
Queue.prototype.getCountsPerPriority = async function(priorities) {
const uniquePriorities = [...new Set(priorities)];
const responses = await scripts.getCountsPerPriority(
this,
uniquePriorities
);
const counts = {};
responses.forEach((res, index) => {
counts[`${uniquePriorities[index]}`] = res || 0;
});
return counts;
};
Queue.prototype._commandByType = function(types, count, callback) {
return _.map(types, type => {
type = type === 'waiting' ? 'wait' : type; // alias
const key = this.toKey(type);
switch (type) {
case 'completed':
case 'failed':
case 'delayed':
case 'repeat':
return callback(key, count ? 'zcard' : 'zrange');
case 'active':
case 'wait':
case 'paused':
return callback(key, count ? 'llen' : 'lrange');
}
});
};
/**
Returns the number of jobs waiting to be processed.
*/
Queue.prototype.count = function() {
return this.getJobCountByTypes('wait', 'paused', 'delayed');
};
// Job counts by type
// Queue#getJobCountByTypes('completed') => completed count
// Queue#getJobCountByTypes('completed,failed') => completed + failed count
// Queue#getJobCountByTypes('completed', 'failed') => completed + failed count
// Queue#getJobCountByTypes('completed,waiting', 'failed') => completed + waiting + failed count
Queue.prototype.getJobCountByTypes = function() {
return this.getJobCounts.apply(this, arguments).then(result => {
return _.chain(result)
.values()
.sum()
.value();
});
};
/**
* Returns the job counts for each type specified or every list/set in the queue by default.
*
*/
Queue.prototype.getJobCounts = function() {
const types = parseTypeArg(arguments);
const multi = this.multi();
this._commandByType(types, true, (key, command) => {
multi[command](key);
});
return multi.exec().then(res => {
const counts = {};
res.forEach((res, index) => {
counts[types[index]] = res[1] || 0;
});
return counts;
});
};
Queue.prototype.getCompletedCount = function() {
return this.getJobCountByTypes('completed');
};
Queue.prototype.getFailedCount = function() {
return this.getJobCountByTypes('failed');
};
Queue.prototype.getDelayedCount = function() {
return this.getJobCountByTypes('delayed');
};
Queue.prototype.getActiveCount = function() {
return this.getJobCountByTypes('active');
};
Queue.prototype.getWaitingCount = function() {
return this.getJobCountByTypes('wait', 'paused');
};
/**
*
* @returns the potential stalled jobs. Only useful for tests.
*/
Queue.prototype.getStalledCount = function() {
const key = this.toKey('stalled');
return this.client.scard(key);
};
// TO BE DEPRECATED --->
Queue.prototype.getPausedCount = function() {
return this.getJobCountByTypes('paused');
};
// <-----
Queue.prototype.getWaiting = function(start, end, opts) {
return this.getJobs(['wait', 'paused'], start, end, true, opts);
};
Queue.prototype.getActive = function(start, end, opts) {
return this.getJobs('active', start, end, true, opts);
};
Queue.prototype.getDelayed = function(start, end, opts) {
return this.getJobs('delayed', start, end, true, opts);
};
Queue.prototype.getCompleted = function(start, end, opts) {
return this.getJobs('completed', start, end, false, opts);
};
Queue.prototype.getFailed = function(start, end, opts) {
return this.getJobs('failed', start, end, false, opts);
};
Queue.prototype.getRanges = function(types, start, end, asc) {
start = _.isUndefined(start) ? 0 : start;
end = _.isUndefined(end) ? -1 : end;
const multi = this.multi();
const multiCommands = [];
this._commandByType(parseTypeArg(types), false, (key, command) => {
switch (command) {
case 'lrange':
if (asc) {
multiCommands.push('lrange');
multi.lrange(key, -(end + 1), -(start + 1));
} else {
multi.lrange(key, start, end);
}
break;
case 'zrange':
multiCommands.push('zrange');
if (asc) {
multi.zrange(key, start, end);
} else {
multi.zrevrange(key, start, end);
}
break;
}
});
return multi.exec().then(responses => {
let results = [];
responses.forEach((response, index) => {
const result = response[1] || [];
if (asc && multiCommands[index] === 'lrange') {
results = results.concat(result.reverse());
} else {
results = results.concat(result);
}
});
return results;
});
};
Queue.prototype.getJobs = function(types, start, end, asc, opts) {
return this.getRanges(types, start, end, asc).then(jobIds => {
return Promise.all(jobIds.map(jobId => this.getJobFromId(jobId, opts)));
});
};
Queue.prototype.getJobLogs = function(jobId, start, end, asc = true) {
start = _.isUndefined(start) ? 0 : start;
end = _.isUndefined(end) ? -1 : end;
const multi = this.multi();
const logsKey = this.toKey(jobId + ':logs');
if (asc) {
multi.lrange(logsKey, start, end);
} else {
multi.lrange(logsKey, -(end + 1), -(start + 1));
}
multi.llen(logsKey);
return multi.exec().then(result => {
if (!asc) {
result[0][1].reverse();
}
return {
logs: result[0][1],
count: result[1][1]
};
});
};
/**
* Get queue metrics related to the queue.
*
* This method returns the gathered metrics for the queue.
* The metrics are represented as an array of job counts
* per unit of time (1 minute).
*
* @param start - Start point of the metrics, where 0
* is the newest point to be returned.
* @param end - End poinf of the metrics, where -1 is the
* oldest point to be returned.
*
* @returns - Returns an object with queue metrics.
*/
Queue.prototype.getMetrics = async function(type, start = 0, end = -1) {
const metricsKey = this.toKey(`metrics:${type}`);
const dataKey = `${metricsKey}:data`;
const multi = this.multi();
multi.hmget(metricsKey, 'count', 'prevTS', 'prevCount');
multi.lrange(dataKey, start, end);
multi.llen(dataKey);
const [hmget, range, len] = await multi.exec();
const [err, [count, prevTS, prevCount]] = hmget;
const [err2, data] = range;
const [err3, numPoints] = len;
if (err || err2) {
throw err || err2 || err3;
}
return {
meta: {
count: parseInt(count || '0', 10),
prevTS: parseInt(prevTS || '0', 10),
prevCount: parseInt(prevCount || '0', 10)
},
data,
count: numPoints
};
};
};
function parseTypeArg(args) {
const types = _.chain([])
.concat(args)
.join(',')
.split(/\s*,\s*/g)
.compact()
.value();
return types.length
? types
: ['waiting', 'active', 'completed', 'failed', 'delayed', 'paused'];
}