memcache-client 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,16 @@
1
+ = 1.2.0
2
+
3
+ NOTE: This version will store keys in different places than previous
4
+ versions! Be prepared for some thrashing while memcached sorts itself
5
+ out!
6
+
7
+ * Fixed multithreaded operations, bug 5994 and 5989.
8
+ Thanks to Blaine Cook, Erik Hetzner, Elliot Smith, Dave Myron (and
9
+ possibly others I have forgotten).
10
+ * Made memcached interoperable with other memcached libraries, bug
11
+ 4509. Thanks to anonymous.
12
+ * Added get_multi to match Perl/etc APIs
13
+
1
14
  = 1.1.0
2
15
 
3
16
  * Added some tests
@@ -1,5 +1,5 @@
1
- All original code copyright 2005 Bob Cottrell, The Robot Co-op. All rights
2
- reserved.
1
+ All original code copyright 2005, 2006 Bob Cottrell, Eric Hodel, The
2
+ Robot Co-op. All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
5
5
  modification, are permitted provided that the following conditions
@@ -14,7 +14,7 @@ are met:
14
14
  may be used to endorse or promote products derived from this software
15
15
  without specific prior written permission.
16
16
  4. Redistribution in Rails or any sub-projects of Rails is not allowed
17
- until Rails runs without warnings with the ``-W2'' flag enabled.
17
+ until Rails runs without warnings with the ``-w'' flag enabled.
18
18
 
19
19
  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20
20
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -5,4 +5,4 @@ README.txt
5
5
  Rakefile
6
6
  lib/memcache.rb
7
7
  lib/memcache_util.rb
8
- test/test_memcache.rb
8
+ test/test_mem_cache.rb
data/README.txt CHANGED
@@ -10,35 +10,37 @@ memcache-client is a fast memcached client.
10
10
 
11
11
  == Installing memcache-client
12
12
 
13
- Just install the gem
13
+ Just install the gem:
14
14
 
15
15
  $ sudo gem install memcache-client
16
16
 
17
17
  == Using memcache-client
18
18
 
19
- CACHE = MemCache.new :c_threshold => 10_000,
20
- :compression => true,
21
- :debug => false,
22
- :namespace => 'my_namespace',
23
- :readonly => false,
24
- :urlencode => false
25
- CACHE.servers = 'localhost:11211'
19
+ With one server:
26
20
 
27
- You can specify multiple servers by sending an Array of servers to #servers=.
21
+ CACHE = MemCache.new 'localhost:11211', :namespace => 'my_namespace'
22
+
23
+ Or with multiple servers:
24
+
25
+ CACHE = MemCache.new %w[one.example.com:11211 two.example.com:11211],
26
+ :namespace => 'my_namespace'
27
+
28
+ See MemCache.new for details.
28
29
 
29
30
  === Using memcache-client with Rails
30
31
 
31
- Rails will automatically load the memcache-client gem, but you may wish to
32
- uninstall Ruby-memcache, I don't know which one it will pick by default.
32
+ Rails will automatically load the memcache-client gem, but you may
33
+ need to uninstall Ruby-memcache, I don't know which one it will pick
34
+ by default.
33
35
 
34
36
  Add your environment-specific caches to config/environment/*. If you run both
35
37
  development and production on the same machine be sure to use different
36
- namespaces. Be careful when running tests under memcache, you may get strange
37
- results, it will be less of a headache to simply use a readonly memcache when
38
+ namespaces. Be careful when running tests using memcache, you may get strange
39
+ results. It will be less of a headache to simply use a readonly memcache when
38
40
  testing.
39
41
 
40
42
  memcache-client also comes with a wrapper called Cache in memcache_util.rb for
41
43
  use with Rails. To use it be sure to assign your memcache connection to
42
44
  CACHE. Cache returns nil on all memcache errors so you don't have to rescue
43
- yourself. It has #get, #put and #delete module functions.
45
+ the errors yourself. It has #get, #put and #delete module functions.
44
46
 
data/Rakefile CHANGED
@@ -4,15 +4,20 @@ require 'hoe'
4
4
 
5
5
  DEV_DOC_PATH = "Libraries/memcache-client"
6
6
 
7
- SPEC = Hoe.new 'memcache-client', '1.1.0' do |p|
7
+ hoe = Hoe.new 'memcache-client', '1.2.0' do |p|
8
8
  p.summary = 'A Ruby memcached client'
9
9
  p.description = 'memcache-client is a pure-ruby client to Danga\'s memcached.'
10
- p.author = 'Robert Cottrell'
10
+ p.author = ['Eric Hodel', 'Robert Cottrell']
11
11
  p.email = 'eric@robotcoop.com'
12
12
  p.url = "http://dev.robotcoop.com/#{DEV_DOC_PATH}"
13
13
 
14
14
  p.rubyforge_name = 'rctools'
15
15
  end
16
16
 
17
- require '../tasks'
17
+ SPEC = hoe.spec
18
+
19
+ begin
20
+ require '../tasks'
21
+ rescue LoadError
22
+ end
18
23
 
@@ -5,6 +5,31 @@ require 'thread'
5
5
  require 'timeout'
6
6
  require 'rubygems'
7
7
 
8
+ class String
9
+
10
+ ##
11
+ # Uses the ITU-T polynomial in the CRC32 algorithm.
12
+
13
+ def crc32_ITU_T
14
+ n = length
15
+ r = 0xFFFFFFFF
16
+
17
+ n.times do |i|
18
+ r ^= self[i]
19
+ 8.times do
20
+ if (r & 1) != 0 then
21
+ r = (r>>1) ^ 0xEDB88320
22
+ else
23
+ r >>= 1
24
+ end
25
+ end
26
+ end
27
+
28
+ r ^ 0xFFFFFFFF
29
+ end
30
+
31
+ end
32
+
8
33
  ##
9
34
  # A Ruby client library for memcached.
10
35
  #
@@ -169,11 +194,22 @@ class MemCache
169
194
  raise new_err
170
195
  end
171
196
 
197
+ ##
198
+ # Retrieves +keys+ and returns a Hash mapping keys to values.
199
+
200
+ def get_multi(*keys)
201
+ values = {}
202
+ keys.flatten.each { |key| values[key] = get key }
203
+ values
204
+ end
205
+
172
206
  ##
173
207
  # Add +key+ to the cache with value +value+ that expires in +expiry+
174
208
  # seconds.
175
209
 
176
210
  def set(key, value, expiry = 0)
211
+ @mutex.lock if @multithread
212
+
177
213
  raise MemCacheError, "No active servers" unless self.active?
178
214
  raise MemCacheError, "Update of readonly cache" if @readonly
179
215
  cache_key = make_cache_key(key)
@@ -186,20 +222,22 @@ class MemCache
186
222
  command = "set #{cache_key} 0 #{expiry} #{marshaled_value.size}\r\n#{marshaled_value}\r\n"
187
223
 
188
224
  begin
189
- @mutex.synchronize do
190
- sock.write command
191
- sock.gets
192
- end
225
+ sock.write command
226
+ sock.gets
193
227
  rescue SystemCallError, IOError => err
194
228
  server.close
195
229
  raise MemCacheError, err.message
196
230
  end
231
+ ensure
232
+ @mutex.unlock if @multithread
197
233
  end
198
234
 
199
235
  ##
200
236
  # Removes +key+ from the cache in +expiry+ seconds.
201
237
 
202
238
  def delete(key, expiry = 0)
239
+ @mutex.lock if @multithread
240
+
203
241
  raise MemCacheError, "No active servers" unless active?
204
242
  cache_key = make_cache_key key
205
243
  server = get_server_for_key cache_key
@@ -208,14 +246,14 @@ class MemCache
208
246
  raise MemCacheError, "No connection to server" if sock.nil?
209
247
 
210
248
  begin
211
- @mutex.synchronize do
212
- sock.write "delete #{cache_key} #{expiry}\r\n"
213
- sock.gets
214
- end
249
+ sock.write "delete #{cache_key} #{expiry}\r\n"
250
+ sock.gets
215
251
  rescue SystemCallError, IOError => err
216
252
  server.close
217
253
  raise MemCacheError, err.message
218
254
  end
255
+ ensure
256
+ @mutex.unlock if @multithread
219
257
  end
220
258
 
221
259
  ##
@@ -261,18 +299,25 @@ class MemCache
261
299
  raise MemCacheError, "No servers available" if @servers.empty?
262
300
  return @servers.first if @servers.length == 1
263
301
 
264
- # Hash the value of the key to select the bucket.
265
- hkey = key.hash
302
+ hkey = hash_for key
266
303
 
267
- # Fetch a server for the given key, retrying if that server is offline.
268
304
  20.times do |try|
269
- server = @buckets[(hkey + try) % @buckets.nitems]
305
+ server = @buckets[hkey % @buckets.nitems]
270
306
  return server if server.alive?
307
+ hkey += hash_for "#{try}#{key}"
271
308
  end
272
309
 
273
310
  raise MemCacheError, "No servers available"
274
311
  end
275
312
 
313
+ ##
314
+ # Returns an interoperable hash value for +key+. (I think, docs are
315
+ # sketchy for down servers).
316
+
317
+ def hash_for(key)
318
+ (key.crc32_ITU_T >> 16) & 0x7fff
319
+ end
320
+
276
321
  ##
277
322
  # Fetches the raw data for +cache_key+ from +server+. Returns nil on cache
278
323
  # miss.
@@ -354,6 +399,7 @@ class MemCache
354
399
  @weight = weight.to_i
355
400
 
356
401
  @multithread = @memcache.multithread
402
+ @mutex = Mutex.new
357
403
 
358
404
  @sock = nil
359
405
  @retry = nil
@@ -398,7 +444,7 @@ class MemCache
398
444
  end
399
445
  @retry = nil
400
446
  @status = 'CONNECTED'
401
- rescue SystemCallError, IOError, Timeout::Error => err
447
+ rescue SocketError, SystemCallError, IOError, Timeout::Error => err
402
448
  mark_dead err.message
403
449
  end
404
450
 
@@ -55,7 +55,7 @@ class TestMemCache < Test::Unit::TestCase
55
55
  end
56
56
 
57
57
  def test_cache_get
58
- server = util_setup_server
58
+ server = util_setup_fake_server
59
59
 
60
60
  assert_equal "\004\b\"\0170123456789",
61
61
  @cache.cache_get(server, 'my_namespace:key')
@@ -76,6 +76,11 @@ class TestMemCache < Test::Unit::TestCase
76
76
  socket.written.string
77
77
  end
78
78
 
79
+ def test_crc32_ITU_T
80
+ assert_equal 0, ''.crc32_ITU_T
81
+ assert_equal 1260851911, 'my_namespace:key'.crc32_ITU_T
82
+ end
83
+
79
84
  def test_initialize
80
85
  cache = MemCache.new :namespace => 'my_namespace', :readonly => true
81
86
 
@@ -124,7 +129,7 @@ class TestMemCache < Test::Unit::TestCase
124
129
  end
125
130
 
126
131
  def test_get
127
- util_setup_server
132
+ util_setup_fake_server
128
133
 
129
134
  value = @cache.get 'key'
130
135
 
@@ -182,12 +187,36 @@ class TestMemCache < Test::Unit::TestCase
182
187
  assert_equal 'No active servers', e.message
183
188
  end
184
189
 
190
+ def test_get_multi
191
+ util_setup_fake_server
192
+
193
+ values = @cache.get_multi 'keya', 'keyb'
194
+
195
+ assert_equal "get my_namespace:keya\r\nget my_namespace:keyb\r\n",
196
+ @cache.servers.first.socket.written.string
197
+
198
+ expected = { 'keya' => '0123456789', 'keyb' => nil }
199
+ assert_equal expected, values
200
+ end
201
+
185
202
  def test_get_server_for_key
186
203
  server = @cache.get_server_for_key 'key'
187
204
  assert_equal 'localhost', server.host
188
205
  assert_equal 1, server.port
189
206
  end
190
207
 
208
+ def test_get_server_for_key_multiple
209
+ s1 = util_setup_server @cache, 'one.example.com', ''
210
+ s2 = util_setup_server @cache, 'two.example.com', ''
211
+ @cache.instance_variable_set :@servers, [s1, s2]
212
+ @cache.instance_variable_set :@buckets, [s1, s2]
213
+
214
+ server = @cache.get_server_for_key 'keya'
215
+ assert_equal 'two.example.com', server.host
216
+ server = @cache.get_server_for_key 'keyb'
217
+ assert_equal 'one.example.com', server.host
218
+ end
219
+
191
220
  def test_get_server_for_key_no_servers
192
221
  @cache.servers = []
193
222
 
@@ -204,7 +233,31 @@ class TestMemCache < Test::Unit::TestCase
204
233
  assert_equal 'key', @cache.make_cache_key('key')
205
234
  end
206
235
 
207
- def util_setup_server
236
+ def test_basic_threaded_operations_should_work
237
+ cache = MemCache.new :multithread => true,
238
+ :namespace => 'my_namespace',
239
+ :readonly => false
240
+ server = util_setup_server cache, 'example.com', "OK\r\n"
241
+ cache.instance_variable_set :@servers, [server]
242
+
243
+ assert_nothing_raised do
244
+ cache.set "test", "test value"
245
+ end
246
+ end
247
+
248
+ def test_basic_unthreaded_operations_should_work
249
+ cache = MemCache.new :multithread => false,
250
+ :namespace => 'my_namespace',
251
+ :readonly => false
252
+ server = util_setup_server cache, 'example.com', "OK\r\n"
253
+ cache.instance_variable_set :@servers, [server]
254
+
255
+ assert_nothing_raised do
256
+ cache.set "test", "test value"
257
+ end
258
+ end
259
+
260
+ def util_setup_fake_server
208
261
  server = FakeServer.new
209
262
  server.socket.data.write "VALUE my_namepsace:key 0 14\r\n"
210
263
  server.socket.data.write "\004\b\"\0170123456789\r\n"
@@ -217,5 +270,15 @@ class TestMemCache < Test::Unit::TestCase
217
270
  return server
218
271
  end
219
272
 
273
+ def util_setup_server(memcache, host, responses)
274
+ server = MemCache::Server.new memcache, host
275
+ server.instance_variable_set :@sock, StringIO.new(responses)
276
+
277
+ @cache.servers = []
278
+ @cache.servers << server
279
+
280
+ return server
281
+ end
282
+
220
283
  end
221
284
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.99
3
3
  specification_version: 1
4
4
  name: memcache-client
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.1.0
7
- date: 2006-09-29 00:00:00 -07:00
6
+ version: 1.2.0
7
+ date: 2006-10-17 00:00:00 -07:00
8
8
  summary: A Ruby memcached client
9
9
  require_paths:
10
10
  - lib
@@ -28,6 +28,7 @@ signing_key:
28
28
  cert_chain:
29
29
  post_install_message:
30
30
  authors:
31
+ - Eric Hodel
31
32
  - Robert Cottrell
32
33
  files:
33
34
  - History.txt
@@ -37,7 +38,7 @@ files:
37
38
  - Rakefile
38
39
  - lib/memcache.rb
39
40
  - lib/memcache_util.rb
40
- - test/test_memcache.rb
41
+ - test/test_mem_cache.rb
41
42
  test_files: []
42
43
 
43
44
  rdoc_options: []
@@ -56,7 +57,7 @@ dependencies:
56
57
  version_requirement:
57
58
  version_requirements: !ruby/object:Gem::Version::Requirement
58
59
  requirements:
59
- - - ">"
60
+ - - ">="
60
61
  - !ruby/object:Gem::Version
61
- version: 0.0.0
62
+ version: 1.1.0
62
63
  version: