arunthampi-memcached 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/BENCHMARKS +120 -0
- data/CHANGELOG +58 -0
- data/LICENSE +184 -0
- data/Manifest +27 -0
- data/README +118 -0
- data/Rakefile +45 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/ext/extconf.rb +106 -0
- data/ext/libmemcached-0.32.tar.gz +0 -0
- data/ext/libmemcached.patch +270 -0
- data/ext/rlibmemcached.i +212 -0
- data/ext/rlibmemcached_wrap.c +13090 -0
- data/lib/memcached.rb +31 -0
- data/lib/memcached/behaviors.rb +78 -0
- data/lib/memcached/exceptions.rb +84 -0
- data/lib/memcached/integer.rb +6 -0
- data/lib/memcached/memcached.rb +554 -0
- data/lib/memcached/rails.rb +97 -0
- data/test/profile/benchmark.rb +210 -0
- data/test/profile/profile.rb +14 -0
- data/test/profile/valgrind.rb +147 -0
- data/test/setup.rb +29 -0
- data/test/teardown.rb +0 -0
- data/test/test_helper.rb +18 -0
- data/test/unit/binding_test.rb +8 -0
- data/test/unit/memcached_test.rb +1132 -0
- data/test/unit/rails_test.rb +102 -0
- metadata +94 -0
data/test/setup.rb
ADDED
@@ -0,0 +1,29 @@
|
|
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
|
+
# TCP memcached
|
15
|
+
(43042..43046).each do |port|
|
16
|
+
cmd = "memcached #{verbosity} -U 0 -p #{port} >> #{log} 2>&1 &"
|
17
|
+
raise "'#{cmd}' failed to start" unless system(cmd)
|
18
|
+
end
|
19
|
+
# UDP memcached
|
20
|
+
(43052..43053).each do |port|
|
21
|
+
cmd = "memcached #{verbosity} -U #{port} -p 0 >> #{log} 2>&1 &"
|
22
|
+
raise "'#{cmd}' failed to start" unless system(cmd)
|
23
|
+
end
|
24
|
+
# Domain socket memcached
|
25
|
+
(0..1).each do |i|
|
26
|
+
cmd = "memcached -M -s #{UNIX_SOCKET_NAME}#{i} #{verbosity} >> #{log} 2>&1 &"
|
27
|
+
raise "'#{cmd}' failed to start" unless system(cmd)
|
28
|
+
end
|
29
|
+
end
|
data/test/teardown.rb
ADDED
File without changes
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
if ENV['DEBUG']
|
8
|
+
require 'ruby-debug'
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'memcached'
|
12
|
+
require 'test/unit'
|
13
|
+
require 'ostruct'
|
14
|
+
|
15
|
+
UNIX_SOCKET_NAME = File.join(ENV['TMPDIR']||'/tmp','memcached') unless defined? UNIX_SOCKET_NAME
|
16
|
+
|
17
|
+
class GenericClass
|
18
|
+
end
|
@@ -0,0 +1,1132 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../test_helper"
|
3
|
+
require 'socket'
|
4
|
+
require 'mocha'
|
5
|
+
require 'benchmark'
|
6
|
+
|
7
|
+
class MemcachedTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@servers = ['localhost:43042', 'localhost:43043', "#{UNIX_SOCKET_NAME}0"]
|
11
|
+
@udp_servers = ['localhost:43052', 'localhost:43053']
|
12
|
+
|
13
|
+
# Maximum allowed prefix key size for :hash_with_prefix_key_key => false
|
14
|
+
@prefix_key = 'prefix_key_'
|
15
|
+
|
16
|
+
@options = {
|
17
|
+
:chunk_split_size => 1048300,
|
18
|
+
:prefix_key => @prefix_key,
|
19
|
+
:hash => :default,
|
20
|
+
:distribution => :modula}
|
21
|
+
@cache = Memcached.new(@servers, @options)
|
22
|
+
|
23
|
+
@binary_protocol_options = {
|
24
|
+
:prefix_key => @prefix_key,
|
25
|
+
:hash => :default,
|
26
|
+
:distribution => :modula,
|
27
|
+
:binary_protocol => true}
|
28
|
+
@binary_protocol_cache = Memcached.new(@servers, @binary_protocol_options)
|
29
|
+
|
30
|
+
@udp_options = {
|
31
|
+
:prefix_key => @prefix_key,
|
32
|
+
:hash => :default,
|
33
|
+
:use_udp => true,
|
34
|
+
:distribution => :modula}
|
35
|
+
@udp_cache = Memcached.new(@udp_servers, @udp_options)
|
36
|
+
|
37
|
+
@noblock_options = {
|
38
|
+
:prefix_key => @prefix_key,
|
39
|
+
:no_block => true,
|
40
|
+
:buffer_requests => true,
|
41
|
+
:hash => :default}
|
42
|
+
@noblock_cache = Memcached.new(@servers, @noblock_options)
|
43
|
+
|
44
|
+
@value = OpenStruct.new(:a => 1, :b => 2, :c => GenericClass)
|
45
|
+
@marshalled_value = Marshal.dump(@value)
|
46
|
+
|
47
|
+
@large_value = 'a' * 1048576 + 'b' * 10 # 1MB of 'a' + 10 bytes of 'b'
|
48
|
+
@large_marshalled_value = Marshal.dump(@large_value)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Initialize
|
52
|
+
|
53
|
+
def test_initialize
|
54
|
+
cache = Memcached.new @servers, :prefix_key => 'test'
|
55
|
+
assert_equal 'test', cache.options[:prefix_key]
|
56
|
+
assert_equal 3, cache.send(:server_structs).size
|
57
|
+
assert_equal 'localhost', cache.send(:server_structs).first.hostname
|
58
|
+
assert_equal 43042, cache.send(:server_structs).first.port
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_initialize_with_ip_addresses
|
62
|
+
cache = Memcached.new ['127.0.0.1:43042', '127.0.0.1:43043']
|
63
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
|
64
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).last.hostname
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_initialize_without_port
|
68
|
+
cache = Memcached.new ['localhost']
|
69
|
+
assert_equal 'localhost', cache.send(:server_structs).first.hostname
|
70
|
+
assert_equal 11211, cache.send(:server_structs).first.port
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_initialize_with_ports_and_weights
|
74
|
+
cache = Memcached.new ['localhost:43042:2', 'localhost:43043:10']
|
75
|
+
assert_equal 2, cache.send(:server_structs).first.weight
|
76
|
+
assert_equal 43043, cache.send(:server_structs).last.port
|
77
|
+
assert_equal 10, cache.send(:server_structs).last.weight
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_initialize_with_hostname_only
|
81
|
+
addresses = (1..8).map { |i| "app-cache-%02d" % i }
|
82
|
+
cache = Memcached.new(addresses)
|
83
|
+
addresses.each_with_index do |address, index|
|
84
|
+
assert_equal address, cache.send(:server_structs)[index].hostname
|
85
|
+
assert_equal 11211, cache.send(:server_structs)[index].port
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_initialize_with_ip_address_and_options
|
90
|
+
cache = Memcached.new '127.0.0.1:43042', :ketama_weighted => false
|
91
|
+
assert_equal '127.0.0.1', cache.send(:server_structs).first.hostname
|
92
|
+
assert_equal false, cache.options[:ketama_weighted]
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_options_are_set
|
96
|
+
Memcached::DEFAULTS.merge(@noblock_options).each do |key, expected|
|
97
|
+
value = @noblock_cache.options[key]
|
98
|
+
unless key == :rcv_timeout or key == :poll_timeout
|
99
|
+
assert(expected == value, "#{key} should be #{expected} but was #{value}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_options_are_frozen
|
105
|
+
assert_raise(TypeError, RuntimeError) do
|
106
|
+
@cache.options[:no_block] = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_behaviors_are_set
|
111
|
+
Memcached::BEHAVIORS.keys.each do |key, value|
|
112
|
+
assert_not_nil @cache.send(:get_behavior, key)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_initialize_with_invalid_server_strings
|
117
|
+
assert_raise(ArgumentError) { Memcached.new ":43042" }
|
118
|
+
assert_raise(ArgumentError) { Memcached.new "localhost:memcached" }
|
119
|
+
assert_raise(ArgumentError) { Memcached.new "local host:43043:1" }
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_initialize_with_invalid_options
|
123
|
+
assert_raise(ArgumentError) do
|
124
|
+
Memcached.new @servers, :sort_hosts => true, :distribution => :consistent
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_initialize_with_invalid_prefix_key
|
129
|
+
assert_raise(ArgumentError) do
|
130
|
+
Memcached.new @servers, :prefix_key => "x" * 128
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_initialize_without_prefix_key
|
135
|
+
cache = Memcached.new @servers
|
136
|
+
assert_equal nil, cache.options[:prefix_key]
|
137
|
+
assert_equal 3, cache.send(:server_structs).size
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_initialize_negative_behavior
|
141
|
+
cache = Memcached.new @servers,
|
142
|
+
:buffer_requests => false
|
143
|
+
assert_nothing_raised do
|
144
|
+
cache.set key, @value
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_initialize_without_backtraces
|
149
|
+
cache = Memcached.new @servers,
|
150
|
+
:show_backtraces => false
|
151
|
+
cache.delete key rescue
|
152
|
+
begin
|
153
|
+
cache.get key
|
154
|
+
rescue Memcached::NotFound => e
|
155
|
+
assert e.backtrace.empty?
|
156
|
+
end
|
157
|
+
begin
|
158
|
+
cache.append key, @value
|
159
|
+
rescue Memcached::NotStored => e
|
160
|
+
assert e.backtrace.empty?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_initialize_with_backtraces
|
165
|
+
cache = Memcached.new @servers,
|
166
|
+
:show_backtraces => true
|
167
|
+
cache.delete key rescue
|
168
|
+
begin
|
169
|
+
cache.get key
|
170
|
+
rescue Memcached::NotFound => e
|
171
|
+
assert !e.backtrace.empty?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_initialize_sort_hosts
|
176
|
+
# Original
|
177
|
+
cache = Memcached.new(@servers.sort,
|
178
|
+
:sort_hosts => false,
|
179
|
+
:distribution => :modula
|
180
|
+
)
|
181
|
+
assert_equal @servers.sort,
|
182
|
+
cache.servers
|
183
|
+
|
184
|
+
# Original with sort_hosts
|
185
|
+
cache = Memcached.new(@servers.sort,
|
186
|
+
:sort_hosts => true,
|
187
|
+
:distribution => :modula
|
188
|
+
)
|
189
|
+
assert_equal @servers.sort,
|
190
|
+
cache.servers
|
191
|
+
|
192
|
+
# Reversed
|
193
|
+
cache = Memcached.new(@servers.sort.reverse,
|
194
|
+
:sort_hosts => false,
|
195
|
+
:distribution => :modula
|
196
|
+
)
|
197
|
+
assert_equal @servers.sort.reverse,
|
198
|
+
cache.servers
|
199
|
+
|
200
|
+
# Reversed with sort_hosts
|
201
|
+
cache = Memcached.new(@servers.sort.reverse,
|
202
|
+
:sort_hosts => true,
|
203
|
+
:distribution => :modula
|
204
|
+
)
|
205
|
+
assert_equal @servers.sort,
|
206
|
+
cache.servers
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_initialize_single_server
|
210
|
+
cache = Memcached.new 'localhost:43042'
|
211
|
+
assert_equal nil, cache.options[:prefix_key]
|
212
|
+
assert_equal 1, cache.send(:server_structs).size
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_initialize_strange_argument
|
216
|
+
assert_raise(ArgumentError) { Memcached.new 1 }
|
217
|
+
end
|
218
|
+
|
219
|
+
# Get
|
220
|
+
|
221
|
+
def test_get
|
222
|
+
@cache.set key, @value
|
223
|
+
result = @cache.get key
|
224
|
+
assert_equal @value, result
|
225
|
+
|
226
|
+
@binary_protocol_cache.set key, @value
|
227
|
+
result = @binary_protocol_cache.get key
|
228
|
+
assert_equal @value, result
|
229
|
+
|
230
|
+
@udp_cache.set(key, @value)
|
231
|
+
assert_raises(Memcached::ActionNotSupported) do
|
232
|
+
@udp_cache.get(key)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_get_nil
|
237
|
+
@cache.set key, nil, 0
|
238
|
+
result = @cache.get key
|
239
|
+
assert_equal nil, result
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_get_missing
|
243
|
+
@cache.delete key rescue nil
|
244
|
+
assert_raise(Memcached::NotFound) do
|
245
|
+
result = @cache.get key
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_get_with_server_timeout
|
250
|
+
socket = stub_server 43047
|
251
|
+
cache = Memcached.new("localhost:43047:1", :timeout => 0.5)
|
252
|
+
assert 0.49 < (Benchmark.measure do
|
253
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
254
|
+
result = cache.get key
|
255
|
+
end
|
256
|
+
end).real
|
257
|
+
|
258
|
+
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.001, :rcv_timeout => 0.5)
|
259
|
+
assert 0.49 < (Benchmark.measure do
|
260
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
261
|
+
result = cache.get key
|
262
|
+
end
|
263
|
+
end).real
|
264
|
+
|
265
|
+
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.25, :rcv_timeout => 0.25)
|
266
|
+
assert 0.51 > (Benchmark.measure do
|
267
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
268
|
+
result = cache.get key
|
269
|
+
end
|
270
|
+
end).real
|
271
|
+
|
272
|
+
socket.close
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_get_with_no_block_server_timeout
|
276
|
+
socket = stub_server 43048
|
277
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true, :timeout => 0.25)
|
278
|
+
assert 0.24 < (Benchmark.measure do
|
279
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
280
|
+
result = cache.get key
|
281
|
+
end
|
282
|
+
end).real
|
283
|
+
|
284
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true, :poll_timeout => 0.25, :rcv_timeout => 0.001)
|
285
|
+
assert 0.24 < (Benchmark.measure do
|
286
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
287
|
+
result = cache.get key
|
288
|
+
end
|
289
|
+
end).real
|
290
|
+
|
291
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true,
|
292
|
+
:poll_timeout => 0.001,
|
293
|
+
:rcv_timeout => 0.25 # No affect in no-block mode
|
294
|
+
)
|
295
|
+
assert 0.24 > (Benchmark.measure do
|
296
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
297
|
+
result = cache.get key
|
298
|
+
end
|
299
|
+
end).real
|
300
|
+
|
301
|
+
socket.close
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_get_with_prefix_key
|
305
|
+
# Prefix_key
|
306
|
+
cache = Memcached.new(
|
307
|
+
# We can only use one server because the key is hashed separately from the prefix key
|
308
|
+
@servers.first,
|
309
|
+
:prefix_key => @prefix_key,
|
310
|
+
:hash => :default,
|
311
|
+
:distribution => :modula
|
312
|
+
)
|
313
|
+
cache.set key, @value
|
314
|
+
assert_equal @value, cache.get(key)
|
315
|
+
|
316
|
+
# No prefix_key specified
|
317
|
+
cache = Memcached.new(
|
318
|
+
@servers.first,
|
319
|
+
:hash => :default,
|
320
|
+
:distribution => :modula
|
321
|
+
)
|
322
|
+
assert_nothing_raised do
|
323
|
+
assert_equal @value, cache.get("#{@prefix_key}#{key}")
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_values_with_null_characters_are_not_truncated
|
328
|
+
value = OpenStruct.new(:a => Object.new) # Marshals with a null \000
|
329
|
+
@cache.set key, value
|
330
|
+
result = @cache.get key, false
|
331
|
+
non_wrapped_result = Rlibmemcached.memcached_get(
|
332
|
+
@cache.instance_variable_get("@struct"),
|
333
|
+
key
|
334
|
+
).first
|
335
|
+
assert result.size > non_wrapped_result.size
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_get_multi
|
339
|
+
@cache.set "#{key}_1", 1
|
340
|
+
@cache.set "#{key}_2", 2
|
341
|
+
assert_equal({"#{key}_1" => 1, "#{key}_2" => 2},
|
342
|
+
@cache.get(["#{key}_1", "#{key}_2"]))
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_get_multi_missing
|
346
|
+
@cache.set "#{key}_1", 1
|
347
|
+
@cache.delete "#{key}_2" rescue nil
|
348
|
+
@cache.set "#{key}_3", 3
|
349
|
+
@cache.delete "#{key}_4" rescue nil
|
350
|
+
assert_equal(
|
351
|
+
{"test_get_multi_missing_3"=>3, "test_get_multi_missing_1"=>1},
|
352
|
+
@cache.get(["#{key}_1", "#{key}_2", "#{key}_3", "#{key}_4"])
|
353
|
+
)
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_get_multi_completely_missing
|
357
|
+
@cache.delete "#{key}_1" rescue nil
|
358
|
+
@cache.delete "#{key}_2" rescue nil
|
359
|
+
assert_equal(
|
360
|
+
{},
|
361
|
+
@cache.get(["#{key}_1", "#{key}_2"])
|
362
|
+
)
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_get_multi_checks_types
|
366
|
+
assert_raises(TypeError, ArgumentError) do
|
367
|
+
@cache.get([nil])
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_set_and_get_unmarshalled
|
372
|
+
@cache.set key, @value
|
373
|
+
result = @cache.get key, false
|
374
|
+
assert_equal @marshalled_value, result
|
375
|
+
end
|
376
|
+
|
377
|
+
def test_get_multi_unmarshalled
|
378
|
+
@cache.set "#{key}_1", 1, 0, false
|
379
|
+
@cache.set "#{key}_2", 2, 0, false
|
380
|
+
assert_equal(
|
381
|
+
{"#{key}_1" => "1", "#{key}_2" => "2"},
|
382
|
+
@cache.get(["#{key}_1", "#{key}_2"], false)
|
383
|
+
)
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_get_multi_mixed_marshalling
|
387
|
+
@cache.set "#{key}_1", 1
|
388
|
+
@cache.set "#{key}_2", 2, 0, false
|
389
|
+
assert_nothing_raised do
|
390
|
+
@cache.get(["#{key}_1", "#{key}_2"], false)
|
391
|
+
end
|
392
|
+
assert_raise(ArgumentError) do
|
393
|
+
@cache.get(["#{key}_1", "#{key}_2"])
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def test_random_distribution_is_statistically_random
|
398
|
+
cache = Memcached.new(@servers, :distribution => :random)
|
399
|
+
cache.flush
|
400
|
+
20.times { |i| cache.set "#{key}#{i}", @value }
|
401
|
+
|
402
|
+
cache, hits = Memcached.new(@servers.first), 0
|
403
|
+
20.times do |i|
|
404
|
+
begin
|
405
|
+
cache.get "#{key}#{i}"
|
406
|
+
hits += 1
|
407
|
+
rescue Memcached::NotFound
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
assert_not_equal 4, hits
|
412
|
+
end
|
413
|
+
|
414
|
+
# Set
|
415
|
+
|
416
|
+
def test_set
|
417
|
+
assert_nothing_raised do
|
418
|
+
@cache.set(key, @value)
|
419
|
+
end
|
420
|
+
|
421
|
+
assert_nothing_raised do
|
422
|
+
@binary_protocol_cache.set(key, @value)
|
423
|
+
end
|
424
|
+
|
425
|
+
assert_nothing_raised do
|
426
|
+
@udp_cache.set(key, @value)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
def test_set_expiry
|
431
|
+
@cache.set key, @value, 1
|
432
|
+
assert_nothing_raised do
|
433
|
+
@cache.get key
|
434
|
+
end
|
435
|
+
sleep(2)
|
436
|
+
assert_raise(Memcached::NotFound) do
|
437
|
+
@cache.get key
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# big_set
|
442
|
+
|
443
|
+
def test_big_set
|
444
|
+
assert_nothing_raised do
|
445
|
+
@cache.big_set key, @value
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_big_set_sets_header_in_key
|
450
|
+
@cache.big_set(key, @large_value, 0, false)
|
451
|
+
|
452
|
+
expected_header = OpenStruct.new(:chunks => 2) # @large_value splits into 2 chunks.
|
453
|
+
|
454
|
+
assert_equal expected_header, @cache.get(key)
|
455
|
+
end
|
456
|
+
|
457
|
+
def test_big_set_splits_into_chunks
|
458
|
+
@cache.big_set(key, @large_value, 0, false)
|
459
|
+
|
460
|
+
expected_1st_chunk = 'a' * @options[:chunk_split_size]
|
461
|
+
expected_2nd_chunk = 'a' * (1048576 - @options[:chunk_split_size]) + 'b' * 10
|
462
|
+
|
463
|
+
assert_equal expected_1st_chunk, @cache.get("#{key}_0", false)
|
464
|
+
assert_equal expected_2nd_chunk, @cache.get("#{key}_1", false)
|
465
|
+
end
|
466
|
+
|
467
|
+
def test_big_set_single_chunk_sets_header_and_single_chunk
|
468
|
+
@cache.big_set(key, 'bar', 0, false)
|
469
|
+
|
470
|
+
expected_header = OpenStruct.new(:chunks => 1)
|
471
|
+
|
472
|
+
assert_equal expected_header, @cache.get(key)
|
473
|
+
|
474
|
+
assert_equal 'bar', @cache.get("#{key}_0", false)
|
475
|
+
|
476
|
+
assert_raise(Memcached::NotFound) do
|
477
|
+
puts @cache.get("#{key}_1", false)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
def test_big_set_marshalled
|
482
|
+
@cache.big_set(key, @large_value, 0, true)
|
483
|
+
|
484
|
+
expected_1st_chunk = @large_marshalled_value[0, @options[:chunk_split_size]]
|
485
|
+
expected_2nd_chunk = @large_marshalled_value[@options[:chunk_split_size], @options[:chunk_split_size]]
|
486
|
+
|
487
|
+
assert_equal expected_1st_chunk, @cache.get("#{key}_0", false)
|
488
|
+
assert_equal expected_2nd_chunk, @cache.get("#{key}_1", false)
|
489
|
+
|
490
|
+
# Unmarshalling the sum of the chunks should result in the original value.
|
491
|
+
assert_equal Marshal.load(expected_1st_chunk + expected_2nd_chunk), @large_value
|
492
|
+
end
|
493
|
+
|
494
|
+
# big_get
|
495
|
+
|
496
|
+
def test_big_get
|
497
|
+
@cache.big_set key, @large_value
|
498
|
+
result = @cache.big_get key
|
499
|
+
assert_equal @large_value, result
|
500
|
+
end
|
501
|
+
|
502
|
+
def test_big_get_marshalled
|
503
|
+
@cache.big_set(key, @large_value, 0, true)
|
504
|
+
result = @cache.big_get key, true
|
505
|
+
assert_equal @large_value, result
|
506
|
+
end
|
507
|
+
|
508
|
+
def test_big_set_and_get_unmarshalled
|
509
|
+
@cache.big_set key, @large_value
|
510
|
+
result = @cache.big_get key, false
|
511
|
+
assert_equal @large_marshalled_value, result
|
512
|
+
end
|
513
|
+
|
514
|
+
def test_big_get_missing_chunk_raises_not_found
|
515
|
+
@cache.big_set key, @large_value
|
516
|
+
|
517
|
+
# Remove the 2nd chunk.
|
518
|
+
@cache.delete "#{key}_1"
|
519
|
+
|
520
|
+
assert_raise(Memcached::NotFound) do
|
521
|
+
@cache.big_get key
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
def test_big_get_invalid_chunk_header_fallsback_to_get
|
526
|
+
@cache.big_set key, @large_value
|
527
|
+
|
528
|
+
# Overwrite the chunk header.
|
529
|
+
@cache.set key, 'foo'
|
530
|
+
|
531
|
+
assert_equal 'foo', @cache.big_get(key)
|
532
|
+
end
|
533
|
+
|
534
|
+
def test_big_get_missing
|
535
|
+
assert_raise(Memcached::NotFound) do
|
536
|
+
@cache.big_get key
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def test_set_with_default_ttl
|
541
|
+
cache = Memcached.new(
|
542
|
+
@servers,
|
543
|
+
:default_ttl => 1
|
544
|
+
)
|
545
|
+
cache.set key, @value
|
546
|
+
assert_nothing_raised do
|
547
|
+
cache.get key
|
548
|
+
end
|
549
|
+
sleep(2)
|
550
|
+
assert_raise(Memcached::NotFound) do
|
551
|
+
cache.get key
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
def disabled_test_set_retry_on_client_error
|
556
|
+
# FIXME Test passes, but Mocha doesn't restore the original method properly
|
557
|
+
Rlibmemcached.stubs(:memcached_set).raises(Memcached::ClientError)
|
558
|
+
Rlibmemcached.stubs(:memcached_set).returns(0)
|
559
|
+
|
560
|
+
assert_nothing_raised do
|
561
|
+
@cache.set(key, @value)
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
# Delete
|
566
|
+
|
567
|
+
def test_delete
|
568
|
+
@cache.set key, @value
|
569
|
+
@cache.delete key
|
570
|
+
assert_raise(Memcached::NotFound) do
|
571
|
+
@cache.get key
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
def test_missing_delete
|
576
|
+
@cache.delete key rescue nil
|
577
|
+
assert_raise(Memcached::NotFound) do
|
578
|
+
@cache.delete key
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
# Flush
|
583
|
+
|
584
|
+
def test_flush
|
585
|
+
@cache.set key, @value
|
586
|
+
assert_equal @value,
|
587
|
+
@cache.get(key)
|
588
|
+
@cache.flush
|
589
|
+
assert_raise(Memcached::NotFound) do
|
590
|
+
@cache.get key
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
# Add
|
595
|
+
|
596
|
+
def test_add
|
597
|
+
@cache.delete key rescue nil
|
598
|
+
@cache.add key, @value
|
599
|
+
assert_equal @value, @cache.get(key)
|
600
|
+
end
|
601
|
+
|
602
|
+
def test_existing_add
|
603
|
+
@cache.set key, @value
|
604
|
+
assert_raise(Memcached::NotStored) do
|
605
|
+
@cache.add key, @value
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def test_add_expiry
|
610
|
+
@cache.delete key rescue nil
|
611
|
+
@cache.set key, @value, 1
|
612
|
+
assert_nothing_raised do
|
613
|
+
@cache.get key
|
614
|
+
end
|
615
|
+
sleep(1)
|
616
|
+
assert_raise(Memcached::NotFound) do
|
617
|
+
@cache.get key
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
def test_unmarshalled_add
|
622
|
+
@cache.delete key rescue nil
|
623
|
+
@cache.add key, @marshalled_value, 0, false
|
624
|
+
assert_equal @marshalled_value, @cache.get(key, false)
|
625
|
+
assert_equal @value, @cache.get(key)
|
626
|
+
end
|
627
|
+
|
628
|
+
# Increment and decrement
|
629
|
+
|
630
|
+
def test_increment
|
631
|
+
@cache.set key, 10, 0, false
|
632
|
+
assert_equal 11, @cache.increment(key)
|
633
|
+
end
|
634
|
+
|
635
|
+
def test_increment_offset
|
636
|
+
@cache.set key, 10, 0, false
|
637
|
+
assert_equal 15, @cache.increment(key, 5)
|
638
|
+
end
|
639
|
+
|
640
|
+
def test_missing_increment
|
641
|
+
@cache.delete key rescue nil
|
642
|
+
assert_raise(Memcached::NotFound) do
|
643
|
+
@cache.increment key
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
def test_decrement
|
648
|
+
@cache.set key, 10, 0, false
|
649
|
+
assert_equal 9, @cache.decrement(key)
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_decrement_offset
|
653
|
+
@cache.set key, 10, 0, false
|
654
|
+
assert_equal 5, @cache.decrement(key, 5)
|
655
|
+
end
|
656
|
+
|
657
|
+
def test_missing_decrement
|
658
|
+
@cache.delete key rescue nil
|
659
|
+
assert_raise(Memcached::NotFound) do
|
660
|
+
@cache.decrement key
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
# Replace
|
665
|
+
|
666
|
+
def test_replace
|
667
|
+
@cache.set key, nil
|
668
|
+
assert_nothing_raised do
|
669
|
+
@cache.replace key, @value
|
670
|
+
end
|
671
|
+
assert_equal @value, @cache.get(key)
|
672
|
+
end
|
673
|
+
|
674
|
+
def test_missing_replace
|
675
|
+
@cache.delete key rescue nil
|
676
|
+
assert_raise(Memcached::NotStored) do
|
677
|
+
@cache.replace key, @value
|
678
|
+
end
|
679
|
+
assert_raise(Memcached::NotFound) do
|
680
|
+
assert_equal @value, @cache.get(key)
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
# Append and prepend
|
685
|
+
|
686
|
+
def test_append
|
687
|
+
@cache.set key, "start", 0, false
|
688
|
+
assert_nothing_raised do
|
689
|
+
@cache.append key, "end"
|
690
|
+
end
|
691
|
+
assert_equal "startend", @cache.get(key, false)
|
692
|
+
|
693
|
+
@binary_protocol_cache.set key, "start", 0, false
|
694
|
+
assert_nothing_raised do
|
695
|
+
@binary_protocol_cache.append key, "end"
|
696
|
+
end
|
697
|
+
assert_equal "startend", @binary_protocol_cache.get(key, false)
|
698
|
+
end
|
699
|
+
|
700
|
+
def test_missing_append
|
701
|
+
@cache.delete key rescue nil
|
702
|
+
assert_raise(Memcached::NotStored) do
|
703
|
+
@cache.append key, "end"
|
704
|
+
end
|
705
|
+
assert_raise(Memcached::NotFound) do
|
706
|
+
assert_equal @value, @cache.get(key)
|
707
|
+
end
|
708
|
+
|
709
|
+
@binary_protocol_cache.delete key rescue nil
|
710
|
+
assert_raise(Memcached::NotStored) do
|
711
|
+
@binary_protocol_cache.append key, "end"
|
712
|
+
end
|
713
|
+
assert_raise(Memcached::NotFound) do
|
714
|
+
assert_equal @value, @binary_protocol_cache.get(key)
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
def test_prepend
|
719
|
+
@cache.set key, "end", 0, false
|
720
|
+
assert_nothing_raised do
|
721
|
+
@cache.prepend key, "start"
|
722
|
+
end
|
723
|
+
assert_equal "startend", @cache.get(key, false)
|
724
|
+
end
|
725
|
+
|
726
|
+
def test_missing_prepend
|
727
|
+
@cache.delete key rescue nil
|
728
|
+
assert_raise(Memcached::NotStored) do
|
729
|
+
@cache.prepend key, "end"
|
730
|
+
end
|
731
|
+
assert_raise(Memcached::NotFound) do
|
732
|
+
assert_equal @value, @cache.get(key)
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_cas
|
737
|
+
cache = Memcached.new(
|
738
|
+
@servers,
|
739
|
+
:prefix_key => @prefix_key,
|
740
|
+
:support_cas => true
|
741
|
+
)
|
742
|
+
value2 = OpenStruct.new(:d => 3, :e => 4, :f => GenericClass)
|
743
|
+
|
744
|
+
# Existing set
|
745
|
+
cache.set key, @value
|
746
|
+
cache.cas(key) do |current|
|
747
|
+
assert_equal @value, current
|
748
|
+
value2
|
749
|
+
end
|
750
|
+
assert_equal value2, cache.get(key)
|
751
|
+
|
752
|
+
# Existing test without marshalling
|
753
|
+
cache.set(key, "foo", 0, false)
|
754
|
+
cache.cas(key, 0, false) do |current|
|
755
|
+
"#{current}bar"
|
756
|
+
end
|
757
|
+
assert_equal "foobar", cache.get(key, false)
|
758
|
+
|
759
|
+
# Missing set
|
760
|
+
cache.delete key
|
761
|
+
assert_raises(Memcached::NotFound) do
|
762
|
+
cache.cas(key) {}
|
763
|
+
end
|
764
|
+
|
765
|
+
# Conflicting set
|
766
|
+
cache.set key, @value
|
767
|
+
assert_raises(Memcached::ConnectionDataExists) do
|
768
|
+
cache.cas(key) do |current|
|
769
|
+
cache.set key, value2
|
770
|
+
current
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
# Error states
|
776
|
+
|
777
|
+
def test_key_with_spaces
|
778
|
+
key = "i have a space"
|
779
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
780
|
+
@cache.set key, @value
|
781
|
+
end
|
782
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
783
|
+
@cache.get(key)
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
def test_key_with_null
|
788
|
+
key = "with\000null"
|
789
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
790
|
+
@cache.set key, @value
|
791
|
+
end
|
792
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
793
|
+
@cache.get(key)
|
794
|
+
end
|
795
|
+
|
796
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
797
|
+
response = @cache.get([key])
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
def test_key_with_invalid_control_characters
|
802
|
+
key = "ch\303\242teau"
|
803
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
804
|
+
@cache.set key, @value
|
805
|
+
end
|
806
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
807
|
+
@cache.get(key)
|
808
|
+
end
|
809
|
+
|
810
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
811
|
+
response = @cache.get([key])
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
def test_key_too_long
|
816
|
+
key = "x"*251
|
817
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
818
|
+
@cache.set key, @value
|
819
|
+
end
|
820
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
821
|
+
@cache.get(key)
|
822
|
+
end
|
823
|
+
|
824
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
825
|
+
@cache.get([key])
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
def test_server_error_message
|
830
|
+
@cache.set key, "I'm big" * 1000000
|
831
|
+
assert false # Never reached
|
832
|
+
rescue Memcached::ServerError => e
|
833
|
+
assert_match /^"object too large for cache". Key/, e.message
|
834
|
+
end
|
835
|
+
|
836
|
+
def test_errno_message
|
837
|
+
Rlibmemcached::MemcachedServerSt.any_instance.stubs("cached_errno").returns(1)
|
838
|
+
@cache.send(:check_return_code, Rlibmemcached::MEMCACHED_ERRNO, key)
|
839
|
+
rescue Memcached::SystemError => e
|
840
|
+
assert_match /^Errno 1: "Operation not permitted". Key/, e.message
|
841
|
+
end
|
842
|
+
|
843
|
+
# Stats
|
844
|
+
|
845
|
+
def test_stats
|
846
|
+
stats = @cache.stats
|
847
|
+
assert_equal 3, stats[:pid].size
|
848
|
+
assert_instance_of Fixnum, stats[:pid].first
|
849
|
+
assert_instance_of String, stats[:version].first
|
850
|
+
end
|
851
|
+
|
852
|
+
def test_missing_stats
|
853
|
+
cache = Memcached.new('localhost:43041')
|
854
|
+
assert_raises(Memcached::SomeErrorsWereReported) { cache.stats }
|
855
|
+
end
|
856
|
+
|
857
|
+
# Clone
|
858
|
+
|
859
|
+
def test_clone
|
860
|
+
cache = @cache.clone
|
861
|
+
assert_equal cache.servers, @cache.servers
|
862
|
+
assert_not_equal cache, @cache
|
863
|
+
|
864
|
+
# Definitely check that the structs are unlinked
|
865
|
+
assert_not_equal @cache.instance_variable_get('@struct').object_id,
|
866
|
+
cache.instance_variable_get('@struct').object_id
|
867
|
+
|
868
|
+
assert_nothing_raised do
|
869
|
+
@cache.set key, @value
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
# Non-blocking IO
|
874
|
+
|
875
|
+
def test_buffered_requests_return_value
|
876
|
+
cache = Memcached.new @servers,
|
877
|
+
:buffer_requests => true
|
878
|
+
assert_nothing_raised do
|
879
|
+
cache.set key, @value
|
880
|
+
end
|
881
|
+
ret = Rlibmemcached.memcached_set(
|
882
|
+
cache.instance_variable_get("@struct"),
|
883
|
+
key,
|
884
|
+
@marshalled_value,
|
885
|
+
0,
|
886
|
+
Memcached::FLAGS
|
887
|
+
)
|
888
|
+
assert_equal Rlibmemcached::MEMCACHED_BUFFERED, ret
|
889
|
+
end
|
890
|
+
|
891
|
+
def test_no_block_return_value
|
892
|
+
assert_nothing_raised do
|
893
|
+
@noblock_cache.set key, @value
|
894
|
+
end
|
895
|
+
ret = Rlibmemcached.memcached_set(
|
896
|
+
@noblock_cache.instance_variable_get("@struct"),
|
897
|
+
key,
|
898
|
+
@marshalled_value,
|
899
|
+
0,
|
900
|
+
Memcached::FLAGS
|
901
|
+
)
|
902
|
+
assert_equal Rlibmemcached::MEMCACHED_BUFFERED, ret
|
903
|
+
end
|
904
|
+
|
905
|
+
def test_no_block_get
|
906
|
+
@noblock_cache.set key, @value
|
907
|
+
assert_equal @value,
|
908
|
+
@noblock_cache.get(key)
|
909
|
+
end
|
910
|
+
|
911
|
+
def test_no_block_missing_delete
|
912
|
+
@noblock_cache.delete key rescue nil
|
913
|
+
assert_nothing_raised do
|
914
|
+
@noblock_cache.delete key
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
def test_no_block_set_invalid_key
|
919
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
920
|
+
@noblock_cache.set "I'm so bad", @value
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
def test_no_block_set_object_too_large
|
925
|
+
assert_nothing_raised do
|
926
|
+
@noblock_cache.set key, "I'm big" * 1000000
|
927
|
+
end
|
928
|
+
end
|
929
|
+
|
930
|
+
def test_no_block_existing_add
|
931
|
+
# Should still raise
|
932
|
+
@noblock_cache.set key, @value
|
933
|
+
assert_raise(Memcached::NotStored) do
|
934
|
+
@noblock_cache.add key, @value
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
# Server removal and consistent hashing
|
939
|
+
|
940
|
+
def test_unresponsive_server
|
941
|
+
socket = stub_server 43041
|
942
|
+
|
943
|
+
cache = Memcached.new(
|
944
|
+
[@servers.last, 'localhost:43041'],
|
945
|
+
:prefix_key => @prefix_key,
|
946
|
+
:auto_eject_hosts => true,
|
947
|
+
:server_failure_limit => 2,
|
948
|
+
:retry_timeout => 1,
|
949
|
+
:hash_with_prefix_key => false,
|
950
|
+
:hash => :md5
|
951
|
+
)
|
952
|
+
|
953
|
+
# Hit second server up to the server_failure_limit
|
954
|
+
key2 = 'test_missing_server'
|
955
|
+
assert_raise(Memcached::ATimeoutOccurred) { cache.set(key2, @value) }
|
956
|
+
assert_raise(Memcached::ATimeoutOccurred) { cache.get(key2, @value) }
|
957
|
+
|
958
|
+
# Hit second server and pass the limit
|
959
|
+
key2 = 'test_missing_server'
|
960
|
+
begin
|
961
|
+
cache.get(key2)
|
962
|
+
rescue => e
|
963
|
+
assert_equal Memcached::ServerIsMarkedDead, e.class
|
964
|
+
assert_match /localhost:43041/, e.message
|
965
|
+
end
|
966
|
+
|
967
|
+
# Hit first server on retry
|
968
|
+
assert_nothing_raised do
|
969
|
+
cache.set(key2, @value)
|
970
|
+
assert_equal cache.get(key2), @value
|
971
|
+
end
|
972
|
+
|
973
|
+
sleep(2)
|
974
|
+
|
975
|
+
# Hit second server again after restore, expect same failure
|
976
|
+
key2 = 'test_missing_server'
|
977
|
+
assert_raise(Memcached::ATimeoutOccurred) do
|
978
|
+
cache.set(key2, @value)
|
979
|
+
end
|
980
|
+
|
981
|
+
socket.close
|
982
|
+
end
|
983
|
+
|
984
|
+
def test_missing_server
|
985
|
+
cache = Memcached.new(
|
986
|
+
[@servers.last, 'localhost:43041'],
|
987
|
+
:prefix_key => @prefix_key,
|
988
|
+
:auto_eject_hosts => true,
|
989
|
+
:server_failure_limit => 2,
|
990
|
+
:retry_timeout => 1,
|
991
|
+
:hash_with_prefix_key => false,
|
992
|
+
:hash => :md5
|
993
|
+
)
|
994
|
+
|
995
|
+
# Hit second server up to the server_failure_limit
|
996
|
+
key2 = 'test_missing_server'
|
997
|
+
assert_raise(Memcached::SystemError) { cache.set(key2, @value) }
|
998
|
+
assert_raise(Memcached::SystemError) { cache.get(key2, @value) }
|
999
|
+
|
1000
|
+
# Hit second server and pass the limit
|
1001
|
+
key2 = 'test_missing_server'
|
1002
|
+
begin
|
1003
|
+
cache.get(key2)
|
1004
|
+
rescue => e
|
1005
|
+
assert_equal Memcached::ServerIsMarkedDead, e.class
|
1006
|
+
assert_match /localhost:43041/, e.message
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
# Hit first server on retry
|
1010
|
+
assert_nothing_raised do
|
1011
|
+
cache.set(key2, @value)
|
1012
|
+
assert_equal cache.get(key2), @value
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
sleep(2)
|
1016
|
+
|
1017
|
+
# Hit second server again after restore, expect same failure
|
1018
|
+
key2 = 'test_missing_server'
|
1019
|
+
assert_raise(Memcached::SystemError) do
|
1020
|
+
cache.set(key2, @value)
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def test_unresponsive_with_random_distribution
|
1025
|
+
socket = stub_server 43041
|
1026
|
+
failures = [Memcached::ATimeoutOccurred, Memcached::ServerIsMarkedDead]
|
1027
|
+
|
1028
|
+
cache = Memcached.new(
|
1029
|
+
[@servers.last, 'localhost:43041'],
|
1030
|
+
:auto_eject_hosts => true,
|
1031
|
+
:distribution => :random,
|
1032
|
+
:server_failure_limit => 1,
|
1033
|
+
:retry_timeout => 1
|
1034
|
+
)
|
1035
|
+
|
1036
|
+
# Provoke the errors in 'failures'
|
1037
|
+
exceptions = []
|
1038
|
+
100.times { begin; cache.set key, @value; rescue => e; exceptions << e; end }
|
1039
|
+
assert_equal failures, exceptions.map { |x| x.class }
|
1040
|
+
|
1041
|
+
# Hit first server on retry
|
1042
|
+
assert_nothing_raised { cache.set(key, @value) }
|
1043
|
+
|
1044
|
+
# Hit second server again after restore, expect same failures
|
1045
|
+
sleep(2)
|
1046
|
+
exceptions = []
|
1047
|
+
100.times { begin; cache.set key, @value; rescue => e; exceptions << e; end }
|
1048
|
+
assert_equal failures, exceptions.map { |x| x.class }
|
1049
|
+
|
1050
|
+
socket.close
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def test_consistent_hashing
|
1054
|
+
keys = %w(EN6qtgMW n6Oz2W4I ss4A8Brr QShqFLZt Y3hgP9bs CokDD4OD Nd3iTSE1 24vBV4AU H9XBUQs5 E5j8vUq1 AzSh8fva PYBlK2Pi Ke3TgZ4I AyAIYanO oxj8Xhyd eBFnE6Bt yZyTikWQ pwGoU7Pw 2UNDkKRN qMJzkgo2 keFXbQXq pBl2QnIg ApRl3mWY wmalTJW1 TLueug8M wPQL4Qfg uACwus23 nmOk9R6w lwgZJrzJ v1UJtKdG RK629Cra U2UXFRqr d9OQLNl8 KAm1K3m5 Z13gKZ1v tNVai1nT LhpVXuVx pRib1Itj I1oLUob7 Z1nUsd5Q ZOwHehUa aXpFX29U ZsnqxlGz ivQRjOdb mB3iBEAj)
|
1055
|
+
|
1056
|
+
# Five servers
|
1057
|
+
cache = Memcached.new(
|
1058
|
+
@servers + ['localhost:43044', 'localhost:43045', 'localhost:43046'],
|
1059
|
+
:prefix_key => @prefix_key
|
1060
|
+
)
|
1061
|
+
|
1062
|
+
cache.flush
|
1063
|
+
keys.each do |key|
|
1064
|
+
cache.set(key, @value)
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
# Pull a server
|
1068
|
+
cache = Memcached.new(
|
1069
|
+
@servers + ['localhost:43044', 'localhost:43046'],
|
1070
|
+
:prefix_key => @prefix_key
|
1071
|
+
)
|
1072
|
+
|
1073
|
+
failed = 0
|
1074
|
+
keys.each_with_index do |key, i|
|
1075
|
+
begin
|
1076
|
+
cache.get(key)
|
1077
|
+
rescue Memcached::NotFound
|
1078
|
+
failed += 1
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
assert(failed < keys.size / 3, "#{failed} failed out of #{keys.size}")
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
# Concurrency
|
1086
|
+
|
1087
|
+
def test_thread_contention
|
1088
|
+
threads = []
|
1089
|
+
4.times do |index|
|
1090
|
+
threads << Thread.new do
|
1091
|
+
cache = @cache.clone
|
1092
|
+
assert_nothing_raised do
|
1093
|
+
cache.set("test_thread_contention_#{index}", index)
|
1094
|
+
end
|
1095
|
+
assert_equal index, cache.get("test_thread_contention_#{index}")
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
threads.each {|thread| thread.join}
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
# Hash
|
1102
|
+
|
1103
|
+
def test_hash
|
1104
|
+
assert_equal 3157003241,
|
1105
|
+
Rlibmemcached.memcached_generate_hash_rvalue("test", Rlibmemcached::MEMCACHED_HASH_FNV1_32)
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
# Memory cleanup
|
1109
|
+
|
1110
|
+
def test_reset
|
1111
|
+
original_struct = @cache.instance_variable_get("@struct")
|
1112
|
+
assert_nothing_raised do
|
1113
|
+
@cache.reset
|
1114
|
+
end
|
1115
|
+
assert_not_equal original_struct,
|
1116
|
+
@cache.instance_variable_get("@struct")
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
private
|
1120
|
+
|
1121
|
+
def key
|
1122
|
+
caller.first[/.*[` ](.*)'/, 1] # '
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
def stub_server(port)
|
1126
|
+
socket = TCPServer.new('127.0.0.1', port)
|
1127
|
+
Thread.new { socket.accept }
|
1128
|
+
socket
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
end
|
1132
|
+
|