memcached 0.12 → 0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/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
|
|