memcached 0.12 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
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
- raise LoadError, "Requires libmemcached #{REQUIRED_VERSION}; you have #{RECEIVED_VERSION}" unless REQUIRED_VERSION == RECEIVED_VERSION
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'
@@ -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? Fixnum and value > 0
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
@@ -16,15 +16,18 @@ class Memcached
16
16
  :support_cas => false,
17
17
  :tcp_nodelay => false,
18
18
  :show_backtraces => false,
19
- :retry_timeout => 60,
20
- :timeout => 0.5,
21
- :connect_timeout => 5,
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
- :failover => false,
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>:failover</tt>:: Whether to permanently eject failed hosts from the pool. Defaults to <tt>false</tt>. Note that in the event of a server failure, <tt>:failover</tt> will remap the entire pool unless <tt>:distribution</tt> is set to <tt>:consistent</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 non-blocking, asynchronous IO for writes. Accepts <tt>true</tt> or <tt>false</tt>.
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>:timeout</tt>:: How long to wait for a response from the server. Defaults to 0.5 seconds. Set to <tt>0</tt> if you want to wait forever.
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 non-blocking IO 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.
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
- # Set timeouts
97
- if options[:timeout] > 0
98
- if options[:no_block]
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 or # Lib::MEMCACHED_SUCCESS
361
- ret == Lib::MEMCACHED_BUFFERED
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
- raise EXCEPTIONS[ret], ""
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
- # Eject the first dead server we find from the pool and reset the struct
375
- def sweep_servers
376
- # XXX This method is annoying, but necessary until we get Lib.memcached_delete_server or equivalent.
377
- server_structs.each do |server|
378
- if server.next_retry > Time.now
379
- server_name = inspect_server(server)
380
- current_servers = servers
381
- current_servers.delete(server_name)
382
- reset(current_servers)
383
- return server_name
384
- end
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
- "(unknown)"
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.12"
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{2008-12-02}
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"]
@@ -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
- assert(expected == value, "#{key} should be #{expected} but was #{value}")
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 ["#{host}:43042"]
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
- "localhost:43048:1",
236
- :no_block => true,
237
- :timeout => 0.25
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::ClientError) do
646
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
621
647
  @cache.set key, @value
622
648
  end
623
- assert_raises(Memcached::ClientError) do
649
+ assert_raises(Memcached::ABadKeyWasProvidedOrCharactersOutOfRange) do
624
650
  @cache.get(key)
625
651
  end
626
652
 
627
- assert_raises(Memcached::ClientError) do
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 test_dying_server
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
- :failover => true,
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
- end
758
-
759
- def test_sweep_servers_with_missing_server_first
760
- cache = Memcached.new(['127.0.0.1:00000'] + @servers)
761
- assert_nothing_raised do
762
- cache.send(:sweep_servers)
763
- end
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