@leansdk/leanrc
Version:
LeanRC is a MVC framework for creating graceful applications
220 lines (192 loc) • 7.02 kB
text/coffeescript
# 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/>.
###
```coffee
module.exports = (Module)->
class BaseResqueExecutor extends Module::MemoryResqueExecutor
Module
return BaseResqueExecutor.initialize()
```
```coffee
module.exports = (Module)->
{RESQUE} = Module::
class PrepareViewCommand extends Module::SimpleCommand
Module
execute: Function,
default: ->
#...
.registerMediator Module::BaseResqueExecutor.new RESQUE_EXECUTOR
#...
PrepareViewCommand.initialize()
```
###
# Чтобы запустить экзекютор нужно послать сиграл с ключем START_RESQUE
# Медиатор надо регистрировать с ключем RESQUE_EXECUTOR
module.exports = (Module)->
{
JOB_RESULT
START_RESQUE
RESQUE
RESQUE_EXECUTOR
PointerT
FuncG, DictG, StructG, MaybeG, UnionG
ResqueInterface, NotificationInterface
Mediator, Mixin
DelayableMixin, ConfigurableMixin
Utils: { _, co, isArangoDB, genRandomAlphaNumbers }
} = Module::
Module.defineMixin Mixin 'MemoryExecutorMixin', (BaseClass = Mediator) ->
class extends BaseClass
DelayableMixin
ConfigurableMixin
fullQueueName: FuncG(String, String),
default: (queueName)-> @[ipoResque].fullQueueName queueName
ipsMultitonKey = PointerT Symbol.for '~multitonKey'
ipoTimer = PointerT timer: MaybeG UnionG Object, Number
ipbIsStopped = PointerT isStopped: Boolean
ipoDefinedProcessors = PointerT definedProcessors: DictG(
String
StructG {
listener: Function
concurrency: Number
}
)
ipoConcurrencyCount = PointerT concurrencyCount: DictG(
String, Number
)
ipoResque = PointerT resque: ResqueInterface
listNotificationInterests: FuncG([], Array),
default: ->
[
JOB_RESULT
START_RESQUE
]
handleNotification: FuncG(NotificationInterface),
default: (aoNotification)->
vsName = aoNotification.getName()
voBody = aoNotification.getBody()
vsType = aoNotification.getType()
switch vsName
when JOB_RESULT
.emit vsType, voBody
when START_RESQUE
return
onRegister: Function,
default: (args...)->
args...
EventEmitter = require 'events'
new EventEmitter()
@[ipoConcurrencyCount] = {}
@[ipoDefinedProcessors] = {}
@[ipoResque] = .retrieveProxy RESQUE
return
reDefineProcessors: Function,
default: ->
@[ipoDefinedProcessors] = {}
yield
yield return
defineProcessors: Function,
default: ->
for {name, concurrency} in yield @[ipoResque].allQueues()
fullQueueName = @[ipoResque].fullQueueName name
[moduleName] = fullQueueName.split '|>'
if moduleName is
self = @
name, {concurrency}, co.wrap (job, done)->
reverse = genRandomAlphaNumbers 32
self.getViewComponent().once reverse, (aoError)->
done aoError
{scriptName, data} = job.data
self.sendNotification scriptName, data, reverse
return
continue
yield return
onRemove: Function,
default: (args...)->
args...
return
cyclePart: Function,
default: ->
for own queueName, queueConfig of @[ipoDefinedProcessors]
{listener, concurrency} = queueConfig
currentQC = @[ipoConcurrencyCount][queueName]
now = Date.now()
progressJobs = yield @[ipoResque].progressJobs queueName
for job in progressJobs
if (now - job.startedAt) < job.lockLifetime
job.status = 'scheduled'
pendingJobs = yield @[ipoResque].pendingJobs queueName
if (currentQC? and currentQC < concurrency) or not currentQC?
for job in pendingJobs
if job.delayUntil < now
listener job
if currentQC >= concurrency
break
yield return
recursion: Function,
default: ->
if @[ipbIsStopped]
yield return
self = @
@[ipoTimer] = setTimeout((co.wrap ->
clearTimeout self[ipoTimer]
return yield self.cyclePart()
), 100)
yield return
start: Function,
default: ->
if isArangoDB()
throw new Error 'MemoryExecutorMixin can not been used for ArrangoDB apps'
yield return
@[ipbIsStopped] = no
yield
yield return
stop: Function,
default: ->
if isArangoDB()
throw new Error 'MemoryExecutorMixin can not been used for ArrangoDB apps'
return
@[ipbIsStopped] = yes
if @[ipoTimer]?
clearTimeout @[ipoTimer]
return
define: FuncG([String, StructG(concurrency: Number), Function]),
default: (queueName, {concurrency}, lambda)->
listener = (job)=>
done = (err)=>
if err?
job.status = 'failed'
job.reason = err
else
job.status = 'completed'
@[ipoConcurrencyCount][queueName] -= 1
return
@[ipoConcurrencyCount][queueName] ?= 0
@[ipoConcurrencyCount][queueName] += 1
job.status = 'running'
job.startedAt = Date.now()
lambda job, done
return
@[ipoDefinedProcessors][queueName] = {listener, concurrency}
return