UNPKG

jqueue

Version:

MySQL backed plugable Node.js job queue based on the Beanstalk Job Lifecycle

310 lines (286 loc) 10.9 kB
var callBack = require('./callback'); var Message = require('./message'); const uuid = require('uuid/v1'); var defaultWatchInterval = 1000; //ms var defaultTimeToRun = 5; //s function Queue(dataSource, name) { var self = this; function execQuery(query, params, cb) { dataSource.getConnection(function (error, connection) { if (error) { cb(error); } else { connection.query(query, params, cb); connection.release(); } }); } this.getName = function () { return name; }; this.put = function (message, parameter1, parameter2, parameter3, parameter4) { var delay, priority, cb, tag; switch (arguments.length) { case 2: cb = parameter1; break; case 3: delay = parameter1; cb = parameter2; break; case 4: delay = parameter1; priority = parameter2; cb = parameter3; break; case 5: delay = parameter1; priority = parameter2; tag = parameter3; cb = parameter4; break; } var queueMessage = new Message(dataSource, message, name, delay, priority, undefined, undefined, undefined, undefined, undefined, tag); writeMessage(queueMessage, function (error, data) { var insertedId = undefined; if (!error) { insertedId = data.insertId; } callBack(cb, error, insertedId); }); }; this.reserve = function (parameter1, parameter2, parameter3) { var cb, timeToRun, tag; switch (arguments.length) { case 1: cb = parameter1; break; case 2: timeToRun = parameter1; cb = parameter2; break; case 3: timeToRun = parameter1; tag = parameter2; cb = parameter3; break; } timeToRun = timeToRun || defaultTimeToRun; var version = uuid(); retrieveMessage(name, tag, timeToRun, version, function (error, data) { var message = undefined; if (!error && data && data.length) { var messageObject = data[0]; message = new Message(dataSource, messageObject.data, name, 0, messageObject.priority, messageObject.status, messageObject.date_time, messageObject.id, timeToRun, version, messageObject.tag, messageObject.reservation_counter, messageObject.error); } callBack(cb, error, message); }); }; this.watch = function (parameter1, parameter2, parameter3, parameter4) { var timeout, tag, timeToRun, cb; switch (arguments.length) { case 1: cb = parameter1; break; case 2: timeToRun = parameter1; cb = parameter2; break; case 3: timeToRun = parameter1; timeout = parameter2; cb = parameter3; break; case 4: timeToRun = parameter1; timeout = parameter2; tag = parameter3; cb = parameter4; break; } timeout = timeout || defaultWatchInterval; var watcher = { cancel: function () { } }; self.reserve(timeToRun, tag, function (error, data) { if (!error && !data) { var interval = setInterval(function () { self.reserve(timeToRun, tag, function (error, data) { if (error || data) { clearInterval(interval); callBack(cb, error, data); } }); }, timeout); watcher.cancel = function () { clearInterval(interval); }; } else { callBack(cb, error, data); } }); return watcher; }; this.kick = function (parameter1, parameter2, parameter3) { var max, delay, cb; switch (arguments.length) { case 1: cb = parameter1; break; case 2: max = parameter1; cb = parameter2; break; case 3: max = parameter1; delay = parameter2; cb = parameter3; break; } var callback = function (error, data) { data = data ? data.affectedRows : undefined; callBack(cb, error, data); }; delay = delay || 0; if (max) { kickMessages(name, max, delay, callback); } else { kickAllMessages(name, delay, callback) } }; this.kickMessage = function (id, parameter1, parameter2) { var delay, cb; switch (arguments.length) { case 2: cb = parameter1; break; case 3: delay = parameter1; cb = parameter2; break; } delay = delay || 0; kickOneMessage(name, id, delay, function (error, data) { callBack(cb, error, data); }) }; this.pick = function (id, parameter1, parameter2) { var cb, timeToRun; switch (arguments.length) { case 2: cb = parameter1; break; case 3: timeToRun = parameter1; cb = parameter2; break; } timeToRun = timeToRun || defaultTimeToRun; var version = uuid(); pickMessage(name, id, timeToRun, version, function (error, data) { var message = undefined; if (error) { callBack(cb, error); } else if (data && data[0]) { var messageObject = data[0]; message = new Message(dataSource, messageObject.data, name, 0, messageObject.priority, messageObject.status, messageObject.date_time, messageObject.id, timeToRun, version, messageObject.tag, messageObject.reservation_counter, messageObject.error); callBack(cb, error, message); } else { getMessageById(name, id, function (error, data) { if (error) { callBack(cb, error); } else { if (data && data[0]) { callBack(cb, 'message reserved by someone else'); } else { callBack(cb, error, message); } } }); } }); }; function writeMessage(message, cb) { execQuery('INSERT INTO ?? (status, data, priority, tag, date_time, created_at, modified_at) \ VALUES (?,?,?,?,DATE_ADD(CURRENT_TIMESTAMP, INTERVAL ? SECOND), now(), now())', [message.getQueueName(), message.getStatus(), message.getData(), message.getPriority(), message.getTag(), message.getDelay()], cb); } function retrieveMessage(queueName, tag, timeToRun, version, cb) { var params = [ queueName, 'reserved', version, timeToRun, 'ready', 'reserved' ]; var sql = 'UPDATE ?? SET status = ?, version = ?, time_to_run = DATE_ADD(CURRENT_TIMESTAMP, INTERVAL ? SECOND), \ reservation_counter = reservation_counter + 1 WHERE ((date_time <= CURRENT_TIMESTAMP AND status = ?) OR (time_to_run IS NOT NULL \ AND time_to_run < CURRENT_TIMESTAMP AND status = ?))'; if (tag) { sql += ' AND tag = ?'; params.push(tag); } sql += ' ORDER BY priority desc, date_time asc LIMIT 1'; execQuery(sql, params, function (err, info) { if (err || !info.affectedRows) { cb(err, null); } else { execQuery('SELECT * FROM ?? WHERE status = ? AND version = ?', [ queueName, 'reserved', version ], cb); } }); } function kickMessages(queueName, max, delay, cb) { execQuery('UPDATE ?? SET status = ?, date_time = DATE_ADD(date_time, INTERVAL ? SECOND) \ WHERE status = ? ORDER BY date_time asc LIMIT ?', [queueName, 'ready', delay, 'buried', max], cb); } function kickOneMessage(queueName, id, delay, cb) { execQuery('UPDATE ?? SET status = ?, date_time = DATE_ADD(date_time, INTERVAL ? SECOND) \ WHERE status = ? AND id = ?', [queueName, 'ready', delay, 'buried', id], cb); } function kickAllMessages(queueName, delay, cb) { execQuery('UPDATE ?? SET status = ?, date_time = DATE_ADD(date_time, INTERVAL ? SECOND) WHERE status = ?', [queueName, 'ready', delay, 'buried'], cb); } function pickMessage(queueName, id, timeToRun, version, cb) { var params = [ queueName, 'reserved', version, timeToRun, ['ready', 'buried'], 'reserved', id ]; var sql = 'UPDATE ?? SET status = ?, version = ?, time_to_run = DATE_ADD(CURRENT_TIMESTAMP, INTERVAL ? SECOND), \ reservation_counter = reservation_counter + 1 WHERE (status IN (?) OR (time_to_run IS NOT NULL \ AND time_to_run < CURRENT_TIMESTAMP AND status = ?)) AND id = ?'; execQuery(sql, params, function (err, info) { if (!info.affectedRows) { cb(err, null); } else { execQuery('SELECT * FROM ?? WHERE id = ?', [ queueName, id ], cb); } }); } function getMessageById(queueName, id, cb) { execQuery('SELECT * FROM ?? WHERE id = ?', [queueName, id], cb); } } Queue.constructor = Queue; module.exports = Queue;