From d956f85cd8e2e2af828b92bf4eec5965d4d8bb4c Mon Sep 17 00:00:00 2001 From: David Terei Date: Fri, 29 Apr 2016 19:09:12 -0700 Subject: [PATCH 1/3] New timeout handling that uses a single active timer We move to a new (yet again!) timeout handling system where we only use one active timer at a time, and track future timeouts / deadlines in our own queue to re-arm the timer. This fixes issue #52. We could use `setMaxListeners` to instead increase the timeout handlers allowed and keep with the existing design, but this approach is O(1) scalable. --- bench/memjs.js | 49 +++++++++++++------------- bench/timers.js | 46 +++++++++++++++++++++++++ lib/memjs/memjs.js | 15 ++++---- lib/memjs/server.js | 84 +++++++++++++++++++++++++++++++++++++++------ lib/memjs/utils.js | 7 ++++ package.json | 3 +- test/client_test.js | 60 ++++++++++++++++---------------- 7 files changed, 194 insertions(+), 70 deletions(-) create mode 100644 bench/timers.js diff --git a/bench/memjs.js b/bench/memjs.js index 88774a0..56bf3bb 100644 --- a/bench/memjs.js +++ b/bench/memjs.js @@ -1,9 +1,21 @@ -var memjs = require("memjs") +var memjs = require("memjs"); var header = require('header'); -var b = require("benchmark") +var b = require("benchmark"); + +function makeString(n) { + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var text = ""; + var i; + + for(i=0; i < n; i++ ) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return text; +} var x = (function() { - suite = new b.Suite; + var suite = new b.Suite(); var headerBuf = new Buffer([0x81, 1, 7, 0, 4, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 0x0a, 0, 0, 0, 0, 0, 0, 0]); var parsedHeader = header.fromBuffer(headerBuf); @@ -20,10 +32,10 @@ var x = (function() { }) // run async .run({ 'async': true }); -})(); +}()); x = (function() { - suite = new b.Suite; + var suite = new b.Suite(); var responseHeader = { magic: 0x81, opcode: 1, @@ -34,7 +46,7 @@ x = (function() { totalBodyLength: 1024 * 10 + 15, opaque: 0, cas: new Buffer([0x0a, 0, 0, 0, 0, 0, 0, 0]) - } + }; var buf = new Buffer(24 + 15 + 1024 * 10); header.toBuffer(responseHeader).copy(buf); buf.write(makeString(55)); @@ -46,7 +58,8 @@ x = (function() { return arg; }; - for (var i = 0; i < 10; i++) { + var i; + for (i = 0; i < 10; i++) { server.onResponse(dummyFunc); } @@ -62,25 +75,15 @@ x = (function() { }) // run async .run({ 'async': true }); -})(); - -function makeString(n) { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for( var i=0; i < n; i++ ) - text += possible.charAt(Math.floor(Math.random() * possible.length)); - - return text; -} +}()); x = (function() { - suite = new b.Suite; - client = memjs.Client.create(); + var suite = new b.Suite(); + var client = memjs.Client.create(); suite.cycles = 0; suite.add('Client#get', function() { - client.get("hello", function(err, val) { + client.get("hello", function(/* err, val */) { suite.cycles++; }); }) @@ -88,9 +91,9 @@ x = (function() { .on('cycle', function(event) { console.log(String(event.target) + " " + suite.cycles); }); - client.set("hello", makeString(10240), function(err, val) { + client.set("hello", makeString(10240), function(/* err, val */) { // run async suite.run({ 'async': true }); }); -})(); +}()); diff --git a/bench/timers.js b/bench/timers.js new file mode 100644 index 0000000..49b3847 --- /dev/null +++ b/bench/timers.js @@ -0,0 +1,46 @@ +/*jshint node: true */ +/*jslint unparam: true*/ +'use strict'; + +/** + * Check how fast various timers are in node. + */ + +var Benchmark = require('benchmark'); +var Microtime = require('microtime'); + +var suite = new Benchmark.Suite(); + +// add tests +suite.add('Date.now()', function() { + // system time, not-monotonic, ms + Date.now(); +}) +.add('Microtime.now()', function() { + // system time, not-monotonic, us (POSIX: gettimeofday) + Microtime.now(); +}) +.add('process.hrtime()', function() { + // monotonic, ns (returns: [seconds, nanoseconds]) + process.hrtime(); +}) +.add('process.hrtime() ms-round', function() { + // monotonic, ns (returns: [seconds, nanoseconds]) + var time = process.hrtime(); + return (time[0] * 1000) + Math.round(time[1] / 1000000); +}) +.add('process.hrtime() ms-floor', function() { + // monotonic, ns (returns: [seconds, nanoseconds]) + var time = process.hrtime(); + return (time[0] * 1000) + Math.floor(time[1] / 1000000); +}) +// add listeners +.on('cycle', function(event) { + console.log(String(event.target)); +}) +.on('complete', function() { + console.log('Fastest is ' + this.filter('fastest').map('name')); +}) +// run async +.run({ 'async': true }); + diff --git a/lib/memjs/memjs.js b/lib/memjs/memjs.js index 947d4a6..3285484 100644 --- a/lib/memjs/memjs.js +++ b/lib/memjs/memjs.js @@ -450,7 +450,7 @@ Client.prototype.flush = function(callback) { callback(lastErr, result); } }); - serv.write(seq, request); + serv.write(request); }; for (i = 0; i < this.servers.length; i++) { @@ -501,7 +501,7 @@ Client.prototype.statsWithKey = function(key, callback) { serv.onError(seq, function(err) { if (callback) { callback(err, serv.host + ':' + serv.port, null); } }); - serv.write(seq, request); + serv.write(request); }; for (i = 0; i < this.servers.length; i++) { @@ -540,7 +540,10 @@ Client.prototype.resetStats = function(callback) { // QUIT // -// Closes the connection to each server, notifying them of this intention. +// Closes the connection to each server, notifying them of this intention. Note +// that quit can race against already outstanding requests when those requests +// fail and are retried, leading to the quit command winning and closing the +// connection before the retries complete. Client.prototype.quit = function() { this.seq++; // TODO: Nicer perhaps to do QUITQ (0x17) but need a new callback for when @@ -556,7 +559,7 @@ Client.prototype.quit = function() { serv.onError(seq, function(/* err */) { serv.close(); }); - serv.write(seq, request); + serv.write(request); }; for (i = 0; i < this.servers.length; i++) { @@ -602,7 +605,7 @@ Client.prototype.perform = function(key, request, callback, retries) { if (--retries > 0) { serv.onResponse(seq, responseHandler); serv.onError(seq, errorHandler); - serv.write(seq, request); + serv.write(request); } else { logger.log('MemJS: Server <' + serv.host + ':' + serv.port + '> failed after (' + origRetries + @@ -618,7 +621,7 @@ Client.prototype.perform = function(key, request, callback, retries) { serv.onResponse(seq, responseHandler); serv.onError(seq, errorHandler); - serv.write(seq, request); + serv.write(request); }; exports.Client = Client; diff --git a/lib/memjs/server.js b/lib/memjs/server.js index de2417b..07f3071 100644 --- a/lib/memjs/server.js +++ b/lib/memjs/server.js @@ -4,6 +4,7 @@ var util = require('util'); var makeRequestBuffer = require('./utils').makeRequestBuffer; var parseMessage = require('./utils').parseMessage; var merge = require('./utils').merge; +var timestamp = require('./utils').timestamp; var Server = function(host, port, username, password, options) { events.EventEmitter.call(this); @@ -11,8 +12,10 @@ var Server = function(host, port, username, password, options) { this.host = host; this.port = port; this.connected = false; + this.timeoutSet = false; this.connectCallbacks = []; this.responseCallbacks = {}; + this.requestTimeouts = []; this.errorCallbacks = {}; this.options = merge(options || {}, {timeout: 0.5, keepAlive: false, keepAliveDelay: 30}); if (this.options.conntimeout === undefined || this.options.conntimeout === null) { @@ -42,6 +45,7 @@ Server.prototype.respond = function(response) { callback(response); if (!callback.quiet || response.header.totalBodyLength === 0) { delete(this.responseCallbacks[response.header.opaque]); + this.requestTimeouts.shift(); delete(this.errorCallbacks[response.header.opaque]); } }; @@ -54,7 +58,9 @@ Server.prototype.error = function(err) { var errcalls = this.errorCallbacks; this.connectCallbacks = []; this.responseCallbacks = {}; + this.requestTimeouts = []; this.errorCallbacks = {}; + this.timeoutSet = false; if (this._socket) { this._socket.destroy(); delete(this._socket); @@ -107,12 +113,20 @@ Server.prototype.responseHandler = function(dataBuf) { Server.prototype.sock = function(sasl, go) { var self = this; + if (!self._socket) { + // CASE 1: completely new socket self.connected = false; self._socket = net.connect(this.port, this.host, function() { + + // SASL authentication handler self.once('authenticated', function() { if (self._socket) { self.connected = true; + // cancel connection timeout + self._socket.setTimeout(0); + self.timeoutSet = false; + // run actual request(s) go(self._socket); self.connectCallbacks.forEach(function(cb) { cb(self._socket); @@ -120,47 +134,97 @@ Server.prototype.sock = function(sasl, go) { self.connectCallbacks = []; } }); + + // setup response handler this.on('data', function(dataBuf) { self.responseHandler(dataBuf); }); + + // kick of SASL if needed if (self.username && self.password) { self.listSasl(); } else { self.emit('authenticated'); } }); + + // setup error handler self._socket.on('error', function(error) { self.connected = false; + if (self.timeoutSet) { + self._socket.setTimeout(0); + self.timeoutSet = false; + } self._socket = undefined; self.error(error); }); + + // setup connection timeout handler + self.timeoutSet = true; self._socket.setTimeout(self.options.conntimeout * 1000, function() { + self.timeoutSet = false; if (!self.connected) { this.end(); self._socket = undefined; - self.error(new Error('socket timed out.')); + self.error(new Error('socket timed out connecting to server.')); } }); + + // use TCP keep-alive self._socket.setKeepAlive(self.options.keepAlive, self.options.keepAliveDelay * 1000); + } else if (!self.connected && !sasl) { + // CASE 2: socket exists, but still connecting / authenticating self.onConnect(go); + } else { + // CASE 3: socket exists and connected / ready to use go(self._socket); } }; -Server.prototype.write = function(seq, blob) { +// We handle tracking timeouts with an array of deadlines (requestTimeouts), as +// node doesn't like us setting up lots of timers, and using just one is more +// efficient anyway. +var timeoutHandler = function(server, sock) { + if (server.requestTimeouts.length === 0) { + // nothing active + server.timeoutSet = false; + return; + } + + // some requests outstanding, check if any have timed-out + var now = timestamp(); + var soonestTimeout = server.requestTimeouts[0]; + + if (soonestTimeout <= now) { + // timeout occurred! + sock.end(); + server.connected = false; + server._socket = undefined; + server.timeoutSet = false; + server.error(new Error('socket timed out waiting on response.')); + } else { + // no timeout! Setup next one. + var deadline = soonestTimeout - now; + sock.setTimeout(deadline, function() { + timeoutHandler(server, sock); + }); + } +}; + +Server.prototype.write = function(blob) { var self = this; + var deadline = Math.round(self.options.timeout * 1000); this.sock(false, function(s) { s.write(blob); - s.setTimeout(self.options.timeout * 1000, function() { - if (self.responseCallbacks[seq]) { - this.end(); - self.connected = false; - self._socket = undefined; - self.error(new Error('socket timed out.')); - } - }); + self.requestTimeouts.push(timestamp() + deadline); + if (!self.timeoutSet) { + self.timeoutSet = true; + s.setTimeout(deadline, function() { + timeoutHandler(self, this); + }); + } }); }; diff --git a/lib/memjs/utils.js b/lib/memjs/utils.js index dedbd60..9ee8a9c 100644 --- a/lib/memjs/utils.js +++ b/lib/memjs/utils.js @@ -86,6 +86,13 @@ exports.merge = function(original, deflt) { return original; }; +// timestamp provides a monotonic timestamp with millisecond accuracy, useful +// for timers. +exports.timestamp = function() { + var times = process.hrtime(); + return (times[0] * 1000) + Math.round((times[1] / 1000000)); +}; + if(!Buffer.concat) { Buffer.concat = function(list, length) { if (!Array.isArray(list)) { diff --git a/package.json b/package.json index 7f42d7d..98c9b6e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ }, "scripts": { "test": "eslint ./lib/memjs/ ./test/ && tap -R spec ./test/*.js", - "bench": "NODE_PATH=lib/memjs/ node bench/memjs.js" + "bench": "NODE_PATH=lib/memjs/ node bench/memjs.js", + "bench-timers": "NODE_PATH=lib/memjs/ node bench/timers.js" }, "dependencies": {}, "devDependencies": {"eslint":"1.10.3", diff --git a/test/client_test.js b/test/client_test.js index fa2dafc..eed9147 100644 --- a/test/client_test.js +++ b/test/client_test.js @@ -5,7 +5,7 @@ var MemJS = require('../'); test('GetSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); n += 1; @@ -27,7 +27,7 @@ test('GetSuccessful', function(t) { test('GetNotFound', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); n += 1; @@ -47,7 +47,7 @@ test('GetNotFound', function(t) { test('SetSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -67,7 +67,7 @@ test('SetSuccessful', function(t) { test('SetWithExpiration', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -88,7 +88,7 @@ test('SetWithExpiration', function(t) { test('SetUnsuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -108,7 +108,7 @@ test('SetUnsuccessful', function(t) { test('SetError', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -129,7 +129,7 @@ test('SetError', function(t) { test('SetError', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -153,7 +153,7 @@ test('SetErrorConcurrent', function(t) { var callbn1 = 0; var callbn2 = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(/* seq, requestBuf */) { + dummyServer.write = function(/* requestBuf */) { process.nextTick(function() { n += 1; dummyServer.error({message: 'This is an expected error.'}); @@ -196,7 +196,7 @@ test('SetErrorConcurrent', function(t) { test('SetUnicode', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('éééoào', request.val.toString()); @@ -215,7 +215,7 @@ test('SetUnicode', function(t) { test('AddSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -236,7 +236,7 @@ test('AddSuccessful', function(t) { test('AddKeyExists', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -256,7 +256,7 @@ test('AddKeyExists', function(t) { test('ReplaceSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -277,7 +277,7 @@ test('ReplaceSuccessful', function(t) { test('ReplaceKeyDNE', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -297,7 +297,7 @@ test('ReplaceKeyDNE', function(t) { test('DeleteSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); n += 1; @@ -316,7 +316,7 @@ test('DeleteSuccessful', function(t) { test('DeleteKeyDNE', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); n += 1; @@ -337,7 +337,7 @@ test('Flush', function(t) { var dummyServer = new MemJS.Server(); dummyServer.host = 'example.com'; dummyServer.port = 1234; - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal(0x08, request.header.opcode); n += 1; @@ -358,7 +358,7 @@ test('Stats', function(t) { var dummyServer = new MemJS.Server(); dummyServer.host = 'myhostname'; dummyServer.port = 5544; - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal(0x10, request.header.opcode); n += 1; @@ -393,7 +393,7 @@ test('IncrementSuccessful', function(t) { '\0\0\0\0\0\0\0\5\0\0\0\0\0\0\0\3\0\0\0\0' ]; - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal(5, request.header.opcode); t.equal('number-increment-test', request.key.toString()); @@ -440,7 +440,7 @@ test('IncrementSuccessful', function(t) { test('DecrementSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal(6, request.header.opcode); t.equal('number-decrement-test', request.key.toString()); @@ -463,7 +463,7 @@ test('DecrementSuccessful', function(t) { test('AppendSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -483,7 +483,7 @@ test('AppendSuccessful', function(t) { test('AppendKeyDNE', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -503,7 +503,7 @@ test('AppendKeyDNE', function(t) { test('PrependSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -523,7 +523,7 @@ test('PrependSuccessful', function(t) { test('PrependKeyDNE', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('world', request.val.toString()); @@ -543,7 +543,7 @@ test('PrependKeyDNE', function(t) { test('TouchSuccessful', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('', request.val.toString()); @@ -564,7 +564,7 @@ test('TouchSuccessful', function(t) { test('TouchKeyDNE', function(t) { var n = 0; var dummyServer = new MemJS.Server(); - dummyServer.write = function(seq, requestBuf) { + dummyServer.write = function(requestBuf) { var request = MemJS.Utils.parseMessage(requestBuf); t.equal('hello', request.key.toString()); t.equal('', request.val.toString()); @@ -586,12 +586,12 @@ test('Failover', function(t) { var n1 = 0; var n2 = 0; var dummyServer1 = new MemJS.Server(); - dummyServer1.write = function(/* seq, requestBuf*/) { + dummyServer1.write = function(/* requestBuf*/) { n1 += 1; dummyServer1.error(new Error('connection failure')); }; var dummyServer2 = new MemJS.Server(); - dummyServer2.write = function(seq, requestBuf) { + dummyServer2.write = function(requestBuf) { n2 += 1; var request = MemJS.Utils.parseMessage(requestBuf); dummyServer2.respond({header: {status: 0, opaque: request.header.opaque}}); @@ -614,12 +614,12 @@ exports.testFailoverRecovery = function(beforeExit, assert) { var n1 = 0; var n2 = 0; var dummyServer1 = new MemJS.Server(); - dummyServer1.write = function(seq, requestBuf) { + dummyServer1.write = function(requestBuf) { n1 += 1; dummyServer1.error(new Error("connection failure")); } var dummyServer2 = new MemJS.Server(); - dummyServer2.write = function(seq, requestBuf) { + dummyServer2.write = function(requestBuf) { n2 += 1; dummyServer2.respond({header: {status: 0, opaque: request.header.opaque}}); } @@ -630,7 +630,7 @@ exports.testFailoverRecovery = function(beforeExit, assert) { assert.equal(null, err); }); - dummyServer1.write = function(seq, requestBuf) { + dummyServer1.write = function(requestBuf) { n1 += 1; dummyServer1.respond({header: {status: 0, opaque: request.header.opaque}}); } From e2f69400073ee8e0bd1f84b7e5276591cf2f1d7c Mon Sep 17 00:00:00 2001 From: David Terei Date: Fri, 29 Apr 2016 19:11:54 -0700 Subject: [PATCH 2/3] Fix spelling mistake in comment --- lib/memjs/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/memjs/server.js b/lib/memjs/server.js index 07f3071..b9148c5 100644 --- a/lib/memjs/server.js +++ b/lib/memjs/server.js @@ -39,7 +39,7 @@ Server.prototype.onResponse = function(seq, func) { Server.prototype.respond = function(response) { var callback = this.responseCallbacks[response.header.opaque]; if (!callback) { - // in case of authentiction, no callback is registered + // in case of authentication, no callback is registered return; } callback(response); From bbc882ca3333d6a933708b37018f22f2b62ed066 Mon Sep 17 00:00:00 2001 From: David Terei Date: Fri, 29 Apr 2016 23:17:35 -0700 Subject: [PATCH 3/3] Bump to version 0.10.0 --- CHANGELOG | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 4c0a9ca..6a17975 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ # Change Log +# Version 0.10.0 + +* Fix issue with too many outstanding socket timeouts (issue #52) +* Add support for touch +* Add support for append +* Add support for prepend +* Fix lint issues across code base +* Update microtime dependency + # Version 0.9.1 * Fix issue with no completing SASL auth before sending a request (issue #65) diff --git a/package.json b/package.json index 98c9b6e..88fad07 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Amit Levy", "name": "memjs", "description": "A memcache client for node using the binary protocol and SASL authentication", - "version": "0.9.1", + "version": "0.10.0", "homepage": "http://github.com/alevy/memjs", "repository": { "type": "git",