-
Notifications
You must be signed in to change notification settings - Fork 275
Expand file tree
/
Copy pathmemcached-connections.test.js
More file actions
311 lines (285 loc) · 10.3 KB
/
Copy pathmemcached-connections.test.js
File metadata and controls
311 lines (285 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
//global it
'use strict';
/**
* Test dependencies
*/
var assert = require('assert')
, fs = require('fs')
, net = require('net')
, common = require('./common')
, Memcached = require('../');
global.testnumbers = global.testnumbers || +(Math.random(10) * 1000000).toFixed();
/**
* Test connection issues
*/
describe('Memcached connections', function () {
it('should instantiate client when not called with new', function(done) {
var memcached = Memcached('127.0.0.1:1234', {});
memcached.end();
done();
});
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();
});
});
it('should remove a failed server', function(done) {
var memcached = new Memcached('127.0.1:1234', {
timeout: 1000,
retries: 0,
failures: 0,
retry: 100,
remove: true });
this.timeout(60000);
memcached.get('idontcare', function (err) {
function noserver() {
memcached.get('idontcare', function(err) {
throw err;
});
};
assert.throws(noserver, new RegExp('Server at 127.0.1.1234 not available'));
memcached.end();
done();
});
});
it('should rebalance to remaining healthy server', function(done) {
var memcached = new Memcached(['127.0.1:1234', common.servers.single], {
timeout: 1000,
retries: 0,
failures: 0,
retry: 100,
remove: true,
redundancy: true });
this.timeout(60000);
// 'a' goes to fake server. first request will cause server to be removed
memcached.get('a', function (err) {
// second request should be rebalanced to healthy server
memcached.get('a', function (err) {
assert.ifError(err);
memcached.end();
done();
});
});
});
it('should properly schedule failed server retries', function(done) {
var server = '127.0.0.1:1234';
var memcached = new Memcached(server, {
retries: 0,
failures: 5,
retry: 100 });
// First request will schedule a retry attempt, and lock scheduling
memcached.get('idontcare', function (err) {
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
assert.deepEqual(memcached.issues[server].failures, 5);
assert.deepEqual(memcached.issues[server].locked, true);
assert.deepEqual(memcached.issues[server].failed, true);
// Immediate request should not decrement failures
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /not available/);
assert.deepEqual(memcached.issues[server].failures, 5);
assert.deepEqual(memcached.issues[server].locked, true);
assert.deepEqual(memcached.issues[server].failed, true);
// Once `retry` time has passed, failures should decrement by one
setTimeout(function() {
// Server should be back in action
assert.deepEqual(memcached.issues[server].locked, false);
assert.deepEqual(memcached.issues[server].failed, false);
memcached.get('idontcare', function(err) {
// Server should be marked healthy again, though we'll get this error
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
assert.deepEqual(memcached.issues[server].failures, 4);
memcached.end();
done();
});
}, 100); // `retry` is 100 so wait 100
});
});
});
it('should properly schedule server reconnection attempts', function(done) {
var server = '127.0.0.1:1234'
, memcached = new Memcached(server, {
retries: 3,
minTimeout: 0,
maxTimeout: 100,
failures: 0,
// Reconnect timeout of 300ms is tied to 400ms test timeout,
// so only one reconnection attempt could occur
reconnect: 300 })
, reconnectAttempts = 0;
memcached.on('reconnecting', function() {
reconnectAttempts++;
});
// First request will mark server dead and schedule reconnect
memcached.get('idontcare', function (err) {
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
// Second request should not schedule another reconnect
memcached.get('idontcare', function (err) {
assert.throws(function() { throw err }, /not available/);
// Allow enough time to pass for a connection retries to occur
setTimeout(function() {
assert.deepEqual(reconnectAttempts, 1);
memcached.end();
done();
}, 400);
});
});
});
it('should reset failures after reconnecting to failed server', function(done) {
var server = '127.0.0.1:12340'
, memcached = new Memcached(server, {
retries: 0,
minTimeout: 0,
maxTimeout: 100,
failures: 1,
retry: 1,
reconnect: 100 })
this.timeout(60000);
// First request will mark server failed
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
// Setting server up
var fakeServer = net.createServer();
fakeServer.listen(12340, '127.0.0.1', function() {
// Wait 10ms, server should be back online
setTimeout(function() {
// Tearing server down
fakeServer.close(function () {
// Second request will mark server dead
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
// Third request should find no servers
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /not available/);
// Setting server up once again
fakeServer = net.createServer();
fakeServer.listen(12340, '127.0.0.1', function() {
// Give enough time for server to reconnect
setTimeout(function() {
// And tearing new server down too
fakeServer.close(function () {
// Server should be reconnected, but expect ECONNREFUSED
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /connect ECONNREFUSED/);
assert.deepEqual(memcached.issues[server].failures,
memcached.issues[server].config.failures);
memcached.end();
done();
});
});
}, 150);
});
});
});
});
},10);
});
});
});
it('should default to port 11211', function(done) {
// Use an IP without port
var server = '127.0.0.1'
, memcached = new Memcached(server);
memcached.get('idontcare', function(err) {
assert.ifError(err);
assert.equal(Object.keys(memcached.connections)[0], '127.0.0.1:11211');
memcached.end();
done();
});
});
it('should not create multiple connections with no port', function(done) {
// Use an IP without port
var server = '127.0.0.1'
, memcached = new Memcached(server)
, conn;
memcached.get('idontcare', function(err) {
assert.ifError(err);
conn = memcached.connections['127.0.0.1:11211'];
memcached.get('idontcare', function(err) {
assert.ifError(err);
assert.equal(memcached.connections['127.0.0.1:11211'], conn);
memcached.end();
done();
});
});
});
it('should return error on connection timeout', function(done) {
// Use a non routable IP
// why this IP: https://en.wikipedia.org/wiki/Reserved_IP_addresses#cite_note-rfc5737-5
var server = '203.0.113.255:1234'
, memcached = new Memcached(server, {
retries: 0,
timeout: 100,
idle: 1000,
failures: 0 });
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /Timed out while trying to establish connection/);
memcached.end();
done();
});
});
it('should remove connection when idle', function(done) {
var memcached = new Memcached(common.servers.single, {
retries: 0,
timeout: 100,
idle: 100,
failures: 0 });
memcached.get('idontcare', function(err) {
assert.deepEqual(memcached.connections[common.servers.single].pool.length, 1);
setTimeout(function() {
assert.deepEqual(memcached.connections[common.servers.single].pool.length, 0);
memcached.end();
done();
}, 110);
});
});
it('should remove server if error occurs after connection established', function(done) {
var memcached = new Memcached(common.servers.single, {
poolSize: 1,
retries: 0,
timeout: 1000,
idle: 5000,
failures: 0 });
// Should work fine
memcached.get('idontcare', function(err) {
assert.ifError(err);
// Fake an error on the connected socket which should mark server failed
var S = memcached.connections[common.servers.single].pool.pop();
S.emit('error', new Error('Dummy error'));
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err; }, /not available/);
done();
});
});
});
it('should reset failures if all failures do not occur within failuresTimeout ms', function(done) {
var server = '203.0.113.255:1234'
, memcached = new Memcached(server, {
retries: 0,
timeout: 10,
idle: 1000,
retry: 10,
failures: 2,
failuresTimeout: 100 });
memcached.get('idontcare', function(err) {
assert.throws(function() { throw err }, /Timed out while trying to establish connection/);
// Allow `retry` ms to pass, which will decrement failures
setTimeout(function() {
assert.deepEqual(memcached.issues[server].failures, 1);
// Allow failuresTimeout ms to pass, which should reset failures
setTimeout(function() {
assert.deepEqual(memcached.issues[server].failures,
memcached.issues[server].config.failures);
memcached.end();
done();
}, 100);
}, 15);
});
});
});