meshblu-core-job-manager
Version:
Meshblu Core Job Manager
110 lines (91 loc) • 3.83 kB
text/coffeescript
_ = require 'lodash'
moment = require 'moment'
When = require 'when'
GenericPool = require 'generic-pool'
{ EventEmitter } = require 'events'
Redis = require 'ioredis'
RedisNS = require '@octoblu/redis-ns'
debug = require('debug')('meshblu-core-job-manager:job-manager-base')
class JobManagerBase extends EventEmitter
constructor: (options={}) ->
{
@namespace
@redisUri
@idleTimeoutMillis
@maxConnections
@minConnections
@evictionRunIntervalMillis
@acquireTimeoutMillis
@jobTimeoutSeconds
@queueTimeoutSeconds
} = options
@idleTimeoutMillis ?= 60000
@evictionRunIntervalMillis ?= 60000
@acquireTimeoutMillis ?= 5000
@minConnections ?= 0
@_heartbeat = moment()
throw new Error 'JobManagerResponder constructor is missing "namespace"' unless @namespace?
throw new Error 'JobManagerResponder constructor is missing "redisUri"' unless @redisUri?
throw new Error 'JobManagerResponder constructor is missing "idleTimeoutMillis"' unless @idleTimeoutMillis?
throw new Error 'JobManagerResponder constructor is missing "maxConnections"' unless @maxConnections?
throw new Error 'JobManagerResponder constructor is missing "minConnections"' unless @minConnections?
throw new Error 'JobManagerRequester constructor is missing "jobTimeoutSeconds"' unless @jobTimeoutSeconds?
throw new Error 'JobManagerRequester constructor is missing "queueTimeoutSeconds"' unless @queueTimeoutSeconds?
@_queuePool = @_createRedisPool { @maxConnections, @minConnections, @idleTimeoutMillis, @namespace, @redisUri }
@_commandPool = @_createRedisPool { @maxConnections, @minConnections, @idleTimeoutMillis, @namespace, @redisUri }
@_pubSubPool = @_createRedisPool { @maxConnections, @minConnections, @idleTimeoutMillis, @namespace, @redisUri }
addMetric: (metadata, metricName, callback) =>
return _.defer callback if _.isEmpty metadata.jobLogs
metadata.metrics ?= {}
metadata.metrics[metricName] = Date.now()
_.defer callback
_closeClient: (client) =>
client.on 'error', =>
# silently deal with it
try
if client.disconnect?
client.quit()
client.disconnect false
return
client.end true
catch
_createRedisPool: ({ maxConnections, minConnections, idleTimeoutMillis, evictionRunIntervalMillis, acquireTimeoutMillis, namespace, redisUri }) =>
factory =
create: =>
return When.promise (resolve, reject) =>
conx = new Redis redisUri, dropBufferSupport: true
client = new RedisNS namespace, conx
rejectError = (error) =>
return reject error
client.once 'error', rejectError
client.once 'ready', =>
client.removeListener 'error', rejectError
resolve client
destroy: (client) =>
return When.promise (resolve, reject) =>
@_closeClient client, (error) =>
return reject error if error?
resolve()
validate: (client) =>
return When.promise (resolve) =>
client.ping (error) =>
return resolve false if error?
resolve true
options = {
max: maxConnections
min: minConnections
testOnBorrow: true
idleTimeoutMillis
evictionRunIntervalMillis
acquireTimeoutMillis
}
pool = GenericPool.createPool factory, options
pool.on 'factoryCreateError', (error) =>
@emit 'factoryCreateError', error
return pool
healthcheck: (callback) =>
healthy = @_heartbeat.isAfter moment().subtract @jobTimeoutSeconds * 2, 'seconds'
_.defer callback, null, healthy
_updateHeartbeat: =>
@_heartbeat = moment()
module.exports = JobManagerBase