redis-spincycle
Version:
library tha connects to a spincycle server using redis pubsub
274 lines (234 loc) • 9.81 kB
text/coffeescript
uuid = require('node-uuid')
$q = require('node-promise')
lru = require('lru')
debug = process.env['DEBUG']
opts =
max: 1000
maxAgeInMilliseconds: 1000 * 60 * 60 * 24 * 4 # 4 days timeout of objects no matter what
class spinredis
constructor: (dbUrl) ->
console.log 'spinclient +++++++++ constructor called +++++++++++'
= []
= []
= []
= []
= []
= []
= null
= new lru(opts)
= new lru({max:1000, maxAgeInMilliseconds: 5000})
if debug then console.log 'redis-spincycle dbUrl = ' + dbUrl
rhost = dbUrl or process.env['REDIS_PORT_6379_TCP_ADDR'] or '127.0.0.1'
rport = process.env['REDIS_PORT_6379_TCP_PORT'] or '6379'
= require('redis').createClient(rport, rhost)
= require('redis').createClient(rport, rhost)
.on 'error', (err) ->
console.log 'spinredis listen ERROR: ' + err
.on 'end', (err) ->
console.log 'spinredis listen end event: ' + err
.on 'error', (err) ->
console.log 'spinredis send ERROR: ' + err
.on 'end', (err) ->
console.log 'spinredis send end event: ' + err
['OBJECT_UPDATE'] = [(obj) =>
console.log 'spinredis +++++++++ obj update message router got obj '+obj.id+' of type '+obj.type
console.dir(obj);
#console.dir()
objsubs = [obj.id] or []
for k,v of objsubs
#console.log 'updating subscriber to @objects updates on id '+k
if not .get(obj.id)
.set(obj.id, obj)
else
o = .get(obj.id)
obj = o
for prop, val of obj
o[prop] = val
v obj
]
failed: (msg)->
console.log 'spinclient message failed!! ' + msg
setSessionId: (id) ->
if(id)
console.log '++++++++++++++++++++++++++++++++++++++ spinclient setting session id to ' + id
= id
dumpOutstanding: ()->
console.log '-------------------------------- ' + .length + ' outstanding messages ---------------------------------'
.forEach (os)->
console.log os.messageId + ' -> ' + os.target + ' - ' + os.d
console.log '-----------------------------------------------------------------------------------------'
emit: (message) =>
message.channelID = 'spinchannel_' +
if debug then console.log 'redisclient emitting message..'
if debug then console.dir message
.set(message.messageId, message)
.publish('spinchannel', JSON.stringify(message))
setup: () =>
= uuid.v4()
.subscribe('spinchannel_' + )
.on 'message', (channel, replystr) =>
if debug then console.log 'spinredis on message got ' + replystr
reply = JSON.parse(replystr)
status = reply.status
message = reply.payload
info = reply.info
if message and message.error and message.error == 'ERRCHILLMAN'
console.log 'got ERRCHILLMAN from spinycle service, preparing to retry sending message...'
oldmsg = [reply.messageId]
setTimeout(
()=>
console.log 'resending message '+oldmsg.messageId+' due to target endpoint not open yet'
,250
)
else if not reply.messageId
.remove(reply.messageId)
if reply.messageId and reply.messageId isnt 'undefined' then .push(reply.messageId)
if .length > 10 then .shift()
if debug then console.log 'redis-spincycle got reply messageId ' + reply.messageId + ' status ' + status + ', info ' + info + ' data ' + message + ' outstandingMessages = ' + .length
if debug then
#console.dir reply
index = -1
if reply.messageId
i = 0
while i < .length
index = i
detail = [i]
if detail and not detail.delivered and detail.messageId == reply.messageId
if reply.status == 'FAILURE' or reply.status == 'NOT_ALLOWED'
console.log 'spinclient message FAILURE'
console.dir reply
detail.d.reject reply
break
else
#console.log 'delivering message '+message+' reply to '+detail.target+' to '+reply.messageId
detail.d.resolve(message)
break
detail.delivered = true
i++
if index > -1
.splice index, 1
else
subs = [info]
if subs
subs.forEach (listener) ->
listener message
else
if debug then console.log 'no subscribers for message ' + message
if debug then console.dir reply
else
if debug then console.log '-- skipped resent message ' + reply.messageId
hasSeenThisMessage: (messageId) =>
.some (mid) -> messageId == mid
registerListener: (detail) =>
#console.log 'spinclient::registerListener called for ' + detail.message
subs = [detail.message] or []
subs.push detail.callback
[detail.message] = subs
registerObjectSubscriber: (detail) =>
d = $q.defer()
sid = uuid.v4()
localsubs = [detail.id]
#console.log 'register@objectsSubscriber localsubs is'
#console.dir localsubs
if not localsubs
localsubs = []
console.log 'spinredis no local subs, so get the original server-side subscription for id ' + detail.id
# actually set up subscription, once for each
.then( (remotesid) =>
localsubs['remotesid'] = remotesid
localsubs[sid] = detail
#console.log '-- adding local callback listener to @objects updates for ' + detail.id + ' local sid = ' + sid + ' remotesid = ' + remotesid
[detail.id] = localsubs
d.resolve(sid)
,(rejection)=>
console.log 'spinredis registerObjectSubscriber rejection: '+rejection
console.dir rejection
)
else
localsubs[sid] = detail
return d.promise
_registerObjectSubscriber: (detail) =>
d = $q.defer()
#console.log 'spinredis message-router registering subscriber for @objects ' + detail.id + ' type ' + detail.type
subs = [detail.id] or []
.then(
(reply)=>
console.log 'spinredis server subscription id for id ' + detail.id + ' is ' + reply
subs[reply] = detail.cb
[detail.id] = subs
d.resolve(reply)
, (reply)=>
)
return d.promise
deRegisterObjectsSubscriber: (sid, o) =>
localsubs = [o.id] or []
if localsubs[sid]
console.log 'deregistering local updates for @objects ' + o.id
delete localsubs[sid]
count = 0
for k,v in localsubs
count++
if count == 1 # only remotesid property left
_deRegisterObjectsSubscriber: (sid, o) =>
subs = [o.id] or []
if subs and subs[sid]
delete subs[sid]
[o.id] = subs
.then (reply)->
console.log 'deregistering server updates for @objects ' + o.id
emitMessage: (detail) =>
if debug then console.log 'emitMessage called'
if debug then console.dir detail
d = $q.defer()
detail.messageId = uuid.v4()
detail.sessionId = detail.sessionId or
detail.d = d
.push detail
if debug then console.log 'saving outstanding reply to messageId ' + detail.messageId + ' and @sessionId ' + detail.sessionId
detail
return d.promise
# ------------------------------------------------------------------------------------------------------------------
getModelFor: (type) =>
d = $q.defer()
if [type]
d.resolve([type])
else
.then((model)->
[type] = model
d.resolve(model)
,(rejection)=>
console.log 'spinredis getModelFor rejection: '+rejection
console.dir rejection
)
return d.promise
listTargets: () =>
d = $q.defer()
.then((targets)->
d.resolve(targets)
,(rejection)->
console.log 'spinredis listTargets rejection: '+rejection
console.dir rejection
)
return d.promise
flattenModel: (model) =>
rv = {}
for k,v of model
if angular.isArray(v)
rv[k] = v.map (e) -> e.id
else
rv[k] = v
return rv
module.exports = spinredis