Class MemCache
In: vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb
Parent: Object

A Ruby client library for memcached.

Methods

Classes and Modules

Class MemCache::MemCacheError
Class MemCache::Server

Constants

VERSION = '1.7.4'   The version of MemCache you are using.
DEFAULT_OPTIONS = { :namespace => nil, :readonly => false, :multithread => true, :failover => true, :timeout => 0.5, :logger => nil, :no_reply => false, }   Default options for the cache object.
DEFAULT_PORT = 11211   Default memcached port.
DEFAULT_WEIGHT = 1   Default memcached server weight.
ONE_MB = 1024 * 1024   Add key to the cache with value value that expires in expiry seconds. If raw is true, value will not be Marshalled.

Warning: Readers should not call this method in the event of a cache miss; see MemCache#add.

Attributes

failover  [R]  Should the client try to failover to another server if the first server is down? Defaults to true.
logger  [R]  Log debug/info/warn/error to the given Logger, defaults to nil.
multithread  [R]  The multithread setting for this instance
namespace  [R]  The namespace for this instance
no_reply  [R]  Don‘t send or look for a reply from the memcached server for write operations. Please note this feature only works in memcached 1.2.5 and later. Earlier versions will reply with "ERROR".
servers  [R]  The servers this client talks to. Play at your own peril.
timeout  [R]  Socket timeout limit with this client, defaults to 0.5 sec. Set to nil to disable timeouts.

Public Class methods

Accepts a list of servers and a list of opts. servers may be omitted. See +servers=+ for acceptable server list arguments.

Valid options for opts are:

  [:namespace]   Prepends this value to all keys added or retrieved.
  [:readonly]    Raises an exception on cache writes when true.
  [:multithread] Wraps cache access in a Mutex for thread safety. Defaults to true.
  [:failover]    Should the client try to failover to another server if the
                 first server is down?  Defaults to true.
  [:timeout]     Time to use as the socket read timeout.  Defaults to 0.5 sec,
                 set to nil to disable timeouts (this is a major performance penalty in Ruby 1.8,
                 "gem install SystemTimer' to remove most of the penalty).
  [:logger]      Logger to use for info/debug output, defaults to nil
  [:no_reply]    Don't bother looking for a reply for write operations (i.e. they
                 become 'fire and forget'), memcached 1.2.5 and later only, speeds up
                 set/add/delete/incr/decr significantly.

Other options are ignored.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 102
102:   def initialize(*args)
103:     servers = []
104:     opts = {}
105: 
106:     case args.length
107:     when 0 then # NOP
108:     when 1 then
109:       arg = args.shift
110:       case arg
111:       when Hash   then opts = arg
112:       when Array  then servers = arg
113:       when String then servers = [arg]
114:       else raise ArgumentError, 'first argument must be Array, Hash or String'
115:       end
116:     when 2 then
117:       servers, opts = args
118:     else
119:       raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
120:     end
121: 
122:     opts = DEFAULT_OPTIONS.merge opts
123:     @namespace   = opts[:namespace]
124:     @readonly    = opts[:readonly]
125:     @multithread = opts[:multithread]
126:     @timeout     = opts[:timeout]
127:     @failover    = opts[:failover]
128:     @logger      = opts[:logger]
129:     @no_reply    = opts[:no_reply]
130:     @mutex       = Mutex.new if @multithread
131: 
132:     logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger
133: 
134:     Thread.current[:memcache_client] = self.object_id if !@multithread
135: 
136:     self.servers = servers
137:   end

Public Instance methods

[](key, raw = false)

Alias for get

Shortcut to save a value in the cache. This method does not set an expiration on the entry. Use set to specify an explicit expiry.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 613
613:   def []=(key, value)
614:     set key, value
615:   end

Returns whether there is at least one active server for the object.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 150
150:   def active?
151:     not @servers.empty?
152:   end

Add key to the cache with value value that expires in expiry seconds, but only if key does not already exist in the cache. If raw is true, value will not be Marshalled.

Readers should call this method in the event of a cache miss, not MemCache#set.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 393
393:   def add(key, value, expiry = 0, raw = false)
394:     raise MemCacheError, "Update of readonly cache" if @readonly
395:     with_server(key) do |server, cache_key|
396:       value = Marshal.dump value unless raw
397:       logger.debug { "add #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
398:       command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
399: 
400:       with_socket_management(server) do |socket|
401:         socket.write command
402:         break nil if @no_reply
403:         result = socket.gets
404:         raise_on_error_response! result
405:         result
406:       end
407:     end
408:   end

Append - ‘add this data to an existing key after existing data’ Please note the value is always passed to memcached as raw since it doesn‘t make a lot of sense to concatenate marshalled data together.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 435
435:   def append(key, value)
436:     raise MemCacheError, "Update of readonly cache" if @readonly
437:     with_server(key) do |server, cache_key|
438:       logger.debug { "append #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
439:       command = "append #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
440: 
441:       with_socket_management(server) do |socket|
442:         socket.write command
443:         break nil if @no_reply
444:         result = socket.gets
445:         raise_on_error_response! result
446:         result
447:       end
448:     end
449:   end

"cas" is a check and set operation which means "store this data but only if no one else has updated since I last fetched it." This can be used as a form of optimistic locking.

Works in block form like so:

  cache.cas('some-key') do |value|
    value + 1
  end

Returns: nil if the value was not found on the memcached server. STORED if the value was updated successfully EXISTS if the value was updated by someone else since last fetch

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 355
355:   def cas(key, expiry=0, raw=false)
356:     raise MemCacheError, "Update of readonly cache" if @readonly
357:     raise MemCacheError, "A block is required" unless block_given?
358: 
359:     (value, token) = gets(key, raw)
360:     return nil unless value
361:     updated = yield value
362: 
363:     with_server(key) do |server, cache_key|
364: 
365:       value = Marshal.dump updated unless raw
366:       logger.debug { "cas #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
367:       command = "cas #{cache_key} 0 #{expiry} #{value.to_s.size} #{token}#{noreply}\r\n#{value}\r\n"
368: 
369:       with_socket_management(server) do |socket|
370:         socket.write command
371:         break nil if @no_reply
372:         result = socket.gets
373:         raise_on_error_response! result
374: 
375:         if result.nil?
376:           server.close
377:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
378:         end
379: 
380:         result
381:       end
382:     end
383:   end

Decrements the value for key by amount and returns the new value. key must already exist. If key is not an integer, it is assumed to be

  1. key can not be decremented below 0.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 193
193:   def decr(key, amount = 1)
194:     raise MemCacheError, "Update of readonly cache" if @readonly
195:     with_server(key) do |server, cache_key|
196:       cache_decr server, cache_key, amount
197:     end
198:   rescue TypeError => err
199:     handle_error nil, err
200:   end

Removes key from the cache in expiry seconds.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 474
474:   def delete(key, expiry = 0)
475:     raise MemCacheError, "Update of readonly cache" if @readonly
476:     with_server(key) do |server, cache_key|
477:       with_socket_management(server) do |socket|
478:         logger.debug { "delete #{cache_key} on #{server}" } if logger
479:         socket.write "delete #{cache_key} #{expiry}#{noreply}\r\n"
480:         break nil if @no_reply
481:         result = socket.gets
482:         raise_on_error_response! result
483:         result
484:       end
485:     end
486:   end

Performs a get with the given key. If the value does not exist and a block was given, the block will be called and the result saved via add.

If you do not provide a block, using this method is the same as using get.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 226
226:   def fetch(key, expiry = 0, raw = false)
227:     value = get(key, raw)
228: 
229:     if value.nil? && block_given?
230:       value = yield
231:       add(key, value, expiry, raw)
232:     end
233: 
234:     value
235:   end

Flush the cache from all memcache servers. A non-zero value for delay will ensure that the flush is propogated slowly through your memcached server farm. The Nth server will be flushed N*delay seconds from now, asynchronously so this method returns quickly. This prevents a huge database spike due to a total flush all at once.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 497
497:   def flush_all(delay=0)
498:     raise MemCacheError, 'No active servers' unless active?
499:     raise MemCacheError, "Update of readonly cache" if @readonly
500: 
501:     begin
502:       delay_time = 0
503:       @servers.each do |server|
504:         with_socket_management(server) do |socket|
505:           logger.debug { "flush_all #{delay_time} on #{server}" } if logger
506:           if delay == 0 # older versions of memcached will fail silently otherwise
507:             socket.write "flush_all#{noreply}\r\n"
508:           else
509:             socket.write "flush_all #{delay_time}#{noreply}\r\n"
510:           end
511:           break nil if @no_reply
512:           result = socket.gets
513:           raise_on_error_response! result
514:           result
515:         end
516:         delay_time += delay
517:       end
518:     rescue IndexError => err
519:       handle_error nil, err
520:     end
521:   end

Retrieves key from memcache. If raw is false, the value will be unmarshalled.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 206
206:   def get(key, raw = false)
207:     with_server(key) do |server, cache_key|
208:       logger.debug { "get #{key} from #{server.inspect}" } if logger
209:       value = cache_get server, cache_key
210:       return nil if value.nil?
211:       value = Marshal.load value unless raw
212:       return value
213:     end
214:   rescue TypeError => err
215:     handle_error nil, err
216:   end

Retrieves multiple values from memcached in parallel, if possible.

The memcached protocol supports the ability to retrieve multiple keys in a single request. Pass in an array of keys to this method and it will:

  1. map the key to the appropriate memcached server
  2. send a single request to each server that has one or more key values

Returns a hash of values.

  cache["a"] = 1
  cache["b"] = 2
  cache.get_multi "a", "b" # => { "a" => 1, "b" => 2 }

Note that get_multi assumes the values are marshalled.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 255
255:   def get_multi(*keys)
256:     raise MemCacheError, 'No active servers' unless active?
257: 
258:     keys.flatten!
259:     key_count = keys.length
260:     cache_keys = {}
261:     server_keys = Hash.new { |h,k| h[k] = [] }
262: 
263:     # map keys to servers
264:     keys.each do |key|
265:       server, cache_key = request_setup key
266:       cache_keys[cache_key] = key
267:       server_keys[server] << cache_key
268:     end
269: 
270:     results = {}
271: 
272:     server_keys.each do |server, keys_for_server|
273:       keys_for_server_str = keys_for_server.join ' '
274:       begin
275:         values = cache_get_multi server, keys_for_server_str
276:         values.each do |key, value|
277:           results[cache_keys[key]] = Marshal.load value
278:         end
279:       rescue IndexError => e
280:         # Ignore this server and try the others
281:         logger.warn { "Unable to retrieve #{keys_for_server.size} elements from #{server.inspect}: #{e.message}"} if logger
282:       end
283:     end
284: 
285:     return results
286:   rescue TypeError => err
287:     handle_error nil, err
288:   end

Increments the value for key by amount and returns the new value. key must already exist. If key is not an integer, it is assumed to be 0.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 295
295:   def incr(key, amount = 1)
296:     raise MemCacheError, "Update of readonly cache" if @readonly
297:     with_server(key) do |server, cache_key|
298:       cache_incr server, cache_key, amount
299:     end
300:   rescue TypeError => err
301:     handle_error nil, err
302:   end

Returns a string representation of the cache object.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 142
142:   def inspect
143:     "<MemCache: %d servers, ns: %p, ro: %p>" %
144:       [@servers.length, @namespace, @readonly]
145:   end

Prepend - ‘add this data to an existing key before existing data’ Please note the value is always passed to memcached as raw since it doesn‘t make a lot of sense to concatenate marshalled data together.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 455
455:   def prepend(key, value)
456:     raise MemCacheError, "Update of readonly cache" if @readonly
457:     with_server(key) do |server, cache_key|
458:       logger.debug { "prepend #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
459:       command = "prepend #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
460: 
461:       with_socket_management(server) do |socket|
462:         socket.write command
463:         break nil if @no_reply
464:         result = socket.gets
465:         raise_on_error_response! result
466:         result
467:       end
468:     end
469:   end

Returns whether or not the cache object was created read only.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 157
157:   def readonly?
158:     @readonly
159:   end

Add key to the cache with value value that expires in expiry seconds, but only if key already exists in the cache. If raw is true, value will not be Marshalled.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 414
414:   def replace(key, value, expiry = 0, raw = false)
415:     raise MemCacheError, "Update of readonly cache" if @readonly
416:     with_server(key) do |server, cache_key|
417:       value = Marshal.dump value unless raw
418:       logger.debug { "replace #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
419:       command = "replace #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
420: 
421:       with_socket_management(server) do |socket|
422:         socket.write command
423:         break nil if @no_reply
424:         result = socket.gets
425:         raise_on_error_response! result
426:         result
427:       end
428:     end
429:   end

Reset the connection to all memcache servers. This should be called if there is a problem with a cache lookup that might have left the connection in a corrupted state.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 528
528:   def reset
529:     @servers.each { |server| server.close }
530:   end

Set the servers that the requests will be distributed between. Entries can be either strings of the form "hostname:port" or "hostname:port:weight" or MemCache::Server objects.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 166
166:   def servers=(servers)
167:     # Create the server objects.
168:     @servers = Array(servers).collect do |server|
169:       case server
170:       when String
171:         host, port, weight = server.split ':', 3
172:         port ||= DEFAULT_PORT
173:         weight ||= DEFAULT_WEIGHT
174:         Server.new self, host, port, weight
175:       else
176:         server
177:       end
178:     end
179: 
180:     logger.debug { "Servers now: #{@servers.inspect}" } if logger
181: 
182:     # There's no point in doing this if there's only one server
183:     @continuum = create_continuum_for(@servers) if @servers.size > 1
184: 
185:     @servers
186:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 313
313:   def set(key, value, expiry = 0, raw = false)
314:     raise MemCacheError, "Update of readonly cache" if @readonly
315:     with_server(key) do |server, cache_key|
316: 
317:       value = Marshal.dump value unless raw
318:       logger.debug { "set #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
319: 
320:       raise MemCacheError, "Value too large, memcached can only store 1MB of data per key" if value.to_s.size > ONE_MB
321: 
322:       command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
323: 
324:       with_socket_management(server) do |socket|
325:         socket.write command
326:         break nil if @no_reply
327:         result = socket.gets
328:         raise_on_error_response! result
329: 
330:         if result.nil?
331:           server.close
332:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
333:         end
334: 
335:         result
336:       end
337:     end
338:   end

Returns statistics for each memcached server. An explanation of the statistics can be found in the memcached docs:

code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

Example:

  >> pp CACHE.stats
  {"localhost:11211"=>
    {"bytes"=>4718,
     "pid"=>20188,
     "connection_structures"=>4,
     "time"=>1162278121,
     "pointer_size"=>32,
     "limit_maxbytes"=>67108864,
     "cmd_get"=>14532,
     "version"=>"1.2.0",
     "bytes_written"=>432583,
     "cmd_set"=>32,
     "get_misses"=>0,
     "total_connections"=>19,
     "curr_connections"=>3,
     "curr_items"=>4,
     "uptime"=>1557,
     "get_hits"=>14532,
     "total_items"=>32,
     "rusage_system"=>0.313952,
     "rusage_user"=>0.119981,
     "bytes_read"=>190619}}
  => nil

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 564
564:   def stats
565:     raise MemCacheError, "No active servers" unless active?
566:     server_stats = {}
567: 
568:     @servers.each do |server|
569:       next unless server.alive?
570: 
571:       with_socket_management(server) do |socket|
572:         value = nil
573:         socket.write "stats\r\n"
574:         stats = {}
575:         while line = socket.gets do
576:           raise_on_error_response! line
577:           break if line == "END\r\n"
578:           if line =~ /\ASTAT ([\S]+) ([\w\.\:]+)/ then
579:             name, value = $1, $2
580:             stats[name] = case name
581:                           when 'version'
582:                             value
583:                           when 'rusage_user', 'rusage_system' then
584:                             seconds, microseconds = value.split(/:/, 2)
585:                             microseconds ||= 0
586:                             Float(seconds) + (Float(microseconds) / 1_000_000)
587:                           else
588:                             if value =~ /\A\d+\Z/ then
589:                               value.to_i
590:                             else
591:                               value
592:                             end
593:                           end
594:           end
595:         end
596:         server_stats["#{server.host}:#{server.port}"] = stats
597:       end
598:     end
599: 
600:     raise MemCacheError, "No active servers" if server_stats.empty?
601:     server_stats
602:   end

Protected Instance methods

Performs a raw decr for cache_key from server. Returns nil if not found.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 666
666:   def cache_decr(server, cache_key, amount)
667:     with_socket_management(server) do |socket|
668:       socket.write "decr #{cache_key} #{amount}#{noreply}\r\n"
669:       break nil if @no_reply
670:       text = socket.gets
671:       raise_on_error_response! text
672:       return nil if text == "NOT_FOUND\r\n"
673:       return text.to_i
674:     end
675:   end

Fetches the raw data for cache_key from server. Returns nil on cache miss.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 681
681:   def cache_get(server, cache_key)
682:     with_socket_management(server) do |socket|
683:       socket.write "get #{cache_key}\r\n"
684:       keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
685: 
686:       if keyline.nil? then
687:         server.close
688:         raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
689:       end
690: 
691:       raise_on_error_response! keyline
692:       return nil if keyline == "END\r\n"
693: 
694:       unless keyline =~ /(\d+)\r/ then
695:         server.close
696:         raise MemCacheError, "unexpected response #{keyline.inspect}"
697:       end
698:       value = socket.read $1.to_i
699:       socket.read 2 # "\r\n"
700:       socket.gets   # "END\r\n"
701:       return value
702:     end
703:   end

Fetches cache_keys from server using a multi-get.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 740
740:   def cache_get_multi(server, cache_keys)
741:     with_socket_management(server) do |socket|
742:       values = {}
743:       socket.write "get #{cache_keys}\r\n"
744: 
745:       while keyline = socket.gets do
746:         return values if keyline == "END\r\n"
747:         raise_on_error_response! keyline
748: 
749:         unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
750:           server.close
751:           raise MemCacheError, "unexpected response #{keyline.inspect}"
752:         end
753: 
754:         key, data_length = $1, $3
755:         values[$1] = socket.read data_length.to_i
756:         socket.read(2) # "\r\n"
757:       end
758: 
759:       server.close
760:       raise MemCacheError, "lost connection to #{server.host}:#{server.port}" # TODO: retry here too
761:     end
762:   end

Performs a raw incr for cache_key from server. Returns nil if not found.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 768
768:   def cache_incr(server, cache_key, amount)
769:     with_socket_management(server) do |socket|
770:       socket.write "incr #{cache_key} #{amount}#{noreply}\r\n"
771:       break nil if @no_reply
772:       text = socket.gets
773:       raise_on_error_response! text
774:       return nil if text == "NOT_FOUND\r\n"
775:       return text.to_i
776:     end
777:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 891
891:   def check_multithread_status!
892:     return if @multithread
893: 
894:     if Thread.current[:memcache_client] != self.object_id
895:       raise MemCacheError, "You are accessing this memcache-client instance from multiple threads but have not enabled multithread support.\nNormally:  MemCache.new(['localhost:11211'], :multithread => true)\nIn Rails:  config.cache_store = [:mem_cache_store, 'localhost:11211', { :multithread => true }]\n"
896:     end
897:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 872
872:   def create_continuum_for(servers)
873:     total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
874:     continuum = []
875: 
876:     servers.each do |server|
877:       entry_count_for(server, servers.size, total_weight).times do |idx|
878:         hash = Digest::SHA1.hexdigest("#{server.host}:#{server.port}:#{idx}")
879:         value = Integer("0x#{hash[0..7]}")
880:         continuum << Continuum::Entry.new(value, server)
881:       end
882:     end
883: 
884:     continuum.sort { |a, b| a.value <=> b.value }
885:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 887
887:   def entry_count_for(server, total_servers, total_weight)
888:     ((total_servers * Continuum::POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
889:   end

Pick a server to handle the request based on a hash of the key.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 642
642:   def get_server_for_key(key, options = {})
643:     raise ArgumentError, "illegal character in key #{key.inspect}" if
644:       key =~ /\s/
645:     raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
646:     raise MemCacheError, "No servers available" if @servers.empty?
647:     return @servers.first if @servers.length == 1
648: 
649:     hkey = hash_for(key)
650: 
651:     20.times do |try|
652:       entryidx = Continuum.binary_search(@continuum, hkey)
653:       server = @continuum[entryidx].server
654:       return server if server.alive?
655:       break unless failover
656:       hkey = hash_for "#{try}#{key}"
657:     end
658:     
659:     raise MemCacheError, "No servers available"
660:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 705
705:   def gets(key, raw = false)
706:     with_server(key) do |server, cache_key|
707:       logger.debug { "gets #{key} from #{server.inspect}" } if logger
708:       result = with_socket_management(server) do |socket|
709:         socket.write "gets #{cache_key}\r\n"
710:         keyline = socket.gets # "VALUE <key> <flags> <bytes> <cas token>\r\n"
711: 
712:         if keyline.nil? then
713:           server.close
714:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
715:         end
716: 
717:         raise_on_error_response! keyline
718:         return nil if keyline == "END\r\n"
719: 
720:         unless keyline =~ /(\d+) (\w+)\r/ then
721:           server.close
722:           raise MemCacheError, "unexpected response #{keyline.inspect}"
723:         end
724:         value = socket.read $1.to_i
725:         socket.read 2 # "\r\n"
726:         socket.gets   # "END\r\n"
727:         [value, $2]
728:       end
729:       result[0] = Marshal.load result[0] unless raw
730:       result
731:     end
732:   rescue TypeError => err
733:     handle_error nil, err
734:   end

Handles error from server.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 843
843:   def handle_error(server, error)
844:     raise error if error.is_a?(MemCacheError)
845:     server.close if server
846:     new_error = MemCacheError.new error.message
847:     new_error.set_backtrace error.backtrace
848:     raise new_error
849:   end

Returns an interoperable hash value for key. (I think, docs are sketchy for down servers).

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 635
635:   def hash_for(key)
636:     Zlib.crc32(key)
637:   end

Create a key for the cache, incorporating the namespace qualifier if requested.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 623
623:   def make_cache_key(key)
624:     if namespace.nil? then
625:       key
626:     else
627:       "#{@namespace}:#{key}"
628:     end
629:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 851
851:   def noreply
852:     @no_reply ? ' noreply' : ''
853:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 866
866:   def raise_on_error_response!(response)
867:     if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/
868:       raise MemCacheError, $1.strip
869:     end
870:   end

Performs setup for making a request with key from memcached. Returns the server to fetch the key from and the complete key to use.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 859
859:   def request_setup(key)
860:     raise MemCacheError, 'No active servers' unless active?
861:     cache_key = make_cache_key key
862:     server = get_server_for_key cache_key
863:     return server, cache_key
864:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 824
824:   def with_server(key)
825:     retried = false
826:     begin
827:       server, cache_key = request_setup(key)
828:       yield server, cache_key
829:     rescue IndexError => e
830:       logger.warn { "Server failed: #{e.class.name}: #{e.message}" } if logger
831:       if !retried && @servers.size > 1
832:         logger.info { "Connection to server #{server.inspect} DIED! Retrying operation..." } if logger
833:         retried = true
834:         retry
835:       end
836:       handle_error(nil, e)
837:     end
838:   end

Gets or creates a socket connected to the given server, and yields it to the block, wrapped in a mutex synchronization if @multithread is true.

If a socket error (SocketError, SystemCallError, IOError) or protocol error (MemCacheError) is raised by the block, closes the socket, attempts to connect again, and retries the block (once). If an error is again raised, reraises it as MemCacheError.

If unable to connect to the server (or if in the reconnect wait period), raises MemCacheError. Note that the socket connect code marks a server dead for a timeout period, so retrying does not apply to connection attempt failures (but does still apply to unexpectedly lost connections etc.).

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb, line 793
793:   def with_socket_management(server, &block)
794:     check_multithread_status!
795: 
796:     @mutex.lock if @multithread
797:     retried = false
798: 
799:     begin
800:       socket = server.socket
801: 
802:       # Raise an IndexError to show this server is out of whack. If were inside
803:       # a with_server block, we'll catch it and attempt to restart the operation.
804: 
805:       raise IndexError, "No connection to server (#{server.status})" if socket.nil?
806: 
807:       block.call(socket)
808: 
809:     rescue SocketError, Errno::EAGAIN, Timeout::Error => err
810:       logger.warn { "Socket failure: #{err.message}" } if logger
811:       server.mark_dead(err)
812:       handle_error(server, err)
813: 
814:     rescue MemCacheError, SystemCallError, IOError => err
815:       logger.warn { "Generic failure: #{err.class.name}: #{err.message}" } if logger
816:       handle_error(server, err) if retried || socket.nil?
817:       retried = true
818:       retry
819:     end
820:   ensure
821:     @mutex.unlock if @multithread
822:   end

[Validate]