@leansdk/leanrc
Version:
LeanRC is a MVC framework for creating graceful applications
324 lines (287 loc) • 10.2 kB
JavaScript
(function() {
// This file is part of LeanRC.
// LeanRC is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// LeanRC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public License
// along with LeanRC. If not, see <https://www.gnu.org/licenses/>.
var hasProp = {}.hasOwnProperty;
/*
```coffee
module.exports = (Module)->
class BaseResqueExecutor extends Module::MemoryResqueExecutor
@inheritProtected()
@module Module
return BaseResqueExecutor.initialize()
```
```coffee
module.exports = (Module)->
{RESQUE} = Module::
class PrepareViewCommand extends Module::SimpleCommand
@inheritProtected()
@module Module
@public execute: Function,
default: ->
#...
@facade.registerMediator Module::BaseResqueExecutor.new RESQUE_EXECUTOR
#...
PrepareViewCommand.initialize()
```
*/
// Чтобы запустить экзекютор нужно послать сиграл с ключем START_RESQUE
// Медиатор надо регистрировать с ключем RESQUE_EXECUTOR
module.exports = function(Module) {
var ConfigurableMixin, DelayableMixin, DictG, FuncG, JOB_RESULT, MaybeG, Mediator, Mixin, NotificationInterface, PointerT, RESQUE, RESQUE_EXECUTOR, ResqueInterface, START_RESQUE, StructG, UnionG, _, co, genRandomAlphaNumbers, isArangoDB;
({
JOB_RESULT,
START_RESQUE,
RESQUE,
RESQUE_EXECUTOR,
PointerT,
FuncG,
DictG,
StructG,
MaybeG,
UnionG,
ResqueInterface,
NotificationInterface,
Mediator,
Mixin,
DelayableMixin,
ConfigurableMixin,
Utils: {_, co, isArangoDB, genRandomAlphaNumbers}
} = Module.prototype);
return Module.defineMixin(Mixin('MemoryExecutorMixin', function(BaseClass = Mediator) {
return (function() {
var _Class, ipbIsStopped, ipoConcurrencyCount, ipoDefinedProcessors, ipoResque, ipoTimer, ipsMultitonKey;
_Class = class extends BaseClass {};
_Class.inheritProtected();
_Class.include(DelayableMixin);
_Class.include(ConfigurableMixin);
_Class.public({
fullQueueName: FuncG(String, String)
}, {
default: function(queueName) {
return this[ipoResque].fullQueueName(queueName);
}
});
ipsMultitonKey = PointerT(Symbol.for('~multitonKey'));
ipoTimer = PointerT(_Class.private({
timer: MaybeG(UnionG(Object, Number))
}));
ipbIsStopped = PointerT(_Class.private({
isStopped: Boolean
}));
ipoDefinedProcessors = PointerT(_Class.private({
definedProcessors: DictG(String, StructG({
listener: Function,
concurrency: Number
}))
}));
ipoConcurrencyCount = PointerT(_Class.private({
concurrencyCount: DictG(String, Number)
}));
ipoResque = PointerT(_Class.private({
resque: ResqueInterface
}));
_Class.public({
listNotificationInterests: FuncG([], Array)
}, {
default: function() {
return [JOB_RESULT, START_RESQUE];
}
});
_Class.public({
handleNotification: FuncG(NotificationInterface)
}, {
default: function(aoNotification) {
var voBody, vsName, vsType;
vsName = aoNotification.getName();
voBody = aoNotification.getBody();
vsType = aoNotification.getType();
switch (vsName) {
case JOB_RESULT:
this.getViewComponent().emit(vsType, voBody);
break;
case START_RESQUE:
this.start();
}
}
});
_Class.public({
onRegister: Function
}, {
default: function(...args) {
var EventEmitter;
this.super(...args);
EventEmitter = require('events');
this.setViewComponent(new EventEmitter());
this[ipoConcurrencyCount] = {};
this[ipoDefinedProcessors] = {};
this[ipoResque] = this.facade.retrieveProxy(RESQUE);
this.defineProcessors();
}
});
_Class.public(_Class.async({
reDefineProcessors: Function
}, {
default: function*() {
this.stop();
this[ipoDefinedProcessors] = {};
yield this.defineProcessors();
}
}));
_Class.public(_Class.async({
defineProcessors: Function
}, {
default: function*() {
var concurrency, fullQueueName, i, len, moduleName, name, ref, self;
ref = (yield this[ipoResque].allQueues());
for (i = 0, len = ref.length; i < len; i++) {
({name, concurrency} = ref[i]);
fullQueueName = this[ipoResque].fullQueueName(name);
[moduleName] = fullQueueName.split('|>');
if (moduleName === this.moduleName()) {
self = this;
this.define(name, {concurrency}, co.wrap(function(job, done) {
var data, reverse, scriptName;
reverse = genRandomAlphaNumbers(32);
self.getViewComponent().once(reverse, function(aoError) {
return done(aoError);
});
({scriptName, data} = job.data);
self.sendNotification(scriptName, data, reverse);
}));
}
continue;
}
}
}));
_Class.public({
onRemove: Function
}, {
default: function(...args) {
this.super(...args);
this.stop();
}
});
_Class.public(_Class.async({
cyclePart: Function
}, {
default: function*() {
var concurrency, currentQC, i, j, job, len, len1, listener, now, pendingJobs, progressJobs, queueConfig, queueName, ref;
ref = this[ipoDefinedProcessors];
for (queueName in ref) {
if (!hasProp.call(ref, queueName)) continue;
queueConfig = ref[queueName];
({listener, concurrency} = queueConfig);
currentQC = this[ipoConcurrencyCount][queueName];
now = Date.now();
progressJobs = (yield this[ipoResque].progressJobs(queueName));
for (i = 0, len = progressJobs.length; i < len; i++) {
job = progressJobs[i];
if ((now - job.startedAt) < job.lockLifetime) {
job.status = 'scheduled';
}
}
pendingJobs = (yield this[ipoResque].pendingJobs(queueName));
if (((currentQC != null) && currentQC < concurrency) || (currentQC == null)) {
for (j = 0, len1 = pendingJobs.length; j < len1; j++) {
job = pendingJobs[j];
if (job.delayUntil < now) {
listener(job);
}
if (currentQC >= concurrency) {
break;
}
}
}
}
this.recursion();
}
}));
_Class.public(_Class.async({
recursion: Function
}, {
default: function*() {
var self;
if (this[ipbIsStopped]) {
return;
}
self = this;
this[ipoTimer] = setTimeout(co.wrap(function*() {
clearTimeout(self[ipoTimer]);
return (yield self.cyclePart());
}), 100);
}
}));
_Class.public(_Class.async({
start: Function
}, {
default: function*() {
if (isArangoDB()) {
throw new Error('MemoryExecutorMixin can not been used for ArrangoDB apps');
return;
}
this[ipbIsStopped] = false;
yield this.recursion();
}
}));
_Class.public({
stop: Function
}, {
default: function() {
if (isArangoDB()) {
throw new Error('MemoryExecutorMixin can not been used for ArrangoDB apps');
return;
}
this[ipbIsStopped] = true;
if (this[ipoTimer] != null) {
clearTimeout(this[ipoTimer]);
}
}
});
_Class.public({
define: FuncG([
String,
StructG({
concurrency: Number
}),
Function
])
}, {
default: function(queueName, {concurrency}, lambda) {
var listener;
listener = (job) => {
var base, done;
done = (err) => {
if (err != null) {
job.status = 'failed';
job.reason = err;
} else {
job.status = 'completed';
}
this[ipoConcurrencyCount][queueName] -= 1;
};
if ((base = this[ipoConcurrencyCount])[queueName] == null) {
base[queueName] = 0;
}
this[ipoConcurrencyCount][queueName] += 1;
job.status = 'running';
job.startedAt = Date.now();
lambda(job, done);
};
this[ipoDefinedProcessors][queueName] = {listener, concurrency};
}
});
_Class.initializeMixin();
return _Class;
}).call(this);
}));
};
}).call(this);