dsander-memcache-client 1.7.7.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/FAQ.rdoc +31 -0
- data/History.rdoc +241 -0
- data/LICENSE.txt +28 -0
- data/README.rdoc +51 -0
- data/Rakefile +39 -0
- data/VERSION.yml +5 -0
- data/lib/continuum_native.rb +41 -0
- data/lib/memcache.rb +1209 -0
- data/lib/memcache_util.rb +105 -0
- data/performance.txt +143 -0
- data/test/test_benchmark.rb +134 -0
- data/test/test_mem_cache.rb +1238 -0
- metadata +70 -0
@@ -0,0 +1,1238 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'logger'
|
3
|
+
require 'stringio'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
begin
|
8
|
+
gem 'flexmock'
|
9
|
+
require 'flexmock/test_unit'
|
10
|
+
rescue LoadError => e
|
11
|
+
puts "Some tests require flexmock, please run `gem install flexmock`"
|
12
|
+
end
|
13
|
+
|
14
|
+
Thread.abort_on_exception = true
|
15
|
+
$TESTING = true
|
16
|
+
|
17
|
+
require File.dirname(__FILE__) + '/../lib/memcache' if not defined?(MemCache)
|
18
|
+
|
19
|
+
class MemCache
|
20
|
+
|
21
|
+
attr_writer :namespace
|
22
|
+
attr_writer :autofix_keys
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
class FakeSocket
|
27
|
+
|
28
|
+
attr_reader :written, :data
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@written = StringIO.new
|
32
|
+
@data = StringIO.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def write(data)
|
36
|
+
@written.write data
|
37
|
+
end
|
38
|
+
|
39
|
+
def gets
|
40
|
+
@data.gets
|
41
|
+
end
|
42
|
+
|
43
|
+
def read(arg)
|
44
|
+
@data.read arg
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class Test::Unit::TestCase
|
50
|
+
def requirement(bool, msg)
|
51
|
+
if bool
|
52
|
+
yield
|
53
|
+
else
|
54
|
+
puts msg
|
55
|
+
assert true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def memcached_running?
|
60
|
+
TCPSocket.new('localhost', 11211) rescue false
|
61
|
+
end
|
62
|
+
|
63
|
+
def xprofile(name, &block)
|
64
|
+
a = Time.now
|
65
|
+
block.call
|
66
|
+
Time.now - a
|
67
|
+
end
|
68
|
+
|
69
|
+
def profile(name, &block)
|
70
|
+
require 'ruby-prof'
|
71
|
+
a = Time.now
|
72
|
+
result = RubyProf.profile(&block)
|
73
|
+
time = Time.now - a
|
74
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
75
|
+
File.open("#{name}.html", 'w') do |f|
|
76
|
+
printer.print(f, :min_percent=>1)
|
77
|
+
end
|
78
|
+
time
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class FakeServer
|
84
|
+
|
85
|
+
attr_accessor :host, :port, :socket, :weight, :multithread, :status
|
86
|
+
|
87
|
+
def initialize(socket = nil)
|
88
|
+
@closed = false
|
89
|
+
@host = 'example.com'
|
90
|
+
@port = 11211
|
91
|
+
@socket = socket || FakeSocket.new
|
92
|
+
@weight = 1
|
93
|
+
@multithread = true
|
94
|
+
@status = "CONNECTED"
|
95
|
+
end
|
96
|
+
|
97
|
+
def close
|
98
|
+
# begin
|
99
|
+
# raise "Already closed"
|
100
|
+
# rescue => e
|
101
|
+
# puts e.backtrace.join("\n")
|
102
|
+
# end
|
103
|
+
@closed = true
|
104
|
+
@socket = nil
|
105
|
+
@status = "NOT CONNECTED"
|
106
|
+
end
|
107
|
+
|
108
|
+
def alive?
|
109
|
+
# puts "I'm #{@closed ? 'dead' : 'alive'}"
|
110
|
+
!@closed
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
class TestMemCache < Test::Unit::TestCase
|
116
|
+
|
117
|
+
def setup
|
118
|
+
@cache = MemCache.new 'localhost:1', {:namespace => 'my_namespace', :gzip => false}
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_performance
|
122
|
+
requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
|
123
|
+
|
124
|
+
cache = MemCache.new(['localhost:11211',"127.0.0.1:11211"], :gzip => false)
|
125
|
+
cache.flush_all
|
126
|
+
cache.add('a', 1, 120)
|
127
|
+
with = xprofile 'get' do
|
128
|
+
1000.times do
|
129
|
+
cache.get('a')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
puts ''
|
133
|
+
puts "1000 gets with socket timeout: #{with} sec"
|
134
|
+
|
135
|
+
cache = MemCache.new(['localhost:11211',"127.0.0.1:11211"], :timeout => nil, :gzip => false)
|
136
|
+
cache.add('a', 1, 120)
|
137
|
+
without = xprofile 'get' do
|
138
|
+
1000.times do
|
139
|
+
cache.get('a')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
puts "1000 gets without socket timeout: #{without} sec"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_consistent_hashing
|
147
|
+
requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
|
148
|
+
|
149
|
+
flexmock(MemCache::Server).new_instances.should_receive(:alive?).and_return(true)
|
150
|
+
|
151
|
+
# Setup a continuum of two servers
|
152
|
+
@cache.servers = ['mike1', 'mike2', 'mike3']
|
153
|
+
|
154
|
+
keys = []
|
155
|
+
1000.times do |idx|
|
156
|
+
keys << idx.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
|
160
|
+
|
161
|
+
@cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
|
162
|
+
|
163
|
+
after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
|
164
|
+
|
165
|
+
same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
|
166
|
+
|
167
|
+
# With continuum, we should see about 75% of the keys map to the same server
|
168
|
+
# With modulo, we would see about 25%.
|
169
|
+
assert same_count > 700
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_get_multi_with_server_failure
|
174
|
+
@cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :gzip => false, :logger => nil #Logger.new(STDOUT)
|
175
|
+
s1 = FakeServer.new
|
176
|
+
s2 = FakeServer.new
|
177
|
+
|
178
|
+
# Write two messages to the socket to test failover
|
179
|
+
s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
|
180
|
+
s1.socket.data.rewind
|
181
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
182
|
+
s2.socket.data.rewind
|
183
|
+
|
184
|
+
@cache.servers = [s1, s2]
|
185
|
+
|
186
|
+
assert s1.alive?
|
187
|
+
assert s2.alive?
|
188
|
+
# a maps to s1, the rest map to s2
|
189
|
+
value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
|
190
|
+
assert_equal({'a'=>'0123456789'}, value)
|
191
|
+
assert s1.alive?
|
192
|
+
assert !s2.alive?
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_cache_get_with_failover
|
196
|
+
@cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :gzip => false, :logger => nil#Logger.new(STDOUT)
|
197
|
+
s1 = FakeServer.new
|
198
|
+
s2 = FakeServer.new
|
199
|
+
|
200
|
+
# Write two messages to the socket to test failover
|
201
|
+
s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
|
202
|
+
s1.socket.data.rewind
|
203
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
204
|
+
s2.socket.data.rewind
|
205
|
+
|
206
|
+
@cache.instance_variable_set(:@failover, true)
|
207
|
+
@cache.servers = [s1, s2]
|
208
|
+
|
209
|
+
assert s1.alive?
|
210
|
+
assert s2.alive?
|
211
|
+
@cache.get('foo')
|
212
|
+
assert s1.alive?
|
213
|
+
assert !s2.alive?
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_cache_get_without_failover
|
217
|
+
s1 = FakeServer.new
|
218
|
+
s2 = FakeServer.new
|
219
|
+
|
220
|
+
s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
|
221
|
+
s1.socket.data.rewind
|
222
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
223
|
+
s2.socket.data.rewind
|
224
|
+
|
225
|
+
@cache.instance_variable_set(:@failover, false)
|
226
|
+
@cache.servers = [s1, s2]
|
227
|
+
|
228
|
+
assert s1.alive?
|
229
|
+
assert s2.alive?
|
230
|
+
e = assert_raise MemCache::MemCacheError do
|
231
|
+
@cache.get('foo')
|
232
|
+
end
|
233
|
+
assert s1.alive?
|
234
|
+
assert !s2.alive?
|
235
|
+
|
236
|
+
assert_equal "No servers available", e.message
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_cache_get
|
240
|
+
server = util_setup_fake_server
|
241
|
+
|
242
|
+
assert_equal "\004\b\"\0170123456789",
|
243
|
+
@cache.cache_get(server, 'my_namespace:key')
|
244
|
+
|
245
|
+
assert_equal "get my_namespace:key\r\n",
|
246
|
+
server.socket.written.string
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_cache_get_EOF
|
250
|
+
server = util_setup_fake_server
|
251
|
+
server.socket.data.string = ''
|
252
|
+
|
253
|
+
e = assert_raise IndexError do
|
254
|
+
@cache.cache_get server, 'my_namespace:key'
|
255
|
+
end
|
256
|
+
|
257
|
+
assert_equal "No connection to server (NOT CONNECTED)", e.message
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_cache_get_bad_state
|
261
|
+
server = FakeServer.new
|
262
|
+
|
263
|
+
# Write two messages to the socket to test failover
|
264
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
265
|
+
server.socket.data.rewind
|
266
|
+
|
267
|
+
@cache.servers = []
|
268
|
+
@cache.servers << server
|
269
|
+
|
270
|
+
e = assert_raise IndexError do
|
271
|
+
@cache.cache_get(server, 'my_namespace:key')
|
272
|
+
end
|
273
|
+
|
274
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
275
|
+
|
276
|
+
assert !server.alive?
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_cache_get_miss
|
280
|
+
socket = FakeSocket.new
|
281
|
+
socket.data.write "END\r\n"
|
282
|
+
socket.data.rewind
|
283
|
+
server = FakeServer.new socket
|
284
|
+
|
285
|
+
assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
|
286
|
+
|
287
|
+
assert_equal "get my_namespace:key\r\n",
|
288
|
+
socket.written.string
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_cache_get_multi
|
292
|
+
server = util_setup_fake_server
|
293
|
+
server.socket.data.write "VALUE foo 0 7\r\n"
|
294
|
+
server.socket.data.write "\004\b\"\bfoo\r\n"
|
295
|
+
server.socket.data.write "VALUE bar 0 7\r\n"
|
296
|
+
server.socket.data.write "\004\b\"\bbar\r\n"
|
297
|
+
server.socket.data.write "END\r\n"
|
298
|
+
server.socket.data.rewind
|
299
|
+
|
300
|
+
result = @cache.cache_get_multi server, 'foo bar baz'
|
301
|
+
|
302
|
+
assert_equal 2, result.length
|
303
|
+
assert_equal "\004\b\"\bfoo", result['foo']
|
304
|
+
assert_equal "\004\b\"\bbar", result['bar']
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_cache_get_multi_EOF
|
308
|
+
server = util_setup_fake_server
|
309
|
+
server.socket.data.string = ''
|
310
|
+
|
311
|
+
e = assert_raise IndexError do
|
312
|
+
@cache.cache_get_multi server, 'my_namespace:key'
|
313
|
+
end
|
314
|
+
|
315
|
+
assert_equal "No connection to server (NOT CONNECTED)", e.message
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_cache_get_multi_bad_state
|
319
|
+
server = FakeServer.new
|
320
|
+
|
321
|
+
# Write two messages to the socket to test failover
|
322
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
323
|
+
server.socket.data.rewind
|
324
|
+
|
325
|
+
@cache.servers = []
|
326
|
+
@cache.servers << server
|
327
|
+
|
328
|
+
e = assert_raise IndexError do
|
329
|
+
@cache.cache_get_multi server, 'my_namespace:key'
|
330
|
+
end
|
331
|
+
|
332
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
333
|
+
|
334
|
+
assert !server.alive?
|
335
|
+
end
|
336
|
+
|
337
|
+
def test_multithread_error
|
338
|
+
server = FakeServer.new
|
339
|
+
server.multithread = false
|
340
|
+
|
341
|
+
@cache = MemCache.new(['localhost:1'], :multithread => false, :gzip => false)
|
342
|
+
|
343
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
344
|
+
server.socket.data.rewind
|
345
|
+
|
346
|
+
@cache.servers = []
|
347
|
+
@cache.servers << server
|
348
|
+
|
349
|
+
assert_nothing_raised do
|
350
|
+
@cache.set 'a', 1
|
351
|
+
end
|
352
|
+
|
353
|
+
passed = true
|
354
|
+
Thread.new do
|
355
|
+
begin
|
356
|
+
@cache.set 'b', 2
|
357
|
+
passed = false
|
358
|
+
rescue MemCache::MemCacheError => me
|
359
|
+
passed = me.message =~ /multiple threads/
|
360
|
+
end
|
361
|
+
end
|
362
|
+
assert passed
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_initialize
|
366
|
+
cache = MemCache.new :namespace => 'my_namespace', :readonly => true, :gzip => false
|
367
|
+
|
368
|
+
assert_equal 'my_namespace', cache.namespace
|
369
|
+
assert_equal true, cache.readonly?
|
370
|
+
assert_equal true, cache.servers.empty?
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_initialize_compatible
|
374
|
+
cache = MemCache.new ['localhost:11211', 'localhost:11212'],
|
375
|
+
:namespace => 'my_namespace', :readonly => true
|
376
|
+
|
377
|
+
assert_equal 'my_namespace', cache.namespace
|
378
|
+
assert_equal true, cache.readonly?
|
379
|
+
assert_equal false, cache.servers.empty?
|
380
|
+
end
|
381
|
+
|
382
|
+
def test_initialize_compatible_no_hash
|
383
|
+
cache = MemCache.new ['localhost:11211', 'localhost:11212']
|
384
|
+
|
385
|
+
assert_equal nil, cache.namespace
|
386
|
+
assert_equal false, cache.readonly?
|
387
|
+
assert_equal false, cache.servers.empty?
|
388
|
+
end
|
389
|
+
|
390
|
+
def test_initialize_compatible_one_server
|
391
|
+
cache = MemCache.new 'localhost:11211'
|
392
|
+
|
393
|
+
assert_equal nil, cache.namespace
|
394
|
+
assert_equal false, cache.readonly?
|
395
|
+
assert_equal false, cache.servers.empty?
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_initialize_compatible_bad_arg
|
399
|
+
e = assert_raise ArgumentError do
|
400
|
+
cache = MemCache.new Object.new
|
401
|
+
end
|
402
|
+
|
403
|
+
assert_equal 'first argument must be Array, Hash or String', e.message
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_initialize_multiple_servers
|
407
|
+
cache = MemCache.new %w[localhost:11211 localhost:11212],
|
408
|
+
:namespace => 'my_namespace', :readonly => true
|
409
|
+
|
410
|
+
assert_equal 'my_namespace', cache.namespace
|
411
|
+
assert_equal true, cache.readonly?
|
412
|
+
assert_equal false, cache.servers.empty?
|
413
|
+
assert !cache.instance_variable_get(:@continuum).empty?
|
414
|
+
end
|
415
|
+
|
416
|
+
def test_initialize_too_many_args
|
417
|
+
assert_raises ArgumentError do
|
418
|
+
MemCache.new 1, 2, 3
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def test_decr
|
423
|
+
server = FakeServer.new
|
424
|
+
server.socket.data.write "5\r\n"
|
425
|
+
server.socket.data.rewind
|
426
|
+
|
427
|
+
@cache.servers = []
|
428
|
+
@cache.servers << server
|
429
|
+
|
430
|
+
value = @cache.decr 'key'
|
431
|
+
|
432
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
433
|
+
@cache.servers.first.socket.written.string
|
434
|
+
|
435
|
+
assert_equal 5, value
|
436
|
+
end
|
437
|
+
|
438
|
+
def test_decr_not_found
|
439
|
+
server = FakeServer.new
|
440
|
+
server.socket.data.write "NOT_FOUND\r\n"
|
441
|
+
server.socket.data.rewind
|
442
|
+
|
443
|
+
@cache.servers = []
|
444
|
+
@cache.servers << server
|
445
|
+
|
446
|
+
value = @cache.decr 'key'
|
447
|
+
|
448
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
449
|
+
@cache.servers.first.socket.written.string
|
450
|
+
|
451
|
+
assert_equal nil, value
|
452
|
+
end
|
453
|
+
|
454
|
+
def test_decr_space_padding
|
455
|
+
server = FakeServer.new
|
456
|
+
server.socket.data.write "5 \r\n"
|
457
|
+
server.socket.data.rewind
|
458
|
+
|
459
|
+
@cache.servers = []
|
460
|
+
@cache.servers << server
|
461
|
+
|
462
|
+
value = @cache.decr 'key'
|
463
|
+
|
464
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
465
|
+
@cache.servers.first.socket.written.string
|
466
|
+
|
467
|
+
assert_equal 5, value
|
468
|
+
end
|
469
|
+
|
470
|
+
def test_get
|
471
|
+
util_setup_fake_server
|
472
|
+
|
473
|
+
value = @cache.get 'key'
|
474
|
+
|
475
|
+
assert_equal "get my_namespace:key\r\n",
|
476
|
+
@cache.servers.first.socket.written.string
|
477
|
+
|
478
|
+
assert_equal '0123456789', value
|
479
|
+
end
|
480
|
+
|
481
|
+
def test_gzip_get
|
482
|
+
# Ugly mix of basic tests
|
483
|
+
requirement(memcached_running?, 'A real memcached server must be running for gzip testing') do
|
484
|
+
cache = MemCache.new 'localhost'
|
485
|
+
cache.set("key", "value")
|
486
|
+
assert_equal 'value', cache.get("key")
|
487
|
+
|
488
|
+
s = ''
|
489
|
+
1024.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
|
490
|
+
s = s*1024
|
491
|
+
cache.set("key", s)
|
492
|
+
assert_equal s, cache.get("key")
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
def test_fetch_without_a_block
|
497
|
+
server = FakeServer.new
|
498
|
+
server.socket.data.write "END\r\n"
|
499
|
+
server.socket.data.rewind
|
500
|
+
|
501
|
+
@cache.servers = [server]
|
502
|
+
|
503
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
|
504
|
+
|
505
|
+
value = @cache.fetch('key', 1)
|
506
|
+
assert_equal nil, value
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_fetch_miss
|
510
|
+
server = FakeServer.new
|
511
|
+
server.socket.data.write "END\r\n"
|
512
|
+
server.socket.data.rewind
|
513
|
+
|
514
|
+
@cache.servers = [server]
|
515
|
+
|
516
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
|
517
|
+
flexmock(@cache).should_receive(:add).with('key', 'value', 1, false)
|
518
|
+
|
519
|
+
value = @cache.fetch('key', 1) { 'value' }
|
520
|
+
|
521
|
+
assert_equal 'value', value
|
522
|
+
end
|
523
|
+
|
524
|
+
def test_fetch_hit
|
525
|
+
server = FakeServer.new
|
526
|
+
server.socket.data.write "END\r\n"
|
527
|
+
server.socket.data.rewind
|
528
|
+
|
529
|
+
@cache.servers = [server]
|
530
|
+
|
531
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return('value')
|
532
|
+
flexmock(@cache).should_receive(:add).never
|
533
|
+
|
534
|
+
value = @cache.fetch('key', 1) { raise 'Should not be called.' }
|
535
|
+
|
536
|
+
assert_equal 'value', value
|
537
|
+
end
|
538
|
+
|
539
|
+
def test_get_bad_key
|
540
|
+
util_setup_fake_server
|
541
|
+
assert_raise ArgumentError do @cache.get 'k y' end
|
542
|
+
|
543
|
+
util_setup_fake_server
|
544
|
+
assert_raise ArgumentError do @cache.get 'k' * 250 end
|
545
|
+
end
|
546
|
+
|
547
|
+
def test_get_cache_get_IOError
|
548
|
+
socket = Object.new
|
549
|
+
def socket.write(arg) raise IOError, 'some io error'; end
|
550
|
+
server = FakeServer.new socket
|
551
|
+
|
552
|
+
@cache.servers = []
|
553
|
+
@cache.servers << server
|
554
|
+
|
555
|
+
e = assert_raise MemCache::MemCacheError do
|
556
|
+
@cache.get 'my_namespace:key'
|
557
|
+
end
|
558
|
+
|
559
|
+
assert_equal 'some io error', e.message
|
560
|
+
end
|
561
|
+
|
562
|
+
def test_get_cache_get_SystemCallError
|
563
|
+
socket = Object.new
|
564
|
+
def socket.write(arg) raise SystemCallError, 'some syscall error'; end
|
565
|
+
server = FakeServer.new socket
|
566
|
+
|
567
|
+
@cache.servers = []
|
568
|
+
@cache.servers << server
|
569
|
+
|
570
|
+
e = assert_raise MemCache::MemCacheError do
|
571
|
+
@cache.get 'my_namespace:key'
|
572
|
+
end
|
573
|
+
|
574
|
+
assert_equal 'unknown error - some syscall error', e.message
|
575
|
+
end
|
576
|
+
|
577
|
+
def test_get_no_connection
|
578
|
+
@cache.servers = 'localhost:1'
|
579
|
+
e = assert_raise MemCache::MemCacheError do
|
580
|
+
@cache.get 'key'
|
581
|
+
end
|
582
|
+
|
583
|
+
assert_match(/^No connection to server/, e.message)
|
584
|
+
end
|
585
|
+
|
586
|
+
def test_get_no_servers
|
587
|
+
@cache.servers = []
|
588
|
+
e = assert_raise MemCache::MemCacheError do
|
589
|
+
@cache.get 'key'
|
590
|
+
end
|
591
|
+
|
592
|
+
assert_equal 'No active servers', e.message
|
593
|
+
end
|
594
|
+
|
595
|
+
def test_get_multi
|
596
|
+
server = FakeServer.new
|
597
|
+
server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
|
598
|
+
server.socket.data.write "\004\b\"\0170123456789\r\n"
|
599
|
+
server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
|
600
|
+
server.socket.data.write "\004\b\"\0179876543210\r\n"
|
601
|
+
server.socket.data.write "END\r\n"
|
602
|
+
server.socket.data.rewind
|
603
|
+
|
604
|
+
@cache.servers = []
|
605
|
+
@cache.servers << server
|
606
|
+
|
607
|
+
values = @cache.get_multi 'key', 'keyb'
|
608
|
+
|
609
|
+
assert_equal "get my_namespace:key my_namespace:keyb\r\n",
|
610
|
+
server.socket.written.string
|
611
|
+
|
612
|
+
expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
|
613
|
+
|
614
|
+
assert_equal expected.sort, values.sort
|
615
|
+
end
|
616
|
+
|
617
|
+
def test_get_raw
|
618
|
+
server = FakeServer.new
|
619
|
+
server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
|
620
|
+
server.socket.data.write "0123456789\r\n"
|
621
|
+
server.socket.data.write "END\r\n"
|
622
|
+
server.socket.data.rewind
|
623
|
+
|
624
|
+
@cache.servers = []
|
625
|
+
@cache.servers << server
|
626
|
+
|
627
|
+
|
628
|
+
value = @cache.get 'key', true
|
629
|
+
|
630
|
+
assert_equal "get my_namespace:key\r\n",
|
631
|
+
@cache.servers.first.socket.written.string
|
632
|
+
|
633
|
+
assert_equal '0123456789', value
|
634
|
+
end
|
635
|
+
|
636
|
+
def test_get_server_for_key
|
637
|
+
server = @cache.get_server_for_key 'key'
|
638
|
+
assert_equal 'localhost', server.host
|
639
|
+
assert_equal 1, server.port
|
640
|
+
end
|
641
|
+
|
642
|
+
def test_get_server_for_key_multiple
|
643
|
+
s1 = util_setup_server @cache, 'one.example.com', ''
|
644
|
+
s2 = util_setup_server @cache, 'two.example.com', ''
|
645
|
+
@cache.servers = [s1, s2]
|
646
|
+
|
647
|
+
server = @cache.get_server_for_key 'keya'
|
648
|
+
assert_equal 'two.example.com', server.host
|
649
|
+
server = @cache.get_server_for_key 'keyb'
|
650
|
+
assert_equal 'two.example.com', server.host
|
651
|
+
server = @cache.get_server_for_key 'keyc'
|
652
|
+
assert_equal 'two.example.com', server.host
|
653
|
+
server = @cache.get_server_for_key 'keyd'
|
654
|
+
assert_equal 'one.example.com', server.host
|
655
|
+
end
|
656
|
+
|
657
|
+
def test_get_server_for_key_no_servers
|
658
|
+
@cache.servers = []
|
659
|
+
|
660
|
+
e = assert_raise MemCache::MemCacheError do
|
661
|
+
@cache.get_server_for_key 'key'
|
662
|
+
end
|
663
|
+
|
664
|
+
assert_equal 'No servers available', e.message
|
665
|
+
end
|
666
|
+
|
667
|
+
def test_get_server_for_key_spaces
|
668
|
+
e = assert_raise ArgumentError do
|
669
|
+
@cache.get_server_for_key 'space key'
|
670
|
+
end
|
671
|
+
assert_equal 'illegal character in key "space key"', e.message
|
672
|
+
end
|
673
|
+
|
674
|
+
def test_get_server_for_key_length
|
675
|
+
@cache.get_server_for_key 'x' * 250
|
676
|
+
long_key = 'x' * 251
|
677
|
+
e = assert_raise ArgumentError do
|
678
|
+
@cache.get_server_for_key long_key
|
679
|
+
end
|
680
|
+
assert_equal "key too long #{long_key.inspect}", e.message
|
681
|
+
end
|
682
|
+
|
683
|
+
def test_incr
|
684
|
+
server = FakeServer.new
|
685
|
+
server.socket.data.write "5\r\n"
|
686
|
+
server.socket.data.rewind
|
687
|
+
|
688
|
+
@cache.servers = []
|
689
|
+
@cache.servers << server
|
690
|
+
|
691
|
+
value = @cache.incr 'key'
|
692
|
+
|
693
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
694
|
+
@cache.servers.first.socket.written.string
|
695
|
+
|
696
|
+
assert_equal 5, value
|
697
|
+
end
|
698
|
+
|
699
|
+
def test_incr_not_found
|
700
|
+
server = FakeServer.new
|
701
|
+
server.socket.data.write "NOT_FOUND\r\n"
|
702
|
+
server.socket.data.rewind
|
703
|
+
|
704
|
+
@cache.servers = []
|
705
|
+
@cache.servers << server
|
706
|
+
|
707
|
+
value = @cache.incr 'key'
|
708
|
+
|
709
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
710
|
+
@cache.servers.first.socket.written.string
|
711
|
+
|
712
|
+
assert_equal nil, value
|
713
|
+
end
|
714
|
+
|
715
|
+
def test_incr_space_padding
|
716
|
+
server = FakeServer.new
|
717
|
+
server.socket.data.write "5 \r\n"
|
718
|
+
server.socket.data.rewind
|
719
|
+
|
720
|
+
@cache.servers = []
|
721
|
+
@cache.servers << server
|
722
|
+
|
723
|
+
value = @cache.incr 'key'
|
724
|
+
|
725
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
726
|
+
@cache.servers.first.socket.written.string
|
727
|
+
|
728
|
+
assert_equal 5, value
|
729
|
+
end
|
730
|
+
|
731
|
+
def test_make_cache_key
|
732
|
+
assert_equal 'my_namespace:key', @cache.make_cache_key('key')
|
733
|
+
@cache.namespace = nil
|
734
|
+
assert_equal 'key', @cache.make_cache_key('key')
|
735
|
+
end
|
736
|
+
|
737
|
+
def test_make_cache_key_without_autofix
|
738
|
+
@cache.autofix_keys = false
|
739
|
+
|
740
|
+
key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
|
741
|
+
hash = Digest::SHA1.hexdigest(key)
|
742
|
+
@cache.namespace = nil
|
743
|
+
assert_equal key, @cache.make_cache_key(key)
|
744
|
+
|
745
|
+
@cache.autofix_keys = true
|
746
|
+
|
747
|
+
@cache.namespace = "my_namespace"
|
748
|
+
assert_equal 'my_namespace:key', @cache.make_cache_key('key')
|
749
|
+
@cache.namespace = nil
|
750
|
+
assert_equal 'key', @cache.make_cache_key('key')
|
751
|
+
|
752
|
+
key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
|
753
|
+
hash = Digest::SHA1.hexdigest(key)
|
754
|
+
@cache.namespace = "my_namespace"
|
755
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
756
|
+
@cache.namespace = nil
|
757
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
758
|
+
|
759
|
+
key = "a short key with spaces"
|
760
|
+
hash = Digest::SHA1.hexdigest(key)
|
761
|
+
@cache.namespace = "my_namespace"
|
762
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
763
|
+
@cache.namespace = nil
|
764
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
765
|
+
end
|
766
|
+
|
767
|
+
def test_servers
|
768
|
+
server = FakeServer.new
|
769
|
+
@cache.servers = []
|
770
|
+
@cache.servers << server
|
771
|
+
assert_equal [server], @cache.servers
|
772
|
+
end
|
773
|
+
|
774
|
+
def test_set
|
775
|
+
server = FakeServer.new
|
776
|
+
server.socket.data.write "STORED\r\n"
|
777
|
+
server.socket.data.rewind
|
778
|
+
@cache.servers = []
|
779
|
+
@cache.servers << server
|
780
|
+
|
781
|
+
@cache.set 'key', 'value'
|
782
|
+
|
783
|
+
dumped = Marshal.dump('value')
|
784
|
+
expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
785
|
+
# expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
|
786
|
+
assert_equal expected, server.socket.written.string
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_set_expiry
|
790
|
+
server = FakeServer.new
|
791
|
+
server.socket.data.write "STORED\r\n"
|
792
|
+
server.socket.data.rewind
|
793
|
+
@cache.servers = []
|
794
|
+
@cache.servers << server
|
795
|
+
|
796
|
+
@cache.set 'key', 'value', 5
|
797
|
+
|
798
|
+
dumped = Marshal.dump('value')
|
799
|
+
expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
|
800
|
+
assert_equal expected, server.socket.written.string
|
801
|
+
end
|
802
|
+
|
803
|
+
def test_set_raw
|
804
|
+
server = FakeServer.new
|
805
|
+
server.socket.data.write "STORED\r\n"
|
806
|
+
server.socket.data.rewind
|
807
|
+
@cache.servers = []
|
808
|
+
@cache.servers << server
|
809
|
+
|
810
|
+
@cache.set 'key', 'value', 0, true
|
811
|
+
|
812
|
+
expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
|
813
|
+
assert_equal expected, server.socket.written.string
|
814
|
+
end
|
815
|
+
|
816
|
+
def test_set_readonly
|
817
|
+
cache = MemCache.new :readonly => true
|
818
|
+
|
819
|
+
e = assert_raise MemCache::MemCacheError do
|
820
|
+
cache.set 'key', 'value'
|
821
|
+
end
|
822
|
+
|
823
|
+
assert_equal 'Update of readonly cache', e.message
|
824
|
+
end
|
825
|
+
|
826
|
+
def test_check_size_on
|
827
|
+
cache = MemCache.new :check_size => true, :gzip => false
|
828
|
+
|
829
|
+
server = FakeServer.new
|
830
|
+
server.socket.data.write "STORED\r\n"
|
831
|
+
server.socket.data.rewind
|
832
|
+
|
833
|
+
cache.servers = []
|
834
|
+
cache.servers << server
|
835
|
+
|
836
|
+
e = assert_raise MemCache::MemCacheError do
|
837
|
+
cache.set 'key', 'v' * 1048577
|
838
|
+
end
|
839
|
+
|
840
|
+
assert_equal 'Value too large, memcached can only store 1MB of data per key', e.message
|
841
|
+
end
|
842
|
+
|
843
|
+
def test_check_size_off
|
844
|
+
cache = MemCache.new :check_size => false
|
845
|
+
|
846
|
+
server = FakeServer.new
|
847
|
+
server.socket.data.write "STORED\r\n"
|
848
|
+
server.socket.data.rewind
|
849
|
+
|
850
|
+
cache.servers = []
|
851
|
+
cache.servers << server
|
852
|
+
|
853
|
+
assert_nothing_raised do
|
854
|
+
cache.set 'key', 'v' * 1048577
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
def test_set_too_big
|
859
|
+
server = FakeServer.new
|
860
|
+
|
861
|
+
# Write two messages to the socket to test failover
|
862
|
+
server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
|
863
|
+
server.socket.data.rewind
|
864
|
+
|
865
|
+
@cache.servers = []
|
866
|
+
@cache.servers << server
|
867
|
+
|
868
|
+
e = assert_raise MemCache::MemCacheError do
|
869
|
+
@cache.set 'key', 'v'
|
870
|
+
end
|
871
|
+
|
872
|
+
assert_match(/object too large for cache/, e.message)
|
873
|
+
end
|
874
|
+
|
875
|
+
def test_prepend
|
876
|
+
server = FakeServer.new
|
877
|
+
server.socket.data.write "STORED\r\n"
|
878
|
+
server.socket.data.rewind
|
879
|
+
@cache.servers = []
|
880
|
+
@cache.servers << server
|
881
|
+
|
882
|
+
@cache.prepend 'key', 'value'
|
883
|
+
|
884
|
+
dumped = Marshal.dump('value')
|
885
|
+
|
886
|
+
expected = "prepend my_namespace:key 0 0 5\r\nvalue\r\n"
|
887
|
+
assert_equal expected, server.socket.written.string
|
888
|
+
end
|
889
|
+
|
890
|
+
def test_append
|
891
|
+
server = FakeServer.new
|
892
|
+
server.socket.data.write "STORED\r\n"
|
893
|
+
server.socket.data.rewind
|
894
|
+
@cache.servers = []
|
895
|
+
@cache.servers << server
|
896
|
+
|
897
|
+
@cache.append 'key', 'value'
|
898
|
+
|
899
|
+
expected = "append my_namespace:key 0 0 5\r\nvalue\r\n"
|
900
|
+
assert_equal expected, server.socket.written.string
|
901
|
+
end
|
902
|
+
|
903
|
+
def test_replace
|
904
|
+
server = FakeServer.new
|
905
|
+
server.socket.data.write "STORED\r\n"
|
906
|
+
server.socket.data.rewind
|
907
|
+
@cache.servers = []
|
908
|
+
@cache.servers << server
|
909
|
+
|
910
|
+
@cache.replace 'key', 'value', 150
|
911
|
+
|
912
|
+
dumped = Marshal.dump('value')
|
913
|
+
|
914
|
+
expected = "replace my_namespace:key 0 150 #{dumped.length}\r\n#{dumped}\r\n"
|
915
|
+
assert_equal expected, server.socket.written.string
|
916
|
+
end
|
917
|
+
|
918
|
+
def test_add
|
919
|
+
server = FakeServer.new
|
920
|
+
server.socket.data.write "STORED\r\n"
|
921
|
+
server.socket.data.rewind
|
922
|
+
@cache.servers = []
|
923
|
+
@cache.servers << server
|
924
|
+
|
925
|
+
@cache.add 'key', 'value'
|
926
|
+
|
927
|
+
dumped = Marshal.dump('value')
|
928
|
+
|
929
|
+
expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
930
|
+
assert_equal expected, server.socket.written.string
|
931
|
+
end
|
932
|
+
|
933
|
+
def test_add_exists
|
934
|
+
server = FakeServer.new
|
935
|
+
server.socket.data.write "NOT_STORED\r\n"
|
936
|
+
server.socket.data.rewind
|
937
|
+
@cache.servers = []
|
938
|
+
@cache.servers << server
|
939
|
+
|
940
|
+
@cache.add 'key', 'value'
|
941
|
+
|
942
|
+
dumped = Marshal.dump('value')
|
943
|
+
expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
944
|
+
assert_equal expected, server.socket.written.string
|
945
|
+
end
|
946
|
+
|
947
|
+
def test_add_expiry
|
948
|
+
server = FakeServer.new
|
949
|
+
server.socket.data.write "STORED\r\n"
|
950
|
+
server.socket.data.rewind
|
951
|
+
@cache.servers = []
|
952
|
+
@cache.servers << server
|
953
|
+
|
954
|
+
@cache.add 'key', 'value', 5
|
955
|
+
|
956
|
+
dumped = Marshal.dump('value')
|
957
|
+
expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
|
958
|
+
assert_equal expected, server.socket.written.string
|
959
|
+
end
|
960
|
+
|
961
|
+
def test_add_raw
|
962
|
+
server = FakeServer.new
|
963
|
+
server.socket.data.write "STORED\r\n"
|
964
|
+
server.socket.data.rewind
|
965
|
+
@cache.servers = []
|
966
|
+
@cache.servers << server
|
967
|
+
|
968
|
+
@cache.add 'key', 'value', 0, true
|
969
|
+
|
970
|
+
expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
|
971
|
+
assert_equal expected, server.socket.written.string
|
972
|
+
end
|
973
|
+
|
974
|
+
def test_add_raw_int
|
975
|
+
server = FakeServer.new
|
976
|
+
server.socket.data.write "STORED\r\n"
|
977
|
+
server.socket.data.rewind
|
978
|
+
@cache.servers = []
|
979
|
+
@cache.servers << server
|
980
|
+
|
981
|
+
@cache.add 'key', 12, 0, true
|
982
|
+
|
983
|
+
expected = "add my_namespace:key 0 0 2\r\n12\r\n"
|
984
|
+
assert_equal expected, server.socket.written.string
|
985
|
+
end
|
986
|
+
|
987
|
+
def test_add_readonly
|
988
|
+
cache = MemCache.new :readonly => true
|
989
|
+
|
990
|
+
e = assert_raise MemCache::MemCacheError do
|
991
|
+
cache.add 'key', 'value'
|
992
|
+
end
|
993
|
+
|
994
|
+
assert_equal 'Update of readonly cache', e.message
|
995
|
+
end
|
996
|
+
|
997
|
+
def test_delete
|
998
|
+
server = FakeServer.new
|
999
|
+
@cache.servers = []
|
1000
|
+
@cache.servers << server
|
1001
|
+
|
1002
|
+
@cache.delete 'key'
|
1003
|
+
|
1004
|
+
expected = "delete my_namespace:key 0\r\n"
|
1005
|
+
assert_equal expected, server.socket.written.string
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def test_delete_with_expiry
|
1009
|
+
server = FakeServer.new
|
1010
|
+
@cache.servers = []
|
1011
|
+
@cache.servers << server
|
1012
|
+
|
1013
|
+
@cache.delete 'key', 300
|
1014
|
+
|
1015
|
+
expected = "delete my_namespace:key 300\r\n"
|
1016
|
+
assert_equal expected, server.socket.written.string
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def test_flush_all
|
1020
|
+
@cache.servers = []
|
1021
|
+
3.times { @cache.servers << FakeServer.new }
|
1022
|
+
|
1023
|
+
@cache.flush_all
|
1024
|
+
|
1025
|
+
expected = "flush_all\r\n"
|
1026
|
+
@cache.servers.each do |server|
|
1027
|
+
assert_equal expected, server.socket.written.string
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def test_flush_all_with_delay
|
1032
|
+
@cache.servers = []
|
1033
|
+
3.times { @cache.servers << FakeServer.new }
|
1034
|
+
|
1035
|
+
@cache.flush_all(10)
|
1036
|
+
|
1037
|
+
@cache.servers.each_with_index do |server, idx|
|
1038
|
+
expected = "flush_all #{idx*10}\r\n"
|
1039
|
+
assert_equal expected, server.socket.written.string
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
def test_flush_all_failure
|
1044
|
+
socket = FakeSocket.new
|
1045
|
+
|
1046
|
+
# Write two messages to the socket to test failover
|
1047
|
+
socket.data.write "ERROR\r\nERROR\r\n"
|
1048
|
+
socket.data.rewind
|
1049
|
+
|
1050
|
+
server = FakeServer.new socket
|
1051
|
+
|
1052
|
+
@cache.servers = []
|
1053
|
+
@cache.servers << server
|
1054
|
+
|
1055
|
+
assert_raise MemCache::MemCacheError do
|
1056
|
+
@cache.flush_all
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
assert_match(/flush_all\r\n/, socket.written.string)
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
def test_flush_all_for_real
|
1063
|
+
requirement(memcached_running?, 'A real memcached server must be running for testing flush_all') do
|
1064
|
+
cache = MemCache.new "localhost:11211", :namespace => "test_flush_all"
|
1065
|
+
k, v = "1234", "test"
|
1066
|
+
assert_nil cache.get(k)
|
1067
|
+
cache.set(k, v)
|
1068
|
+
assert_equal v, cache.get(k)
|
1069
|
+
cache.flush_all
|
1070
|
+
assert_nil cache.get(k)
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
def test_stats
|
1075
|
+
socket = FakeSocket.new
|
1076
|
+
socket.data.write "STAT pid 20188\r\nSTAT total_items 32\r\nSTAT version 1.2.3\r\nSTAT rusage_user 1:300\r\nSTAT dummy ok\r\nEND\r\n"
|
1077
|
+
socket.data.rewind
|
1078
|
+
server = FakeServer.new socket
|
1079
|
+
def server.host() 'localhost'; end
|
1080
|
+
def server.port() 11211; end
|
1081
|
+
|
1082
|
+
@cache.servers = []
|
1083
|
+
@cache.servers << server
|
1084
|
+
|
1085
|
+
expected = {
|
1086
|
+
'localhost:11211' => {
|
1087
|
+
'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
|
1088
|
+
'rusage_user' => 1.0003, 'dummy' => 'ok'
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
assert_equal expected, @cache.stats
|
1092
|
+
|
1093
|
+
assert_equal "stats\r\n", socket.written.string
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def test_basic_threaded_operations_should_work
|
1097
|
+
cache = MemCache.new :multithread => true,
|
1098
|
+
:namespace => 'my_namespace',
|
1099
|
+
:readonly => false,
|
1100
|
+
:gzip => false
|
1101
|
+
|
1102
|
+
server = FakeServer.new
|
1103
|
+
server.socket.data.write "STORED\r\n"
|
1104
|
+
server.socket.data.rewind
|
1105
|
+
|
1106
|
+
cache.servers = []
|
1107
|
+
cache.servers << server
|
1108
|
+
|
1109
|
+
assert cache.multithread
|
1110
|
+
|
1111
|
+
assert_nothing_raised do
|
1112
|
+
cache.set "test", "test value"
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
output = server.socket.written.string
|
1116
|
+
assert_match(/set my_namespace:test/, output)
|
1117
|
+
assert_match(/test value/, output)
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
def test_namespace_separator
|
1121
|
+
cache = MemCache.new :namespace => 'ns', :namespace_separator => '', :gzip => false
|
1122
|
+
|
1123
|
+
server = FakeServer.new
|
1124
|
+
server.socket.data.write "STORED\r\n"
|
1125
|
+
server.socket.data.rewind
|
1126
|
+
|
1127
|
+
cache.servers = []
|
1128
|
+
cache.servers << server
|
1129
|
+
|
1130
|
+
assert_nothing_raised do
|
1131
|
+
cache.set "test", "test value"
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
output = server.socket.written.string
|
1135
|
+
assert_match(/set nstest/, output)
|
1136
|
+
assert_match(/test value/, output)
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def test_basic_unthreaded_operations_should_work
|
1140
|
+
cache = MemCache.new :multithread => false,
|
1141
|
+
:namespace => 'my_namespace',
|
1142
|
+
:readonly => false,
|
1143
|
+
:gzip => false
|
1144
|
+
|
1145
|
+
server = FakeServer.new
|
1146
|
+
server.socket.data.write "STORED\r\n"
|
1147
|
+
server.socket.data.rewind
|
1148
|
+
|
1149
|
+
cache.servers = []
|
1150
|
+
cache.servers << server
|
1151
|
+
|
1152
|
+
assert !cache.multithread
|
1153
|
+
|
1154
|
+
assert_nothing_raised do
|
1155
|
+
cache.set "test", "test value"
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
output = server.socket.written.string
|
1159
|
+
assert_match(/set my_namespace:test/, output)
|
1160
|
+
assert_match(/test value/, output)
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
def util_setup_fake_server
|
1164
|
+
server = FakeServer.new
|
1165
|
+
server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
|
1166
|
+
server.socket.data.write "\004\b\"\0170123456789\r\n"
|
1167
|
+
server.socket.data.write "END\r\n"
|
1168
|
+
server.socket.data.rewind
|
1169
|
+
|
1170
|
+
@cache.servers = []
|
1171
|
+
@cache.servers << server
|
1172
|
+
|
1173
|
+
return server
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
def util_setup_server(memcache, host, responses)
|
1177
|
+
server = MemCache::Server.new memcache, host
|
1178
|
+
server.instance_variable_set :@sock, StringIO.new(responses)
|
1179
|
+
|
1180
|
+
@cache.servers = []
|
1181
|
+
@cache.servers << server
|
1182
|
+
|
1183
|
+
return server
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
def test_crazy_multithreaded_access
|
1187
|
+
requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
|
1188
|
+
|
1189
|
+
# Use a null logger to verify logging doesn't blow up at runtime
|
1190
|
+
cache = MemCache.new(['localhost:11211', '127.0.0.1:11211'], :logger => Logger.new('/dev/null'))
|
1191
|
+
cache.flush_all
|
1192
|
+
workers = []
|
1193
|
+
|
1194
|
+
cache.set('f', 'zzz')
|
1195
|
+
assert_equal "STORED\r\n", (cache.cas('f') do |value|
|
1196
|
+
value << 'z'
|
1197
|
+
end)
|
1198
|
+
assert_equal 'zzzz', cache.get('f')
|
1199
|
+
|
1200
|
+
# Have a bunch of threads perform a bunch of operations at the same time.
|
1201
|
+
# Verify the result of each operation to ensure the request and response
|
1202
|
+
# are not intermingled between threads.
|
1203
|
+
10.times do
|
1204
|
+
workers << Thread.new do
|
1205
|
+
100.times do
|
1206
|
+
cache.set('a', 9)
|
1207
|
+
cache.set('b', 11)
|
1208
|
+
cache.add('c', 10, 0, true)
|
1209
|
+
cache.set('d', 'a', 100, true)
|
1210
|
+
cache.set('e', 'x', 100, true)
|
1211
|
+
cache.set('f', 'zzz')
|
1212
|
+
assert_not_nil(cache.cas('f') do |value|
|
1213
|
+
value << 'z'
|
1214
|
+
end)
|
1215
|
+
cache.append('d', 'b')
|
1216
|
+
cache.prepend('e', 'y')
|
1217
|
+
assert_equal "NOT_STORED\r\n", cache.add('a', 11)
|
1218
|
+
assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
|
1219
|
+
inc = cache.incr('c', 10)
|
1220
|
+
assert_equal 0, inc % 5
|
1221
|
+
assert inc > 14
|
1222
|
+
assert cache.decr('c', 5) > 14
|
1223
|
+
assert_equal 11, cache.get('b')
|
1224
|
+
d = cache.get('d', true)
|
1225
|
+
assert_match(/\Aab*\Z/, d)
|
1226
|
+
e = cache.get('e', true)
|
1227
|
+
assert_match(/\Ay*x\Z/, e)
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
workers.each { |w| w.join }
|
1233
|
+
cache.flush_all
|
1234
|
+
end
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
end
|
1238
|
+
|