carldr-memcache-client 1.7.0

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,102 @@
1
+ ##
2
+ # A utility wrapper around the MemCache client to simplify cache access. All
3
+ # methods silently ignore MemCache errors.
4
+
5
+ module Cache
6
+
7
+ ##
8
+ # Try to return a logger object that does not rely
9
+ # on ActiveRecord for logging.
10
+ def self.logger
11
+ @logger ||= if defined? Rails.logger # Rails 2.1 +
12
+ Rails.logger
13
+ elsif defined? RAILS_DEFAULT_LOGGER # Rails 1.2.2 +
14
+ RAILS_DEFAULT_LOGGER
15
+ else
16
+ ActiveRecord::Base.logger # ... very old Rails.
17
+ end
18
+ end
19
+ ##
20
+ # Returns the object at +key+ from the cache if successful, or nil if either
21
+ # the object is not in the cache or if there was an error attermpting to
22
+ # access the cache.
23
+ #
24
+ # If there is a cache miss and a block is given the result of the block will
25
+ # be stored in the cache with optional +expiry+, using the +add+ method rather
26
+ # than +set+.
27
+
28
+ def self.get(key, expiry = 0)
29
+ start_time = Time.now
30
+ value = CACHE.get key
31
+ elapsed = Time.now - start_time
32
+ logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
33
+ if value.nil? and block_given? then
34
+ value = yield
35
+ add key, value, expiry
36
+ end
37
+ value
38
+ rescue MemCache::MemCacheError => err
39
+ logger.debug "MemCache Error: #{err.message}"
40
+ if block_given? then
41
+ value = yield
42
+ put key, value, expiry
43
+ end
44
+ value
45
+ end
46
+
47
+ ##
48
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
49
+ # seconds.
50
+
51
+ def self.put(key, value, expiry = 0)
52
+ start_time = Time.now
53
+ CACHE.set key, value, expiry
54
+ elapsed = Time.now - start_time
55
+ logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
56
+ value
57
+ rescue MemCache::MemCacheError => err
58
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
59
+ nil
60
+ end
61
+
62
+ ##
63
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
64
+ # seconds. If +key+ already exists in cache, returns nil.
65
+
66
+ def self.add(key, value, expiry = 0)
67
+ start_time = Time.now
68
+ response = CACHE.add key, value, expiry
69
+ elapsed = Time.now - start_time
70
+ logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
71
+ (response == "STORED\r\n") ? value : nil
72
+ rescue MemCache::MemCacheError => err
73
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
74
+ nil
75
+ end
76
+
77
+ ##
78
+ # Deletes +key+ from the cache in +delay+ seconds.
79
+
80
+ def self.delete(key, delay = nil)
81
+ start_time = Time.now
82
+ CACHE.delete key, delay
83
+ elapsed = Time.now - start_time
84
+ logger.debug('MemCache Delete (%0.6f) %s' %
85
+ [elapsed, key])
86
+ nil
87
+ rescue MemCache::MemCacheError => err
88
+ logger.debug "MemCache Error: #{err.message}"
89
+ nil
90
+ end
91
+
92
+ ##
93
+ # Resets all connections to MemCache servers.
94
+
95
+ def self.reset
96
+ CACHE.reset
97
+ logger.debug 'MemCache Connections Reset'
98
+ nil
99
+ end
100
+
101
+ end
102
+
@@ -0,0 +1,1127 @@
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
+
142
+ assert without < with
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', :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', :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)
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
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_fetch_without_a_block
482
+ server = FakeServer.new
483
+ server.socket.data.write "END\r\n"
484
+ server.socket.data.rewind
485
+
486
+ @cache.servers = [server]
487
+
488
+ flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
489
+
490
+ value = @cache.fetch('key', 1)
491
+ assert_equal nil, value
492
+ end
493
+
494
+ def test_fetch_miss
495
+ server = FakeServer.new
496
+ server.socket.data.write "END\r\n"
497
+ server.socket.data.rewind
498
+
499
+ @cache.servers = [server]
500
+
501
+ flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
502
+ flexmock(@cache).should_receive(:add).with('key', 'value', 1, false)
503
+
504
+ value = @cache.fetch('key', 1) { 'value' }
505
+
506
+ assert_equal 'value', value
507
+ end
508
+
509
+ def test_fetch_hit
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('value')
517
+ flexmock(@cache).should_receive(:add).never
518
+
519
+ value = @cache.fetch('key', 1) { raise 'Should not be called.' }
520
+
521
+ assert_equal 'value', value
522
+ end
523
+
524
+ def test_get_bad_key
525
+ util_setup_fake_server
526
+ assert_raise ArgumentError do @cache.get 'k y' end
527
+
528
+ util_setup_fake_server
529
+ assert_raise ArgumentError do @cache.get 'k' * 250 end
530
+ end
531
+
532
+ def test_get_cache_get_IOError
533
+ socket = Object.new
534
+ def socket.write(arg) raise IOError, 'some io error'; end
535
+ server = FakeServer.new socket
536
+
537
+ @cache.servers = []
538
+ @cache.servers << server
539
+
540
+ e = assert_raise MemCache::MemCacheError do
541
+ @cache.get 'my_namespace:key'
542
+ end
543
+
544
+ assert_equal 'some io error', e.message
545
+ end
546
+
547
+ def test_get_cache_get_SystemCallError
548
+ socket = Object.new
549
+ def socket.write(arg) raise SystemCallError, 'some syscall 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 'unknown error - some syscall error', e.message
560
+ end
561
+
562
+ def test_get_no_connection
563
+ @cache.servers = 'localhost:1'
564
+ e = assert_raise MemCache::MemCacheError do
565
+ @cache.get 'key'
566
+ end
567
+
568
+ assert_match /^No connection to server/, e.message
569
+ end
570
+
571
+ def test_get_no_servers
572
+ @cache.servers = []
573
+ e = assert_raise MemCache::MemCacheError do
574
+ @cache.get 'key'
575
+ end
576
+
577
+ assert_equal 'No active servers', e.message
578
+ end
579
+
580
+ def test_get_multi
581
+ server = FakeServer.new
582
+ server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
583
+ server.socket.data.write "\004\b\"\0170123456789\r\n"
584
+ server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
585
+ server.socket.data.write "\004\b\"\0179876543210\r\n"
586
+ server.socket.data.write "END\r\n"
587
+ server.socket.data.rewind
588
+
589
+ @cache.servers = []
590
+ @cache.servers << server
591
+
592
+ values = @cache.get_multi 'key', 'keyb'
593
+
594
+ assert_equal "get my_namespace:key my_namespace:keyb\r\n",
595
+ server.socket.written.string
596
+
597
+ expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
598
+
599
+ assert_equal expected.sort, values.sort
600
+ end
601
+
602
+ def test_get_raw
603
+ server = FakeServer.new
604
+ server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
605
+ server.socket.data.write "0123456789\r\n"
606
+ server.socket.data.write "END\r\n"
607
+ server.socket.data.rewind
608
+
609
+ @cache.servers = []
610
+ @cache.servers << server
611
+
612
+
613
+ value = @cache.get 'key', true
614
+
615
+ assert_equal "get my_namespace:key\r\n",
616
+ @cache.servers.first.socket.written.string
617
+
618
+ assert_equal '0123456789', value
619
+ end
620
+
621
+ def test_get_server_for_key
622
+ server = @cache.get_server_for_key 'key'
623
+ assert_equal 'localhost', server.host
624
+ assert_equal 1, server.port
625
+ end
626
+
627
+ def test_get_server_for_key_multiple
628
+ s1 = util_setup_server @cache, 'one.example.com', ''
629
+ s2 = util_setup_server @cache, 'two.example.com', ''
630
+ @cache.servers = [s1, s2]
631
+
632
+ server = @cache.get_server_for_key 'keya'
633
+ assert_equal 'two.example.com', server.host
634
+ server = @cache.get_server_for_key 'keyb'
635
+ assert_equal 'two.example.com', server.host
636
+ server = @cache.get_server_for_key 'keyc'
637
+ assert_equal 'two.example.com', server.host
638
+ server = @cache.get_server_for_key 'keyd'
639
+ assert_equal 'one.example.com', server.host
640
+ end
641
+
642
+ def test_get_server_for_key_no_servers
643
+ @cache.servers = []
644
+
645
+ e = assert_raise MemCache::MemCacheError do
646
+ @cache.get_server_for_key 'key'
647
+ end
648
+
649
+ assert_equal 'No servers available', e.message
650
+ end
651
+
652
+ def test_get_server_for_key_spaces
653
+ e = assert_raise ArgumentError do
654
+ @cache.get_server_for_key 'space key'
655
+ end
656
+ assert_equal 'illegal character in key "space key"', e.message
657
+ end
658
+
659
+ def test_get_server_for_key_length
660
+ @cache.get_server_for_key 'x' * 250
661
+ long_key = 'x' * 251
662
+ e = assert_raise ArgumentError do
663
+ @cache.get_server_for_key long_key
664
+ end
665
+ assert_equal "key too long #{long_key.inspect}", e.message
666
+ end
667
+
668
+ def test_incr
669
+ server = FakeServer.new
670
+ server.socket.data.write "5\r\n"
671
+ server.socket.data.rewind
672
+
673
+ @cache.servers = []
674
+ @cache.servers << server
675
+
676
+ value = @cache.incr 'key'
677
+
678
+ assert_equal "incr my_namespace:key 1\r\n",
679
+ @cache.servers.first.socket.written.string
680
+
681
+ assert_equal 5, value
682
+ end
683
+
684
+ def test_incr_not_found
685
+ server = FakeServer.new
686
+ server.socket.data.write "NOT_FOUND\r\n"
687
+ server.socket.data.rewind
688
+
689
+ @cache.servers = []
690
+ @cache.servers << server
691
+
692
+ value = @cache.incr 'key'
693
+
694
+ assert_equal "incr my_namespace:key 1\r\n",
695
+ @cache.servers.first.socket.written.string
696
+
697
+ assert_equal nil, value
698
+ end
699
+
700
+ def test_incr_space_padding
701
+ server = FakeServer.new
702
+ server.socket.data.write "5 \r\n"
703
+ server.socket.data.rewind
704
+
705
+ @cache.servers = []
706
+ @cache.servers << server
707
+
708
+ value = @cache.incr 'key'
709
+
710
+ assert_equal "incr my_namespace:key 1\r\n",
711
+ @cache.servers.first.socket.written.string
712
+
713
+ assert_equal 5, value
714
+ end
715
+
716
+ def test_make_cache_key
717
+ assert_equal 'my_namespace:key', @cache.make_cache_key('key')
718
+ @cache.namespace = nil
719
+ assert_equal 'key', @cache.make_cache_key('key')
720
+ end
721
+
722
+ def test_servers
723
+ server = FakeServer.new
724
+ @cache.servers = []
725
+ @cache.servers << server
726
+ assert_equal [server], @cache.servers
727
+ end
728
+
729
+ def test_set
730
+ server = FakeServer.new
731
+ server.socket.data.write "STORED\r\n"
732
+ server.socket.data.rewind
733
+ @cache.servers = []
734
+ @cache.servers << server
735
+
736
+ @cache.set 'key', 'value'
737
+
738
+ dumped = Marshal.dump('value')
739
+ expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
740
+ # expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
741
+ assert_equal expected, server.socket.written.string
742
+ end
743
+
744
+ def test_set_expiry
745
+ server = FakeServer.new
746
+ server.socket.data.write "STORED\r\n"
747
+ server.socket.data.rewind
748
+ @cache.servers = []
749
+ @cache.servers << server
750
+
751
+ @cache.set 'key', 'value', 5
752
+
753
+ dumped = Marshal.dump('value')
754
+ expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
755
+ assert_equal expected, server.socket.written.string
756
+ end
757
+
758
+ def test_set_raw
759
+ server = FakeServer.new
760
+ server.socket.data.write "STORED\r\n"
761
+ server.socket.data.rewind
762
+ @cache.servers = []
763
+ @cache.servers << server
764
+
765
+ @cache.set 'key', 'value', 0, true
766
+
767
+ expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
768
+ assert_equal expected, server.socket.written.string
769
+ end
770
+
771
+ def test_set_readonly
772
+ cache = MemCache.new :readonly => true
773
+
774
+ e = assert_raise MemCache::MemCacheError do
775
+ cache.set 'key', 'value'
776
+ end
777
+
778
+ assert_equal 'Update of readonly cache', e.message
779
+ end
780
+
781
+ def test_set_too_big
782
+ server = FakeServer.new
783
+
784
+ # Write two messages to the socket to test failover
785
+ server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
786
+ server.socket.data.rewind
787
+
788
+ @cache.servers = []
789
+ @cache.servers << server
790
+
791
+ e = assert_raise MemCache::MemCacheError do
792
+ @cache.set 'key', 'v'
793
+ end
794
+
795
+ assert_match /object too large for cache/, e.message
796
+ end
797
+
798
+ def test_prepend
799
+ server = FakeServer.new
800
+ server.socket.data.write "STORED\r\n"
801
+ server.socket.data.rewind
802
+ @cache.servers = []
803
+ @cache.servers << server
804
+
805
+ @cache.prepend 'key', 'value'
806
+
807
+ dumped = Marshal.dump('value')
808
+
809
+ expected = "prepend my_namespace:key 0 0 5\r\nvalue\r\n"
810
+ assert_equal expected, server.socket.written.string
811
+ end
812
+
813
+ def test_append
814
+ server = FakeServer.new
815
+ server.socket.data.write "STORED\r\n"
816
+ server.socket.data.rewind
817
+ @cache.servers = []
818
+ @cache.servers << server
819
+
820
+ @cache.append 'key', 'value'
821
+
822
+ expected = "append my_namespace:key 0 0 5\r\nvalue\r\n"
823
+ assert_equal expected, server.socket.written.string
824
+ end
825
+
826
+ def test_replace
827
+ server = FakeServer.new
828
+ server.socket.data.write "STORED\r\n"
829
+ server.socket.data.rewind
830
+ @cache.servers = []
831
+ @cache.servers << server
832
+
833
+ @cache.replace 'key', 'value', 150
834
+
835
+ dumped = Marshal.dump('value')
836
+
837
+ expected = "replace my_namespace:key 0 150 #{dumped.length}\r\n#{dumped}\r\n"
838
+ assert_equal expected, server.socket.written.string
839
+ end
840
+
841
+ def test_add
842
+ server = FakeServer.new
843
+ server.socket.data.write "STORED\r\n"
844
+ server.socket.data.rewind
845
+ @cache.servers = []
846
+ @cache.servers << server
847
+
848
+ @cache.add 'key', 'value'
849
+
850
+ dumped = Marshal.dump('value')
851
+
852
+ expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
853
+ assert_equal expected, server.socket.written.string
854
+ end
855
+
856
+ def test_add_exists
857
+ server = FakeServer.new
858
+ server.socket.data.write "NOT_STORED\r\n"
859
+ server.socket.data.rewind
860
+ @cache.servers = []
861
+ @cache.servers << server
862
+
863
+ @cache.add 'key', 'value'
864
+
865
+ dumped = Marshal.dump('value')
866
+ expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
867
+ assert_equal expected, server.socket.written.string
868
+ end
869
+
870
+ def test_add_expiry
871
+ server = FakeServer.new
872
+ server.socket.data.write "STORED\r\n"
873
+ server.socket.data.rewind
874
+ @cache.servers = []
875
+ @cache.servers << server
876
+
877
+ @cache.add 'key', 'value', 5
878
+
879
+ dumped = Marshal.dump('value')
880
+ expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
881
+ assert_equal expected, server.socket.written.string
882
+ end
883
+
884
+ def test_add_raw
885
+ server = FakeServer.new
886
+ server.socket.data.write "STORED\r\n"
887
+ server.socket.data.rewind
888
+ @cache.servers = []
889
+ @cache.servers << server
890
+
891
+ @cache.add 'key', 'value', 0, true
892
+
893
+ expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
894
+ assert_equal expected, server.socket.written.string
895
+ end
896
+
897
+ def test_add_raw_int
898
+ server = FakeServer.new
899
+ server.socket.data.write "STORED\r\n"
900
+ server.socket.data.rewind
901
+ @cache.servers = []
902
+ @cache.servers << server
903
+
904
+ @cache.add 'key', 12, 0, true
905
+
906
+ expected = "add my_namespace:key 0 0 2\r\n12\r\n"
907
+ assert_equal expected, server.socket.written.string
908
+ end
909
+
910
+ def test_add_readonly
911
+ cache = MemCache.new :readonly => true
912
+
913
+ e = assert_raise MemCache::MemCacheError do
914
+ cache.add 'key', 'value'
915
+ end
916
+
917
+ assert_equal 'Update of readonly cache', e.message
918
+ end
919
+
920
+ def test_delete
921
+ server = FakeServer.new
922
+ @cache.servers = []
923
+ @cache.servers << server
924
+
925
+ @cache.delete 'key'
926
+
927
+ expected = "delete my_namespace:key 0\r\n"
928
+ assert_equal expected, server.socket.written.string
929
+ end
930
+
931
+ def test_delete_with_expiry
932
+ server = FakeServer.new
933
+ @cache.servers = []
934
+ @cache.servers << server
935
+
936
+ @cache.delete 'key', 300
937
+
938
+ expected = "delete my_namespace:key 300\r\n"
939
+ assert_equal expected, server.socket.written.string
940
+ end
941
+
942
+ def test_flush_all
943
+ @cache.servers = []
944
+ 3.times { @cache.servers << FakeServer.new }
945
+
946
+ @cache.flush_all
947
+
948
+ expected = "flush_all 0\r\n"
949
+ @cache.servers.each do |server|
950
+ assert_equal expected, server.socket.written.string
951
+ end
952
+ end
953
+
954
+ def test_flush_all_with_delay
955
+ @cache.servers = []
956
+ 3.times { @cache.servers << FakeServer.new }
957
+
958
+ @cache.flush_all(10)
959
+
960
+ @cache.servers.each_with_index do |server, idx|
961
+ expected = "flush_all #{idx*10}\r\n"
962
+ assert_equal expected, server.socket.written.string
963
+ end
964
+ end
965
+
966
+ def test_flush_all_failure
967
+ socket = FakeSocket.new
968
+
969
+ # Write two messages to the socket to test failover
970
+ socket.data.write "ERROR\r\nERROR\r\n"
971
+ socket.data.rewind
972
+
973
+ server = FakeServer.new socket
974
+
975
+ @cache.servers = []
976
+ @cache.servers << server
977
+
978
+ assert_raise MemCache::MemCacheError do
979
+ @cache.flush_all
980
+ end
981
+
982
+ assert_match /flush_all 0\r\n/, socket.written.string
983
+ end
984
+
985
+ def test_stats
986
+ socket = FakeSocket.new
987
+ 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"
988
+ socket.data.rewind
989
+ server = FakeServer.new socket
990
+ def server.host() 'localhost'; end
991
+ def server.port() 11211; end
992
+
993
+ @cache.servers = []
994
+ @cache.servers << server
995
+
996
+ expected = {
997
+ 'localhost:11211' => {
998
+ 'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
999
+ 'rusage_user' => 1.0003, 'dummy' => 'ok'
1000
+ }
1001
+ }
1002
+ assert_equal expected, @cache.stats
1003
+
1004
+ assert_equal "stats\r\n", socket.written.string
1005
+ end
1006
+
1007
+ def test_basic_threaded_operations_should_work
1008
+ cache = MemCache.new :multithread => true,
1009
+ :namespace => 'my_namespace',
1010
+ :readonly => false
1011
+
1012
+ server = FakeServer.new
1013
+ server.socket.data.write "STORED\r\n"
1014
+ server.socket.data.rewind
1015
+
1016
+ cache.servers = []
1017
+ cache.servers << server
1018
+
1019
+ assert cache.multithread
1020
+
1021
+ assert_nothing_raised do
1022
+ cache.set "test", "test value"
1023
+ end
1024
+
1025
+ output = server.socket.written.string
1026
+ assert_match /set my_namespace:test/, output
1027
+ assert_match /test value/, output
1028
+ end
1029
+
1030
+ def test_basic_unthreaded_operations_should_work
1031
+ cache = MemCache.new :multithread => false,
1032
+ :namespace => 'my_namespace',
1033
+ :readonly => false
1034
+
1035
+ server = FakeServer.new
1036
+ server.socket.data.write "STORED\r\n"
1037
+ server.socket.data.rewind
1038
+
1039
+ cache.servers = []
1040
+ cache.servers << server
1041
+
1042
+ assert !cache.multithread
1043
+
1044
+ assert_nothing_raised do
1045
+ cache.set "test", "test value"
1046
+ end
1047
+
1048
+ output = server.socket.written.string
1049
+ assert_match /set my_namespace:test/, output
1050
+ assert_match /test value/, output
1051
+ end
1052
+
1053
+ def util_setup_fake_server
1054
+ server = FakeServer.new
1055
+ server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
1056
+ server.socket.data.write "\004\b\"\0170123456789\r\n"
1057
+ server.socket.data.write "END\r\n"
1058
+ server.socket.data.rewind
1059
+
1060
+ @cache.servers = []
1061
+ @cache.servers << server
1062
+
1063
+ return server
1064
+ end
1065
+
1066
+ def util_setup_server(memcache, host, responses)
1067
+ server = MemCache::Server.new memcache, host
1068
+ server.instance_variable_set :@sock, StringIO.new(responses)
1069
+
1070
+ @cache.servers = []
1071
+ @cache.servers << server
1072
+
1073
+ return server
1074
+ end
1075
+
1076
+ def test_crazy_multithreaded_access
1077
+ requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
1078
+
1079
+ cache = MemCache.new(['localhost:11211', '127.0.0.1:11211'])
1080
+ cache.flush_all
1081
+ workers = []
1082
+
1083
+ cache.set('f', 'zzz')
1084
+ assert_equal "STORED\r\n", (cache.cas('f') do |value|
1085
+ value << 'z'
1086
+ end)
1087
+ assert_equal 'zzzz', cache.get('f')
1088
+
1089
+ # Have a bunch of threads perform a bunch of operations at the same time.
1090
+ # Verify the result of each operation to ensure the request and response
1091
+ # are not intermingled between threads.
1092
+ 10.times do
1093
+ workers << Thread.new do
1094
+ 100.times do
1095
+ cache.set('a', 9)
1096
+ cache.set('b', 11)
1097
+ cache.add('c', 10, 0, true)
1098
+ cache.set('d', 'a', 100, true)
1099
+ cache.set('e', 'x', 100, true)
1100
+ cache.set('f', 'zzz')
1101
+ assert_not_nil(cache.cas('f') do |value|
1102
+ value << 'z'
1103
+ end)
1104
+ cache.append('d', 'b')
1105
+ cache.prepend('e', 'y')
1106
+ assert_equal "NOT_STORED\r\n", cache.add('a', 11)
1107
+ assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
1108
+ inc = cache.incr('c', 10)
1109
+ assert_equal 0, inc % 5
1110
+ assert inc > 14
1111
+ assert cache.decr('c', 5) > 14
1112
+ assert_equal 11, cache.get('b')
1113
+ d = cache.get('d', true)
1114
+ assert_match /\Aab+\Z/, d
1115
+ e = cache.get('e', true)
1116
+ assert_match /\Ay+x\Z/, e
1117
+ end
1118
+ end
1119
+ end
1120
+
1121
+ workers.each { |w| w.join }
1122
+ cache.flush_all
1123
+ end
1124
+ end
1125
+
1126
+ end
1127
+