dsander-memcache-client 1.7.7.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+