memcached 0.11 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,7 +23,7 @@ class Memcached
23
23
  DISTRIBUTION_VALUES = {}
24
24
  BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_DISTRIBUTION_", DISTRIBUTION_VALUES))
25
25
 
26
- DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :socket_recv_size, :poll_timeout, :socket_send_size]
26
+ DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :rcv_timeout, :socket_recv_size, :poll_timeout, :socket_send_size]
27
27
 
28
28
  #:startdoc:
29
29
 
@@ -66,21 +66,14 @@ Subclasses correspond one-to-one with server response strings or libmemcached er
66
66
  end
67
67
 
68
68
  class NotFound
69
- def self.remove_backtraces
70
- class_eval do
71
- def set_backtrace(*args); []; end
72
- alias :backtrace :set_backtrace
73
- end
69
+ attr_accessor :no_backtrace
70
+
71
+ def set_backtrace(*args)
72
+ @no_backtrace ? [] : super
74
73
  end
75
74
 
76
- def self.restore_backtraces
77
- class_eval do
78
- begin
79
- remove_method :set_backtrace
80
- remove_method :backtrace
81
- rescue NameError
82
- end
83
- end
75
+ def backtrace(*args)
76
+ @no_backtrace ? [] : super
84
77
  end
85
78
  end
86
79
 
@@ -9,46 +9,58 @@ class Memcached
9
9
  DEFAULTS = {
10
10
  :hash => :default,
11
11
  :no_block => false,
12
- :distribution => :consistent,
12
+ :distribution => :consistent_ketama,
13
+ :ketama_weighted => true,
13
14
  :buffer_requests => false,
14
15
  :cache_lookups => true,
15
16
  :support_cas => false,
16
17
  :tcp_nodelay => false,
17
- :show_not_found_backtraces => false,
18
+ :show_backtraces => false,
18
19
  :retry_timeout => 60,
19
- # :poll_timeout => 5,
20
+ :timeout => 0.5,
20
21
  :connect_timeout => 5,
21
22
  :prefix_key => nil,
23
+ :hash_with_prefix_key => true,
24
+ :default_ttl => 604800,
25
+ :default_weight => 8,
22
26
  :sort_hosts => false,
23
27
  :failover => false,
24
28
  :verify_key => true
25
- }
26
-
29
+ }
30
+
27
31
  #:stopdoc:
28
32
  IGNORED = 0
29
-
30
- NOTFOUND_INSTANCE = NotFound.new
31
33
  #:startdoc:
32
-
34
+
33
35
  attr_reader :options # Return the options Hash used to configure this instance.
34
36
 
35
37
  ###### Configuration
36
38
 
37
- =begin rdoc
38
- Create a new Memcached instance. Accepts a single server string such as 'localhost:11211', or an array of such strings, as well an an optional configuration hash.
39
+ =begin rdoc
40
+ Create a new Memcached instance. Accepts string or array of server strings, as well an an optional configuration hash.
41
+
42
+ Memcached.new('localhost', ...) # A single server
43
+ Memcached.new(['web001:11212', 'web002:11212'], ...) # Two servers with custom ports
44
+ Memcached.new(['web001:11211:2', 'web002:11211:8'], ...) # Two servers with default ports and explicit weights
45
+
46
+ Weights only affect Ketama hashing. If you use Ketama hashing and don't specify a weight, the client will poll each server's stats and use its size as the weight.
39
47
 
40
48
  Valid option parameters are:
41
49
 
42
- <tt>:prefix_key</tt>:: A string to prepend to every key, for namespacing. Max length is 11.
50
+ <tt>:prefix_key</tt>:: A string to prepend to every key, for namespacing. Max length is 127.
43
51
  <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.
44
- <tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent</tt>, or <tt>:consistent_wheel</tt>. Defaults to <tt>:consistent</tt>, which is ketama-compatible.
52
+ <tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent_ketama</tt>, <tt>:consistent_wheel</tt>, or <tt>:ketama</tt>. Defaults to <tt>:ketama</tt>.
45
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>.
46
54
  <tt>:cache_lookups</tt>:: Whether to cache hostname lookups for the life of the instance. Defaults to <tt>true</tt>.
47
55
  <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.
48
56
  <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.
49
57
  <tt>:no_block</tt>:: Whether to use non-blocking, asynchronous IO for writes. Accepts <tt>true</tt> or <tt>false</tt>.
50
58
  <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.
51
- <tt>:show_not_found_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.
59
+ <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.
61
+ <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
+ <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
+ <tt>:hash_with_prefix_key</tt>:: Whether to include the prefix when calculating which server a key falls on. Defaults to <tt>true</tt>.
52
64
  <tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is rarely useful.
53
65
  <tt>:verify_key</tt>:: Validate keys before accepting them. Never disable this.
54
66
 
@@ -57,25 +69,42 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
57
69
  =end
58
70
 
59
71
  def initialize(servers, opts = {})
60
- @struct = Lib::MemcachedSt.new
72
+ @struct = Lib::MemcachedSt.new
61
73
  Lib.memcached_create(@struct)
62
74
 
63
- # Merge option defaults
64
- @options = DEFAULTS.merge(opts)
65
-
75
+ # Merge option defaults and discard meaningless keys
76
+ @options = Hash[*DEFAULTS.map do |key, default|
77
+ [key, opts[key] || default]
78
+ end.flatten]
79
+
66
80
  # Force :buffer_requests to use :no_block
67
81
  # XXX Deleting the :no_block key should also work, but libmemcached doesn't seem to set it
68
82
  # consistently
69
- options[:no_block] = true if options[:buffer_requests]
70
-
83
+ options[:no_block] = true if options[:buffer_requests]
84
+
85
+ # Disallow weights without ketama
86
+ options.delete(:ketama_weighted) if options[:distribution] != :consistent_ketama
87
+
71
88
  # Legacy accessor
72
89
  options[:prefix_key] = options.delete(:namespace) if options[:namespace]
73
-
90
+
74
91
  # Disallow :sort_hosts with consistent hashing
75
92
  if options[:sort_hosts] and options[:distribution] == :consistent
76
93
  raise ArgumentError, ":sort_hosts defeats :consistent hashing"
77
94
  end
78
-
95
+
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
107
+
79
108
  # Set the behaviors on the struct
80
109
  set_behaviors
81
110
  set_callbacks
@@ -84,46 +113,43 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
84
113
  options.freeze
85
114
 
86
115
  # Set the servers on the struct
87
- set_servers(servers)
88
-
116
+ set_servers(servers)
117
+
89
118
  # Not found exceptions
90
- # Note that these have global effects since the NotFound class itself is modified. You should only
91
- # be enabling the backtrace for debugging purposes, so it's not really a big deal.
92
- if options[:show_not_found_backtraces]
93
- NotFound.restore_backtraces
94
- else
95
- NotFound.remove_backtraces
96
- end
119
+ unless options[:show_backtraces]
120
+ @not_found_instance = NotFound.new
121
+ @not_found_instance.no_backtrace = true
122
+ end
97
123
  end
98
124
 
99
125
  # Return the array of server strings used to configure this instance.
100
126
  def servers
101
127
  server_structs.map do |server|
102
- "#{server.hostname}:#{server.port}"
128
+ inspect_server(server)
103
129
  end
104
130
  end
105
-
106
- # Safely copy this instance. Returns a Memcached instance.
131
+
132
+ # Safely copy this instance. Returns a Memcached instance.
107
133
  #
108
- # <tt>clone</tt> is useful for threading, since each thread must have its own unshared Memcached
109
- # object.
134
+ # <tt>clone</tt> is useful for threading, since each thread must have its own unshared Memcached
135
+ # object.
110
136
  #
111
137
  def clone
112
138
  memcached = super
113
139
  memcached.instance_variable_set('@struct', Lib.memcached_clone(nil, @struct))
114
140
  memcached
115
141
  end
116
-
142
+
117
143
  # Reset the state of the libmemcached struct. This is useful for changing the server list at runtime.
118
144
  def reset(current_servers = nil)
119
145
  current_servers ||= servers
120
- @struct = Lib::MemcachedSt.new
146
+ @struct = Lib::MemcachedSt.new
121
147
  Lib.memcached_create(@struct)
122
148
  set_behaviors
123
149
  set_callbacks
124
150
  set_servers(current_servers)
125
- end
126
-
151
+ end
152
+
127
153
  #:stopdoc:
128
154
  alias :dup :clone #:nodoc:
129
155
  #:startdoc:
@@ -131,44 +157,46 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
131
157
  ### Configuration helpers
132
158
 
133
159
  private
134
-
160
+
135
161
  # Return an array of raw <tt>memcached_host_st</tt> structs for this instance.
136
162
  def server_structs
137
163
  array = []
138
- @struct.hosts.count.times do |i|
139
- array << Lib.memcached_select_server_at(@struct, i)
164
+ if @struct.hosts
165
+ @struct.hosts.count.times do |i|
166
+ array << Lib.memcached_select_server_at(@struct, i)
167
+ end
140
168
  end
141
169
  array
142
- end
143
-
170
+ end
171
+
144
172
  ###### Operations
145
-
173
+
146
174
  public
147
-
175
+
148
176
  ### Setters
149
-
177
+
150
178
  # Set a key/value pair. Accepts a String <tt>key</tt> and an arbitrary Ruby object. Overwrites any existing value on the server.
151
179
  #
152
- # Accepts an optional <tt>timeout</tt> value to specify the maximum lifetime of the key on the server. <tt>timeout</tt> can be either an integer number of seconds, or a Time elapsed time object. <tt>0</tt> means no timeout. Note that there is no guarantee that the key will persist as long as the <tt>timeout</tt>, but it will not persist longer.
180
+ # Accepts an optional <tt>ttl</tt> value to specify the maximum lifetime of the key on the server. <tt>ttl</tt> can be either an integer number of seconds, or a Time elapsed time object. <tt>0</tt> means no ttl. Note that there is no guarantee that the key will persist as long as the <tt>ttl</tt>, but it will not persist longer.
153
181
  #
154
- # Also accepts a <tt>marshal</tt> value, which defaults to <tt>true</tt>. Set <tt>marshal</tt> to <tt>false</tt> if you want the <tt>value</tt> to be set directly.
155
- #
156
- def set(key, value, timeout=0, marshal=true)
182
+ # Also accepts a <tt>marshal</tt> value, which defaults to <tt>true</tt>. Set <tt>marshal</tt> to <tt>false</tt> if you want the <tt>value</tt> to be set directly.
183
+ #
184
+ def set(key, value, ttl=nil, marshal=true, flags=FLAGS)
157
185
  value = marshal ? Marshal.dump(value) : value.to_s
158
186
  check_return_code(
159
- Lib.memcached_set(@struct, key, value, timeout, FLAGS)
187
+ Lib.memcached_set(@struct, key, value, ttl || options[:default_ttl], flags)
160
188
  )
161
189
  end
162
190
 
163
191
  # Add a key/value pair. Raises <b>Memcached::NotStored</b> if the key already exists on the server. The parameters are the same as <tt>set</tt>.
164
- def add(key, value, timeout=0, marshal=true)
192
+ def add(key, value, ttl=nil, marshal=true, flags=FLAGS)
165
193
  value = marshal ? Marshal.dump(value) : value.to_s
166
194
  check_return_code(
167
- Lib.memcached_add(@struct, key, value, timeout, FLAGS)
195
+ Lib.memcached_add(@struct, key, value, ttl || options[:default_ttl], flags)
168
196
  )
169
197
  end
170
198
 
171
- # Increment a key's value. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
199
+ # Increment a key's value. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
172
200
  #
173
201
  # Also accepts an optional <tt>offset</tt> paramater, which defaults to 1. <tt>offset</tt> must be an integer.
174
202
  #
@@ -185,53 +213,53 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
185
213
  check_return_code(ret)
186
214
  value
187
215
  end
188
-
189
- #:stopdoc:
216
+
217
+ #:stopdoc:
190
218
  alias :incr :increment
191
219
  alias :decr :decrement
192
220
  #:startdoc:
193
221
 
194
222
  # Replace a key/value pair. Raises <b>Memcached::NotFound</b> if the key does not exist on the server. The parameters are the same as <tt>set</tt>.
195
- def replace(key, value, timeout=0, marshal=true)
223
+ def replace(key, value, ttl=nil, marshal=true, flags=FLAGS)
196
224
  value = marshal ? Marshal.dump(value) : value.to_s
197
225
  check_return_code(
198
- Lib.memcached_replace(@struct, key, value, timeout, FLAGS)
226
+ Lib.memcached_replace(@struct, key, value, ttl || options[:default_ttl], flags)
199
227
  )
200
228
  end
201
229
 
202
- # Appends a string to a key's value. Accepts a String <tt>key</tt> and a String <tt>value</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist on the server.
230
+ # Appends a string to a key's value. Accepts a String <tt>key</tt> and a String <tt>value</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist on the server.
203
231
  #
204
232
  # Note that the key must be initialized to an unmarshalled string first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>marshal</tt> set to <tt>false</tt>.
205
233
  def append(key, value)
206
234
  # Requires memcached 1.2.4
207
235
  check_return_code(
208
- Lib.memcached_append(@struct, key, value.to_s, IGNORED, FLAGS)
236
+ Lib.memcached_append(@struct, key, value.to_s, IGNORED, IGNORED)
209
237
  )
210
238
  end
211
-
239
+
212
240
  # Prepends a string to a key's value. The parameters and exception behavior are the same as <tt>append</tt>.
213
241
  def prepend(key, value)
214
242
  # Requires memcached 1.2.4
215
243
  check_return_code(
216
- Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, FLAGS)
244
+ Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, IGNORED)
217
245
  )
218
246
  end
219
-
247
+
220
248
  # Reads a key's value from the server and yields it to a block. Replaces the key's value with the result of the block as long as the key hasn't been updated in the meantime, otherwise raises <b>Memcached::NotStored</b>. Accepts a String <tt>key</tt> and a block.
221
249
  #
222
- # Also accepts an optional <tt>timeout</tt> value.
250
+ # Also accepts an optional <tt>ttl</tt> value.
223
251
  #
224
252
  # CAS stands for "compare and swap", and avoids the need for manual key mutexing. CAS support must be enabled in Memcached.new or a <b>Memcached::ClientError</b> will be raised. Note that CAS may be buggy in memcached itself.
225
253
  #
226
- def cas(key, timeout = 0, marshal = true)
254
+ def cas(key, ttl=nil, marshal=true, flags=FLAGS)
227
255
  raise ClientError, "CAS not enabled for this Memcached instance" unless options[:support_cas]
228
-
256
+
229
257
  value = get(key, marshal)
230
258
  value = yield value
231
259
  value = marshal ? Marshal.dump(value) : value.to_s
232
-
260
+
233
261
  check_return_code(
234
- Lib.memcached_cas(@struct, key, value, timeout, FLAGS, @struct.result.cas)
262
+ Lib.memcached_cas(@struct, key, value, ttl || options[:default_ttl], flags, @struct.result.cas)
235
263
  )
236
264
  end
237
265
 
@@ -241,18 +269,18 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
241
269
  def delete(key)
242
270
  check_return_code(
243
271
  Lib.memcached_delete(@struct, key, IGNORED)
244
- )
272
+ )
245
273
  end
246
-
274
+
247
275
  # Flushes all key/value pairs from all the servers.
248
276
  def flush
249
277
  check_return_code(
250
278
  Lib.memcached_flush(@struct, IGNORED)
251
279
  )
252
280
  end
253
-
254
- ### Getters
255
-
281
+
282
+ ### Getters
283
+
256
284
  # Gets a key's value from the server. Accepts a String <tt>key</tt> or array of String <tt>keys</tt>.
257
285
  #
258
286
  # Also accepts a <tt>marshal</tt> value, which defaults to <tt>true</tt>. Set <tt>marshal</tt> to <tt>false</tt> if you want the <tt>value</tt> to be returned directly as a String. Otherwise it will be assumed to be a marshalled Ruby object and unmarshalled.
@@ -266,13 +294,11 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
266
294
  def get(keys, marshal=true)
267
295
  if keys.is_a? Array
268
296
  # Multi get
269
- keys.map! { |key| key }
270
- hash = {}
271
-
272
297
  ret = Lib.memcached_mget(@struct, keys);
273
298
  check_return_code(ret)
274
-
275
- keys.size.times do
299
+
300
+ hash = {}
301
+ keys.size.times do
276
302
  value, key, flags, ret = Lib.memcached_fetch_rvalue(@struct)
277
303
  break if ret == Lib::MEMCACHED_END
278
304
  check_return_code(ret)
@@ -287,88 +313,92 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
287
313
  check_return_code(ret)
288
314
  value = Marshal.load(value) if marshal
289
315
  value
290
- end
291
- end
292
-
316
+ end
317
+ end
318
+
293
319
  ### Information methods
294
-
320
+
295
321
  # 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.
296
322
  def stats
297
323
  stats = Hash.new([])
298
-
324
+
299
325
  stat_struct, ret = Lib.memcached_stat(@struct, "")
300
326
  check_return_code(ret)
301
-
327
+
302
328
  keys, ret = Lib.memcached_stat_get_keys(@struct, stat_struct)
303
329
  check_return_code(ret)
304
-
330
+
305
331
  keys.each do |key|
306
332
  server_structs.size.times do |index|
307
333
 
308
334
  value, ret = Lib.memcached_stat_get_rvalue(
309
- @struct,
335
+ @struct,
310
336
  Lib.memcached_select_stat_at(@struct, stat_struct, index),
311
337
  key)
312
338
  check_return_code(ret)
313
339
 
314
340
  value = case value
315
- when /^\d+\.\d+$/: value.to_f
341
+ when /^\d+\.\d+$/: value.to_f
316
342
  when /^\d+$/: value.to_i
317
343
  else value
318
- end
319
-
344
+ end
345
+
320
346
  stats[key.to_sym] += [value]
321
347
  end
322
348
  end
323
-
349
+
324
350
  Lib.memcached_stat_free(@struct, stat_struct)
325
351
  stats
326
- end
327
-
352
+ end
353
+
328
354
  ### Operations helpers
329
-
355
+
330
356
  private
331
-
357
+
332
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.
333
359
  def check_return_code(ret) #:doc:
334
- # 0.16 --enable-debug returns 0 for an ActionQueued result but --disable-debug does not
335
- return if ret == 0 or ret == 31
360
+ return if ret == 0 or # Lib::MEMCACHED_SUCCESS
361
+ ret == Lib::MEMCACHED_BUFFERED
336
362
 
337
363
  # SystemError; eject from the pool
338
- if ret == 25 and options[:failover]
364
+ if ret == Lib::MEMCACHED_NOTFOUND and !options[:show_backtraces]
365
+ raise @not_found_instance
366
+ elsif options[:failover] and (ret == Lib::MEMCACHED_UNKNOWN_READ_FAILURE or ret == Lib::MEMCACHED_ERRNO)
339
367
  failed = sweep_servers
340
368
  raise EXCEPTIONS[ret], "Server #{failed} failed permanently"
341
369
  else
342
- raise EXCEPTIONS[ret], ""
370
+ raise EXCEPTIONS[ret], ""
343
371
  end
344
- end
372
+ end
345
373
 
346
374
  # Eject the first dead server we find from the pool and reset the struct
347
375
  def sweep_servers
348
376
  # XXX This method is annoying, but necessary until we get Lib.memcached_delete_server or equivalent.
349
377
  server_structs.each do |server|
350
- if server.next_retry > Time.now
351
- server_name = "#{server.hostname}:#{server.port}"
378
+ if server.next_retry > Time.now
379
+ server_name = inspect_server(server)
352
380
  current_servers = servers
353
- current_servers.delete(server_name)
381
+ current_servers.delete(server_name)
354
382
  reset(current_servers)
355
- return server_name
383
+ return server_name
356
384
  end
357
385
  end
358
386
  "(unknown)"
359
387
  end
360
-
388
+
361
389
  # Set the servers on the struct
362
390
  def set_servers(servers)
363
391
  Array(servers).each_with_index do |server, index|
364
- unless server.is_a? String and server =~ /^[\w\d\.]+(:\d{1,5})?$/
365
- raise ArgumentError, "Servers must be in the format host:port (e.g., 'localhost:11211')"
392
+ unless server.is_a? String and server =~ /^[\w\d\.-]+(:\d{1,5}){0,2}$/
393
+ raise ArgumentError, "Servers must be in the format host:port[:weight] (e.g., 'localhost:11211' or 'localhost:11211:10')"
366
394
  end
367
- host, port = server.split(":")
368
- Lib.memcached_server_add(@struct, host, port.to_i)
369
- end
395
+ host, port, weight = server.split(":")
396
+ Lib.memcached_server_add_with_weight(@struct, host, port.to_i, (weight || options[:default_weight]).to_i)
397
+ end
398
+ # For inspect
399
+ @servers = send(:servers)
370
400
  end
371
-
401
+
372
402
  # Set the behaviors on the struct from the current options
373
403
  def set_behaviors
374
404
  BEHAVIORS.keys.each do |behavior|
@@ -377,15 +407,19 @@ Please note that when non-blocking IO is enabled, setter and deleter methods do
377
407
  end
378
408
 
379
409
  # Set the callbacks on the struct from the current options
380
- def set_callbacks
410
+ def set_callbacks
381
411
  # Only support prefix_key for now
382
412
  if options[:prefix_key]
383
- # XXX Libmemcached doesn't validate the key length properly
384
- if options[:prefix_key].size > Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE - 1
413
+ unless options[:prefix_key].size < Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE
385
414
  raise ArgumentError, "Max prefix_key size is #{Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE - 1}"
386
- end
415
+ end
387
416
  Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, options[:prefix_key])
388
417
  end
389
418
  end
390
419
 
420
+ # Stringify an opaque server struct
421
+ def inspect_server(server)
422
+ "#{server.hostname}:#{server.port}#{":#{server.weight}" if options[:ketama_weighted]}"
423
+ end
424
+
391
425
  end