UNPKG

redis-spincycle

Version:

library tha connects to a spincycle server using redis pubsub

406 lines (379 loc) 13.9 kB
// Generated by CoffeeScript 1.9.3 (function() { var $q, debug, lru, opts, spinredis, uuid, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; uuid = require('node-uuid'); $q = require('node-promise'); lru = require('lru'); debug = process.env['DEBUG']; opts = { max: 1000, maxAgeInMilliseconds: 1000 * 60 * 60 * 24 * 4 }; spinredis = (function() { function spinredis(dbUrl) { this.flattenModel = bind(this.flattenModel, this); this.listTargets = bind(this.listTargets, this); this.getModelFor = bind(this.getModelFor, this); this.emitMessage = bind(this.emitMessage, this); this._deRegisterObjectsSubscriber = bind(this._deRegisterObjectsSubscriber, this); this.deRegisterObjectsSubscriber = bind(this.deRegisterObjectsSubscriber, this); this._registerObjectSubscriber = bind(this._registerObjectSubscriber, this); this.registerObjectSubscriber = bind(this.registerObjectSubscriber, this); this.registerListener = bind(this.registerListener, this); this.hasSeenThisMessage = bind(this.hasSeenThisMessage, this); this.setup = bind(this.setup, this); this.emit = bind(this.emit, this); var rhost, rport; console.log('spinclient +++++++++ constructor called +++++++++++'); this.subscribers = []; this.objsubscribers = []; this.objectsSubscribedTo = []; this.outstandingMessages = []; this.modelcache = []; this.seenMessages = []; this.sessionId = null; this.objects = new lru(opts); this.savedMessagesInCaseOfRetries = new lru({ max: 1000, maxAgeInMilliseconds: 5000 }); if (debug) { console.log('redis-spincycle dbUrl = ' + dbUrl); } rhost = dbUrl || process.env['REDIS_PORT_6379_TCP_ADDR'] || '127.0.0.1'; rport = process.env['REDIS_PORT_6379_TCP_PORT'] || '6379'; this.sendredis = require('redis').createClient(rport, rhost); this.listenredis = require('redis').createClient(rport, rhost); this.listenredis.on('error', function(err) { return console.log('spinredis listen ERROR: ' + err); }); this.listenredis.on('end', function(err) { return console.log('spinredis listen end event: ' + err); }); this.sendredis.on('error', function(err) { return console.log('spinredis send ERROR: ' + err); }); this.sendredis.on('end', function(err) { return console.log('spinredis send end event: ' + err); }); this.subscribers['OBJECT_UPDATE'] = [ (function(_this) { return function(obj) { var k, o, objsubs, prop, results, v, val; console.log('spinredis +++++++++ obj update message router got obj ' + obj.id + ' of type ' + obj.type); console.dir(obj); objsubs = _this.objsubscribers[obj.id] || []; results = []; for (k in objsubs) { v = objsubs[k]; if (!_this.objects.get(obj.id)) { _this.objects.set(obj.id, obj); } else { o = _this.objects.get(obj.id); obj = o; for (prop in obj) { val = obj[prop]; o[prop] = val; } } results.push(v(obj)); } return results; }; })(this) ]; this.setup(); } spinredis.prototype.failed = function(msg) { return console.log('spinclient message failed!! ' + msg); }; spinredis.prototype.setSessionId = function(id) { if (id) { console.log('++++++++++++++++++++++++++++++++++++++ spinclient setting session id to ' + id); return this.sessionId = id; } }; spinredis.prototype.dumpOutstanding = function() { console.log('-------------------------------- ' + this.outstandingMessages.length + ' outstanding messages ---------------------------------'); this.outstandingMessages.forEach(function(os) { return console.log(os.messageId + ' -> ' + os.target + ' - ' + os.d); }); return console.log('-----------------------------------------------------------------------------------------'); }; spinredis.prototype.emit = function(message) { message.channelID = 'spinchannel_' + this.channelID; if (debug) { console.log('redisclient emitting message..'); } if (debug) { console.dir(message); } this.savedMessagesInCaseOfRetries.set(message.messageId, message); return this.sendredis.publish('spinchannel', JSON.stringify(message)); }; spinredis.prototype.setup = function() { this.channelID = uuid.v4(); this.listenredis.subscribe('spinchannel_' + this.channelID); return this.listenredis.on('message', (function(_this) { return function(channel, replystr) { var detail, i, index, info, message, oldmsg, reply, status, subs; if (debug) { console.log('spinredis on message got ' + replystr); } reply = JSON.parse(replystr); status = reply.status; message = reply.payload; info = reply.info; if (message && message.error && message.error === 'ERRCHILLMAN') { console.log('got ERRCHILLMAN from spinycle service, preparing to retry sending message...'); oldmsg = _this.savedMessagesInCaseOfRetries[reply.messageId]; return setTimeout(function() { console.log('resending message ' + oldmsg.messageId + ' due to target endpoint not open yet'); return _this.emit(oldmsg); }, 250); } else if (!_this.hasSeenThisMessage(reply.messageId)) { _this.savedMessagesInCaseOfRetries.remove(reply.messageId); if (reply.messageId && reply.messageId !== 'undefined') { _this.seenMessages.push(reply.messageId); } if (_this.seenMessages.length > 10) { _this.seenMessages.shift(); } if (debug) { console.log('redis-spincycle got reply messageId ' + reply.messageId + ' status ' + status + ', info ' + info + ' data ' + message + ' outstandingMessages = ' + _this.outstandingMessages.length); } if (debug) { _this.dumpOutstanding(); } index = -1; if (reply.messageId) { i = 0; while (i < _this.outstandingMessages.length) { index = i; detail = _this.outstandingMessages[i]; if (detail && !detail.delivered && detail.messageId === reply.messageId) { if (reply.status === 'FAILURE' || reply.status === 'NOT_ALLOWED') { console.log('spinclient message FAILURE'); console.dir(reply); detail.d.reject(reply); break; } else { detail.d.resolve(message); break; } detail.delivered = true; } i++; } if (index > -1) { return _this.outstandingMessages.splice(index, 1); } } else { subs = _this.subscribers[info]; if (subs) { return subs.forEach(function(listener) { return listener(message); }); } else { if (debug) { console.log('no subscribers for message ' + message); } if (debug) { return console.dir(reply); } } } } else { if (debug) { return console.log('-- skipped resent message ' + reply.messageId); } } }; })(this)); }; spinredis.prototype.hasSeenThisMessage = function(messageId) { return this.seenMessages.some(function(mid) { return messageId === mid; }); }; spinredis.prototype.registerListener = function(detail) { var subs; subs = this.subscribers[detail.message] || []; subs.push(detail.callback); return this.subscribers[detail.message] = subs; }; spinredis.prototype.registerObjectSubscriber = function(detail) { var d, localsubs, sid; d = $q.defer(); sid = uuid.v4(); localsubs = this.objectsSubscribedTo[detail.id]; if (!localsubs) { localsubs = []; console.log('spinredis no local subs, so get the original server-side subscription for id ' + detail.id); this._registerObjectSubscriber({ id: detail.id, type: detail.type, cb: (function(_this) { return function(updatedobj) { var k, lsubs, results, v; lsubs = _this.objectsSubscribedTo[detail.id]; results = []; for (k in lsubs) { v = lsubs[k]; if (v.cb) { results.push(v.cb(updatedobj)); } else { results.push(void 0); } } return results; }; })(this) }).then((function(_this) { return function(remotesid) { localsubs['remotesid'] = remotesid; localsubs[sid] = detail; _this.objectsSubscribedTo[detail.id] = localsubs; return d.resolve(sid); }; })(this), (function(_this) { return function(rejection) { console.log('spinredis registerObjectSubscriber rejection: ' + rejection); return console.dir(rejection); }; })(this)); } else { localsubs[sid] = detail; } return d.promise; }; spinredis.prototype._registerObjectSubscriber = function(detail) { var d, subs; d = $q.defer(); subs = this.objsubscribers[detail.id] || []; this.emitMessage({ target: 'registerForUpdatesOn', obj: { id: detail.id, type: detail.type } }).then((function(_this) { return function(reply) { console.log('spinredis server subscription id for id ' + detail.id + ' is ' + reply); subs[reply] = detail.cb; _this.objsubscribers[detail.id] = subs; return d.resolve(reply); }; })(this), (function(_this) { return function(reply) { return _this.failed(reply); }; })(this)); return d.promise; }; spinredis.prototype.deRegisterObjectsSubscriber = function(sid, o) { var count, j, k, len, localsubs, v; localsubs = this.objectsSubscribedTo[o.id] || []; if (localsubs[sid]) { console.log('deregistering local updates for @objects ' + o.id); delete localsubs[sid]; count = 0; for (v = j = 0, len = localsubs.length; j < len; v = ++j) { k = localsubs[v]; count++; } if (count === 1) { return this._deRegisterObjectsSubscriber('remotesid', o); } } }; spinredis.prototype._deRegisterObjectsSubscriber = function(sid, o) { var subs; subs = this.objsubscribers[o.id] || []; if (subs && subs[sid]) { delete subs[sid]; this.objsubscribers[o.id] = subs; return this.emitMessage({ target: 'deRegisterForUpdatesOn', id: o.id, type: o.type, listenerid: sid }).then(function(reply) { return console.log('deregistering server updates for @objects ' + o.id); }); } }; spinredis.prototype.emitMessage = function(detail) { var d; if (debug) { console.log('emitMessage called'); } if (debug) { console.dir(detail); } d = $q.defer(); detail.messageId = uuid.v4(); detail.sessionId = detail.sessionId || this.sessionId; detail.d = d; this.outstandingMessages.push(detail); if (debug) { console.log('saving outstanding reply to messageId ' + detail.messageId + ' and @sessionId ' + detail.sessionId); } this.emit(detail); return d.promise; }; spinredis.prototype.getModelFor = function(type) { var d; d = $q.defer(); if (this.modelcache[type]) { d.resolve(this.modelcache[type]); } else { this.emitMessage({ target: 'getModelFor', modelname: type }).then(function(model) { this.modelcache[type] = model; return d.resolve(model); }, (function(_this) { return function(rejection) { console.log('spinredis getModelFor rejection: ' + rejection); return console.dir(rejection); }; })(this)); } return d.promise; }; spinredis.prototype.listTargets = function() { var d; d = $q.defer(); this.emitMessage({ target: 'listcommands' }).then(function(targets) { return d.resolve(targets); }, function(rejection) { console.log('spinredis listTargets rejection: ' + rejection); return console.dir(rejection); }); return d.promise; }; spinredis.prototype.flattenModel = function(model) { var k, rv, v; rv = {}; for (k in model) { v = model[k]; if (angular.isArray(v)) { rv[k] = v.map(function(e) { return e.id; }); } else { rv[k] = v; } } return rv; }; return spinredis; })(); module.exports = spinredis; }).call(this); //# sourceMappingURL=spinredis.js.map