memcache-client 1.5.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/memcache_util.rb CHANGED
@@ -3,7 +3,19 @@
3
3
  # methods silently ignore MemCache errors.
4
4
 
5
5
  module Cache
6
-
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
7
19
  ##
8
20
  # Returns the object at +key+ from the cache if successful, or nil if either
9
21
  # the object is not in the cache or if there was an error attermpting to
@@ -17,14 +29,14 @@ module Cache
17
29
  start_time = Time.now
18
30
  value = CACHE.get key
19
31
  elapsed = Time.now - start_time
20
- ActiveRecord::Base.logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
32
+ logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
21
33
  if value.nil? and block_given? then
22
34
  value = yield
23
35
  add key, value, expiry
24
36
  end
25
37
  value
26
38
  rescue MemCache::MemCacheError => err
27
- ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
39
+ logger.debug "MemCache Error: #{err.message}"
28
40
  if block_given? then
29
41
  value = yield
30
42
  put key, value, expiry
@@ -40,7 +52,7 @@ module Cache
40
52
  start_time = Time.now
41
53
  CACHE.set key, value, expiry
42
54
  elapsed = Time.now - start_time
43
- ActiveRecord::Base.logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
55
+ logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
44
56
  value
45
57
  rescue MemCache::MemCacheError => err
46
58
  ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
@@ -55,7 +67,7 @@ module Cache
55
67
  start_time = Time.now
56
68
  response = CACHE.add key, value, expiry
57
69
  elapsed = Time.now - start_time
58
- ActiveRecord::Base.logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
70
+ logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
59
71
  (response == "STORED\r\n") ? value : nil
60
72
  rescue MemCache::MemCacheError => err
61
73
  ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
@@ -69,11 +81,11 @@ module Cache
69
81
  start_time = Time.now
70
82
  CACHE.delete key, delay
71
83
  elapsed = Time.now - start_time
72
- ActiveRecord::Base.logger.debug('MemCache Delete (%0.6f) %s' %
84
+ logger.debug('MemCache Delete (%0.6f) %s' %
73
85
  [elapsed, key])
74
86
  nil
75
87
  rescue MemCache::MemCacheError => err
76
- ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
88
+ logger.debug "MemCache Error: #{err.message}"
77
89
  nil
78
90
  end
79
91
 
@@ -82,7 +94,7 @@ module Cache
82
94
 
83
95
  def self.reset
84
96
  CACHE.reset
85
- ActiveRecord::Base.logger.debug 'MemCache Connections Reset'
97
+ logger.debug 'MemCache Connections Reset'
86
98
  nil
87
99
  end
88
100
 
@@ -1,11 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'logger'
1
3
  require 'stringio'
2
4
  require 'test/unit'
3
5
  require 'rubygems'
4
- require 'test/zentest_assertions'
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
5
12
 
6
13
  $TESTING = true
7
14
 
8
- require 'memcache'
15
+ require File.dirname(__FILE__) + '/../lib/memcache'
9
16
 
10
17
  class MemCache
11
18
 
@@ -36,22 +43,67 @@ class FakeSocket
36
43
 
37
44
  end
38
45
 
46
+ class Test::Unit::TestCase
47
+ def requirement(bool, msg)
48
+ if bool
49
+ yield
50
+ else
51
+ puts msg
52
+ assert true
53
+ end
54
+ end
55
+
56
+ def memcached_running?
57
+ TCPSocket.new('localhost', 11211) rescue false
58
+ end
59
+
60
+ def xprofile(name, &block)
61
+ a = Time.now
62
+ block.call
63
+ Time.now - a
64
+ end
65
+
66
+ def profile(name, &block)
67
+ require 'ruby-prof'
68
+ a = Time.now
69
+ result = RubyProf.profile(&block)
70
+ time = Time.now - a
71
+ printer = RubyProf::GraphHtmlPrinter.new(result)
72
+ File.open("#{name}.html", 'w') do |f|
73
+ printer.print(f, :min_percent=>1)
74
+ end
75
+ time
76
+ end
77
+
78
+ end
79
+
39
80
  class FakeServer
40
81
 
41
- attr_reader :host, :port, :socket
82
+ attr_reader :host, :port, :socket, :weight, :multithread, :status
42
83
 
43
84
  def initialize(socket = nil)
44
85
  @closed = false
45
86
  @host = 'example.com'
46
87
  @port = 11211
47
88
  @socket = socket || FakeSocket.new
89
+ @weight = 1
90
+ @multithread = false
91
+ @status = "CONNECTED"
48
92
  end
49
93
 
50
94
  def close
95
+ # begin
96
+ # raise "Already closed"
97
+ # rescue => e
98
+ # puts e.backtrace.join("\n")
99
+ # end
51
100
  @closed = true
101
+ @socket = nil
102
+ @status = "NOT CONNECTED"
52
103
  end
53
104
 
54
105
  def alive?
106
+ # puts "I'm #{@closed ? 'dead' : 'alive'}"
55
107
  !@closed
56
108
  end
57
109
 
@@ -63,6 +115,126 @@ class TestMemCache < Test::Unit::TestCase
63
115
  @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace'
64
116
  end
65
117
 
118
+ def test_performance
119
+ requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
120
+ host = Socket.gethostname
121
+
122
+ cache = MemCache.new(['localhost:11211',"#{host}:11211"])
123
+ cache.add('a', 1, 120)
124
+ with = xprofile 'get' do
125
+ 1000.times do
126
+ cache.get('a')
127
+ end
128
+ end
129
+ puts ''
130
+ puts "1000 gets with socket timeout: #{with} sec"
131
+
132
+ cache = MemCache.new(['localhost:11211',"#{host}:11211"], :timeout => nil)
133
+ cache.add('a', 1, 120)
134
+ without = xprofile 'get' do
135
+ 1000.times do
136
+ cache.get('a')
137
+ end
138
+ end
139
+ puts "1000 gets without socket timeout: #{without} sec"
140
+
141
+ assert without < with
142
+ end
143
+ end
144
+
145
+ def test_consistent_hashing
146
+ requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
147
+
148
+ flexmock(MemCache::Server).new_instances.should_receive(:alive?).and_return(true)
149
+
150
+ # Setup a continuum of two servers
151
+ @cache.servers = ['mike1', 'mike2', 'mike3']
152
+
153
+ keys = []
154
+ 1000.times do |idx|
155
+ keys << idx.to_s
156
+ end
157
+
158
+ before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
159
+
160
+ @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
161
+
162
+ after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
163
+
164
+ same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
165
+
166
+ # With continuum, we should see about 75% of the keys map to the same server
167
+ # With modulo, we would see about 25%.
168
+ assert same_count > 700
169
+ end
170
+ end
171
+
172
+ def test_get_multi_with_server_failure
173
+ @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil #Logger.new(STDOUT)
174
+ s1 = FakeServer.new
175
+ s2 = FakeServer.new
176
+
177
+ # Write two messages to the socket to test failover
178
+ s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
179
+ s1.socket.data.rewind
180
+ s2.socket.data.write "bogus response\r\nbogus response\r\n"
181
+ s2.socket.data.rewind
182
+
183
+ @cache.servers = [s1, s2]
184
+
185
+ assert s1.alive?
186
+ assert s2.alive?
187
+ # a maps to s1, the rest map to s2
188
+ value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
189
+ assert_equal({'a'=>'0123456789'}, value)
190
+ assert s1.alive?
191
+ assert !s2.alive?
192
+ end
193
+
194
+ def test_cache_get_with_failover
195
+ @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
196
+ s1 = FakeServer.new
197
+ s2 = FakeServer.new
198
+
199
+ # Write two messages to the socket to test failover
200
+ s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
201
+ s1.socket.data.rewind
202
+ s2.socket.data.write "bogus response\r\nbogus response\r\n"
203
+ s2.socket.data.rewind
204
+
205
+ @cache.instance_variable_set(:@failover, true)
206
+ @cache.servers = [s1, s2]
207
+
208
+ assert s1.alive?
209
+ assert s2.alive?
210
+ @cache.get('foo')
211
+ assert s1.alive?
212
+ assert !s2.alive?
213
+ end
214
+
215
+ def test_cache_get_without_failover
216
+ s1 = FakeServer.new
217
+ s2 = FakeServer.new
218
+
219
+ s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
220
+ s1.socket.data.rewind
221
+ s2.socket.data.write "bogus response\r\nbogus response\r\n"
222
+ s2.socket.data.rewind
223
+
224
+ @cache.instance_variable_set(:@failover, false)
225
+ @cache.servers = [s1, s2]
226
+
227
+ assert s1.alive?
228
+ assert s2.alive?
229
+ e = assert_raise MemCache::MemCacheError do
230
+ @cache.get('foo')
231
+ end
232
+ assert s1.alive?
233
+ assert !s2.alive?
234
+
235
+ assert_equal "No servers available", e.message
236
+ end
237
+
66
238
  def test_cache_get
67
239
  server = util_setup_fake_server
68
240
 
@@ -77,31 +249,30 @@ class TestMemCache < Test::Unit::TestCase
77
249
  server = util_setup_fake_server
78
250
  server.socket.data.string = ''
79
251
 
80
- e = assert_raise MemCache::MemCacheError do
252
+ e = assert_raise IndexError do
81
253
  @cache.cache_get server, 'my_namespace:key'
82
254
  end
83
255
 
84
- assert_equal "lost connection to example.com:11211", e.message
256
+ assert_equal "No connection to server (NOT CONNECTED)", e.message
85
257
  end
86
258
 
87
259
  def test_cache_get_bad_state
88
260
  server = FakeServer.new
89
- server.socket.data.write "bogus response\r\n"
261
+
262
+ # Write two messages to the socket to test failover
263
+ server.socket.data.write "bogus response\r\nbogus response\r\n"
90
264
  server.socket.data.rewind
91
265
 
92
266
  @cache.servers = []
93
267
  @cache.servers << server
94
268
 
95
- e = assert_raise MemCache::MemCacheError do
269
+ e = assert_raise IndexError do
96
270
  @cache.cache_get(server, 'my_namespace:key')
97
271
  end
98
272
 
99
- assert_equal "unexpected response \"bogus response\\r\\n\"", e.message
100
-
101
- deny server.alive?
273
+ assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
102
274
 
103
- assert_equal "get my_namespace:key\r\n",
104
- server.socket.written.string
275
+ assert !server.alive?
105
276
  end
106
277
 
107
278
  def test_cache_get_miss
@@ -136,36 +307,30 @@ class TestMemCache < Test::Unit::TestCase
136
307
  server = util_setup_fake_server
137
308
  server.socket.data.string = ''
138
309
 
139
- e = assert_raise MemCache::MemCacheError do
310
+ e = assert_raise IndexError do
140
311
  @cache.cache_get_multi server, 'my_namespace:key'
141
312
  end
142
313
 
143
- assert_equal "lost connection to example.com:11211", e.message
314
+ assert_equal "No connection to server (NOT CONNECTED)", e.message
144
315
  end
145
316
 
146
317
  def test_cache_get_multi_bad_state
147
318
  server = FakeServer.new
148
- server.socket.data.write "bogus response\r\n"
319
+
320
+ # Write two messages to the socket to test failover
321
+ server.socket.data.write "bogus response\r\nbogus response\r\n"
149
322
  server.socket.data.rewind
150
323
 
151
324
  @cache.servers = []
152
325
  @cache.servers << server
153
326
 
154
- e = assert_raise MemCache::MemCacheError do
327
+ e = assert_raise IndexError do
155
328
  @cache.cache_get_multi server, 'my_namespace:key'
156
329
  end
157
330
 
158
- assert_equal "unexpected response \"bogus response\\r\\n\"", e.message
159
-
160
- deny server.alive?
161
-
162
- assert_equal "get my_namespace:key\r\n",
163
- server.socket.written.string
164
- end
331
+ assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
165
332
 
166
- def test_crc32_ITU_T
167
- assert_equal 0, ''.crc32_ITU_T
168
- assert_equal 1260851911, 'my_namespace:key'.crc32_ITU_T
333
+ assert !server.alive?
169
334
  end
170
335
 
171
336
  def test_initialize
@@ -216,7 +381,7 @@ class TestMemCache < Test::Unit::TestCase
216
381
  assert_equal 'my_namespace', cache.namespace
217
382
  assert_equal true, cache.readonly?
218
383
  assert_equal false, cache.servers.empty?
219
- deny_empty cache.instance_variable_get(:@buckets)
384
+ assert !cache.instance_variable_get(:@continuum).empty?
220
385
  end
221
386
 
222
387
  def test_initialize_too_many_args
@@ -328,7 +493,7 @@ class TestMemCache < Test::Unit::TestCase
328
493
  @cache.get 'key'
329
494
  end
330
495
 
331
- assert_equal 'No connection to server', e.message
496
+ assert_match /^No connection to server/, e.message
332
497
  end
333
498
 
334
499
  def test_get_no_servers
@@ -390,12 +555,15 @@ class TestMemCache < Test::Unit::TestCase
390
555
  def test_get_server_for_key_multiple
391
556
  s1 = util_setup_server @cache, 'one.example.com', ''
392
557
  s2 = util_setup_server @cache, 'two.example.com', ''
393
- @cache.instance_variable_set :@servers, [s1, s2]
394
- @cache.instance_variable_set :@buckets, [s1, s2]
558
+ @cache.servers = [s1, s2]
395
559
 
396
560
  server = @cache.get_server_for_key 'keya'
397
561
  assert_equal 'two.example.com', server.host
398
562
  server = @cache.get_server_for_key 'keyb'
563
+ assert_equal 'two.example.com', server.host
564
+ server = @cache.get_server_for_key 'keyc'
565
+ assert_equal 'two.example.com', server.host
566
+ server = @cache.get_server_for_key 'keyd'
399
567
  assert_equal 'one.example.com', server.host
400
568
  end
401
569
 
@@ -486,14 +654,6 @@ class TestMemCache < Test::Unit::TestCase
486
654
  assert_equal [server], @cache.servers
487
655
  end
488
656
 
489
- def test_servers_equals_type_error
490
- e = assert_raise TypeError do
491
- @cache.servers = [Object.new]
492
- end
493
-
494
- assert_equal 'cannot convert Object into MemCache::Server', e.message
495
- end
496
-
497
657
  def test_set
498
658
  server = FakeServer.new
499
659
  server.socket.data.write "STORED\r\n"
@@ -503,7 +663,9 @@ class TestMemCache < Test::Unit::TestCase
503
663
 
504
664
  @cache.set 'key', 'value'
505
665
 
506
- expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
666
+ dumped = Marshal.dump('value')
667
+ expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
668
+ # expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
507
669
  assert_equal expected, server.socket.written.string
508
670
  end
509
671
 
@@ -516,7 +678,8 @@ class TestMemCache < Test::Unit::TestCase
516
678
 
517
679
  @cache.set 'key', 'value', 5
518
680
 
519
- expected = "set my_namespace:key 0 5 9\r\n\004\b\"\nvalue\r\n"
681
+ dumped = Marshal.dump('value')
682
+ expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
520
683
  assert_equal expected, server.socket.written.string
521
684
  end
522
685
 
@@ -545,8 +708,11 @@ class TestMemCache < Test::Unit::TestCase
545
708
 
546
709
  def test_set_too_big
547
710
  server = FakeServer.new
548
- server.socket.data.write "SERVER_ERROR object too large for cache\r\n"
711
+
712
+ # Write two messages to the socket to test failover
713
+ server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
549
714
  server.socket.data.rewind
715
+
550
716
  @cache.servers = []
551
717
  @cache.servers << server
552
718
 
@@ -554,7 +720,7 @@ class TestMemCache < Test::Unit::TestCase
554
720
  @cache.set 'key', 'v'
555
721
  end
556
722
 
557
- assert_equal 'object too large for cache', e.message
723
+ assert_match /object too large for cache/, e.message
558
724
  end
559
725
 
560
726
  def test_add
@@ -565,8 +731,10 @@ class TestMemCache < Test::Unit::TestCase
565
731
  @cache.servers << server
566
732
 
567
733
  @cache.add 'key', 'value'
734
+
735
+ dumped = Marshal.dump('value')
568
736
 
569
- expected = "add my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
737
+ expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
570
738
  assert_equal expected, server.socket.written.string
571
739
  end
572
740
 
@@ -579,7 +747,8 @@ class TestMemCache < Test::Unit::TestCase
579
747
 
580
748
  @cache.add 'key', 'value'
581
749
 
582
- expected = "add my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
750
+ dumped = Marshal.dump('value')
751
+ expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
583
752
  assert_equal expected, server.socket.written.string
584
753
  end
585
754
 
@@ -592,7 +761,8 @@ class TestMemCache < Test::Unit::TestCase
592
761
 
593
762
  @cache.add 'key', 'value', 5
594
763
 
595
- expected = "add my_namespace:key 0 5 9\r\n\004\b\"\nvalue\r\n"
764
+ dumped = Marshal.dump('value')
765
+ expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
596
766
  assert_equal expected, server.socket.written.string
597
767
  end
598
768
 
@@ -609,6 +779,19 @@ class TestMemCache < Test::Unit::TestCase
609
779
  assert_equal expected, server.socket.written.string
610
780
  end
611
781
 
782
+ def test_add_raw_int
783
+ server = FakeServer.new
784
+ server.socket.data.write "STORED\r\n"
785
+ server.socket.data.rewind
786
+ @cache.servers = []
787
+ @cache.servers << server
788
+
789
+ @cache.add 'key', 12, 0, true
790
+
791
+ expected = "add my_namespace:key 0 0 2\r\n12\r\n"
792
+ assert_equal expected, server.socket.written.string
793
+ end
794
+
612
795
  def test_add_readonly
613
796
  cache = MemCache.new :readonly => true
614
797
 
@@ -655,11 +838,12 @@ class TestMemCache < Test::Unit::TestCase
655
838
 
656
839
  def test_flush_all_failure
657
840
  socket = FakeSocket.new
658
- socket.data.write "ERROR\r\n"
841
+
842
+ # Write two messages to the socket to test failover
843
+ socket.data.write "ERROR\r\nERROR\r\n"
659
844
  socket.data.rewind
845
+
660
846
  server = FakeServer.new socket
661
- def server.host() "localhost"; end
662
- def server.port() 11211; end
663
847
 
664
848
  @cache.servers = []
665
849
  @cache.servers << server
@@ -668,7 +852,7 @@ class TestMemCache < Test::Unit::TestCase
668
852
  @cache.flush_all
669
853
  end
670
854
 
671
- assert_equal "flush_all\r\n", socket.written.string
855
+ assert_match /flush_all\r\n/, socket.written.string
672
856
  end
673
857
 
674
858
  def test_stats
@@ -697,24 +881,46 @@ class TestMemCache < Test::Unit::TestCase
697
881
  cache = MemCache.new :multithread => true,
698
882
  :namespace => 'my_namespace',
699
883
  :readonly => false
700
- server = util_setup_server cache, 'example.com', "OK\r\n"
701
- cache.instance_variable_set :@servers, [server]
884
+
885
+ server = FakeServer.new
886
+ server.socket.data.write "STORED\r\n"
887
+ server.socket.data.rewind
888
+
889
+ cache.servers = []
890
+ cache.servers << server
891
+
892
+ assert cache.multithread
702
893
 
703
894
  assert_nothing_raised do
704
895
  cache.set "test", "test value"
705
896
  end
897
+
898
+ output = server.socket.written.string
899
+ assert_match /set my_namespace:test/, output
900
+ assert_match /test value/, output
706
901
  end
707
902
 
708
903
  def test_basic_unthreaded_operations_should_work
709
904
  cache = MemCache.new :multithread => false,
710
905
  :namespace => 'my_namespace',
711
906
  :readonly => false
712
- server = util_setup_server cache, 'example.com', "OK\r\n"
713
- cache.instance_variable_set :@servers, [server]
907
+
908
+ server = FakeServer.new
909
+ server.socket.data.write "STORED\r\n"
910
+ server.socket.data.rewind
911
+
912
+ cache.servers = []
913
+ cache.servers << server
914
+
915
+ assert !cache.multithread
714
916
 
715
917
  assert_nothing_raised do
716
918
  cache.set "test", "test value"
717
919
  end
920
+
921
+ output = server.socket.written.string
922
+ assert_match /set my_namespace:test/, output
923
+ assert_match /test value/, output
718
924
  end
719
925
 
720
926
  def util_setup_fake_server