redis-spincycle
Version:
library tha connects to a spincycle server using redis pubsub
406 lines (379 loc) • 13.9 kB
JavaScript
// 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