UNPKG

metatasks

Version:

FIFO queue for Promise-based tasks. Optionally persistent to ensure tasks survive reboots. Supports concurrency-limiting, metadata and state-tracking for statistics.

260 lines (199 loc) 8.62 kB
'use strict'; var _bluebird = require('bluebird'); Object.defineProperty(exports, '__esModule', { value: true }); var _slice = Array.prototype.slice; var queueRestore = _bluebird.coroutine(function* (queueId, queue, persistence) { var db = persistence['interface']; yield db.setAllStartedAsInterrupted(queueId); var tasksJson = yield db.getTasksToQueue(queueId, true); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = tasksJson[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var task = _step.value; queue.scheduleTask.apply(queue, [task.metadata, task.hasOwnProperty('target') ? (yield persistence.thisRestorer.call(persistence.hasOwnProperty('storerRestorerContext') ? persistence.storerRestorerContext : undefined, task.target)) : undefined].concat(_toConsumableArray(task.parameters))); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }); exports.getOrMakeQueue = getOrMakeQueue; exports.makeItObeyQueue = makeItObeyQueue; exports.useQueue = useQueue; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } var _metatasksJs = require('./metatasks.js'); require('core-js/shim'); //import Map from 'core-js/fn/map'; var _stackTrace = require('stack-trace'); var _stackTrace2 = _interopRequireDefault(_stackTrace); var _pureUuid = require('pure-uuid'); var _pureUuid2 = _interopRequireDefault(_pureUuid); var _harmonyReflect = require('harmony-reflect'); var _harmonyReflect2 = _interopRequireDefault(_harmonyReflect); var _chai = require('chai'); var _chai2 = _interopRequireDefault(_chai); function queueEnforcePersistence(queueId, queue, persistence) { var db = persistence['interface']; var wait = function wait(time) { return new Promise(function (resolve, reject) { setTimeout(resolve, time); }); }; var waitForPreviousEvent = _bluebird.coroutine(function* (task, event) { var fullEvent = 'persistenceLock' + event; if (task[fullEvent]) { yield task[fullEvent]; } else { console.log('waiting for ' + event + ' event!'); yield wait(100); yield waitForPreviousEvent(task, event); } }); queue.on('added', function (task) { var promiseLock = _bluebird.coroutine(function* () { if (!task.id) { var id = new _pureUuid2['default'](5, 'ns:OID', queueId + ':' + task.name).format(); var store = { id: id, queueId: queueId, parameters: task.callParameters, metadata: task.metadata }; if (task.target) store.target = yield persistence.thisStorer.call(persistence.hasOwnProperty('storerRestorerContext') ? persistence.storerRestorerContext : undefined, task.target); yield db.taskEnqueue(queueId, store); task.id = id; } }); task.persistenceLockEnqueued = promiseLock(); queue.asyncEventPromises.push(task.persistenceLockEnqueued); }); queue.on('started', function (task) { var promiseLock = _bluebird.coroutine(function* () { yield waitForPreviousEvent(task, 'Enqueued'); yield db.taskSetStarted(queueId, task.id); }); task.persistenceLockStarted = promiseLock(); queue.asyncEventPromises.push(task.persistenceLockStarted); }); queue.on('finished', function (task) { var promiseLock = _bluebird.coroutine(function* () { yield waitForPreviousEvent(task, 'Enqueued'); yield waitForPreviousEvent(task, 'Started'); if (task.success) { yield db.taskSetCompleted(queueId, task.id, task.value, task.metadata); // adds task.value } else { yield db.taskSetFailed(queueId, task.id, task.error, task.metadata); // adds task.error } // Garbage Collection delete task['persistenceLock' + 'Enqueued']; delete task['persistenceLock' + 'Started']; }); var persistenceFinishStarted = promiseLock(); queue.asyncEventPromises.push(persistenceFinishStarted); }); } var Queues = new Map(); exports.Queues = Queues; function getOrMakeQueue(method, options) { var queue = undefined; if (Queues.has(options.queueId)) { queue = Queues.get(options.queueId); _chai.assert.equal(queue.promiseGenerator, method); return queue; // TODO: add assertions of options } queue = new _metatasksJs.TaskQueue(method, options.concurrency || Infinity); queue.injectTask = options.injectTask; if (options.hasOwnProperty('queueModifier')) options.queueModifier(queue); if (options.persistence && options.persistence['interface']) { (function () { // add defaults options.persistence = Object.assign({ thisRestorer: _bluebird.coroutine(function* (obj) { return obj; }), thisStorer: _bluebird.coroutine(function* (target) { return target; }) }, options.persistence); var db = options.persistence['interface']; queue.overrideInTask = { setMetadata: _bluebird.coroutine(function* (key, value) { this.metadata[key] = value; yield db.taskSetMetadata(options.queueId, this.id, key, value); }), mergeMetadata: _bluebird.coroutine(function* (obj) { Object.assign(this.metadata, obj); yield db.taskMergeMetadata(options.queueId, this.id, obj); }) }; queueEnforcePersistence(options.queueId, queue, options.persistence); queue.restorationProcess = queueRestore(options.queueId, queue, options.persistence); })(); } Queues.set(options.queueId, queue); return queue; } function makeItObeyQueue(method, options) { var queue = getOrMakeQueue(method, options); var injectedContext = undefined; if (this) injectedContext = this; var persistence = options.persistence && options.persistence['interface']; if (!options.hasOwnProperty('taskNameResolver')) { (function () { var taskCounter = persistence ? Math.floor(Math.random() * 100000000000000) : 0; options.taskNameResolver = function () { return method.name + ':' + taskCounter++; }; })(); } return _bluebird.coroutine(function* () { var taskMetadata = {}; if (options.taskMetadataResolver) taskMetadata = options.taskMetadataResolver.apply(this, arguments); if (!taskMetadata.name) taskMetadata.name = options.taskNameResolver.apply(this, arguments); // make sure we completed restoring the queue before we start adding a new task if (queue.restorationProcess) yield queue.restorationProcess; var task = queue.scheduleTask.apply(queue, [taskMetadata, injectedContext ? injectedContext : this].concat(_slice.call(arguments))); return yield task.promise; }); } function useQueue() { var concurrencyOrOptions = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0]; return function (parentClass, name, descriptor) { if (descriptor.hasOwnProperty('value')) { // this is a function var options = typeof concurrencyOrOptions === 'number' ? { concurrency: concurrencyOrOptions } : concurrencyOrOptions; var persistence = options.persistence && options.persistence['interface']; if (persistence && !options.persistence.hasOwnProperty('storerRestorerContext')) options.persistence.storerRestorerContext = parentClass; if (!options.hasOwnProperty('taskNameResolver')) { (function () { var taskCounter = persistence ? Math.floor(Math.random() * 100000000000000) : 0; options.taskNameResolver = function () { return parentClass.constructor.name + '.' + name + ':' + taskCounter++; }; })(); } options.queueId = options.queueId || _stackTrace2['default'].get()[1].getFileName() + ':' + parentClass.constructor.name + '.' + name; descriptor.value = makeItObeyQueue(descriptor.value, options); } else //if (descriptor.hasOwnProperty('initializer')) throw new Error('Decorator not on a method.'); }; } //# sourceMappingURL=decorator-queue.js.map