memcached 0.12 → 0.14
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +4 -0
- data/COMPATIBILITY +3 -1
- data/README +3 -5
- data/ext/rlibmemcached.i +13 -3
- data/ext/rlibmemcached_wrap.c +356 -170
- data/lib/memcached.rb +2 -3
- data/lib/memcached/behaviors.rb +21 -15
- data/lib/memcached/memcached.rb +81 -61
- data/memcached.gemspec +2 -2
- data/test/unit/memcached_test.rb +74 -30
- metadata +2 -2
- metadata.gz.sig +0 -0
data/lib/memcached.rb
CHANGED
@@ -22,9 +22,8 @@ class Memcached
|
|
22
22
|
Lib = Rlibmemcached
|
23
23
|
REQUIRED_VERSION = File.read("#{File.dirname(__FILE__)}/../COMPATIBILITY")[/:: ([\d\.]+)/, 1]
|
24
24
|
RECEIVED_VERSION = Lib.memcached_lib_version
|
25
|
-
|
26
|
-
end
|
27
|
-
|
25
|
+
STDERR.puts "Warning: libmemcached #{REQUIRED_VERSION} suggested; you have #{RECEIVED_VERSION}." unless REQUIRED_VERSION == RECEIVED_VERSION
|
26
|
+
end
|
28
27
|
|
29
28
|
require 'memcached/integer'
|
30
29
|
require 'memcached/exceptions'
|
data/lib/memcached/behaviors.rb
CHANGED
@@ -22,39 +22,45 @@ class Memcached
|
|
22
22
|
|
23
23
|
DISTRIBUTION_VALUES = {}
|
24
24
|
BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_DISTRIBUTION_", DISTRIBUTION_VALUES))
|
25
|
-
|
26
|
-
DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :rcv_timeout, :socket_recv_size, :poll_timeout, :socket_send_size]
|
25
|
+
|
26
|
+
DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :rcv_timeout, :socket_recv_size, :poll_timeout, :socket_send_size, :server_failure_limit]
|
27
|
+
|
28
|
+
CONVERSION_FACTORS = {
|
29
|
+
:connect_timeout => 1_000_000,
|
30
|
+
:rcv_timeout => 1_000_000,
|
31
|
+
:poll_timeout => 1_000
|
32
|
+
}
|
27
33
|
|
28
34
|
#:startdoc:
|
29
35
|
|
30
36
|
private
|
31
|
-
|
37
|
+
|
32
38
|
# Set a behavior option for this Memcached instance. Accepts a Symbol <tt>behavior</tt> and either <tt>true</tt>, <tt>false</tt>, or a Symbol for <tt>value</tt>. Arguments are validated and converted into integers for the struct setter method.
|
33
39
|
def set_behavior(behavior, value) #:doc:
|
34
|
-
raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
|
35
|
-
|
40
|
+
raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
|
41
|
+
|
36
42
|
# Scoped validations; annoying
|
37
|
-
msg = "Invalid behavior value #{value.inspect} for #{behavior.inspect}"
|
38
|
-
case behavior
|
43
|
+
msg = "Invalid behavior value #{value.inspect} for #{behavior.inspect}"
|
44
|
+
case behavior
|
39
45
|
when :hash then raise(ArgumentError, msg) unless HASH_VALUES[value]
|
40
46
|
when :distribution then raise(ArgumentError, msg) unless DISTRIBUTION_VALUES[value]
|
41
|
-
when *DIRECT_VALUE_BEHAVIORS then raise(ArgumentError, msg) unless value.is_a?
|
47
|
+
when *DIRECT_VALUE_BEHAVIORS then raise(ArgumentError, msg) unless value.is_a?(Numeric) and value > 0
|
42
48
|
else
|
43
49
|
raise(ArgumentError, msg) unless BEHAVIOR_VALUES[value]
|
44
50
|
end
|
45
|
-
|
46
|
-
lib_value = BEHAVIOR_VALUES[value] || value
|
51
|
+
|
52
|
+
lib_value = BEHAVIOR_VALUES[value] || (value * (CONVERSION_FACTORS[behavior] || 1)).to_i
|
47
53
|
#STDERR.puts "Setting #{behavior}:#{b_id} => #{value} (#{lib_value})"
|
48
|
-
Lib.memcached_behavior_set(@struct, b_id, lib_value)
|
54
|
+
Lib.memcached_behavior_set(@struct, b_id, lib_value)
|
49
55
|
#STDERR.puts " -> set to #{get_behavior(behavior).inspect}"
|
50
|
-
end
|
51
|
-
|
56
|
+
end
|
57
|
+
|
52
58
|
# Get a behavior value for this Memcached instance. Accepts a Symbol.
|
53
59
|
def get_behavior(behavior)
|
54
60
|
raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
|
55
|
-
value = Lib.memcached_behavior_get(@struct, b_id)
|
61
|
+
value = Lib.memcached_behavior_get(@struct, b_id)
|
56
62
|
|
57
|
-
if BEHAVIOR_VALUES.invert.has_key?(value)
|
63
|
+
if BEHAVIOR_VALUES.invert.has_key?(value)
|
58
64
|
# False, nil are valid values so we can not rely on direct lookups
|
59
65
|
case behavior
|
60
66
|
# Scoped values; still annoying
|
data/lib/memcached/memcached.rb
CHANGED
@@ -16,15 +16,18 @@ class Memcached
|
|
16
16
|
:support_cas => false,
|
17
17
|
:tcp_nodelay => false,
|
18
18
|
:show_backtraces => false,
|
19
|
-
:retry_timeout =>
|
20
|
-
:timeout => 0.
|
21
|
-
:
|
19
|
+
:retry_timeout => 30,
|
20
|
+
:timeout => 0.25,
|
21
|
+
:rcv_timeout => nil,
|
22
|
+
:poll_timeout => nil,
|
23
|
+
:connect_timeout => 2,
|
22
24
|
:prefix_key => nil,
|
23
25
|
:hash_with_prefix_key => true,
|
24
26
|
:default_ttl => 604800,
|
25
27
|
:default_weight => 8,
|
26
28
|
:sort_hosts => false,
|
27
|
-
:
|
29
|
+
:auto_eject_hosts => false,
|
30
|
+
:server_failure_limit => 2,
|
28
31
|
:verify_key => true
|
29
32
|
}
|
30
33
|
|
@@ -50,21 +53,25 @@ Valid option parameters are:
|
|
50
53
|
<tt>:prefix_key</tt>:: A string to prepend to every key, for namespacing. Max length is 127.
|
51
54
|
<tt>:hash</tt>:: The name of a hash function to use. Possible values are: <tt>:crc</tt>, <tt>:default</tt>, <tt>:fnv1_32</tt>, <tt>:fnv1_64</tt>, <tt>:fnv1a_32</tt>, <tt>:fnv1a_64</tt>, <tt>:hsieh</tt>, <tt>:md5</tt>, and <tt>:murmur</tt>. <tt>:default</tt> is the fastest. Use <tt>:md5</tt> for compatibility with other ketama clients.
|
52
55
|
<tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent_ketama</tt>, <tt>:consistent_wheel</tt>, or <tt>:ketama</tt>. Defaults to <tt>:ketama</tt>.
|
53
|
-
<tt>:
|
56
|
+
<tt>:server_failure_limit</tt>:: How many consecutive failures to allow before marking a host as dead. Has no effect unless <tt>:retry_timeout</tt> is also set.
|
57
|
+
<tt>:retry_timeout</tt>:: How long to wait until retrying a dead server. Has no effect unless <tt>:server_failure_limit</tt> is non-zero. Defaults to <tt>30</tt>.
|
58
|
+
<tt>:auto_eject_hosts</tt>:: Whether to temporarily eject dead hosts from the pool. Defaults to <tt>false</tt>. Note that in the event of an ejection, <tt>:auto_eject_hosts</tt> will remap the entire pool unless <tt>:distribution</tt> is set to <tt>:consistent</tt>.
|
54
59
|
<tt>:cache_lookups</tt>:: Whether to cache hostname lookups for the life of the instance. Defaults to <tt>true</tt>.
|
55
60
|
<tt>:support_cas</tt>:: Flag CAS support in the client. Accepts <tt>true</tt> or <tt>false</tt>. Defaults to <tt>false</tt> because it imposes a slight performance penalty. Note that your server must also support CAS or you will trigger <b>Memcached::ProtocolError</b> exceptions.
|
56
61
|
<tt>:tcp_nodelay</tt>:: Turns on the no-delay feature for connecting sockets. Accepts <tt>true</tt> or <tt>false</tt>. Performance may or may not change, depending on your system.
|
57
|
-
<tt>:no_block</tt>:: Whether to use
|
62
|
+
<tt>:no_block</tt>:: Whether to use pipelining for writes. Accepts <tt>true</tt> or <tt>false</tt>.
|
58
63
|
<tt>:buffer_requests</tt>:: Whether to use an internal write buffer. Accepts <tt>true</tt> or <tt>false</tt>. Calling <tt>get</tt> or closing the connection will force the buffer to flush. Note that <tt>:buffer_requests</tt> might not work well without <tt>:no_block</tt> also enabled.
|
59
64
|
<tt>:show_backtraces</tt>:: Whether <b>Memcached::NotFound</b> exceptions should include backtraces. Generating backtraces is slow, so this is off by default. Turn it on to ease debugging.
|
60
|
-
<tt>:
|
65
|
+
<tt>:connect_timeout</tt>:: How long to wait for a connection to a server. Defaults to 2 seconds. Set to <tt>0</tt> if you want to wait forever.
|
66
|
+
<tt>:timeout</tt>:: How long to wait for a response from the server. Defaults to 0.25 seconds. Set to <tt>0</tt> if you want to wait forever.
|
67
|
+
<tt>:poll_timeout</tt>:: How long to wait for a response from the server in the non-blocking select loop. Defaults to 0.25 seconds. Set to <tt>0</tt> if you want to wait forever.
|
61
68
|
<tt>:default_ttl</tt>:: The <tt>ttl</tt> to use on set if no <tt>ttl</tt> is specified, in seconds. Defaults to one week. Set to <tt>0</tt> if you want things to never expire.
|
62
69
|
<tt>:default_weight</tt>:: The weight to use if <tt>:ketama_weighted</tt> is <tt>true</tt>, but no weight is specified for a server.
|
63
70
|
<tt>:hash_with_prefix_key</tt>:: Whether to include the prefix when calculating which server a key falls on. Defaults to <tt>true</tt>.
|
64
71
|
<tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is rarely useful.
|
65
72
|
<tt>:verify_key</tt>:: Validate keys before accepting them. Never disable this.
|
66
73
|
|
67
|
-
Please note that when
|
74
|
+
Please note that when pipelining is enabled, setter and deleter methods do not raise on errors. For example, if you try to set an invalid key with <tt>:no_block => true</tt>, it will appear to succeed. The actual setting of the key occurs after libmemcached has returned control to your program, so there is no way to backtrack and raise the exception.
|
68
75
|
|
69
76
|
=end
|
70
77
|
|
@@ -93,17 +100,9 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
93
100
|
raise ArgumentError, ":sort_hosts defeats :consistent hashing"
|
94
101
|
end
|
95
102
|
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
options[:poll_timeout] = (options[:timeout] * 1_000).to_i
|
100
|
-
else
|
101
|
-
options[:poll_timeout] = 1
|
102
|
-
options[:rcv_timeout] = (options[:timeout] * 1_000_000).to_i
|
103
|
-
end
|
104
|
-
elsif options[:no_block]
|
105
|
-
raise ArgumentError, "Can't set :timeout => 0 when :no_block => true."
|
106
|
-
end
|
103
|
+
# Read timeouts
|
104
|
+
options[:rcv_timeout] ||= options[:timeout]
|
105
|
+
options[:poll_timeout] ||= options[:timeout]
|
107
106
|
|
108
107
|
# Set the behaviors on the struct
|
109
108
|
set_behaviors
|
@@ -114,7 +113,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
114
113
|
|
115
114
|
# Set the servers on the struct
|
116
115
|
set_servers(servers)
|
117
|
-
|
116
|
+
|
118
117
|
# Not found exceptions
|
119
118
|
unless options[:show_backtraces]
|
120
119
|
@not_found_instance = NotFound.new
|
@@ -128,7 +127,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
128
127
|
inspect_server(server)
|
129
128
|
end
|
130
129
|
end
|
131
|
-
|
130
|
+
|
132
131
|
# Safely copy this instance. Returns a Memcached instance.
|
133
132
|
#
|
134
133
|
# <tt>clone</tt> is useful for threading, since each thread must have its own unshared Memcached
|
@@ -150,6 +149,12 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
150
149
|
set_servers(current_servers)
|
151
150
|
end
|
152
151
|
|
152
|
+
# Disconnect from all currently connected servers
|
153
|
+
def quit
|
154
|
+
Lib.memcached_quit(@struct)
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
153
158
|
#:stopdoc:
|
154
159
|
alias :dup :clone #:nodoc:
|
155
160
|
#:startdoc:
|
@@ -184,7 +189,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
184
189
|
def set(key, value, ttl=nil, marshal=true, flags=FLAGS)
|
185
190
|
value = marshal ? Marshal.dump(value) : value.to_s
|
186
191
|
check_return_code(
|
187
|
-
Lib.memcached_set(@struct, key, value, ttl || options[:default_ttl], flags)
|
192
|
+
Lib.memcached_set(@struct, key, value, ttl || options[:default_ttl], flags),
|
193
|
+
key
|
188
194
|
)
|
189
195
|
end
|
190
196
|
|
@@ -192,7 +198,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
192
198
|
def add(key, value, ttl=nil, marshal=true, flags=FLAGS)
|
193
199
|
value = marshal ? Marshal.dump(value) : value.to_s
|
194
200
|
check_return_code(
|
195
|
-
Lib.memcached_add(@struct, key, value, ttl || options[:default_ttl], flags)
|
201
|
+
Lib.memcached_add(@struct, key, value, ttl || options[:default_ttl], flags),
|
202
|
+
key
|
196
203
|
)
|
197
204
|
end
|
198
205
|
|
@@ -203,14 +210,14 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
203
210
|
# Note that the key must be initialized to an unmarshalled integer first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>marshal</tt> set to <tt>false</tt>.
|
204
211
|
def increment(key, offset=1)
|
205
212
|
ret, value = Lib.memcached_increment(@struct, key, offset)
|
206
|
-
check_return_code(ret)
|
213
|
+
check_return_code(ret, key)
|
207
214
|
value
|
208
215
|
end
|
209
216
|
|
210
217
|
# Decrement a key's value. The parameters and exception behavior are the same as <tt>increment</tt>.
|
211
218
|
def decrement(key, offset=1)
|
212
219
|
ret, value = Lib.memcached_decrement(@struct, key, offset)
|
213
|
-
check_return_code(ret)
|
220
|
+
check_return_code(ret, key)
|
214
221
|
value
|
215
222
|
end
|
216
223
|
|
@@ -223,7 +230,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
223
230
|
def replace(key, value, ttl=nil, marshal=true, flags=FLAGS)
|
224
231
|
value = marshal ? Marshal.dump(value) : value.to_s
|
225
232
|
check_return_code(
|
226
|
-
Lib.memcached_replace(@struct, key, value, ttl || options[:default_ttl], flags)
|
233
|
+
Lib.memcached_replace(@struct, key, value, ttl || options[:default_ttl], flags),
|
234
|
+
key
|
227
235
|
)
|
228
236
|
end
|
229
237
|
|
@@ -233,7 +241,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
233
241
|
def append(key, value)
|
234
242
|
# Requires memcached 1.2.4
|
235
243
|
check_return_code(
|
236
|
-
Lib.memcached_append(@struct, key, value.to_s, IGNORED, IGNORED)
|
244
|
+
Lib.memcached_append(@struct, key, value.to_s, IGNORED, IGNORED),
|
245
|
+
key
|
237
246
|
)
|
238
247
|
end
|
239
248
|
|
@@ -241,7 +250,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
241
250
|
def prepend(key, value)
|
242
251
|
# Requires memcached 1.2.4
|
243
252
|
check_return_code(
|
244
|
-
Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, IGNORED)
|
253
|
+
Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, IGNORED),
|
254
|
+
key
|
245
255
|
)
|
246
256
|
end
|
247
257
|
|
@@ -259,7 +269,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
259
269
|
value = marshal ? Marshal.dump(value) : value.to_s
|
260
270
|
|
261
271
|
check_return_code(
|
262
|
-
Lib.memcached_cas(@struct, key, value, ttl || options[:default_ttl], flags, @struct.result.cas)
|
272
|
+
Lib.memcached_cas(@struct, key, value, ttl || options[:default_ttl], flags, @struct.result.cas),
|
273
|
+
key
|
263
274
|
)
|
264
275
|
end
|
265
276
|
|
@@ -268,7 +279,8 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
268
279
|
# Deletes a key/value pair from the server. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
|
269
280
|
def delete(key)
|
270
281
|
check_return_code(
|
271
|
-
Lib.memcached_delete(@struct, key, IGNORED)
|
282
|
+
Lib.memcached_delete(@struct, key, IGNORED),
|
283
|
+
key
|
272
284
|
)
|
273
285
|
end
|
274
286
|
|
@@ -295,13 +307,13 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
295
307
|
if keys.is_a? Array
|
296
308
|
# Multi get
|
297
309
|
ret = Lib.memcached_mget(@struct, keys);
|
298
|
-
check_return_code(ret)
|
310
|
+
check_return_code(ret, keys)
|
299
311
|
|
300
312
|
hash = {}
|
301
313
|
keys.size.times do
|
302
314
|
value, key, flags, ret = Lib.memcached_fetch_rvalue(@struct)
|
303
315
|
break if ret == Lib::MEMCACHED_END
|
304
|
-
check_return_code(ret)
|
316
|
+
check_return_code(ret, key)
|
305
317
|
value = Marshal.load(value) if marshal
|
306
318
|
# Assign the value
|
307
319
|
hash[key] = value
|
@@ -310,7 +322,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
310
322
|
else
|
311
323
|
# Single get
|
312
324
|
value, flags, ret = Lib.memcached_get_rvalue(@struct, keys)
|
313
|
-
check_return_code(ret)
|
325
|
+
check_return_code(ret, keys)
|
314
326
|
value = Marshal.load(value) if marshal
|
315
327
|
value
|
316
328
|
end
|
@@ -318,6 +330,12 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
318
330
|
|
319
331
|
### Information methods
|
320
332
|
|
333
|
+
# Return the server used by a particular key.
|
334
|
+
def server_by_key(key)
|
335
|
+
ret = Lib.memcached_server_by_key(@struct, key)
|
336
|
+
inspect_server(ret.first) if ret.is_a?(Array)
|
337
|
+
end
|
338
|
+
|
321
339
|
# Return a Hash of statistics responses from the set of servers. Each value is an array with one entry for each server, in the same order the servers were defined.
|
322
340
|
def stats
|
323
341
|
stats = Hash.new([])
|
@@ -335,7 +353,7 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
335
353
|
@struct,
|
336
354
|
Lib.memcached_select_stat_at(@struct, stat_struct, index),
|
337
355
|
key)
|
338
|
-
check_return_code(ret)
|
356
|
+
check_return_code(ret, key)
|
339
357
|
|
340
358
|
value = case value
|
341
359
|
when /^\d+\.\d+$/: value.to_f
|
@@ -355,38 +373,40 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
355
373
|
|
356
374
|
private
|
357
375
|
|
358
|
-
# Checks the return code from Rlibmemcached against the exception list. Raises the corresponding exception if the return code is not Memcached::Success or Memcached::ActionQueued. Accepts an integer return code.
|
359
|
-
def check_return_code(ret) #:doc:
|
360
|
-
return if ret == 0
|
361
|
-
|
376
|
+
# Checks the return code from Rlibmemcached against the exception list. Raises the corresponding exception if the return code is not Memcached::Success or Memcached::ActionQueued. Accepts an integer return code and an optional key, for exception messages.
|
377
|
+
def check_return_code(ret, key = nil) #:doc:
|
378
|
+
return if ret == 0 # Lib::MEMCACHED_SUCCESS
|
379
|
+
return if ret == Lib::MEMCACHED_BUFFERED
|
362
380
|
|
363
|
-
# SystemError; eject from the pool
|
364
381
|
if ret == Lib::MEMCACHED_NOTFOUND and !options[:show_backtraces]
|
365
382
|
raise @not_found_instance
|
366
|
-
elsif options[:failover] and (ret == Lib::MEMCACHED_UNKNOWN_READ_FAILURE or ret == Lib::MEMCACHED_ERRNO)
|
367
|
-
failed = sweep_servers
|
368
|
-
raise EXCEPTIONS[ret], "Server #{failed} failed permanently"
|
369
383
|
else
|
370
|
-
|
384
|
+
message = "Key #{inspect_keys(key, (detect_failure if ret == Lib::MEMCACHED_SERVER_MARKED_DEAD)).inspect}"
|
385
|
+
if ret == Lib::MEMCACHED_ERRNO and key.is_a?(String)
|
386
|
+
server = Lib.memcached_server_by_key(@struct, key)
|
387
|
+
message = "Errno #{server.first.cached_errno}. #{message}"
|
388
|
+
end
|
389
|
+
raise EXCEPTIONS[ret], message
|
371
390
|
end
|
372
391
|
end
|
373
392
|
|
374
|
-
#
|
375
|
-
def
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
393
|
+
# Turn an array of keys into a hash of keys to servers.
|
394
|
+
def inspect_keys(keys, server = nil)
|
395
|
+
Hash[*Array(keys).map do |key|
|
396
|
+
[key, server || server_by_key(key)]
|
397
|
+
end.flatten]
|
398
|
+
end
|
399
|
+
|
400
|
+
# Find which server failed most recently.
|
401
|
+
def detect_failure
|
402
|
+
time = Time.now
|
403
|
+
server = server_structs.detect do |server|
|
404
|
+
server.next_retry > time
|
385
405
|
end
|
386
|
-
|
406
|
+
inspect_server(server) if server
|
387
407
|
end
|
388
408
|
|
389
|
-
# Set the servers on the struct
|
409
|
+
# Set the servers on the struct.
|
390
410
|
def set_servers(servers)
|
391
411
|
Array(servers).each_with_index do |server, index|
|
392
412
|
unless server.is_a? String and server =~ /^[\w\d\.-]+(:\d{1,5}){0,2}$/
|
@@ -399,14 +419,14 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
399
419
|
@servers = send(:servers)
|
400
420
|
end
|
401
421
|
|
402
|
-
# Set the behaviors on the struct from the current options
|
422
|
+
# Set the behaviors on the struct from the current options.
|
403
423
|
def set_behaviors
|
404
424
|
BEHAVIORS.keys.each do |behavior|
|
405
425
|
set_behavior(behavior, options[behavior]) if options.key?(behavior)
|
406
426
|
end
|
407
427
|
end
|
408
428
|
|
409
|
-
# Set the callbacks on the struct from the current options
|
429
|
+
# Set the callbacks on the struct from the current options.
|
410
430
|
def set_callbacks
|
411
431
|
# Only support prefix_key for now
|
412
432
|
if options[:prefix_key]
|
@@ -416,10 +436,10 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
|
|
416
436
|
Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, options[:prefix_key])
|
417
437
|
end
|
418
438
|
end
|
419
|
-
|
420
|
-
# Stringify an opaque server struct
|
439
|
+
|
440
|
+
# Stringify an opaque server struct.
|
421
441
|
def inspect_server(server)
|
422
|
-
"#{server.hostname}:#{server.port}#{":#{server.weight}" if options[:ketama_weighted]}"
|
442
|
+
"#{server.hostname}:#{server.port}#{":#{server.weight}" if options[:ketama_weighted]}"
|
423
443
|
end
|
424
444
|
|
425
445
|
end
|
data/memcached.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{memcached}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.14"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Evan Weaver"]
|
9
9
|
s.cert_chain = ["/Users/eweaver/p/configuration/gem_certificates/evan_weaver-original-public_cert.pem"]
|
10
|
-
s.date = %q{
|
10
|
+
s.date = %q{2009-02-02}
|
11
11
|
s.description = %q{An interface to the libmemcached C client.}
|
12
12
|
s.email = %q{}
|
13
13
|
s.extensions = ["ext/extconf.rb"]
|
data/test/unit/memcached_test.rb
CHANGED
@@ -72,7 +72,9 @@ class MemcachedTest < Test::Unit::TestCase
|
|
72
72
|
def test_options_are_set
|
73
73
|
Memcached::DEFAULTS.merge(@nb_options).each do |key, expected|
|
74
74
|
value = @nb_cache.options[key]
|
75
|
-
|
75
|
+
unless key == :rcv_timeout or key == :poll_timeout
|
76
|
+
assert(expected == value, "#{key} should be #{expected} but was #{value}")
|
77
|
+
end
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
@@ -94,14 +96,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
94
96
|
assert_raise(ArgumentError) { Memcached.new "local host:43043:1" }
|
95
97
|
end
|
96
98
|
|
97
|
-
def test_initialize_with_resolvable_hosts
|
98
|
-
host = `hostname`.chomp
|
99
|
-
cache = Memcached.new
|
100
|
-
assert_equal host, cache.send(:server_structs).first.hostname
|
101
|
-
|
102
|
-
cache.set(key, @value)
|
103
|
-
assert_equal @value, cache.get(key)
|
104
|
-
end
|
99
|
+
# def test_initialize_with_resolvable_hosts
|
100
|
+
# host = `hostname`.chomp
|
101
|
+
# cache = Memcached.new("#{host}:43042")
|
102
|
+
# assert_equal host, cache.send(:server_structs).first.hostname
|
103
|
+
#
|
104
|
+
# cache.set(key, @value)
|
105
|
+
# assert_equal @value, cache.get(key)
|
106
|
+
# end
|
105
107
|
|
106
108
|
def test_initialize_with_invalid_options
|
107
109
|
assert_raise(ArgumentError) do
|
@@ -218,29 +220,53 @@ class MemcachedTest < Test::Unit::TestCase
|
|
218
220
|
|
219
221
|
def test_get_with_server_timeout
|
220
222
|
socket = stub_server 43047
|
221
|
-
cache = Memcached.new(
|
222
|
-
"localhost:43047:1",
|
223
|
-
:timeout => 0.5
|
224
|
-
)
|
223
|
+
cache = Memcached.new("localhost:43047:1", :timeout => 0.5)
|
225
224
|
assert 0.49 < (Benchmark.measure do
|
226
225
|
assert_raise(Memcached::UnknownReadFailure) do
|
227
226
|
result = cache.get key
|
228
227
|
end
|
229
228
|
end).real
|
229
|
+
|
230
|
+
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.001, :rcv_timeout => 0.5)
|
231
|
+
assert 0.49 < (Benchmark.measure do
|
232
|
+
assert_raise(Memcached::UnknownReadFailure) do
|
233
|
+
result = cache.get key
|
234
|
+
end
|
235
|
+
end).real
|
236
|
+
|
237
|
+
cache = Memcached.new("localhost:43047:1", :poll_timeout => 0.25, :rcv_timeout => 0.25)
|
238
|
+
assert 0.4 > (Benchmark.measure do
|
239
|
+
assert_raise(Memcached::UnknownReadFailure) do
|
240
|
+
result = cache.get key
|
241
|
+
end
|
242
|
+
end).real
|
230
243
|
end
|
231
244
|
|
232
245
|
def test_get_with_no_block_server_timeout
|
233
246
|
socket = stub_server 43048
|
234
|
-
cache = Memcached.new(
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
247
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true, :timeout => 0.25)
|
248
|
+
assert 0.24 < (Benchmark.measure do
|
249
|
+
assert_raise(Memcached::UnknownReadFailure) do
|
250
|
+
result = cache.get key
|
251
|
+
end
|
252
|
+
end).real
|
253
|
+
|
254
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true, :poll_timeout => 0.25, :rcv_timeout => 0.001)
|
239
255
|
assert 0.24 < (Benchmark.measure do
|
240
256
|
assert_raise(Memcached::UnknownReadFailure) do
|
241
257
|
result = cache.get key
|
242
258
|
end
|
243
259
|
end).real
|
260
|
+
|
261
|
+
cache = Memcached.new("localhost:43048:1", :no_block => true,
|
262
|
+
:poll_timeout => 0.001,
|
263
|
+
:rcv_timeout => 0.25 # No affect in no-block mode
|
264
|
+
)
|
265
|
+
assert 0.24 > (Benchmark.measure do
|
266
|
+
assert_raise(Memcached::UnknownReadFailure) do
|
267
|
+
result = cache.get key
|
268
|
+
end
|
269
|
+
end).real
|
244
270
|
end
|
245
271
|
|
246
272
|
def test_get_with_prefix_key
|
@@ -617,14 +643,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
617
643
|
|
618
644
|
def test_key_too_long
|
619
645
|
key = "x"*251
|
620
|
-
assert_raises(Memcached::
|
646
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
621
647
|
@cache.set key, @value
|
622
648
|
end
|
623
|
-
assert_raises(Memcached::
|
649
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
624
650
|
@cache.get(key)
|
625
651
|
end
|
626
652
|
|
627
|
-
assert_raises(Memcached::
|
653
|
+
assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
|
628
654
|
@cache.get([key])
|
629
655
|
end
|
630
656
|
end
|
@@ -727,12 +753,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
727
753
|
|
728
754
|
# Server removal and consistent hashing
|
729
755
|
|
730
|
-
def
|
756
|
+
def test_missing_server
|
731
757
|
socket = stub_server 43041
|
732
758
|
cache = Memcached.new(
|
733
759
|
[@servers.last, 'localhost:43041'],
|
734
760
|
:prefix_key => @prefix_key,
|
735
|
-
:
|
761
|
+
:auto_eject_hosts => true,
|
762
|
+
:server_failure_limit => 1,
|
763
|
+
:retry_timeout => 1,
|
736
764
|
:hash_with_prefix_key => false,
|
737
765
|
:hash => :md5
|
738
766
|
)
|
@@ -746,7 +774,15 @@ class MemcachedTest < Test::Unit::TestCase
|
|
746
774
|
key2 = 'test_missing_server'
|
747
775
|
assert_raise(Memcached::UnknownReadFailure) do
|
748
776
|
cache.set(key2, @value)
|
777
|
+
end
|
778
|
+
|
779
|
+
# Hit second server again
|
780
|
+
key2 = 'test_missing_server'
|
781
|
+
begin
|
749
782
|
cache.get(key2)
|
783
|
+
rescue => e
|
784
|
+
assert_equal Memcached::ServerIsMarkedDead, e.class
|
785
|
+
assert_match /localhost:43041/, e.message
|
750
786
|
end
|
751
787
|
|
752
788
|
# Hit first server on retry
|
@@ -754,13 +790,14 @@ class MemcachedTest < Test::Unit::TestCase
|
|
754
790
|
cache.set(key2, @value)
|
755
791
|
cache.get(key2)
|
756
792
|
end
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
793
|
+
|
794
|
+
sleep(2)
|
795
|
+
|
796
|
+
# Hit second server again after restore, expect same failure
|
797
|
+
key2 = 'test_missing_server'
|
798
|
+
assert_raise(Memcached::UnknownReadFailure) do
|
799
|
+
cache.set(key2, @value)
|
800
|
+
end
|
764
801
|
end
|
765
802
|
|
766
803
|
def test_consistent_hashing
|
@@ -811,6 +848,13 @@ class MemcachedTest < Test::Unit::TestCase
|
|
811
848
|
end
|
812
849
|
threads.each {|thread| thread.join}
|
813
850
|
end
|
851
|
+
|
852
|
+
# Hash
|
853
|
+
|
854
|
+
def test_hash
|
855
|
+
assert_equal 3157003241,
|
856
|
+
Rlibmemcached.memcached_generate_hash_rvalue("test", Rlibmemcached::MEMCACHED_HASH_FNV1_32)
|
857
|
+
end
|
814
858
|
|
815
859
|
# Memory cleanup
|
816
860
|
|