memcached 0.14 → 0.15
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.tar.gz.sig +0 -0
- data/BENCHMARKS +83 -64
- data/CHANGELOG +9 -1
- data/COMPATIBILITY +2 -1
- data/Manifest +2 -0
- data/README +4 -11
- data/Rakefile +1 -0
- data/ext/extconf.rb +40 -17
- data/ext/libmemcached-0.31.tar.gz +0 -0
- data/ext/rlibmemcached.i +29 -10
- data/ext/rlibmemcached_wrap.c +3167 -1659
- data/lib/memcached/behaviors.rb +2 -2
- data/lib/memcached/exceptions.rb +4 -4
- data/lib/memcached/memcached.rb +61 -37
- data/lib/memcached/rails.rb +13 -5
- data/memcached.gemspec +5 -9
- data/test/profile/benchmark.rb +198 -49
- data/test/profile/profile.rb +3 -8
- data/test/profile/valgrind.rb +5 -12
- data/test/setup.rb +21 -16
- data/test/test_helper.rb +5 -1
- data/test/unit/memcached_test.rb +98 -30
- data/test/unit/rails_test.rb +32 -2
- metadata +10 -16
- metadata.gz.sig +0 -0
data/test/profile/profile.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
$LOAD_PATH << "#{HERE}/../../lib/"
|
2
|
+
require "#{File.dirname(__FILE__)}/../setup"
|
4
3
|
|
4
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../../lib/"
|
5
5
|
require 'rubygems'
|
6
6
|
require 'memcached'
|
7
7
|
require 'ruby-prof'
|
8
8
|
|
9
|
-
@cache = Memcached.new(
|
10
|
-
'127.0.0.1:43042', '127.0.0.1:43043'],
|
11
|
-
:namespace => "namespace"
|
12
|
-
)
|
13
|
-
|
14
9
|
result = RubyProf.profile do
|
15
|
-
load "#{HERE}/valgrind.rb"
|
10
|
+
load "#{HERE}/profile/valgrind.rb"
|
16
11
|
end
|
17
12
|
|
18
13
|
printer = RubyProf::GraphPrinter.new(result)
|
data/test/profile/valgrind.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
$LOAD_PATH << "#{HERE}/../../lib/"
|
2
|
+
require "#{File.dirname(__FILE__)}/../setup"
|
4
3
|
|
4
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../../lib/"
|
5
5
|
require 'memcached'
|
6
6
|
require 'rubygems'
|
7
7
|
|
8
8
|
class Worker
|
9
9
|
def initialize(method_name, iterations)
|
10
10
|
@method = method_name || 'mixed'
|
11
|
-
@i = (iterations ||
|
11
|
+
@i = (iterations || 10000).to_i
|
12
12
|
|
13
13
|
@key1 = "key1-"*8
|
14
14
|
@key2 = "key2-"*8
|
@@ -17,15 +17,13 @@ class Worker
|
|
17
17
|
@marshalled = Marshal.dump(@value)
|
18
18
|
|
19
19
|
@opts = [
|
20
|
-
[
|
20
|
+
["#{UNIX_SOCKET_NAME}0", "#{UNIX_SOCKET_NAME}1"],
|
21
21
|
{
|
22
22
|
:buffer_requests => false,
|
23
23
|
:no_block => false,
|
24
24
|
:namespace => "namespace"
|
25
25
|
}
|
26
26
|
]
|
27
|
-
system("ruby #{HERE}/../setup.rb")
|
28
|
-
sleep(1)
|
29
27
|
@cache = Memcached.new(*@opts)
|
30
28
|
|
31
29
|
@cache.set @key1, @value
|
@@ -103,11 +101,6 @@ class Worker
|
|
103
101
|
when "clone"
|
104
102
|
@i.times do
|
105
103
|
cache = @cache.clone
|
106
|
-
cache.destroy(false)
|
107
|
-
end
|
108
|
-
when "clone-nodestroy"
|
109
|
-
@i.times do
|
110
|
-
@cache.clone
|
111
104
|
end
|
112
105
|
when "servers"
|
113
106
|
@i.times do
|
@@ -117,7 +110,7 @@ class Worker
|
|
117
110
|
raise "No such method"
|
118
111
|
end
|
119
112
|
|
120
|
-
|
113
|
+
GC.start
|
121
114
|
end
|
122
115
|
|
123
116
|
end
|
data/test/setup.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
system("
|
1
|
+
|
2
|
+
unless defined? UNIX_SOCKET_NAME
|
3
|
+
HERE = File.dirname(__FILE__)
|
4
|
+
UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached')
|
5
|
+
|
6
|
+
# Kill memcached
|
7
|
+
system("killall -9 memcached")
|
8
|
+
|
9
|
+
# Start memcached
|
10
|
+
verbosity = (ENV['DEBUG'] ? "-vv" : "")
|
11
|
+
log = "/tmp/memcached.log"
|
12
|
+
system ">#{log}"
|
13
|
+
|
14
|
+
# Network memcached
|
15
|
+
(43042..43046).each do |port|
|
16
|
+
system "memcached #{verbosity} -U #{port} -p #{port} >> #{log} 2>&1 &"
|
17
|
+
end
|
18
|
+
# Domain socket memcached
|
19
|
+
(0..1).each do |i|
|
20
|
+
system "memcached -M -s #{UNIX_SOCKET_NAME}#{i} #{verbosity} >> #{log} 2>&1 &"
|
21
|
+
end
|
8
22
|
end
|
9
|
-
|
10
|
-
log = "/tmp/memcached.log"
|
11
|
-
system ">#{log}"
|
12
|
-
|
13
|
-
verbosity = (ENV['DEBUG'] ? "-vv" : "")
|
14
|
-
|
15
|
-
(43042..43046).each do |port|
|
16
|
-
system "memcached #{verbosity} -p #{port} >> #{log} 2>&1 &"
|
17
|
-
end
|
data/test/test_helper.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
|
2
2
|
$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
|
3
3
|
|
4
|
+
require 'rubygems'
|
5
|
+
require 'mocha'
|
6
|
+
|
4
7
|
if ENV['DEBUG']
|
5
|
-
require 'rubygems'
|
6
8
|
require 'ruby-debug'
|
7
9
|
end
|
8
10
|
|
@@ -10,5 +12,7 @@ require 'memcached'
|
|
10
12
|
require 'test/unit'
|
11
13
|
require 'ostruct'
|
12
14
|
|
15
|
+
UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached') unless defined? UNIX_SOCKET_NAME
|
16
|
+
|
13
17
|
class GenericClass
|
14
18
|
end
|
data/test/unit/memcached_test.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
require "#{File.dirname(__FILE__)}/../test_helper"
|
3
3
|
require 'socket'
|
4
|
+
require 'mocha'
|
4
5
|
require 'benchmark'
|
5
6
|
|
6
7
|
class MemcachedTest < Test::Unit::TestCase
|
7
8
|
|
8
9
|
def setup
|
9
|
-
@servers = ['localhost:43042', 'localhost:43043']
|
10
|
+
@servers = ['localhost:43042', 'localhost:43043', "#{UNIX_SOCKET_NAME}0"]
|
10
11
|
|
11
12
|
# Maximum allowed prefix key size for :hash_with_prefix_key_key => false
|
12
13
|
@prefix_key = 'prefix_key_'
|
@@ -18,6 +19,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
18
19
|
}
|
19
20
|
@cache = Memcached.new(@servers, @options)
|
20
21
|
|
22
|
+
@udp_options = {
|
23
|
+
:prefix_key => @prefix_key,
|
24
|
+
:hash => :default,
|
25
|
+
:udp => true,
|
26
|
+
:distribution => :modula
|
27
|
+
}
|
28
|
+
@udp_cache = Memcached.new(@servers, @udp_options)
|
29
|
+
|
21
30
|
@nb_options = {
|
22
31
|
:prefix_key => @prefix_key,
|
23
32
|
:no_block => true,
|
@@ -35,10 +44,9 @@ class MemcachedTest < Test::Unit::TestCase
|
|
35
44
|
def test_initialize
|
36
45
|
cache = Memcached.new @servers, :prefix_key => 'test'
|
37
46
|
assert_equal 'test', cache.options[:prefix_key]
|
38
|
-
assert_equal
|
47
|
+
assert_equal 3, cache.send(:server_structs).size
|
39
48
|
assert_equal 'localhost', cache.send(:server_structs).first.hostname
|
40
|
-
assert_equal
|
41
|
-
assert_equal 43043, cache.send(:server_structs).last.port
|
49
|
+
assert_equal 43042, cache.send(:server_structs).first.port
|
42
50
|
end
|
43
51
|
|
44
52
|
def test_initialize_with_ip_addresses
|
@@ -69,6 +77,12 @@ class MemcachedTest < Test::Unit::TestCase
|
|
69
77
|
end
|
70
78
|
end
|
71
79
|
|
80
|
+
def test_initialize_with_ip_address_and_options
|
81
|
+
cache = Memcached.new '127.0.0.1:43042', :ketama_weighted => false
|
82
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
|
83
|
+
assert_equal false, cache.options[:ketama_weighted]
|
84
|
+
end
|
85
|
+
|
72
86
|
def test_options_are_set
|
73
87
|
Memcached::DEFAULTS.merge(@nb_options).each do |key, expected|
|
74
88
|
value = @nb_cache.options[key]
|
@@ -120,7 +134,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
120
134
|
def test_initialize_without_prefix_key
|
121
135
|
cache = Memcached.new @servers
|
122
136
|
assert_equal nil, cache.options[:prefix_key]
|
123
|
-
assert_equal
|
137
|
+
assert_equal 3, cache.send(:server_structs).size
|
124
138
|
end
|
125
139
|
|
126
140
|
def test_initialize_negative_behavior
|
@@ -204,6 +218,12 @@ class MemcachedTest < Test::Unit::TestCase
|
|
204
218
|
result = @cache.get key
|
205
219
|
assert_equal @value, result
|
206
220
|
end
|
221
|
+
|
222
|
+
def test_udp_get
|
223
|
+
@udp_cache.set key, @value
|
224
|
+
result = @udp_cache.get key
|
225
|
+
assert_equal @value, result
|
226
|
+
end
|
207
227
|
|
208
228
|
def test_get_nil
|
209
229
|
@cache.set key, nil, 0
|
@@ -222,21 +242,21 @@ class MemcachedTest < Test::Unit::TestCase
|
|
222
242
|
socket = stub_server 43047
|
223
243
|
cache = Memcached.new("localhost:43047:1", :timeout => 0.5)
|
224
244
|
assert 0.49 < (Benchmark.measure do
|
225
|
-
assert_raise(Memcached::
|
245
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
226
246
|
result = cache.get key
|
227
247
|
end
|
228
248
|
end).real
|
229
249
|
|
230
250
|
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.001, :rcv_timeout => 0.5)
|
231
251
|
assert 0.49 < (Benchmark.measure do
|
232
|
-
assert_raise(Memcached::
|
252
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
233
253
|
result = cache.get key
|
234
254
|
end
|
235
255
|
end).real
|
236
256
|
|
237
257
|
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.25, :rcv_timeout => 0.25)
|
238
|
-
assert 0.
|
239
|
-
assert_raise(Memcached::
|
258
|
+
assert 0.51 > (Benchmark.measure do
|
259
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
240
260
|
result = cache.get key
|
241
261
|
end
|
242
262
|
end).real
|
@@ -246,14 +266,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
246
266
|
socket = stub_server 43048
|
247
267
|
cache = Memcached.new("localhost:43048:1", :no_block => true, :timeout => 0.25)
|
248
268
|
assert 0.24 < (Benchmark.measure do
|
249
|
-
assert_raise(Memcached::
|
269
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
250
270
|
result = cache.get key
|
251
271
|
end
|
252
272
|
end).real
|
253
273
|
|
254
274
|
cache = Memcached.new("localhost:43048:1", :no_block => true, :poll_timeout => 0.25, :rcv_timeout => 0.001)
|
255
275
|
assert 0.24 < (Benchmark.measure do
|
256
|
-
assert_raise(Memcached::
|
276
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
257
277
|
result = cache.get key
|
258
278
|
end
|
259
279
|
end).real
|
@@ -263,7 +283,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
263
283
|
:rcv_timeout => 0.25 # No affect in no-block mode
|
264
284
|
)
|
265
285
|
assert 0.24 > (Benchmark.measure do
|
266
|
-
assert_raise(Memcached::
|
286
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
267
287
|
result = cache.get key
|
268
288
|
end
|
269
289
|
end).real
|
@@ -379,6 +399,12 @@ class MemcachedTest < Test::Unit::TestCase
|
|
379
399
|
end
|
380
400
|
end
|
381
401
|
|
402
|
+
def test_udp_set
|
403
|
+
assert_nothing_raised do
|
404
|
+
@udp_cache.set(key, @value)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
382
408
|
def test_set_expiry
|
383
409
|
@cache.set key, @value, 1
|
384
410
|
assert_nothing_raised do
|
@@ -655,17 +681,25 @@ class MemcachedTest < Test::Unit::TestCase
|
|
655
681
|
end
|
656
682
|
end
|
657
683
|
|
658
|
-
def
|
659
|
-
|
660
|
-
|
661
|
-
|
684
|
+
def test_server_error_message
|
685
|
+
@cache.set key, "I'm big" * 1000000
|
686
|
+
assert false # Never reached
|
687
|
+
rescue Memcached::ServerError => e
|
688
|
+
assert_match /^"object too large for cache". Key/, e.message
|
689
|
+
end
|
690
|
+
|
691
|
+
def test_errno_message
|
692
|
+
Rlibmemcached::MemcachedServerSt.any_instance.expects("cached_errno").returns(1)
|
693
|
+
@cache.send(:check_return_code, Rlibmemcached::MEMCACHED_ERRNO, key)
|
694
|
+
rescue Memcached::SystemError => e
|
695
|
+
assert_match /^Errno 1: "Operation not permitted". Key/, e.message
|
662
696
|
end
|
663
697
|
|
664
698
|
# Stats
|
665
699
|
|
666
700
|
def test_stats
|
667
701
|
stats = @cache.stats
|
668
|
-
assert_equal
|
702
|
+
assert_equal 3, stats[:pid].size
|
669
703
|
assert_instance_of Fixnum, stats[:pid].first
|
670
704
|
assert_instance_of String, stats[:version].first
|
671
705
|
end
|
@@ -753,30 +787,64 @@ class MemcachedTest < Test::Unit::TestCase
|
|
753
787
|
|
754
788
|
# Server removal and consistent hashing
|
755
789
|
|
756
|
-
def
|
790
|
+
def test_unresponsive_server
|
757
791
|
socket = stub_server 43041
|
758
792
|
cache = Memcached.new(
|
759
793
|
[@servers.last, 'localhost:43041'],
|
760
794
|
:prefix_key => @prefix_key,
|
761
795
|
:auto_eject_hosts => true,
|
762
|
-
:server_failure_limit =>
|
796
|
+
:server_failure_limit => 2,
|
763
797
|
:retry_timeout => 1,
|
764
798
|
:hash_with_prefix_key => false,
|
765
799
|
:hash => :md5
|
766
800
|
)
|
767
801
|
|
768
|
-
# Hit
|
769
|
-
|
770
|
-
cache.set(
|
771
|
-
cache.get(
|
802
|
+
# Hit second server up to the server_failure_limit
|
803
|
+
key2 = 'test_missing_server'
|
804
|
+
assert_raise(Memcached::ATimeoutOccurred) { cache.set(key2, @value) }
|
805
|
+
assert_raise(Memcached::ATimeoutOccurred) { cache.get(key2, @value) }
|
772
806
|
|
773
|
-
# Hit second server
|
807
|
+
# Hit second server and pass the limit
|
774
808
|
key2 = 'test_missing_server'
|
775
|
-
|
809
|
+
begin
|
810
|
+
cache.get(key2)
|
811
|
+
rescue => e
|
812
|
+
assert_equal Memcached::ServerIsMarkedDead, e.class
|
813
|
+
assert_match /localhost:43041/, e.message
|
814
|
+
end
|
815
|
+
|
816
|
+
# Hit first server on retry
|
817
|
+
assert_nothing_raised do
|
776
818
|
cache.set(key2, @value)
|
819
|
+
assert_equal cache.get(key2), @value
|
777
820
|
end
|
821
|
+
|
822
|
+
sleep(2)
|
823
|
+
|
824
|
+
# Hit second server again after restore, expect same failure
|
825
|
+
key2 = 'test_missing_server'
|
826
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
827
|
+
cache.set(key2, @value)
|
828
|
+
end
|
829
|
+
end
|
778
830
|
|
779
|
-
|
831
|
+
def test_missing_server
|
832
|
+
cache = Memcached.new(
|
833
|
+
[@servers.last, 'localhost:43041'],
|
834
|
+
:prefix_key => @prefix_key,
|
835
|
+
:auto_eject_hosts => true,
|
836
|
+
:server_failure_limit => 2,
|
837
|
+
:retry_timeout => 1,
|
838
|
+
:hash_with_prefix_key => false,
|
839
|
+
:hash => :md5
|
840
|
+
)
|
841
|
+
|
842
|
+
# Hit second server up to the server_failure_limit
|
843
|
+
key2 = 'test_missing_server'
|
844
|
+
assert_raise(Memcached::SystemError) { cache.set(key2, @value) }
|
845
|
+
assert_raise(Memcached::SystemError) { cache.get(key2, @value) }
|
846
|
+
|
847
|
+
# Hit second server and pass the limit
|
780
848
|
key2 = 'test_missing_server'
|
781
849
|
begin
|
782
850
|
cache.get(key2)
|
@@ -788,14 +856,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
788
856
|
# Hit first server on retry
|
789
857
|
assert_nothing_raised do
|
790
858
|
cache.set(key2, @value)
|
791
|
-
cache.get(key2)
|
859
|
+
assert_equal cache.get(key2), @value
|
792
860
|
end
|
793
861
|
|
794
862
|
sleep(2)
|
795
863
|
|
796
864
|
# Hit second server again after restore, expect same failure
|
797
865
|
key2 = 'test_missing_server'
|
798
|
-
assert_raise(Memcached::
|
866
|
+
assert_raise(Memcached::SystemError) do
|
799
867
|
cache.set(key2, @value)
|
800
868
|
end
|
801
869
|
end
|
@@ -849,7 +917,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
849
917
|
threads.each {|thread| thread.join}
|
850
918
|
end
|
851
919
|
|
852
|
-
# Hash
|
920
|
+
# Hash
|
853
921
|
|
854
922
|
def test_hash
|
855
923
|
assert_equal 3157003241,
|
data/test/unit/rails_test.rb
CHANGED
@@ -4,7 +4,7 @@ require "#{File.dirname(__FILE__)}/../test_helper"
|
|
4
4
|
class RailsTest < Test::Unit::TestCase
|
5
5
|
|
6
6
|
def setup
|
7
|
-
@servers = ['127.0.0.1:43042', '127.0.0.1:43043']
|
7
|
+
@servers = ['127.0.0.1:43042', '127.0.0.1:43043', "#{UNIX_SOCKET_NAME}0"]
|
8
8
|
@namespace = 'rails_test'
|
9
9
|
@cache = Memcached::Rails.new(:servers => @servers, :namespace => @namespace)
|
10
10
|
@value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
|
@@ -19,7 +19,7 @@ class RailsTest < Test::Unit::TestCase
|
|
19
19
|
|
20
20
|
def test_get_multi
|
21
21
|
@cache.set key, @value
|
22
|
-
result = @cache.get_multi
|
22
|
+
result = @cache.get_multi([key])
|
23
23
|
assert_equal(
|
24
24
|
{key => @value},
|
25
25
|
result
|
@@ -47,6 +47,36 @@ class RailsTest < Test::Unit::TestCase
|
|
47
47
|
assert_equal @value, result
|
48
48
|
end
|
49
49
|
|
50
|
+
def test_cas
|
51
|
+
cache = Memcached::Rails.new(:servers => @servers, :namespace => @namespace, :support_cas => true)
|
52
|
+
value2 = OpenStruct.new(:d => 3, :e => 4, :f => GenericClass)
|
53
|
+
|
54
|
+
# Existing set
|
55
|
+
cache.set key, @value
|
56
|
+
cache.cas(key) do |current|
|
57
|
+
assert_equal @value, current
|
58
|
+
value2
|
59
|
+
end
|
60
|
+
assert_equal value2, cache.get(key)
|
61
|
+
|
62
|
+
# Missing set
|
63
|
+
cache.delete key
|
64
|
+
assert_nothing_raised do
|
65
|
+
cache.cas(key) { @called = true }
|
66
|
+
end
|
67
|
+
assert_nil cache.get(key)
|
68
|
+
assert_nil @called
|
69
|
+
|
70
|
+
# Conflicting set
|
71
|
+
cache.set key, @value
|
72
|
+
assert_raises(Memcached::ConnectionDataExists) do
|
73
|
+
cache.cas(key) do |current|
|
74
|
+
cache.set key, value2
|
75
|
+
current
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
50
80
|
def test_get_missing
|
51
81
|
@cache.delete key rescue nil
|
52
82
|
result = @cache.get key
|