brontes3d-memcache-client 1.7.4.1

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