...
 
Commits (36)
# ensure that we only run ci test on the Node.js enabled servers
language: node_js
node_js:
- 0.8
# create a travis enabled environment for the test suite to run in
script: "make travisci"
services: memcache
# the `sevices: memcache` will start a memcached service on localhost
# and on the default port, but in order to test against multiple memcache
# instances we need to spawn a couple more, so we do that during the before
# script
before_script:
- memcached -p 11212 -d
- memcached -p 11213 -d
# Authors and contributors of the Memcached project in alphabetical order
Alfonso Boza
Anton Onyshchenko
Arek Flinik
Arnout Kazemier <info@3rd-Eden.com>
Nebojsa Sabovic
Ron Korving
Tobias Müllerleile
# Authors and contributors of the node Memcached driver project in alphabetical order.
- Alfonso Boza
- Anton Onyshchenko
- Arek Flinik
- Arnout Kazemier
- Jan Krems
- Jason Pearlman
- Joseph Mordetsky
- Kinya TERASAKA
- Near Privman
- Nebojsa Sabovic
- René van Sweeden
- Ron Korving
- Sebastian Seilund
- Tobias Müllerleile
0.2.2
- Support for touch command #86
- Fix for chunked responses from the server #84
0.2.1
- Supports for a queued callback limit so it would crash the process when we queue
to much callbacks. #81
0.2.0
- [breaking] We are now returning Error instances instead of strings for errors
- Dependency bump for a critical bug in our connection pool.
0.1.5
- Don't execute callbacks multiple times if the connection fails
- Parser fix for handling server responses that contain Memcached Procotol
keywords
- Make sure that the retry option is set correctly
0.1.4
- Added missing error listener to the 3rd-Eden/jackpot module, this prevents crashes
when it's unable to connect to a server.
0.1.3
- Handle Memcached responses that contain no value.
- Travis CI integration.
0.1.2
- Returning an error when the Memcached server issues a NOT_STORED response.
......@@ -18,7 +44,7 @@
that they are still strings instead of Error instances (legacy)
0.0.10
- Compatiblity with Node.js 0.8
- Compatibility with Node.js 0.8
- Don't saturate the Node process by retrying to connect if pool is full #43
- Minor code formatting
......
ALL_TESTS = $(shell find tests -name '*.test.js')
REPORTER = spec
UI = bdd
ALL_TESTS = $(shell find test -name '*.test.js')
test:
@./node_modules/.bin/mocha \
--require should \
--reporter $(REPORTER) \
--ui $(UI) \
--growl \
$(ALL_TESTS)
@./node_modules/.bin/mocha $(ALL_TESTS)
travisci:
MEMCACHED__HOST=localhost $(MAKE) test
doc:
dox --title "node-memcached" lib/* > doc/index.html
......
This diff is collapsed.
/**
* Created with IntelliJ IDEA.
* User: jmordetsky
* Date: 1/24/13
* Time: 6:33 PM
* To change this template use File | Settings | File Templates.
*/
var crc32 = require('crc32');
var Membase = require('../index.js');
var m1 = new Membase("127.0.0.1:11211", {poolSize:50, maxQueueSize:1000});
//var m2 = new Membase("127.0.0.1:11212", {poolSize:50, maxQueueSize:1000});
//var m3 = new Membase("127.0.0.1:11213", {poolSize:50, maxQueueSize:1000});
//var m4 = new Membase("127.0.0.1:11214", {poolSize:50, maxQueueSize:1000});
//var m5 = new Membase("127.0.0.1:11215", {poolSize:50, maxQueueSize:1000});
//var m6 = new Membase("127.0.0.1:11216", {poolSize:50, maxQueueSize:1000});
//var m7 = new Membase("127.0.0.1:11217", {poolSize:50, maxQueueSize:1000});
//var m8 = new Membase("127.0.0.1:11218", {poolSize:50, maxQueueSize:1000});
//var m9 = new Membase("127.0.0.1:11219", {poolSize:50, maxQueueSize:1000});
//var membases = [m1,m2,m3,m4,m5,m6,m7,m8,m9];
var i =0;
for (i =0;i<10000;i++){
setInterval(function(){
go(i);
},0);
}
function go(val){
//random key
var key = GUID();
var value = GUID();
//set
m1.set(key, value, 60, function(err, result){
if (err){
if (err !== "over queue limit"){
throw new Error(err);
}else{
return;
}
}
console.log("SET: " + result);
m1.get(key, function(err, result){
if (err){
if (err !== "over queue limit"){
throw new Error(err);
}else{
return;
}
}
console.log("GET: " + result);
});
} );
}
//function defaultShard(key){
// return (((crc32(key) >>> 16) & 0x7fff) % membases.length) || 0;
//}
function S4()
{
return Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16);
}
function GUID ()
{
return (
S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4()
);
};
\ No newline at end of file
......@@ -16,7 +16,7 @@ function ping (host, callback) {
});
pong.stderr.on('data', function stderrdata (data) {
callback(data.toString().split('\n')[0].substr(14), false);
callback(new Error(data.toString().split('\n')[0].substr(14)), false);
pong.kill();
});
}
......@@ -27,6 +27,7 @@ function IssueLog (args) {
this.failed = false;
this.totalRetries = 0;
this.retry = 0;
this.totalReconnectsAttempted = 0;
this.totalReconnectsSuccess = 0;
......
This diff is collapsed.
......@@ -4,8 +4,7 @@ var createHash = require('crypto').createHash
, toString = Object.prototype.toString;
exports.validateArg = function validateArg (args, config) {
var err
, callback;
var err;
args.validate.forEach(function (tokens) {
var key = tokens[0]
......@@ -58,7 +57,7 @@ exports.validateArg = function validateArg (args, config) {
args[key] = createHash('md5').update(value).digest('hex');
// also make sure you update the command
args.command.replace(value, args[key]);
args.command = args.command.replace(value, args[key]);
} else {
err = 'Argument "' + key + '" is longer than the maximum allowed length of ' + config.maxKeySize;
}
......@@ -76,7 +75,7 @@ exports.validateArg = function validateArg (args, config) {
});
if (err){
if(callback) callback(err, false);
if(args.callback) args.callback(new Error(err));
return false;
}
......@@ -129,3 +128,13 @@ exports.Iterator = function iterator (collection, callback) {
return index < maximum;
};
};
//Escapes values by putting backslashes before line breaks
exports.escapeValue = function(value) {
return value.replace(/(\r|\n)/g, '\\$1');
};
//Unescapes escaped values by removing backslashes before line breaks
exports.unescapeValue = function(value) {
return value.replace(/\\(\r|\n)/g, '$1');
};
\ No newline at end of file
{
"name": "memcached"
, "version": "0.1.2"
, "version": "0.2.2"
, "author": "Arnout Kazemier"
, "description": "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."
, "main": "index"
......@@ -35,10 +35,13 @@
}
, "dependencies": {
"hashring": "0.0.x"
, "jackpot": "0.0.x"
, "jackpot": ">=0.0.2"
}
, "devDependencies": {
"mocha": "*"
, "should": "*"
}
, "scripts": {
"test": "./node_modules/.bin/mocha $(shell find test -name '*.test.js')"
}
}
'use strict';
/**
* Server ip addresses that get used during the tests
* NOTE! Make sure you configure empty servers as they
* will get flushed!.
*
* If your memcache hosts is not the default one
* (10.211.55.5), you can pass another one using the
* environment variable MEMCACHED__HOST. E.g.:
*
* MEMCACHED__HOST=localhost npm test
*
* @type {Object}
* @api public
*/
var testMemcachedHost = process.env.MEMCACHED__HOST || '10.211.55.5';
exports.servers = {
single: '10.211.55.5:11211'
, multi: ['10.211.55.5:11211', '10.211.55.5:11212', '10.211.55.5:11213']
single: testMemcachedHost + ':11211'
, multi: [
testMemcachedHost + ':11211'
, testMemcachedHost + ':11212'
, testMemcachedHost + ':11213'
]
};
/**
......@@ -18,7 +32,7 @@ exports.servers = {
* @returns {String} a random generated string
* @api public
*/
exports.alphabet = function(n){
exports.alphabet = function alphabet(n){
for (var a = '', i = 0; i < n; i++) {
a += String.fromCharCode(97 + Math.floor(Math.random() * 26));
}
......@@ -26,7 +40,14 @@ exports.alphabet = function(n){
return a;
};
exports.numbers = function(n){
/**
* Generate a bunch of random numbers
*
* @param {Number} n the amount of numbers
* @returns {Number}
* @api public
*/
exports.numbers = function numbers(n){
for (var a = 0, i = 0; i < n; i++) {
a += Math.floor(Math.random() * 26);
}
......
//global it
'use strict';
/**
* Test dependencies
*/
var assert = require('assert')
, fs = require('fs')
, common = require('./common')
, Memcached = require('../');
global.testnumbers = global.testnumbers || +(Math.random(10) * 1000000).toFixed();
/**
* Test connection issues
*/
describe('Memcached connections', function () {
it('should call the callback only once if theres an error', function (done) {
var memcached = new Memcached('127.0.1:1234', { retries: 3 })
, calls = 0;
this.timeout(60000);
memcached.get('idontcare', function (err) {
calls++;
// it should only be called once
assert.equal(calls, 1);
memcached.end();
done();
});
});
});
......@@ -47,6 +47,33 @@ describe("Memcached GET SET", function() {
});
});
it("set and get an empty string", function(done) {
var memcached = new Memcached(common.servers.single)
, testnr = ++global.testnumbers
, callbacks = 0;
memcached.set("test:" + testnr, "", 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
assert.ok(typeof answer === 'string');
answer.should.eql("");
memcached.end(); // close connections
assert.equal(callbacks, 2);
done();
});
});
});
/**
* Set a stringified JSON object, and make sure we only return a string
* this should not be flagged as JSON object
......@@ -418,4 +445,146 @@ describe("Memcached GET SET", function() {
});
});
});
/**
* Make sure that a string beginning with OK is not interpreted as
* a command response.
*/
it("set and get a string beginning with OK", function(done) {
var memcached = new Memcached(common.servers.single)
, message = 'OK123456'
, testnr = ++global.testnumbers
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
assert.ok(typeof answer === 'string');
answer.should.eql(message);
memcached.end(); // close connections
assert.equal(callbacks, 2);
done();
});
});
});
/**
* Make sure that a string beginning with OK is not interpreted as
* a command response.
*/
it("set and get a string beginning with VALUE", function(done) {
var memcached = new Memcached(common.servers.single)
, message = 'VALUE hello, I\'m not really a value.'
, testnr = ++global.testnumbers
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
assert.ok(typeof answer === 'string');
answer.should.eql(message);
memcached.end(); // close connections
assert.equal(callbacks, 2);
done();
});
});
});
/**
* Make sure that a string containing line breaks are escaped and
* unescaped correctly.
*/
it("set and get a string with line breaks", function(done) {
var memcached = new Memcached(common.servers.single)
, message = '1\n2\r\n3\n\r4\\n5\\r\\n6\\n\\r7'
, testnr = ++global.testnumbers
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
assert.ok(typeof answer === 'string');
answer.should.eql(message);
memcached.end(); // close connections
assert.equal(callbacks, 2);
done();
});
});
});
/**
* Make sure long keys are hashed
*/
it("make sure you can get really long strings", function(done) {
var memcached = new Memcached(common.servers.single)
, message = 'VALUE hello, I\'m not really a value.'
, testnr = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"+(++global.testnumbers)
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
assert.ok(typeof answer === 'string');
answer.should.eql(message);
memcached.end(); // close connections
assert.equal(callbacks, 2);
done();
});
});
});
/**
* Make sure keys with spaces return an error
*/
it("errors on spaces in strings", function(done) {
var memcached = new Memcached(common.servers.single)
, message = 'VALUE hello, I\'m not really a value.'
, testnr = " "+(++global.testnumbers)
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(error);
assert.ok(error.message == 'The key should not contain any whitespace or new lines')
done();
});
});
});
/**
* Test dependencies
*/
var assert = require('assert')
, fs = require('fs')
, common = require('./common')
, Memcached = require('../');
global.testnumbers = global.testnumbers || +(Math.random(10) * 1000000).toFixed();
/**
* Expresso test suite for all `touch` related
* memcached commands
*/
describe("Memcached TOUCH", function() {
/**
* Make sure that touching a key with 1 sec lifetime and getting it 1.1 sec after invoke deletion
*/
it("changes lifetime", function(done) {
var memcached = new Memcached(common.servers.single)
, message = common.alphabet(256)
, testnr = ++global.testnumbers
, callbacks = 0;
memcached.set("test:" + testnr, message, 1000, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
memcached.touch("test:" + testnr, 1, function(error, ok){
++callbacks;
assert.ok(!error);
ok.should.be.true;
setTimeout(function(){
memcached.get("test:" + testnr, function(error, answer){
++callbacks;
assert.ok(!error);
answer.should.be.false;
memcached.end(); // close connections
assert.equal(callbacks, 3);
done();
})}, 1100); // 1.1 sec after
});
});
});
});
--require should
--reporter spec
--ui bdd