memcache-client 1.1.0 → 1.2.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.
- data/History.txt +13 -0
- data/LICENSE.txt +3 -3
- data/Manifest.txt +1 -1
- data/README.txt +16 -14
- data/Rakefile +8 -3
- data/lib/memcache.rb +59 -13
- data/test/{test_memcache.rb → test_mem_cache.rb} +66 -3
- metadata +6 -5
data/History.txt
CHANGED
@@ -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
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
All original code copyright 2005 Bob Cottrell,
|
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 ``-
|
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
|
data/Manifest.txt
CHANGED
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
|
-
|
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
|
-
|
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
|
32
|
-
uninstall Ruby-memcache, I don't know which one it will pick
|
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
|
37
|
-
results
|
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
|
-
|
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
|
-
|
17
|
+
SPEC = hoe.spec
|
18
|
+
|
19
|
+
begin
|
20
|
+
require '../tasks'
|
21
|
+
rescue LoadError
|
22
|
+
end
|
18
23
|
|
data/lib/memcache.rb
CHANGED
@@ -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
|
-
|
190
|
-
|
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
|
-
|
212
|
-
|
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
|
-
|
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[
|
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 =
|
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
|
-
|
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
|
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.
|
7
|
-
date: 2006-
|
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/
|
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:
|
62
|
+
version: 1.1.0
|
62
63
|
version:
|