UNPKG

memcached

Version:

A fully featured Memcached API client, supporting both single and clustered Memcached servers through consistent hashing and failover/failure. Memcached is rewrite of nMemcached, which will be deprecated in the near future.

131 lines (103 loc) 3.83 kB
"use strict"; var EventEmitter = require('events').EventEmitter , spawn = require('child_process').spawn , Utils = require('./utils') , util = require('util'); exports.IssueLog = IssueLog; // connection issue handling exports.Available = ping; // connection availablity function ping (host, callback) { var isWin = process.platform.indexOf('win') === 0; // win32 or win64 var arg = isWin ? '-n' : '-c'; var pong = spawn('ping', [arg, '3', host]); // only ping 3 times pong.stdout.on('data', function stdoutdata (data) { callback(false, data.toString().split('\n')[0].substr(14)); pong.kill(); }); pong.stderr.on('data', function stderrdata (data) { callback(new Error(data.toString().split('\n')[0].substr(14)), false); pong.kill(); }); } function IssueLog (args) { this.config = args; this.messages = []; this.failed = false; this.locked = false; this.isScheduledToReconnect = false; this.totalFailures = 0; this.retry = 0; this.totalReconnectsAttempted = 0; this.totalReconnectsSuccess = 0; Utils.merge(this, args); EventEmitter.call(this); } util.inherits(IssueLog, EventEmitter); var issues = IssueLog.prototype; issues.log = function log (message) { var issue = this; this.failed = true; this.messages.push(message || 'No message specified'); // All failures must occur within `failuresTimeout` ms from the initial // failure in order for node to be disconnected or removed. if (this.failures && this.failures == this.config.failures) this.failuresResetId = setTimeout(issue.failuresReset.bind(issue), this.failuresTimeout); if (this.failures && !this.locked) { this.locked = true; setTimeout(issue.attemptRetry.bind(issue), this.retry); return this.emit('issue', this.details); } if (this.failuresResetId) clearTimeout(this.failuresResetId); if (this.remove) return this.emit('remove', this.details); if (!this.isScheduledToReconnect) { this.isScheduledToReconnect = true; setTimeout(issue.attemptReconnect.bind(issue), this.reconnect); } }; issues.failuresReset = function failuresReset() { //this.failures = this.config.failures; Utils.merge(this, JSON.parse(JSON.stringify(this.config))); }; Object.defineProperty(issues, 'details', { get: function getDetails () { var res = {}; res.server = this.server; res.tokens = this.tokens; res.messages = this.messages; if (this.failures) { res.failures = this.failures; res.totalFailures = this.totalFailures; } else { res.totalReconnectsAttempted = this.totalReconnectsAttempted; res.totalReconnectsSuccess = this.totalReconnectsSuccess; res.totalReconnectsFailed = this.totalReconnectsAttempted - this.totalReconnectsSuccess; res.totalDownTime = (res.totalReconnectsFailed * this.reconnect) + (this.totalFailures * this.retry); } return res; } }); issues.attemptRetry = function attemptRetry () { this.totalFailures++; this.failures--; this.failed = false; this.locked = false; }; issues.attemptReconnect = function attemptReconnect () { var issue = this; this.totalReconnectsAttempted++; this.emit('reconnecting', this.details); // Ping the server ping(this.tokens[1], function pingpong (err) { // still no access to the server if (err) { issue.messages.push(err.message || 'No message specified'); return setTimeout(issue.attemptReconnect.bind(issue), issue.reconnect); } issue.emit('reconnected', issue.details); issue.totalReconnectsSuccess++; issue.messages.length = 0; issue.failed = false; issue.isScheduledToReconnect = false; // we connected again, so we are going through the whole cycle again Utils.merge(issue, JSON.parse(JSON.stringify(issue.config))); }); };