memcachedb-client 0.0.1
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 +31 -0
- data/History.rdoc +2 -0
- data/LICENSE.txt +28 -0
- data/README.rdoc +98 -0
- data/Rakefile +38 -0
- data/VERSION.yml +5 -0
- data/lib/continuum_native.rb +41 -0
- data/lib/memcachedb.rb +1222 -0
- data/performance.txt +143 -0
- data/test/test_benchmark.rb +134 -0
- data/test/test_mem_cache.rb +1220 -0
- metadata +70 -0
data/performance.txt
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
== 1.5.0, 1.8.6 (default in Rails 2.2 and lower)
|
2
|
+
|
3
|
+
user system total real
|
4
|
+
set:plain:memcache-client 41.550000 0.590000 42.140000 ( 43.740685)
|
5
|
+
set:ruby:memcache-client 41.540000 0.590000 42.130000 ( 43.733796)
|
6
|
+
get:plain:memcache-client 41.920000 0.610000 42.530000 ( 44.031005)
|
7
|
+
get:ruby:memcache-client 41.940000 0.600000 42.540000 ( 44.082447)
|
8
|
+
multiget:ruby:memcache-client 46.120000 0.440000 46.560000 ( 47.354041)
|
9
|
+
missing:ruby:memcache-client 41.490000 0.580000 42.070000 ( 43.610837)
|
10
|
+
mixed:ruby:memcache-client 83.820000 1.190000 85.010000 ( 88.117077)
|
11
|
+
|
12
|
+
|
13
|
+
== 1.7.0, timeout, 1.8.6 (closest to default in Rails 2.3)
|
14
|
+
user system total real
|
15
|
+
set:plain:memcache-client 4.320000 2.280000 6.600000 ( 7.102900)
|
16
|
+
set:ruby:memcache-client 4.400000 2.300000 6.700000 ( 6.856992)
|
17
|
+
get:plain:memcache-client 9.890000 6.830000 16.720000 ( 16.984208)
|
18
|
+
get:ruby:memcache-client 10.040000 6.890000 16.930000 ( 17.141128)
|
19
|
+
multiget:ruby:memcache-client 5.350000 4.110000 9.460000 ( 9.542898)
|
20
|
+
missing:ruby:memcache-client 4.710000 3.180000 7.890000 ( 8.030969)
|
21
|
+
mixed:ruby:memcache-client 14.540000 9.200000 23.740000 ( 24.121824)
|
22
|
+
|
23
|
+
== 1.7.0, timeout, system_timer, 1.8.6
|
24
|
+
user system total real
|
25
|
+
set:plain:memcache-client 3.840000 0.640000 4.480000 ( 4.643790)
|
26
|
+
set:ruby:memcache-client 3.930000 0.650000 4.580000 ( 4.731868)
|
27
|
+
get:plain:memcache-client 8.320000 1.290000 9.610000 ( 9.903877)
|
28
|
+
get:ruby:memcache-client 8.460000 1.310000 9.770000 ( 9.986694)
|
29
|
+
multiget:ruby:memcache-client 4.250000 0.560000 4.810000 ( 4.935326)
|
30
|
+
missing:ruby:memcache-client 3.840000 0.640000 4.480000 ( 4.569696)
|
31
|
+
mixed:ruby:memcache-client 12.400000 1.960000 14.360000 ( 14.857924)
|
32
|
+
|
33
|
+
== 1.7.0, timeout, 1.9.1
|
34
|
+
user system total real
|
35
|
+
set:plain:memcache-client 2.130000 2.150000 4.280000 ( 3.774238)
|
36
|
+
set:ruby:memcache-client 2.230000 2.230000 4.460000 ( 3.883686)
|
37
|
+
get:plain:memcache-client 4.030000 4.250000 8.280000 ( 6.702740)
|
38
|
+
get:ruby:memcache-client 4.090000 4.220000 8.310000 ( 6.749134)
|
39
|
+
multiget:ruby:memcache-client 1.960000 1.840000 3.800000 ( 3.089448)
|
40
|
+
missing:ruby:memcache-client 2.110000 2.210000 4.320000 ( 3.659019)
|
41
|
+
mixed:ruby:memcache-client 6.400000 6.560000 12.960000 ( 11.116317)
|
42
|
+
|
43
|
+
== 1.7.0, no timeout, 1.9.1
|
44
|
+
user system total real
|
45
|
+
set:plain:memcache-client 0.560000 0.320000 0.880000 ( 1.849380)
|
46
|
+
set:ruby:memcache-client 0.630000 0.320000 0.950000 ( 1.968208)
|
47
|
+
get:plain:memcache-client 0.640000 0.330000 0.970000 ( 1.962473)
|
48
|
+
get:ruby:memcache-client 0.690000 0.320000 1.010000 ( 2.002295)
|
49
|
+
multiget:ruby:memcache-client 0.460000 0.110000 0.570000 ( 0.885827)
|
50
|
+
missing:ruby:memcache-client 0.530000 0.320000 0.850000 ( 1.721371)
|
51
|
+
mixed:ruby:memcache-client 1.340000 0.660000 2.000000 ( 3.973213)
|
52
|
+
|
53
|
+
== 1.7.0, no timeout, 1.8.6
|
54
|
+
user system total real
|
55
|
+
set:plain:memcache-client 1.220000 0.310000 1.530000 ( 2.763310)
|
56
|
+
set:ruby:memcache-client 1.270000 0.300000 1.570000 ( 2.806251)
|
57
|
+
get:plain:memcache-client 1.400000 0.300000 1.700000 ( 2.944343)
|
58
|
+
get:ruby:memcache-client 1.450000 0.310000 1.760000 ( 2.997234)
|
59
|
+
multiget:ruby:memcache-client 1.120000 0.110000 1.230000 ( 1.665716)
|
60
|
+
missing:ruby:memcache-client 1.160000 0.300000 1.460000 ( 2.683376)
|
61
|
+
mixed:ruby:memcache-client 2.760000 0.610000 3.370000 ( 5.851047)
|
62
|
+
|
63
|
+
== 1.7.1, timeout, 1.8.6, raw + gets SystemTimer
|
64
|
+
user system total real
|
65
|
+
set:plain:memcache-client 2.670000 0.510000 3.180000 ( 3.489509)
|
66
|
+
set:ruby:memcache-client 2.810000 0.530000 3.340000 ( 3.675955)
|
67
|
+
get:plain:memcache-client 4.380000 0.720000 5.100000 ( 5.400587)
|
68
|
+
get:ruby:memcache-client 4.490000 0.730000 5.220000 ( 5.477543)
|
69
|
+
multiget:ruby:memcache-client 2.570000 0.310000 2.880000 ( 3.034944)
|
70
|
+
missing:ruby:memcache-client 2.800000 0.530000 3.330000 ( 3.547073)
|
71
|
+
mixed:ruby:memcache-client 7.460000 1.250000 8.710000 ( 9.272177)
|
72
|
+
|
73
|
+
== 1.7.1, timeout, 1.9.1, raw + gets Timeout
|
74
|
+
user system total real
|
75
|
+
set:plain:memcache-client 1.370000 1.300000 2.670000 ( 2.708669)
|
76
|
+
set:ruby:memcache-client 1.400000 1.240000 2.640000 ( 2.713737)
|
77
|
+
get:plain:memcache-client 2.070000 2.020000 4.090000 ( 3.950879)
|
78
|
+
get:ruby:memcache-client 2.160000 2.090000 4.250000 ( 3.924613)
|
79
|
+
multiget:ruby:memcache-client 1.080000 0.820000 1.900000 ( 1.744107)
|
80
|
+
missing:ruby:memcache-client 1.330000 1.270000 2.600000 ( 2.547597)
|
81
|
+
mixed:ruby:memcache-client 3.540000 3.270000 6.810000 ( 6.735349)
|
82
|
+
|
83
|
+
== 1.7.1, timeout, 1.8.6, raw + gets SystemTimer, native binary search
|
84
|
+
user system total real
|
85
|
+
set:plain:memcache-client 1.840000 0.450000 2.290000 ( 2.651285)
|
86
|
+
set:ruby:memcache-client 1.960000 0.460000 2.420000 ( 2.712650)
|
87
|
+
get:plain:memcache-client 3.180000 0.630000 3.810000 ( 4.079930)
|
88
|
+
get:ruby:memcache-client 3.290000 0.640000 3.930000 ( 4.242648)
|
89
|
+
multiget:ruby:memcache-client 1.640000 0.250000 1.890000 ( 2.003687)
|
90
|
+
missing:ruby:memcache-client 1.940000 0.450000 2.390000 ( 2.619675)
|
91
|
+
mixed:ruby:memcache-client 5.360000 1.100000 6.460000 ( 7.040998)
|
92
|
+
|
93
|
+
== 1.7.2, timeout, 1.8.6, SystemTimer, native binary search
|
94
|
+
user system total real
|
95
|
+
set:plain:memcache-client 3.260000 0.590000 3.850000 ( 4.067382)
|
96
|
+
set:ruby:memcache-client 3.370000 0.590000 3.960000 ( 4.364004)
|
97
|
+
get:plain:memcache-client 6.740000 1.240000 7.980000 ( 8.586676)
|
98
|
+
get:ruby:memcache-client 6.780000 1.210000 7.990000 ( 8.423400)
|
99
|
+
multiget:ruby:memcache-client 3.480000 0.540000 4.020000 ( 4.288633)
|
100
|
+
missing:ruby:memcache-client 3.250000 0.590000 3.840000 ( 4.043602)
|
101
|
+
mixed:ruby:memcache-client 10.150000 1.810000 11.960000 ( 12.372054)
|
102
|
+
|
103
|
+
== 1.7.4, 1.8.6, buffered and non-blocking IO
|
104
|
+
user system total real
|
105
|
+
set:plain:memcache-client 2.450000 0.790000 3.240000 ( 3.397091)
|
106
|
+
set:ruby:memcache-client 2.490000 0.790000 3.280000 ( 3.555436)
|
107
|
+
get:plain:memcache-client 2.840000 0.810000 3.650000 ( 3.759695)
|
108
|
+
get:ruby:memcache-client 2.890000 0.790000 3.680000 ( 3.778011)
|
109
|
+
multiget:ruby:memcache-client 1.380000 0.280000 1.660000 ( 1.695290)
|
110
|
+
missing:ruby:memcache-client 2.380000 0.780000 3.160000 ( 3.251136)
|
111
|
+
mixed:ruby:memcache-client 5.360000 1.600000 6.960000 ( 7.189314)
|
112
|
+
|
113
|
+
== memcached 0.13 + libmemcached 0.25.4 versus memcache-client 1.7.4
|
114
|
+
|
115
|
+
user system total real
|
116
|
+
set:plain:noblock:memcached 0.090000 0.030000 0.120000 ( 0.277929)
|
117
|
+
set:plain:memcached 0.220000 0.270000 0.490000 ( 1.251547)
|
118
|
+
set:plain:memcache-client 0.610000 0.270000 0.880000 ( 1.670718)
|
119
|
+
set:ruby:noblock:memcached 0.150000 0.020000 0.170000 ( 0.309201)
|
120
|
+
set:ruby:memcached 0.300000 0.290000 0.590000 ( 1.390354)
|
121
|
+
set:ruby:memcache-client 0.670000 0.270000 0.940000 ( 1.713558)
|
122
|
+
get:plain:memcached 0.240000 0.270000 0.510000 ( 1.169909)
|
123
|
+
get:plain:memcache-client 0.850000 0.270000 1.120000 ( 1.885270)
|
124
|
+
get:ruby:memcached 0.270000 0.280000 0.550000 ( 1.229705)
|
125
|
+
get:ruby:memcache-client 0.890000 0.260000 1.150000 ( 1.861660)
|
126
|
+
multiget:ruby:memcached 0.190000 0.090000 0.280000 ( 0.396264)
|
127
|
+
multiget:ruby:memcache-client 0.530000 0.100000 0.630000 ( 0.901016)
|
128
|
+
missing:ruby:memcached 0.280000 0.290000 0.570000 ( 1.254400)
|
129
|
+
missing:ruby:memcached:inline 0.300000 0.290000 0.590000 ( 1.235122)
|
130
|
+
missing:ruby:memcache-client 0.570000 0.250000 0.820000 ( 1.461293)
|
131
|
+
mixed:ruby:noblock:memcached 0.540000 0.620000 1.160000 ( 2.429200)
|
132
|
+
mixed:ruby:memcached 0.580000 0.570000 1.150000 ( 2.610819)
|
133
|
+
mixed:ruby:memcache-client 1.580000 0.540000 2.120000 ( 3.632775)
|
134
|
+
|
135
|
+
== 1.7.6, 1.8.7 64-bit (Snow Leopard), SystemTimer
|
136
|
+
user system total real
|
137
|
+
set:plain:memcache-client 3.070000 0.380000 3.450000 ( 3.643275)
|
138
|
+
set:ruby:memcache-client 3.140000 0.370000 3.510000 ( 3.698602)
|
139
|
+
get:plain:memcache-client 3.480000 0.360000 3.840000 ( 3.983941)
|
140
|
+
get:ruby:memcache-client 3.540000 0.360000 3.900000 ( 4.034308)
|
141
|
+
multiget:ruby:memcache-client 1.690000 0.140000 1.830000 ( 1.889290)
|
142
|
+
missing:ruby:memcache-client 3.070000 0.360000 3.430000 ( 3.571754)
|
143
|
+
mixed:ruby:memcache-client 6.720000 0.750000 7.470000 ( 7.838771)
|
@@ -0,0 +1,134 @@
|
|
1
|
+
HERE = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift "#{HERE}/../lib"
|
3
|
+
#$LOAD_PATH << "/Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1"
|
4
|
+
|
5
|
+
require 'benchmark'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
$TESTING = true
|
10
|
+
require 'memcachedb' if not defined?(MemCacheDb)
|
11
|
+
|
12
|
+
class TestBenchmark < Test::Unit::TestCase
|
13
|
+
|
14
|
+
def setup
|
15
|
+
puts "Testing #{MemCacheDb::VERSION}"
|
16
|
+
# We'll use a simple @value to try to avoid spending time in Marshal,
|
17
|
+
# which is a constant penalty that both clients have to pay
|
18
|
+
@value = []
|
19
|
+
@marshalled = Marshal.dump(@value)
|
20
|
+
|
21
|
+
@opts = [
|
22
|
+
[{:servers=>['127.0.0.1:11211', '127.0.0.1:11212']}],
|
23
|
+
{
|
24
|
+
:namespace => "namespace",
|
25
|
+
# :no_reply => true,
|
26
|
+
# :timeout => nil,
|
27
|
+
}
|
28
|
+
]
|
29
|
+
@key1 = "Short"
|
30
|
+
@key2 = "Sym1-2-3::45"*8
|
31
|
+
@key3 = "Long"*40
|
32
|
+
@key4 = "Medium"*8
|
33
|
+
# 5 and 6 are only used for multiget miss test
|
34
|
+
@key5 = "Medium2"*8
|
35
|
+
@key6 = "Long3"*40
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_benchmark
|
39
|
+
Benchmark.bm(31) do |x|
|
40
|
+
|
41
|
+
n = 2500
|
42
|
+
# n = 1000
|
43
|
+
|
44
|
+
@m = MemCacheDb.new(*@opts)
|
45
|
+
x.report("set:plain:memcache-client") do
|
46
|
+
n.times do
|
47
|
+
@m.set @key1, @marshalled, 0, true
|
48
|
+
@m.set @key2, @marshalled, 0, true
|
49
|
+
@m.set @key3, @marshalled, 0, true
|
50
|
+
@m.set @key1, @marshalled, 0, true
|
51
|
+
@m.set @key2, @marshalled, 0, true
|
52
|
+
@m.set @key3, @marshalled, 0, true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@m = MemCacheDb.new(*@opts)
|
57
|
+
x.report("set:ruby:memcache-client") do
|
58
|
+
n.times do
|
59
|
+
@m.set @key1, @value
|
60
|
+
@m.set @key2, @value
|
61
|
+
@m.set @key3, @value
|
62
|
+
@m.set @key1, @value
|
63
|
+
@m.set @key2, @value
|
64
|
+
@m.set @key3, @value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@m = MemCacheDb.new(*@opts)
|
69
|
+
x.report("get:plain:memcache-client") do
|
70
|
+
n.times do
|
71
|
+
@m.get @key1, true
|
72
|
+
@m.get @key2, true
|
73
|
+
@m.get @key3, true
|
74
|
+
@m.get @key1, true
|
75
|
+
@m.get @key2, true
|
76
|
+
@m.get @key3, true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@m = MemCacheDb.new(*@opts)
|
81
|
+
x.report("get:ruby:memcache-client") do
|
82
|
+
n.times do
|
83
|
+
@m.get @key1
|
84
|
+
@m.get @key2
|
85
|
+
@m.get @key3
|
86
|
+
@m.get @key1
|
87
|
+
@m.get @key2
|
88
|
+
@m.get @key3
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
@m = MemCacheDb.new(*@opts)
|
93
|
+
x.report("multiget:ruby:memcache-client") do
|
94
|
+
n.times do
|
95
|
+
# We don't use the keys array because splat is slow
|
96
|
+
@m.get_multi @key1, @key2, @key3, @key4, @key5, @key6
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
@m = MemCacheDb.new(*@opts)
|
101
|
+
x.report("missing:ruby:memcache-client") do
|
102
|
+
n.times do
|
103
|
+
begin @m.delete @key1; rescue; end
|
104
|
+
begin @m.get @key1; rescue; end
|
105
|
+
begin @m.delete @key2; rescue; end
|
106
|
+
begin @m.get @key2; rescue; end
|
107
|
+
begin @m.delete @key3; rescue; end
|
108
|
+
begin @m.get @key3; rescue; end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
@m = MemCacheDb.new(*@opts)
|
113
|
+
x.report("mixed:ruby:memcache-client") do
|
114
|
+
n.times do
|
115
|
+
@m.set @key1, @value
|
116
|
+
@m.set @key2, @value
|
117
|
+
@m.set @key3, @value
|
118
|
+
@m.get @key1
|
119
|
+
@m.get @key2
|
120
|
+
@m.get @key3
|
121
|
+
@m.set @key1, @value
|
122
|
+
@m.get @key1
|
123
|
+
@m.set @key2, @value
|
124
|
+
@m.get @key2
|
125
|
+
@m.set @key3, @value
|
126
|
+
@m.get @key3
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
assert true
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,1220 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'logger'
|
3
|
+
require 'stringio'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'rubygems'
|
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
|
12
|
+
|
13
|
+
Thread.abort_on_exception = true
|
14
|
+
$TESTING = true
|
15
|
+
|
16
|
+
require File.dirname(__FILE__) + '/../lib/memcachedb' if not defined?(MemCacheDb)
|
17
|
+
|
18
|
+
class MemCacheDb
|
19
|
+
|
20
|
+
attr_writer :namespace
|
21
|
+
attr_writer :autofix_keys
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class FakeSocket
|
26
|
+
|
27
|
+
attr_reader :written, :data
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@written = StringIO.new
|
31
|
+
@data = StringIO.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def write(data)
|
35
|
+
@written.write data
|
36
|
+
end
|
37
|
+
|
38
|
+
def gets
|
39
|
+
@data.gets || 'STORED\n\r'
|
40
|
+
end
|
41
|
+
|
42
|
+
def read(arg)
|
43
|
+
@data.read arg
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class Test::Unit::TestCase
|
49
|
+
def requirement(bool, msg)
|
50
|
+
if bool
|
51
|
+
yield
|
52
|
+
else
|
53
|
+
puts msg
|
54
|
+
assert true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def memcached_running?
|
59
|
+
TCPSocket.new('localhost', 11211) rescue false
|
60
|
+
end
|
61
|
+
|
62
|
+
def xprofile(name, &block)
|
63
|
+
a = Time.now
|
64
|
+
block.call
|
65
|
+
Time.now - a
|
66
|
+
end
|
67
|
+
|
68
|
+
def profile(name, &block)
|
69
|
+
require 'ruby-prof'
|
70
|
+
a = Time.now
|
71
|
+
result = RubyProf.profile(&block)
|
72
|
+
time = Time.now - a
|
73
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
74
|
+
File.open("#{name}.html", 'w') do |f|
|
75
|
+
printer.print(f, :min_percent=>1)
|
76
|
+
end
|
77
|
+
time
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class FakeGroup
|
83
|
+
attr_reader :weight
|
84
|
+
attr_reader :servers
|
85
|
+
attr_reader :name
|
86
|
+
|
87
|
+
def initialize(server = FakeServer.new, name = nil)
|
88
|
+
@server = server
|
89
|
+
@servers = []
|
90
|
+
@servers << server
|
91
|
+
@weight = 1
|
92
|
+
@name = name || (server.host.to_s + server.port.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
def alive?
|
96
|
+
master.alive?
|
97
|
+
end
|
98
|
+
|
99
|
+
def master
|
100
|
+
@server
|
101
|
+
end
|
102
|
+
|
103
|
+
def next_slave
|
104
|
+
@server
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class FakeServer
|
109
|
+
|
110
|
+
attr_accessor :host, :port, :socket, :weight, :multithread, :status
|
111
|
+
|
112
|
+
def initialize(socket = nil)
|
113
|
+
@closed = false
|
114
|
+
@host = 'example.com'
|
115
|
+
@port = 11211
|
116
|
+
@socket = socket || FakeSocket.new
|
117
|
+
@weight = 1
|
118
|
+
@multithread = true
|
119
|
+
@status = "CONNECTED"
|
120
|
+
end
|
121
|
+
|
122
|
+
def close
|
123
|
+
# begin
|
124
|
+
# raise "Already closed"
|
125
|
+
# rescue => e
|
126
|
+
# puts e.backtrace.join("\n")
|
127
|
+
# end
|
128
|
+
@closed = true
|
129
|
+
@socket = nil
|
130
|
+
@status = "NOT CONNECTED"
|
131
|
+
end
|
132
|
+
|
133
|
+
def alive?
|
134
|
+
# puts "I'm #{@closed ? 'dead' : 'alive'}"
|
135
|
+
!@closed
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
class TestMemCache < Test::Unit::TestCase
|
141
|
+
|
142
|
+
def setup
|
143
|
+
@cache = MemCacheDb.new( {}, {:namespace => 'my_namespace'})
|
144
|
+
end
|
145
|
+
|
146
|
+
def util_setup_fake_server
|
147
|
+
server = FakeServer.new
|
148
|
+
server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
|
149
|
+
server.socket.data.write "\004\b\"\0170123456789\r\n"
|
150
|
+
server.socket.data.write "END\r\n"
|
151
|
+
server.socket.data.rewind
|
152
|
+
|
153
|
+
group = FakeGroup.new(server)
|
154
|
+
@cache.groups = []
|
155
|
+
@cache.groups << group
|
156
|
+
|
157
|
+
return server
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def test_add
|
162
|
+
server = FakeServer.new
|
163
|
+
server.socket.data.write "STORED\r\n"
|
164
|
+
server.socket.data.rewind
|
165
|
+
|
166
|
+
group = FakeGroup.new(server)
|
167
|
+
@cache.groups = []
|
168
|
+
@cache.groups << group
|
169
|
+
|
170
|
+
@cache.add 'key', 'value'
|
171
|
+
|
172
|
+
dumped = Marshal.dump('value')
|
173
|
+
|
174
|
+
expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
175
|
+
assert_equal expected, server.socket.written.string
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
def test_performance
|
180
|
+
requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
|
181
|
+
|
182
|
+
cache = MemCacheDb.new({:servers=>['localhost:11211',"localhost:11212"]})
|
183
|
+
cache.add('a', 1, 120)
|
184
|
+
with = xprofile 'get' do
|
185
|
+
1000.times do
|
186
|
+
cache.get('a')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
puts ''
|
190
|
+
puts "1000 gets with socket timeout: #{with} sec"
|
191
|
+
|
192
|
+
cache = MemCacheDb.new([{:servers=>['localhost:11211',"localhost:11212"]}], :timeout => nil)
|
193
|
+
cache.add('a', 1, 120)
|
194
|
+
without = xprofile 'get' do
|
195
|
+
1000.times do
|
196
|
+
cache.get('a')
|
197
|
+
end
|
198
|
+
end
|
199
|
+
puts "1000 gets without socket timeout: #{without} sec"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
def test_get_multi_with_server_failure
|
205
|
+
@cache = MemCacheDb.new({:server=>'localhost:1'}, {:namespace => 'my_namespace', :logger=>nil})
|
206
|
+
s1 = FakeServer.new
|
207
|
+
s2 = FakeServer.new
|
208
|
+
|
209
|
+
g1 = FakeGroup.new(s1, "1")
|
210
|
+
g2 = FakeGroup.new(s2, "2")
|
211
|
+
|
212
|
+
# Write two messages to the socket to test failover
|
213
|
+
s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
|
214
|
+
s1.socket.data.rewind
|
215
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
216
|
+
s2.socket.data.rewind
|
217
|
+
|
218
|
+
@cache.groups = [g1, g2]
|
219
|
+
|
220
|
+
assert s1.alive?
|
221
|
+
assert s2.alive?
|
222
|
+
# a maps to s1, the rest map to s2
|
223
|
+
value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
|
224
|
+
assert_equal({'a'=>'0123456789'}, value)
|
225
|
+
assert s1.alive?
|
226
|
+
assert !s2.alive?
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def test_consistent_hashing
|
231
|
+
requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
|
232
|
+
|
233
|
+
flexmock(MemCacheDb::Server).new_instances.should_receive(:alive?).and_return(true)
|
234
|
+
|
235
|
+
# Setup a continuum of two servers
|
236
|
+
@cache.groups = [FakeGroup.new(FakeServer.new, "1"), FakeGroup.new(FakeServer.new, "2"), FakeGroup.new(FakeServer.new, "3")]
|
237
|
+
|
238
|
+
keys = []
|
239
|
+
1000.times do |idx|
|
240
|
+
keys << idx.to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
before_continuum = keys.map {|key| @cache.get_group_for_key(key) }
|
244
|
+
|
245
|
+
@cache.groups = [FakeGroup.new(FakeServer.new, "1"), FakeGroup.new(FakeServer.new, "2"), FakeGroup.new(FakeServer.new, "3"), FakeGroup.new(FakeServer.new, "4")]
|
246
|
+
|
247
|
+
after_continuum = keys.map {|key| @cache.get_group_for_key(key) }
|
248
|
+
|
249
|
+
same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].name == a[1].name }.size
|
250
|
+
|
251
|
+
# With continuum, we should see about 75% of the keys map to the same server
|
252
|
+
# With modulo, we would see about 25%.
|
253
|
+
assert same_count > 700
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_cache_get_with_failover
|
258
|
+
@cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
|
259
|
+
s1 = FakeServer.new
|
260
|
+
s2 = FakeServer.new
|
261
|
+
|
262
|
+
g1 = FakeGroup.new(s1)
|
263
|
+
g2 = FakeGroup.new(s2)
|
264
|
+
|
265
|
+
# Write two messages to the socket to test failover
|
266
|
+
s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
|
267
|
+
s1.socket.data.rewind
|
268
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
269
|
+
s2.socket.data.rewind
|
270
|
+
|
271
|
+
@cache.instance_variable_set(:@failover, true)
|
272
|
+
@cache.groups = [g1, g2]
|
273
|
+
|
274
|
+
assert s1.alive?
|
275
|
+
assert s2.alive?
|
276
|
+
@cache.get('foo')
|
277
|
+
assert s1.alive?
|
278
|
+
assert !s2.alive?
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_cache_get_without_failover
|
282
|
+
s1 = FakeServer.new
|
283
|
+
s2 = FakeServer.new
|
284
|
+
|
285
|
+
g1 = FakeGroup.new(s1)
|
286
|
+
g2 = FakeGroup.new(s2)
|
287
|
+
|
288
|
+
s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
|
289
|
+
s1.socket.data.rewind
|
290
|
+
s2.socket.data.write "bogus response\r\nbogus response\r\n"
|
291
|
+
s2.socket.data.rewind
|
292
|
+
|
293
|
+
@cache.instance_variable_set(:@failover, false)
|
294
|
+
@cache.groups = [g1, g2]
|
295
|
+
|
296
|
+
assert s1.alive?
|
297
|
+
assert s2.alive?
|
298
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
299
|
+
@cache.get('foo')
|
300
|
+
end
|
301
|
+
assert s1.alive?
|
302
|
+
assert !s2.alive?
|
303
|
+
|
304
|
+
assert_equal "No servers available", e.message
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_cache_get
|
308
|
+
server = util_setup_fake_server
|
309
|
+
|
310
|
+
assert_equal "\004\b\"\0170123456789",
|
311
|
+
@cache.cache_get(server, 'my_namespace:key')
|
312
|
+
|
313
|
+
assert_equal "get my_namespace:key\r\n",
|
314
|
+
server.socket.written.string
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_cache_get_EOF
|
318
|
+
server = util_setup_fake_server
|
319
|
+
server.socket.data.string = ''
|
320
|
+
|
321
|
+
e = assert_raise IndexError do
|
322
|
+
@cache.cache_get server, 'my_namespace:key'
|
323
|
+
end
|
324
|
+
|
325
|
+
assert_equal "No connection to server (NOT CONNECTED)", e.message
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_cache_get_bad_state
|
329
|
+
server = FakeServer.new
|
330
|
+
|
331
|
+
# Write two messages to the socket to test failover
|
332
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
333
|
+
server.socket.data.rewind
|
334
|
+
|
335
|
+
group = FakeGroup.new(server)
|
336
|
+
@cache.groups = []
|
337
|
+
@cache.groups << group
|
338
|
+
|
339
|
+
e = assert_raise IndexError do
|
340
|
+
@cache.cache_get(server, 'my_namespace:key')
|
341
|
+
end
|
342
|
+
|
343
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
344
|
+
|
345
|
+
assert !server.alive?
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_cache_get_miss
|
349
|
+
socket = FakeSocket.new
|
350
|
+
socket.data.write "END\r\n"
|
351
|
+
socket.data.rewind
|
352
|
+
server = FakeServer.new socket
|
353
|
+
|
354
|
+
assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
|
355
|
+
|
356
|
+
assert_equal "get my_namespace:key\r\n",
|
357
|
+
socket.written.string
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_cache_get_multi
|
361
|
+
server = util_setup_fake_server
|
362
|
+
server.socket.data.write "VALUE foo 0 7\r\n"
|
363
|
+
server.socket.data.write "\004\b\"\bfoo\r\n"
|
364
|
+
server.socket.data.write "VALUE bar 0 7\r\n"
|
365
|
+
server.socket.data.write "\004\b\"\bbar\r\n"
|
366
|
+
server.socket.data.write "END\r\n"
|
367
|
+
server.socket.data.rewind
|
368
|
+
|
369
|
+
result = @cache.cache_get_multi server, 'foo bar baz'
|
370
|
+
|
371
|
+
assert_equal 2, result.length
|
372
|
+
assert_equal "\004\b\"\bfoo", result['foo']
|
373
|
+
assert_equal "\004\b\"\bbar", result['bar']
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_cache_get_multi_EOF
|
377
|
+
server = util_setup_fake_server
|
378
|
+
server.socket.data.string = ''
|
379
|
+
|
380
|
+
e = assert_raise IndexError do
|
381
|
+
@cache.cache_get_multi server, 'my_namespace:key'
|
382
|
+
end
|
383
|
+
|
384
|
+
assert_equal "No connection to server (NOT CONNECTED)", e.message
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_cache_get_multi_bad_state
|
388
|
+
server = FakeServer.new
|
389
|
+
|
390
|
+
# Write two messages to the socket to test failover
|
391
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
392
|
+
server.socket.data.rewind
|
393
|
+
|
394
|
+
group = FakeGroup.new(server)
|
395
|
+
@cache.groups = []
|
396
|
+
@cache.groups << group
|
397
|
+
|
398
|
+
e = assert_raise IndexError do
|
399
|
+
@cache.cache_get_multi server, 'my_namespace:key'
|
400
|
+
end
|
401
|
+
|
402
|
+
assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
|
403
|
+
|
404
|
+
assert !server.alive?
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_multithread_error
|
408
|
+
server = FakeServer.new
|
409
|
+
server.multithread = false
|
410
|
+
|
411
|
+
@cache = MemCacheDb.new(['localhost:1'], :multithread => false)
|
412
|
+
|
413
|
+
server.socket.data.write "bogus response\r\nbogus response\r\n"
|
414
|
+
server.socket.data.rewind
|
415
|
+
|
416
|
+
group = FakeGroup.new(server)
|
417
|
+
@cache.groups = []
|
418
|
+
@cache.groups << group
|
419
|
+
|
420
|
+
assert_nothing_raised do
|
421
|
+
@cache.set 'a', 1
|
422
|
+
end
|
423
|
+
|
424
|
+
passed = true
|
425
|
+
Thread.new do
|
426
|
+
begin
|
427
|
+
@cache.set 'b', 2
|
428
|
+
passed = false
|
429
|
+
rescue MemCacheDb::MemCacheDbError => me
|
430
|
+
passed = me.message =~ /multiple threads/
|
431
|
+
end
|
432
|
+
end
|
433
|
+
assert passed
|
434
|
+
end
|
435
|
+
|
436
|
+
def test_initialize
|
437
|
+
cache = MemCacheDb.new ([], {:namespace => 'my_namespace', :readonly => true})
|
438
|
+
|
439
|
+
assert_equal 'my_namespace', cache.namespace
|
440
|
+
assert_equal true, cache.readonly?
|
441
|
+
assert_equal true, cache.groups.empty?
|
442
|
+
end
|
443
|
+
|
444
|
+
def test_initialize_compatible
|
445
|
+
cache = MemCacheDb.new ([:servers=>['localhost:11211', 'localhost:11212']],
|
446
|
+
:namespace => 'my_namespace', :readonly => true)
|
447
|
+
|
448
|
+
assert_equal 'my_namespace', cache.namespace
|
449
|
+
assert_equal true, cache.readonly?
|
450
|
+
assert_equal false, cache.groups.empty?
|
451
|
+
end
|
452
|
+
|
453
|
+
def test_initialize_compatible_no_hash
|
454
|
+
cache = MemCacheDb.new (:servers=>['localhost:11211', 'localhost:11212'])
|
455
|
+
|
456
|
+
assert_equal nil, cache.namespace
|
457
|
+
assert_equal false, cache.readonly?
|
458
|
+
assert_equal false, cache.groups.empty?
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_initialize_compatible_bad_arg
|
462
|
+
e = assert_raise ArgumentError do
|
463
|
+
cache = MemCacheDb.new Object.new
|
464
|
+
end
|
465
|
+
|
466
|
+
assert_equal 'first argument must be Array, Hash', e.message
|
467
|
+
end
|
468
|
+
|
469
|
+
def test_initialize_too_many_args
|
470
|
+
assert_raises ArgumentError do
|
471
|
+
MemCacheDb.new 1, 2, 3
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
|
476
|
+
def test_initialize_multiple_servers
|
477
|
+
cache = MemCacheDb.new([{:servers=>['localhost:11211', 'localhost:11212']}, {:servers=>['localhost:11211', 'localhost:11212']}],
|
478
|
+
{:namespace => 'my_namespace', :readonly => true})
|
479
|
+
|
480
|
+
assert_equal 'my_namespace', cache.namespace
|
481
|
+
assert_equal true, cache.readonly?
|
482
|
+
assert_equal false, cache.groups.empty?
|
483
|
+
assert !cache.instance_variable_get(:@continuum).empty?
|
484
|
+
end
|
485
|
+
|
486
|
+
def test_decr
|
487
|
+
server = FakeServer.new
|
488
|
+
server.socket.data.write "5\r\n"
|
489
|
+
server.socket.data.rewind
|
490
|
+
|
491
|
+
group = FakeGroup.new(server)
|
492
|
+
@cache.groups = []
|
493
|
+
@cache.groups << group
|
494
|
+
|
495
|
+
value = @cache.decr 'key'
|
496
|
+
|
497
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
498
|
+
@cache.groups.first.servers.first.socket.written.string
|
499
|
+
|
500
|
+
assert_equal 5, value
|
501
|
+
end
|
502
|
+
|
503
|
+
def test_decr_not_found
|
504
|
+
server = FakeServer.new
|
505
|
+
server.socket.data.write "NOT_FOUND\r\n"
|
506
|
+
server.socket.data.rewind
|
507
|
+
|
508
|
+
group = FakeGroup.new(server)
|
509
|
+
@cache.groups = []
|
510
|
+
@cache.groups << group
|
511
|
+
|
512
|
+
value = @cache.decr 'key'
|
513
|
+
|
514
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
515
|
+
@cache.groups.first.servers.first.socket.written.string
|
516
|
+
|
517
|
+
assert_equal nil, value
|
518
|
+
end
|
519
|
+
|
520
|
+
def test_decr_space_padding
|
521
|
+
server = FakeServer.new
|
522
|
+
server.socket.data.write "5 \r\n"
|
523
|
+
server.socket.data.rewind
|
524
|
+
|
525
|
+
group = FakeGroup.new(server)
|
526
|
+
@cache.groups = []
|
527
|
+
@cache.groups << group
|
528
|
+
|
529
|
+
value = @cache.decr 'key'
|
530
|
+
|
531
|
+
assert_equal "decr my_namespace:key 1\r\n",
|
532
|
+
@cache.groups.first.servers.first.socket.written.string
|
533
|
+
|
534
|
+
assert_equal 5, value
|
535
|
+
end
|
536
|
+
|
537
|
+
def test_get
|
538
|
+
util_setup_fake_server
|
539
|
+
|
540
|
+
value = @cache.get 'key'
|
541
|
+
|
542
|
+
assert_equal "get my_namespace:key\r\n",
|
543
|
+
@cache.groups.first.servers.first.socket.written.string
|
544
|
+
|
545
|
+
assert_equal '0123456789', value
|
546
|
+
end
|
547
|
+
|
548
|
+
|
549
|
+
def test_fetch_without_a_block
|
550
|
+
server = FakeServer.new
|
551
|
+
server.socket.data.write "END\r\n"
|
552
|
+
server.socket.data.rewind
|
553
|
+
|
554
|
+
group = FakeGroup.new(server)
|
555
|
+
@cache.groups = [group]
|
556
|
+
|
557
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
|
558
|
+
|
559
|
+
value = @cache.fetch('key', 1)
|
560
|
+
assert_equal nil, value
|
561
|
+
end
|
562
|
+
|
563
|
+
def test_fetch_miss
|
564
|
+
server = FakeServer.new
|
565
|
+
server.socket.data.write "END\r\n"
|
566
|
+
server.socket.data.rewind
|
567
|
+
|
568
|
+
group = FakeGroup.new(server)
|
569
|
+
@cache.groups = [group]
|
570
|
+
|
571
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
|
572
|
+
flexmock(@cache).should_receive(:add).with('key', 'value', 1, false)
|
573
|
+
|
574
|
+
value = @cache.fetch('key', 1) { 'value' }
|
575
|
+
|
576
|
+
assert_equal 'value', value
|
577
|
+
end
|
578
|
+
|
579
|
+
def test_fetch_hit
|
580
|
+
server = FakeServer.new
|
581
|
+
server.socket.data.write "END\r\n"
|
582
|
+
server.socket.data.rewind
|
583
|
+
|
584
|
+
group = FakeGroup.new(server)
|
585
|
+
@cache.groups = [group]
|
586
|
+
|
587
|
+
flexmock(@cache).should_receive(:get).with('key', false).and_return('value')
|
588
|
+
flexmock(@cache).should_receive(:add).never
|
589
|
+
|
590
|
+
value = @cache.fetch('key', 1) { raise 'Should not be called.' }
|
591
|
+
|
592
|
+
assert_equal 'value', value
|
593
|
+
end
|
594
|
+
|
595
|
+
def test_get_bad_key
|
596
|
+
util_setup_fake_server
|
597
|
+
assert_raise ArgumentError do @cache.get 'k y' end
|
598
|
+
|
599
|
+
util_setup_fake_server
|
600
|
+
assert_raise ArgumentError do @cache.get 'k' * 250 end
|
601
|
+
end
|
602
|
+
|
603
|
+
def test_get_cache_get_IOError
|
604
|
+
socket = Object.new
|
605
|
+
def socket.write(arg) raise IOError, 'some io error'; end
|
606
|
+
server = FakeServer.new socket
|
607
|
+
|
608
|
+
group = FakeGroup.new(server)
|
609
|
+
@cache.groups = []
|
610
|
+
@cache.groups << group
|
611
|
+
|
612
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
613
|
+
@cache.get 'my_namespace:key'
|
614
|
+
end
|
615
|
+
|
616
|
+
assert_equal 'some io error', e.message
|
617
|
+
end
|
618
|
+
|
619
|
+
|
620
|
+
def test_get_cache_get_SystemCallError
|
621
|
+
socket = Object.new
|
622
|
+
def socket.write(arg) raise SystemCallError, 'some syscall error'; end
|
623
|
+
server = FakeServer.new socket
|
624
|
+
|
625
|
+
group = FakeGroup.new(server)
|
626
|
+
@cache.groups = []
|
627
|
+
@cache.groups << group
|
628
|
+
|
629
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
630
|
+
@cache.get 'my_namespace:key'
|
631
|
+
end
|
632
|
+
|
633
|
+
assert_equal 'unknown error - some syscall error', e.message
|
634
|
+
end
|
635
|
+
|
636
|
+
def test_get_no_connection
|
637
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
638
|
+
@cache.groups = [:servers=>'localhost:1']
|
639
|
+
end
|
640
|
+
|
641
|
+
assert_match(/^No Master Server found/, e.message)
|
642
|
+
end
|
643
|
+
|
644
|
+
def test_get_no_servers
|
645
|
+
@cache.groups = []
|
646
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
647
|
+
@cache.get 'key'
|
648
|
+
end
|
649
|
+
|
650
|
+
assert_equal 'No active servers', e.message
|
651
|
+
end
|
652
|
+
|
653
|
+
def test_get_multi
|
654
|
+
server = FakeServer.new
|
655
|
+
server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
|
656
|
+
server.socket.data.write "\004\b\"\0170123456789\r\n"
|
657
|
+
server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
|
658
|
+
server.socket.data.write "\004\b\"\0179876543210\r\n"
|
659
|
+
server.socket.data.write "END\r\n"
|
660
|
+
server.socket.data.rewind
|
661
|
+
|
662
|
+
group = FakeGroup.new(server)
|
663
|
+
@cache.groups = []
|
664
|
+
@cache.groups << group
|
665
|
+
|
666
|
+
values = @cache.get_multi 'key', 'keyb'
|
667
|
+
|
668
|
+
assert_equal "get my_namespace:key my_namespace:keyb\r\n",
|
669
|
+
server.socket.written.string
|
670
|
+
|
671
|
+
expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
|
672
|
+
|
673
|
+
assert_equal expected.sort, values.sort
|
674
|
+
end
|
675
|
+
|
676
|
+
def test_get_raw
|
677
|
+
server = FakeServer.new
|
678
|
+
server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
|
679
|
+
server.socket.data.write "0123456789\r\n"
|
680
|
+
server.socket.data.write "END\r\n"
|
681
|
+
server.socket.data.rewind
|
682
|
+
|
683
|
+
group = FakeGroup.new(server)
|
684
|
+
@cache.groups = []
|
685
|
+
@cache.groups << group
|
686
|
+
|
687
|
+
|
688
|
+
value = @cache.get 'key', true
|
689
|
+
|
690
|
+
assert_equal "get my_namespace:key\r\n",
|
691
|
+
@cache.groups.first.servers.first.socket.written.string
|
692
|
+
|
693
|
+
assert_equal '0123456789', value
|
694
|
+
end
|
695
|
+
|
696
|
+
def test_get_server_for_key_no_servers
|
697
|
+
@cache.groups = []
|
698
|
+
|
699
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
700
|
+
@cache.get_group_for_key 'key'
|
701
|
+
end
|
702
|
+
|
703
|
+
assert_equal 'No servers available', e.message
|
704
|
+
end
|
705
|
+
|
706
|
+
def test_get_server_for_key_spaces
|
707
|
+
e = assert_raise ArgumentError do
|
708
|
+
@cache.get_group_for_key 'space key'
|
709
|
+
end
|
710
|
+
assert_equal 'illegal character in key "space key"', e.message
|
711
|
+
end
|
712
|
+
|
713
|
+
|
714
|
+
def test_get_server_for_key_length
|
715
|
+
@cache.groups = [FakeGroup.new]
|
716
|
+
@cache.get_group_for_key 'x' * 250
|
717
|
+
long_key = 'x' * 251
|
718
|
+
e = assert_raise ArgumentError do
|
719
|
+
@cache.get_group_for_key long_key
|
720
|
+
end
|
721
|
+
assert_equal "key too long #{long_key.inspect}", e.message
|
722
|
+
end
|
723
|
+
|
724
|
+
def test_incr
|
725
|
+
server = FakeServer.new
|
726
|
+
server.socket.data.write "5\r\n"
|
727
|
+
server.socket.data.rewind
|
728
|
+
|
729
|
+
group = FakeGroup.new(server)
|
730
|
+
@cache.groups = []
|
731
|
+
@cache.groups << group
|
732
|
+
|
733
|
+
value = @cache.incr 'key'
|
734
|
+
|
735
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
736
|
+
@cache.groups.first.servers.first.socket.written.string
|
737
|
+
|
738
|
+
assert_equal 5, value
|
739
|
+
end
|
740
|
+
|
741
|
+
def test_incr_not_found
|
742
|
+
server = FakeServer.new
|
743
|
+
server.socket.data.write "NOT_FOUND\r\n"
|
744
|
+
server.socket.data.rewind
|
745
|
+
|
746
|
+
group = FakeGroup.new(server)
|
747
|
+
@cache.groups = []
|
748
|
+
@cache.groups << group
|
749
|
+
|
750
|
+
value = @cache.incr 'key'
|
751
|
+
|
752
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
753
|
+
@cache.groups.first.servers.first.socket.written.string
|
754
|
+
|
755
|
+
assert_equal nil, value
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_incr_space_padding
|
759
|
+
server = FakeServer.new
|
760
|
+
server.socket.data.write "5 \r\n"
|
761
|
+
server.socket.data.rewind
|
762
|
+
|
763
|
+
group = FakeGroup.new(server)
|
764
|
+
@cache.groups = []
|
765
|
+
@cache.groups << group
|
766
|
+
|
767
|
+
value = @cache.incr 'key'
|
768
|
+
|
769
|
+
assert_equal "incr my_namespace:key 1\r\n",
|
770
|
+
@cache.groups.first.servers.first.socket.written.string
|
771
|
+
|
772
|
+
assert_equal 5, value
|
773
|
+
end
|
774
|
+
|
775
|
+
def test_make_cache_key
|
776
|
+
assert_equal 'my_namespace:key', @cache.make_cache_key('key')
|
777
|
+
@cache.namespace = nil
|
778
|
+
assert_equal 'key', @cache.make_cache_key('key')
|
779
|
+
end
|
780
|
+
|
781
|
+
def test_make_cache_key_without_autofix
|
782
|
+
@cache.autofix_keys = false
|
783
|
+
|
784
|
+
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"
|
785
|
+
hash = Digest::SHA1.hexdigest(key)
|
786
|
+
@cache.namespace = nil
|
787
|
+
assert_equal key, @cache.make_cache_key(key)
|
788
|
+
end
|
789
|
+
|
790
|
+
def test_make_cache_key_with_autofix
|
791
|
+
@cache.autofix_keys = true
|
792
|
+
|
793
|
+
@cache.namespace = "my_namespace"
|
794
|
+
assert_equal 'my_namespace:key', @cache.make_cache_key('key')
|
795
|
+
@cache.namespace = nil
|
796
|
+
assert_equal 'key', @cache.make_cache_key('key')
|
797
|
+
|
798
|
+
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"
|
799
|
+
hash = Digest::SHA1.hexdigest(key)
|
800
|
+
@cache.namespace = "my_namespace"
|
801
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
802
|
+
@cache.namespace = nil
|
803
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
804
|
+
|
805
|
+
key = "a short key with spaces"
|
806
|
+
hash = Digest::SHA1.hexdigest(key)
|
807
|
+
@cache.namespace = "my_namespace"
|
808
|
+
assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
|
809
|
+
@cache.namespace = nil
|
810
|
+
assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
|
811
|
+
|
812
|
+
# namespace + separator + key > 250
|
813
|
+
key = 'k' * 240
|
814
|
+
hash = Digest::SHA1.hexdigest(key)
|
815
|
+
@cache.namespace = 'n' * 10
|
816
|
+
assert_equal "#{@cache.namespace}:#{hash}-autofixed", @cache.make_cache_key(key)
|
817
|
+
end
|
818
|
+
|
819
|
+
def test_servers
|
820
|
+
server = FakeServer.new
|
821
|
+
group = FakeGroup.new(server)
|
822
|
+
@cache.groups = []
|
823
|
+
@cache.groups << group
|
824
|
+
assert_equal [group], @cache.groups
|
825
|
+
end
|
826
|
+
|
827
|
+
def test_set
|
828
|
+
server = FakeServer.new
|
829
|
+
server.socket.data.write "STORED\r\n"
|
830
|
+
server.socket.data.rewind
|
831
|
+
group = FakeGroup.new(server)
|
832
|
+
@cache.groups = []
|
833
|
+
@cache.groups << group
|
834
|
+
|
835
|
+
@cache.set 'key', 'value'
|
836
|
+
|
837
|
+
dumped = Marshal.dump('value')
|
838
|
+
expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
839
|
+
# expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
|
840
|
+
assert_equal expected, server.socket.written.string
|
841
|
+
end
|
842
|
+
|
843
|
+
def test_set_expiry
|
844
|
+
server = FakeServer.new
|
845
|
+
server.socket.data.write "STORED\r\n"
|
846
|
+
server.socket.data.rewind
|
847
|
+
group = FakeGroup.new(server)
|
848
|
+
@cache.groups = []
|
849
|
+
@cache.groups << group
|
850
|
+
|
851
|
+
@cache.set 'key', 'value', 5
|
852
|
+
|
853
|
+
dumped = Marshal.dump('value')
|
854
|
+
expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
|
855
|
+
assert_equal expected, server.socket.written.string
|
856
|
+
end
|
857
|
+
|
858
|
+
def test_set_raw
|
859
|
+
server = FakeServer.new
|
860
|
+
server.socket.data.write "STORED\r\n"
|
861
|
+
server.socket.data.rewind
|
862
|
+
group = FakeGroup.new(server)
|
863
|
+
@cache.groups = []
|
864
|
+
@cache.groups << group
|
865
|
+
|
866
|
+
@cache.set 'key', 'value', 0, true
|
867
|
+
|
868
|
+
expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
|
869
|
+
assert_equal expected, server.socket.written.string
|
870
|
+
end
|
871
|
+
|
872
|
+
def test_set_readonly
|
873
|
+
cache = MemCacheDb.new( [FakeGroup.new], :readonly => true)
|
874
|
+
|
875
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
876
|
+
cache.set 'key', 'value'
|
877
|
+
end
|
878
|
+
|
879
|
+
assert_equal 'Update of readonly cache', e.message
|
880
|
+
end
|
881
|
+
|
882
|
+
def test_check_size_on
|
883
|
+
cache = MemCacheDb.new( [FakeGroup.new], :check_size => true)
|
884
|
+
|
885
|
+
server = FakeServer.new
|
886
|
+
server.socket.data.write "STORED\r\n"
|
887
|
+
server.socket.data.rewind
|
888
|
+
|
889
|
+
group = FakeGroup.new(server)
|
890
|
+
cache.groups = []
|
891
|
+
cache.groups << group
|
892
|
+
|
893
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
894
|
+
cache.set 'key', 'v' * 1048577
|
895
|
+
end
|
896
|
+
|
897
|
+
assert_equal 'Value too large, MemCacheDbd can only store 1MB of data per key', e.message
|
898
|
+
end
|
899
|
+
|
900
|
+
def test_check_size_off
|
901
|
+
cache = MemCacheDb.new([FakeGroup.new], :check_size => false)
|
902
|
+
|
903
|
+
server = FakeServer.new
|
904
|
+
server.socket.data.write "STORED\r\n"
|
905
|
+
server.socket.data.rewind
|
906
|
+
|
907
|
+
group = FakeGroup.new(server)
|
908
|
+
cache.groups = []
|
909
|
+
cache.groups << group
|
910
|
+
|
911
|
+
assert_nothing_raised do
|
912
|
+
cache.set 'key', 'v' * 1048577
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
def test_set_too_big
|
917
|
+
server = FakeServer.new
|
918
|
+
|
919
|
+
# Write two messages to the socket to test failover
|
920
|
+
server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
|
921
|
+
server.socket.data.rewind
|
922
|
+
|
923
|
+
group = FakeGroup.new(server)
|
924
|
+
@cache.groups = []
|
925
|
+
@cache.groups << group
|
926
|
+
|
927
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
928
|
+
@cache.set 'key', 'v'
|
929
|
+
end
|
930
|
+
|
931
|
+
assert_match(/object too large for cache/, e.message)
|
932
|
+
end
|
933
|
+
|
934
|
+
def test_prepend
|
935
|
+
server = FakeServer.new
|
936
|
+
server.socket.data.write "STORED\r\n"
|
937
|
+
server.socket.data.rewind
|
938
|
+
group = FakeGroup.new(server)
|
939
|
+
@cache.groups = []
|
940
|
+
@cache.groups << group
|
941
|
+
|
942
|
+
@cache.prepend 'key', 'value'
|
943
|
+
|
944
|
+
dumped = Marshal.dump('value')
|
945
|
+
|
946
|
+
expected = "prepend my_namespace:key 0 0 5\r\nvalue\r\n"
|
947
|
+
assert_equal expected, server.socket.written.string
|
948
|
+
end
|
949
|
+
|
950
|
+
def test_append
|
951
|
+
server = FakeServer.new
|
952
|
+
server.socket.data.write "STORED\r\n"
|
953
|
+
server.socket.data.rewind
|
954
|
+
group = FakeGroup.new(server)
|
955
|
+
@cache.groups = []
|
956
|
+
@cache.groups << group
|
957
|
+
|
958
|
+
@cache.append 'key', 'value'
|
959
|
+
|
960
|
+
expected = "append my_namespace:key 0 0 5\r\nvalue\r\n"
|
961
|
+
assert_equal expected, server.socket.written.string
|
962
|
+
end
|
963
|
+
|
964
|
+
def test_replace
|
965
|
+
server = FakeServer.new
|
966
|
+
server.socket.data.write "STORED\r\n"
|
967
|
+
server.socket.data.rewind
|
968
|
+
group = FakeGroup.new(server)
|
969
|
+
@cache.groups = []
|
970
|
+
@cache.groups << group
|
971
|
+
|
972
|
+
@cache.replace 'key', 'value', 150
|
973
|
+
|
974
|
+
dumped = Marshal.dump('value')
|
975
|
+
|
976
|
+
expected = "replace my_namespace:key 0 150 #{dumped.length}\r\n#{dumped}\r\n"
|
977
|
+
assert_equal expected, server.socket.written.string
|
978
|
+
end
|
979
|
+
|
980
|
+
def test_add_exists
|
981
|
+
server = FakeServer.new
|
982
|
+
server.socket.data.write "NOT_STORED\r\n"
|
983
|
+
server.socket.data.rewind
|
984
|
+
group = FakeGroup.new(server)
|
985
|
+
@cache.groups = []
|
986
|
+
@cache.groups << group
|
987
|
+
|
988
|
+
@cache.add 'key', 'value'
|
989
|
+
|
990
|
+
dumped = Marshal.dump('value')
|
991
|
+
expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
|
992
|
+
assert_equal expected, server.socket.written.string
|
993
|
+
end
|
994
|
+
|
995
|
+
def test_add_expiry
|
996
|
+
server = FakeServer.new
|
997
|
+
server.socket.data.write "STORED\r\n"
|
998
|
+
server.socket.data.rewind
|
999
|
+
group = FakeGroup.new(server)
|
1000
|
+
@cache.groups = []
|
1001
|
+
@cache.groups << group
|
1002
|
+
|
1003
|
+
@cache.add 'key', 'value', 5
|
1004
|
+
|
1005
|
+
dumped = Marshal.dump('value')
|
1006
|
+
expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
|
1007
|
+
assert_equal expected, server.socket.written.string
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def test_add_raw
|
1011
|
+
server = FakeServer.new
|
1012
|
+
server.socket.data.write "STORED\r\n"
|
1013
|
+
server.socket.data.rewind
|
1014
|
+
group = FakeGroup.new(server)
|
1015
|
+
@cache.groups = []
|
1016
|
+
@cache.groups << group
|
1017
|
+
|
1018
|
+
@cache.add 'key', 'value', 0, true
|
1019
|
+
|
1020
|
+
expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
|
1021
|
+
assert_equal expected, server.socket.written.string
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def test_add_raw_int
|
1025
|
+
server = FakeServer.new
|
1026
|
+
server.socket.data.write "STORED\r\n"
|
1027
|
+
server.socket.data.rewind
|
1028
|
+
group = FakeGroup.new(server)
|
1029
|
+
@cache.groups = []
|
1030
|
+
@cache.groups << group
|
1031
|
+
|
1032
|
+
@cache.add 'key', 12, 0, true
|
1033
|
+
|
1034
|
+
expected = "add my_namespace:key 0 0 2\r\n12\r\n"
|
1035
|
+
assert_equal expected, server.socket.written.string
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def test_add_readonly
|
1039
|
+
cache = MemCacheDb.new ([FakeGroup.new],:readonly => true)
|
1040
|
+
|
1041
|
+
e = assert_raise MemCacheDb::MemCacheDbError do
|
1042
|
+
cache.add 'key', 'value'
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
assert_equal 'Update of readonly cache', e.message
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def test_delete
|
1049
|
+
server = FakeServer.new
|
1050
|
+
group = FakeGroup.new(server)
|
1051
|
+
@cache.groups = []
|
1052
|
+
@cache.groups << group
|
1053
|
+
|
1054
|
+
@cache.delete 'key'
|
1055
|
+
|
1056
|
+
expected = "delete my_namespace:key\r\n"
|
1057
|
+
assert_equal expected, group.master.socket.written.string
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def test_delete_with_expiry
|
1061
|
+
server = FakeServer.new
|
1062
|
+
group = FakeGroup.new(server)
|
1063
|
+
@cache.groups = []
|
1064
|
+
@cache.groups << group
|
1065
|
+
|
1066
|
+
@cache.delete 'key', 300
|
1067
|
+
|
1068
|
+
expected = "delete my_namespace:key\r\n"
|
1069
|
+
assert_equal expected, group.master.socket.written.string
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
|
1073
|
+
|
1074
|
+
|
1075
|
+
def test_basic_threaded_operations_should_work
|
1076
|
+
cache = MemCacheDb.new ([FakeGroup.new], :multithread => true,
|
1077
|
+
:namespace => 'my_namespace',
|
1078
|
+
:readonly => false)
|
1079
|
+
|
1080
|
+
server = FakeServer.new
|
1081
|
+
server.socket.data.write "STORED\r\n"
|
1082
|
+
server.socket.data.rewind
|
1083
|
+
|
1084
|
+
group = FakeGroup.new(server)
|
1085
|
+
cache.groups = []
|
1086
|
+
cache.groups << group
|
1087
|
+
|
1088
|
+
assert cache.multithread
|
1089
|
+
|
1090
|
+
assert_nothing_raised do
|
1091
|
+
cache.set "test", "test value"
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
output = server.socket.written.string
|
1095
|
+
assert_match(/set my_namespace:test/, output)
|
1096
|
+
assert_match(/test value/, output)
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
def test_namespace_separator
|
1100
|
+
cache = MemCacheDb.new ( [FakeGroup.new], :namespace => 'ns', :namespace_separator => '')
|
1101
|
+
|
1102
|
+
server = FakeServer.new
|
1103
|
+
server.socket.data.write "STORED\r\n"
|
1104
|
+
server.socket.data.rewind
|
1105
|
+
|
1106
|
+
group = FakeGroup.new(server)
|
1107
|
+
cache.groups = []
|
1108
|
+
cache.groups << group
|
1109
|
+
|
1110
|
+
assert_nothing_raised do
|
1111
|
+
cache.set "test", "test value"
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
output = server.socket.written.string
|
1115
|
+
assert_match(/set nstest/, output)
|
1116
|
+
assert_match(/test value/, output)
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def test_basic_unthreaded_operations_should_work
|
1120
|
+
cache = MemCacheDb.new ( [FakeGroup.new], :multithread => false,
|
1121
|
+
:namespace => 'my_namespace',
|
1122
|
+
:readonly => false)
|
1123
|
+
|
1124
|
+
server = FakeServer.new
|
1125
|
+
server.socket.data.write "STORED\r\n"
|
1126
|
+
server.socket.data.rewind
|
1127
|
+
|
1128
|
+
group = FakeGroup.new(server)
|
1129
|
+
cache.groups = []
|
1130
|
+
cache.groups << group
|
1131
|
+
|
1132
|
+
assert !cache.multithread
|
1133
|
+
|
1134
|
+
assert_nothing_raised do
|
1135
|
+
cache.set "test", "test value"
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
output = server.socket.written.string
|
1139
|
+
assert_match(/set my_namespace:test/, output)
|
1140
|
+
assert_match(/test value/, output)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
|
1144
|
+
|
1145
|
+
def util_setup_server(memcache, host, responses)
|
1146
|
+
server = MemCacheDb::Server.new memcache, host
|
1147
|
+
server.instance_variable_set :@sock, StringIO.new(responses)
|
1148
|
+
|
1149
|
+
group = FakeGroup.new(server)
|
1150
|
+
@cache.groups = []
|
1151
|
+
@cache.groups << group
|
1152
|
+
|
1153
|
+
return server
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
def test_crazy_multithreaded_access
|
1157
|
+
requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
|
1158
|
+
|
1159
|
+
# Use a null logger to verify logging doesn't blow up at runtime
|
1160
|
+
cache = MemCacheDb.new([{:servers=>['localhost:11211', 'localhost:11212']}], :logger => Logger.new('/dev/null'))
|
1161
|
+
workers = []
|
1162
|
+
|
1163
|
+
|
1164
|
+
|
1165
|
+
# Have a bunch of threads perform a bunch of operations at the same time.
|
1166
|
+
# Verify the result of each operation to ensure the request and response
|
1167
|
+
# are not intermingled between threads.
|
1168
|
+
10.times do
|
1169
|
+
workers << Thread.new do
|
1170
|
+
100.times do
|
1171
|
+
cache.set('a', 9)
|
1172
|
+
cache.set('b', 11)
|
1173
|
+
cache.add('c', 10, 0, true)
|
1174
|
+
cache.set('d', 'a', 100, true)
|
1175
|
+
cache.set('e', 'x', 100, true)
|
1176
|
+
cache.set('f', 'zzz')
|
1177
|
+
cache.append('d', 'b')
|
1178
|
+
cache.prepend('e', 'y')
|
1179
|
+
assert_equal "NOT_STORED\r\n", cache.add('a', 11)
|
1180
|
+
assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
|
1181
|
+
inc = cache.incr('c', 10)
|
1182
|
+
assert_equal 0, inc % 5
|
1183
|
+
assert inc > 14
|
1184
|
+
assert cache.decr('c', 5) > 14
|
1185
|
+
assert_equal 11, cache.get('b')
|
1186
|
+
d = cache.get('d', true)
|
1187
|
+
assert_match(/\Aab*\Z/, d)
|
1188
|
+
e = cache.get('e', true)
|
1189
|
+
assert_match(/\Ay*x\Z/, e)
|
1190
|
+
end
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
workers.each { |w| w.join }
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
def test_stats
|
1198
|
+
socket = FakeSocket.new
|
1199
|
+
socket.data.write "STAT pid 20188\r\nSTAT total_items 32\r\nSTAT version 1.2.3\r\nSTAT rusage_user 1:300\r\nSTAT dummy ok\r\nEND\r\n"
|
1200
|
+
socket.data.rewind
|
1201
|
+
server = FakeServer.new socket
|
1202
|
+
def server.host() 'localhost'; end
|
1203
|
+
def server.port() 11211; end
|
1204
|
+
|
1205
|
+
group = FakeGroup.new(server)
|
1206
|
+
@cache.groups = []
|
1207
|
+
@cache.groups << group
|
1208
|
+
|
1209
|
+
expected = {
|
1210
|
+
'localhost:11211' => {
|
1211
|
+
'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
|
1212
|
+
'rusage_user' => 1.0003, 'dummy' => 'ok'
|
1213
|
+
}
|
1214
|
+
}
|
1215
|
+
assert_equal expected, @cache.stats
|
1216
|
+
|
1217
|
+
assert_equal "stats\r\n", socket.written.string
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|