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
JavaScript
;
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