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.
@@ -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
+