memcache-client 1.7.4 → 1.7.5
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/FAQ.rdoc +5 -5
- data/History.rdoc +9 -0
- data/README.rdoc +3 -2
- data/Rakefile +3 -1
- data/lib/memcache.rb +73 -49
- data/test/test_mem_cache.rb +75 -12
- metadata +4 -4
data/FAQ.rdoc
CHANGED
@@ -23,9 +23,9 @@ You can increase the timeout or disable them completely with the following confi
|
|
23
23
|
MemCache.new ['server1', 'server2'], { :timeout => 1.0 } # 1 second timeout
|
24
24
|
|
25
25
|
|
26
|
-
==
|
26
|
+
== Isn't Evan Weaver's memcached gem faster?
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
The latest version of memcached-client is anywhere from 33% to 100% slower than memcached in various benchmarks. Keep in mind this means that 10,000 get requests take 1.8 sec instead of 1.2 seconds.
|
29
|
+
In practice, memcache-client is unlikely to be a bottleneck in your system but there is always going
|
30
|
+
to be an overhead to pure Ruby. Evan's memcached gem is a thin wrapper around a C library, which
|
31
|
+
makes it very fast but also difficult to install for some people.
|
data/History.rdoc
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
= 1.7.5 (2009-09-09)
|
2
|
+
|
3
|
+
* Fix ruby warnings (josh)
|
4
|
+
* Make megabyte value size limit optional since Tokyo Tyrant can accept values larger than 1MB.
|
5
|
+
Use :check_size => false to disable the size check. (jsl)
|
6
|
+
* Ruby 1.9 support for recent I/O changes.
|
7
|
+
* Fix duplicate value marshalling on server error. (rajiv)
|
8
|
+
* Added option :autofix_keys (disabled by default) to replace long keys with md5 hashes (sd)
|
9
|
+
|
1
10
|
= 1.7.4 (2009-06-09)
|
2
11
|
|
3
12
|
* Fix issue with raising timeout errors.
|
data/README.rdoc
CHANGED
@@ -32,8 +32,9 @@ multiple memcached servers, you should install the RubyInline gem for ultimate p
|
|
32
32
|
|
33
33
|
== Using memcache-client with Rails
|
34
34
|
|
35
|
-
Rails 2.1+ includes memcache-client out of the box. See ActiveSupport::Cache::MemCacheStore
|
36
|
-
and the Rails.cache method for more details.
|
35
|
+
Rails 2.1+ includes memcache-client 1.5.0 out of the box. See ActiveSupport::Cache::MemCacheStore
|
36
|
+
and the Rails.cache method for more details. Rails 2.3+ will use the latest memcache-client
|
37
|
+
gem installed.
|
37
38
|
|
38
39
|
|
39
40
|
== Questions?
|
data/Rakefile
CHANGED
data/lib/memcache.rb
CHANGED
@@ -15,19 +15,21 @@ class MemCache
|
|
15
15
|
##
|
16
16
|
# The version of MemCache you are using.
|
17
17
|
|
18
|
-
VERSION = '1.7.
|
18
|
+
VERSION = '1.7.5'
|
19
19
|
|
20
20
|
##
|
21
21
|
# Default options for the cache object.
|
22
22
|
|
23
23
|
DEFAULT_OPTIONS = {
|
24
|
-
:namespace
|
25
|
-
:readonly
|
26
|
-
:multithread
|
27
|
-
:failover
|
28
|
-
:timeout
|
29
|
-
:logger
|
30
|
-
:no_reply
|
24
|
+
:namespace => nil,
|
25
|
+
:readonly => false,
|
26
|
+
:multithread => true,
|
27
|
+
:failover => true,
|
28
|
+
:timeout => 0.5,
|
29
|
+
:logger => nil,
|
30
|
+
:no_reply => false,
|
31
|
+
:check_size => true,
|
32
|
+
:autofix_keys => false
|
31
33
|
}
|
32
34
|
|
33
35
|
##
|
@@ -50,6 +52,19 @@ class MemCache
|
|
50
52
|
|
51
53
|
attr_reader :multithread
|
52
54
|
|
55
|
+
##
|
56
|
+
# Whether to try to fix keys that are too long and will be truncated by
|
57
|
+
# using their SHA1 hash instead.
|
58
|
+
# The hash is only used on keys longer than 250 characters, or containing spaces,
|
59
|
+
# to avoid impacting performance unnecesarily.
|
60
|
+
#
|
61
|
+
# In theory, your code should generate correct keys when calling memcache,
|
62
|
+
# so it's your responsibility and you should try to fix this problem at its source.
|
63
|
+
#
|
64
|
+
# But if that's not possible, enable this option and memcache-client will give you a hand.
|
65
|
+
|
66
|
+
attr_reader :autofix_keys
|
67
|
+
|
53
68
|
##
|
54
69
|
# The servers this client talks to. Play at your own peril.
|
55
70
|
|
@@ -84,19 +99,21 @@ class MemCache
|
|
84
99
|
#
|
85
100
|
# Valid options for +opts+ are:
|
86
101
|
#
|
87
|
-
# [:namespace]
|
88
|
-
# [:readonly]
|
89
|
-
# [:multithread]
|
90
|
-
# [:failover]
|
91
|
-
#
|
92
|
-
# [:timeout]
|
93
|
-
#
|
94
|
-
#
|
95
|
-
# [:
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
102
|
+
# [:namespace] Prepends this value to all keys added or retrieved.
|
103
|
+
# [:readonly] Raises an exception on cache writes when true.
|
104
|
+
# [:multithread] Wraps cache access in a Mutex for thread safety. Defaults to true.
|
105
|
+
# [:failover] Should the client try to failover to another server if the
|
106
|
+
# first server is down? Defaults to true.
|
107
|
+
# [:timeout] Time to use as the socket read timeout. Defaults to 0.5 sec,
|
108
|
+
# set to nil to disable timeouts.
|
109
|
+
# [:logger] Logger to use for info/debug output, defaults to nil
|
110
|
+
# [:no_reply] Don't bother looking for a reply for write operations (i.e. they
|
111
|
+
# become 'fire and forget'), memcached 1.2.5 and later only, speeds up
|
112
|
+
# set/add/delete/incr/decr significantly.
|
113
|
+
# [:check_size] Raises a MemCacheError if the value to be set is greater than 1 MB, which
|
114
|
+
# is the maximum key size for the standard memcached server. Defaults to true.
|
115
|
+
# [:autofix_keys] If a key is longer than 250 characters or contains spaces,
|
116
|
+
# use an SHA1 hash instead, to prevent collisions on truncated keys.
|
100
117
|
# Other options are ignored.
|
101
118
|
|
102
119
|
def initialize(*args)
|
@@ -120,14 +137,16 @@ class MemCache
|
|
120
137
|
end
|
121
138
|
|
122
139
|
opts = DEFAULT_OPTIONS.merge opts
|
123
|
-
@namespace
|
124
|
-
@readonly
|
125
|
-
@multithread
|
126
|
-
@
|
127
|
-
@
|
128
|
-
@
|
129
|
-
@
|
130
|
-
@
|
140
|
+
@namespace = opts[:namespace]
|
141
|
+
@readonly = opts[:readonly]
|
142
|
+
@multithread = opts[:multithread]
|
143
|
+
@autofix_keys = opts[:autofix_keys]
|
144
|
+
@timeout = opts[:timeout]
|
145
|
+
@failover = opts[:failover]
|
146
|
+
@logger = opts[:logger]
|
147
|
+
@no_reply = opts[:no_reply]
|
148
|
+
@check_size = opts[:check_size]
|
149
|
+
@mutex = Mutex.new if @multithread
|
131
150
|
|
132
151
|
logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger
|
133
152
|
|
@@ -312,12 +331,14 @@ class MemCache
|
|
312
331
|
|
313
332
|
def set(key, value, expiry = 0, raw = false)
|
314
333
|
raise MemCacheError, "Update of readonly cache" if @readonly
|
315
|
-
with_server(key) do |server, cache_key|
|
316
334
|
|
317
|
-
|
335
|
+
value = Marshal.dump value unless raw
|
336
|
+
with_server(key) do |server, cache_key|
|
318
337
|
logger.debug { "set #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
|
319
338
|
|
320
|
-
|
339
|
+
if @check_size && value.to_s.size > ONE_MB
|
340
|
+
raise MemCacheError, "Value too large, memcached can only store 1MB of data per key"
|
341
|
+
end
|
321
342
|
|
322
343
|
command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
|
323
344
|
|
@@ -359,10 +380,9 @@ class MemCache
|
|
359
380
|
(value, token) = gets(key, raw)
|
360
381
|
return nil unless value
|
361
382
|
updated = yield value
|
383
|
+
value = Marshal.dump updated unless raw
|
362
384
|
|
363
385
|
with_server(key) do |server, cache_key|
|
364
|
-
|
365
|
-
value = Marshal.dump updated unless raw
|
366
386
|
logger.debug { "cas #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
|
367
387
|
command = "cas #{cache_key} 0 #{expiry} #{value.to_s.size} #{token}#{noreply}\r\n#{value}\r\n"
|
368
388
|
|
@@ -392,8 +412,8 @@ class MemCache
|
|
392
412
|
|
393
413
|
def add(key, value, expiry = 0, raw = false)
|
394
414
|
raise MemCacheError, "Update of readonly cache" if @readonly
|
415
|
+
value = Marshal.dump value unless raw
|
395
416
|
with_server(key) do |server, cache_key|
|
396
|
-
value = Marshal.dump value unless raw
|
397
417
|
logger.debug { "add #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
|
398
418
|
command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
|
399
419
|
|
@@ -413,8 +433,8 @@ class MemCache
|
|
413
433
|
# If +raw+ is true, +value+ will not be Marshalled.
|
414
434
|
def replace(key, value, expiry = 0, raw = false)
|
415
435
|
raise MemCacheError, "Update of readonly cache" if @readonly
|
436
|
+
value = Marshal.dump value unless raw
|
416
437
|
with_server(key) do |server, cache_key|
|
417
|
-
value = Marshal.dump value unless raw
|
418
438
|
logger.debug { "replace #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
|
419
439
|
command = "replace #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
|
420
440
|
|
@@ -621,6 +641,10 @@ class MemCache
|
|
621
641
|
# requested.
|
622
642
|
|
623
643
|
def make_cache_key(key)
|
644
|
+
if @autofix_keys and (key =~ /\s/ or (key.length + (namespace.nil? ? 0 : namespace.length)) > 250)
|
645
|
+
key = "#{Digest::SHA1.hexdigest(key)}-autofixed"
|
646
|
+
end
|
647
|
+
|
624
648
|
if namespace.nil? then
|
625
649
|
key
|
626
650
|
else
|
@@ -1040,23 +1064,23 @@ class MemCache
|
|
1040
1064
|
class BufferedIO < Net::BufferedIO # :nodoc:
|
1041
1065
|
BUFSIZE = 1024 * 16
|
1042
1066
|
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1067
|
+
if RUBY_VERSION < '1.9.1'
|
1068
|
+
def rbuf_fill
|
1069
|
+
begin
|
1070
|
+
@rbuf << @io.read_nonblock(BUFSIZE)
|
1071
|
+
rescue Errno::EWOULDBLOCK
|
1072
|
+
retry unless @read_timeout
|
1073
|
+
if IO.select([@io], nil, nil, @read_timeout)
|
1074
|
+
retry
|
1075
|
+
else
|
1076
|
+
raise Timeout::Error, 'IO timeout'
|
1077
|
+
end
|
1054
1078
|
end
|
1055
1079
|
end
|
1056
1080
|
end
|
1057
1081
|
|
1058
|
-
def setsockopt
|
1059
|
-
@io.setsockopt
|
1082
|
+
def setsockopt(*args)
|
1083
|
+
@io.setsockopt(*args)
|
1060
1084
|
end
|
1061
1085
|
|
1062
1086
|
def gets
|
data/test/test_mem_cache.rb
CHANGED
@@ -18,7 +18,8 @@ require File.dirname(__FILE__) + '/../lib/memcache' if not defined?(MemCache)
|
|
18
18
|
class MemCache
|
19
19
|
|
20
20
|
attr_writer :namespace
|
21
|
-
|
21
|
+
attr_writer :autofix_keys
|
22
|
+
|
22
23
|
end
|
23
24
|
|
24
25
|
class FakeSocket
|
@@ -269,7 +270,7 @@ class TestMemCache < Test::Unit::TestCase
|
|
269
270
|
@cache.cache_get(server, 'my_namespace:key')
|
270
271
|
end
|
271
272
|
|
272
|
-
assert_match
|
273
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
273
274
|
|
274
275
|
assert !server.alive?
|
275
276
|
end
|
@@ -327,7 +328,7 @@ class TestMemCache < Test::Unit::TestCase
|
|
327
328
|
@cache.cache_get_multi server, 'my_namespace:key'
|
328
329
|
end
|
329
330
|
|
330
|
-
assert_match
|
331
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
331
332
|
|
332
333
|
assert !server.alive?
|
333
334
|
end
|
@@ -563,7 +564,7 @@ class TestMemCache < Test::Unit::TestCase
|
|
563
564
|
@cache.get 'key'
|
564
565
|
end
|
565
566
|
|
566
|
-
assert_match
|
567
|
+
assert_match(/^No connection to server/, e.message)
|
567
568
|
end
|
568
569
|
|
569
570
|
def test_get_no_servers
|
@@ -717,6 +718,36 @@ class TestMemCache < Test::Unit::TestCase
|
|
717
718
|
assert_equal 'key', @cache.make_cache_key('key')
|
718
719
|
end
|
719
720
|
|
721
|
+
def test_make_cache_key_without_autofix
|
722
|
+
@cache.autofix_keys = false
|
723
|
+
|
724
|
+
key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
|
725
|
+
hash = Digest::SHA1.hexdigest(key)
|
726
|
+
@cache.namespace = nil
|
727
|
+
assert_equal key, @cache.make_cache_key(key)
|
728
|
+
|
729
|
+
@cache.autofix_keys = true
|
730
|
+
|
731
|
+
@cache.namespace = "my_namespace"
|
732
|
+
assert_equal 'my_namespace:key', @cache.make_cache_key('key')
|
733
|
+
@cache.namespace = nil
|
734
|
+
assert_equal 'key', @cache.make_cache_key('key')
|
735
|
+
|
736
|
+
key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
|
737
|
+
hash = Digest::SHA1.hexdigest(key)
|
738
|
+
@cache.namespace = "my_namespace"
|
739
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
740
|
+
@cache.namespace = nil
|
741
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
742
|
+
|
743
|
+
key = "a short key with spaces"
|
744
|
+
hash = Digest::SHA1.hexdigest(key)
|
745
|
+
@cache.namespace = "my_namespace"
|
746
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
747
|
+
@cache.namespace = nil
|
748
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
749
|
+
end
|
750
|
+
|
720
751
|
def test_servers
|
721
752
|
server = FakeServer.new
|
722
753
|
@cache.servers = []
|
@@ -776,6 +807,38 @@ class TestMemCache < Test::Unit::TestCase
|
|
776
807
|
assert_equal 'Update of readonly cache', e.message
|
777
808
|
end
|
778
809
|
|
810
|
+
def test_check_size_on
|
811
|
+
cache = MemCache.new :check_size => true
|
812
|
+
|
813
|
+
server = FakeServer.new
|
814
|
+
server.socket.data.write "STORED\r\n"
|
815
|
+
server.socket.data.rewind
|
816
|
+
|
817
|
+
cache.servers = []
|
818
|
+
cache.servers << server
|
819
|
+
|
820
|
+
e = assert_raise MemCache::MemCacheError do
|
821
|
+
cache.set 'key', 'v' * 1048577
|
822
|
+
end
|
823
|
+
|
824
|
+
assert_equal 'Value too large, memcached can only store 1MB of data per key', e.message
|
825
|
+
end
|
826
|
+
|
827
|
+
def test_check_size_off
|
828
|
+
cache = MemCache.new :check_size => false
|
829
|
+
|
830
|
+
server = FakeServer.new
|
831
|
+
server.socket.data.write "STORED\r\n"
|
832
|
+
server.socket.data.rewind
|
833
|
+
|
834
|
+
cache.servers = []
|
835
|
+
cache.servers << server
|
836
|
+
|
837
|
+
assert_nothing_raised do
|
838
|
+
cache.set 'key', 'v' * 1048577
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
779
842
|
def test_set_too_big
|
780
843
|
server = FakeServer.new
|
781
844
|
|
@@ -790,7 +853,7 @@ class TestMemCache < Test::Unit::TestCase
|
|
790
853
|
@cache.set 'key', 'v'
|
791
854
|
end
|
792
855
|
|
793
|
-
assert_match
|
856
|
+
assert_match(/object too large for cache/, e.message)
|
794
857
|
end
|
795
858
|
|
796
859
|
def test_prepend
|
@@ -977,7 +1040,7 @@ class TestMemCache < Test::Unit::TestCase
|
|
977
1040
|
@cache.flush_all
|
978
1041
|
end
|
979
1042
|
|
980
|
-
assert_match
|
1043
|
+
assert_match(/flush_all\r\n/, socket.written.string)
|
981
1044
|
end
|
982
1045
|
|
983
1046
|
def test_flush_all_for_real
|
@@ -1033,8 +1096,8 @@ class TestMemCache < Test::Unit::TestCase
|
|
1033
1096
|
end
|
1034
1097
|
|
1035
1098
|
output = server.socket.written.string
|
1036
|
-
assert_match
|
1037
|
-
assert_match
|
1099
|
+
assert_match(/set my_namespace:test/, output)
|
1100
|
+
assert_match(/test value/, output)
|
1038
1101
|
end
|
1039
1102
|
|
1040
1103
|
def test_basic_unthreaded_operations_should_work
|
@@ -1056,8 +1119,8 @@ class TestMemCache < Test::Unit::TestCase
|
|
1056
1119
|
end
|
1057
1120
|
|
1058
1121
|
output = server.socket.written.string
|
1059
|
-
assert_match
|
1060
|
-
assert_match
|
1122
|
+
assert_match(/set my_namespace:test/, output)
|
1123
|
+
assert_match(/test value/, output)
|
1061
1124
|
end
|
1062
1125
|
|
1063
1126
|
def util_setup_fake_server
|
@@ -1122,9 +1185,9 @@ class TestMemCache < Test::Unit::TestCase
|
|
1122
1185
|
assert cache.decr('c', 5) > 14
|
1123
1186
|
assert_equal 11, cache.get('b')
|
1124
1187
|
d = cache.get('d', true)
|
1125
|
-
assert_match
|
1188
|
+
assert_match(/\Aab*\Z/, d)
|
1126
1189
|
e = cache.get('e', true)
|
1127
|
-
assert_match
|
1190
|
+
assert_match(/\Ay*x\Z/, e)
|
1128
1191
|
end
|
1129
1192
|
end
|
1130
1193
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memcache-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Hodel
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2009-
|
14
|
+
date: 2009-09-09 00:00:00 -05:00
|
15
15
|
default_executable:
|
16
16
|
dependencies: []
|
17
17
|
|
@@ -31,7 +31,7 @@ files:
|
|
31
31
|
- Rakefile
|
32
32
|
- lib/memcache.rb
|
33
33
|
- lib/continuum_native.rb
|
34
|
-
has_rdoc:
|
34
|
+
has_rdoc: true
|
35
35
|
homepage: http://github.com/mperham/memcache-client
|
36
36
|
licenses: []
|
37
37
|
|
@@ -55,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
55
|
requirements: []
|
56
56
|
|
57
57
|
rubyforge_project: seattlerb
|
58
|
-
rubygems_version: 1.3.
|
58
|
+
rubygems_version: 1.3.5
|
59
59
|
signing_key:
|
60
60
|
specification_version: 3
|
61
61
|
summary: A Ruby library for accessing memcached.
|