Parent

MemCache

A Ruby client library for memcached.

Constants

VERSION

The version of MemCache you are using.

DEFAULT_OPTIONS

Default options for the cache object.

DEFAULT_PORT

Default memcached port.

DEFAULT_WEIGHT

Default memcached server weight.

ONE_MB

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

namespace[R]

The namespace for this instance

multithread[R]

The multithread setting for this instance

autofix_keys[R]

Whether to try to fix keys that are too long and will be truncated by using their SHA1 hash instead. The hash is only used on keys longer than 250 characters, or containing spaces, to avoid impacting performance unnecesarily.

In theory, your code should generate correct keys when calling memcache, so it’s your responsibility and you should try to fix this problem at its source.

But if that’s not possible, enable this option and memcache-client will give you a hand.

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.

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.

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”.

Public Class Methods

new(*args) click to toggle source

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.
  [: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.
  [:check_size]   Raises a MemCacheError if the value to be set is greater than 1 MB, which
                  is the maximum key size for the standard memcached server.  Defaults to true.
  [:autofix_keys] If a key is longer than 250 characters or contains spaces,
                  use an SHA1 hash instead, to prevent collisions on truncated keys.

Other options are ignored.

     # File lib/memcache.rb, line 136
136:   def initialize(*args)
137:     servers = []
138:     opts = {}
139: 
140:     case args.length
141:     when 0 then # NOP
142:     when 1 then
143:       arg = args.shift
144:       case arg
145:       when Hash   then opts = arg
146:       when Array  then servers = arg
147:       when String then servers = [arg]
148:       else raise ArgumentError, 'first argument must be Array, Hash or String'
149:       end
150:     when 2 then
151:       servers, opts = args
152:     else
153:       raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
154:     end
155: 
156:     @evented = defined?(EM) && EM.reactor_running?
157:     opts = DEFAULT_OPTIONS.merge opts
158:     @namespace    = opts[:namespace]
159:     @readonly     = opts[:readonly]
160:     @multithread  = opts[:multithread] && !@evented
161:     @autofix_keys = opts[:autofix_keys]
162:     @timeout      = opts[:timeout]
163:     @failover     = opts[:failover]
164:     @logger       = opts[:logger]
165:     @no_reply     = opts[:no_reply]
166:     @check_size   = opts[:check_size]
167:     @namespace_separator = opts[:namespace_separator]
168:     @mutex        = Mutex.new if @multithread
169: 
170:     logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger
171: 
172:     Thread.current[:memcache_client] = self.object_id if !@multithread
173:     
174: 
175:     self.servers = servers
176:   end

Public Instance Methods

[](key, raw = false) click to toggle source

Shortcut to get a value from the cache.

Alias for: get
[]=(key, value) click to toggle source

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.

     # File lib/memcache.rb, line 659
659:   def []=(key, value)
660:     set key, value
661:   end
active?() click to toggle source

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

     # File lib/memcache.rb, line 189
189:   def active?
190:     not @servers.empty?
191:   end
add(key, value, expiry = 0, raw = false) click to toggle source

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.

     # File lib/memcache.rb, line 438
438:   def add(key, value, expiry = 0, raw = false)
439:     raise MemCacheError, "Update of readonly cache" if @readonly
440:     value = Marshal.dump value unless raw
441:     with_server(key) do |server, cache_key|
442:       logger.debug { "add #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
443:       command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
444: 
445:       with_socket_management(server) do |socket|
446:         socket.write command
447:         break nil if @no_reply
448:         result = socket.gets
449:         raise_on_error_response! result
450:         result
451:       end
452:     end
453:   end
append(key, value) click to toggle source

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.

     # File lib/memcache.rb, line 480
480:   def append(key, value)
481:     raise MemCacheError, "Update of readonly cache" if @readonly
482:     with_server(key) do |server, cache_key|
483:       logger.debug { "append #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
484:       command = "append #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
485: 
486:       with_socket_management(server) do |socket|
487:         socket.write command
488:         break nil if @no_reply
489:         result = socket.gets
490:         raise_on_error_response! result
491:         result
492:       end
493:     end
494:   end
cas(key, expiry=0, raw=false) click to toggle source

“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

     # File lib/memcache.rb, line 401
401:   def cas(key, expiry=0, raw=false)
402:     raise MemCacheError, "Update of readonly cache" if @readonly
403:     raise MemCacheError, "A block is required" unless block_given?
404: 
405:     (value, token) = gets(key, raw)
406:     return nil unless value
407:     updated = yield value
408:     value = raw ? updated : Marshal.dump(updated)
409: 
410:     with_server(key) do |server, cache_key|
411:       logger.debug { "cas #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
412:       command = "cas #{cache_key} 0 #{expiry} #{value.to_s.size} #{token}#{noreply}\r\n#{value}\r\n"
413: 
414:       with_socket_management(server) do |socket|
415:         socket.write command
416:         break nil if @no_reply
417:         result = socket.gets
418:         raise_on_error_response! result
419: 
420:         if result.nil?
421:           server.close
422:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
423:         end
424: 
425:         result
426:       end
427:     end
428:   end
decr(key, amount = 1) click to toggle source

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.

     # File lib/memcache.rb, line 232
232:   def decr(key, amount = 1)
233:     raise MemCacheError, "Update of readonly cache" if @readonly
234:     with_server(key) do |server, cache_key|
235:       cache_decr server, cache_key, amount
236:     end
237:   rescue TypeError => err
238:     handle_error nil, err
239:   end
delete(key, expiry = 0) click to toggle source

Removes key from the cache. expiry is ignored as it has been removed from the latest memcached version.

     # File lib/memcache.rb, line 520
520:   def delete(key, expiry = 0)
521:     raise MemCacheError, "Update of readonly cache" if @readonly
522:     with_server(key) do |server, cache_key|
523:       with_socket_management(server) do |socket|
524:         logger.debug { "delete #{cache_key} on #{server}" } if logger
525:         socket.write "delete #{cache_key}#{noreply}\r\n"
526:         break nil if @no_reply
527:         result = socket.gets
528:         raise_on_error_response! result
529:         result
530:       end
531:     end
532:   end
fetch(key, expiry = 0, raw = false) click to toggle source

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.

     # File lib/memcache.rb, line 265
265:   def fetch(key, expiry = 0, raw = false)
266:     value = get(key, raw)
267: 
268:     if value.nil? && block_given?
269:       value = yield
270:       add(key, value, expiry, raw)
271:     end
272: 
273:     value
274:   end
flush_all(delay=0) click to toggle source

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.

     # File lib/memcache.rb, line 543
543:   def flush_all(delay=0)
544:     raise MemCacheError, 'No active servers' unless active?
545:     raise MemCacheError, "Update of readonly cache" if @readonly
546: 
547:     begin
548:       delay_time = 0
549:       @servers.each do |server|
550:         with_socket_management(server) do |socket|
551:           logger.debug { "flush_all #{delay_time} on #{server}" } if logger
552:           if delay == 0 # older versions of memcached will fail silently otherwise
553:             socket.write "flush_all#{noreply}\r\n"
554:           else
555:             socket.write "flush_all #{delay_time}#{noreply}\r\n"
556:           end
557:           break nil if @no_reply
558:           result = socket.gets
559:           raise_on_error_response! result
560:           result
561:         end
562:         delay_time += delay
563:       end
564:     rescue IndexError => err
565:       handle_error nil, err
566:     end
567:   end
get(key, raw = false) click to toggle source

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

     # File lib/memcache.rb, line 245
245:   def get(key, raw = false)
246:     with_server(key) do |server, cache_key|
247:       logger.debug { "get #{key} from #{server.inspect}" } if logger
248:       value = cache_get server, cache_key
249:       return nil if value.nil?
250:       value = Marshal.load value unless raw
251:       return value
252:     end
253:   rescue TypeError => err
254:     handle_error nil, err
255:   end
Also aliased as: []
get_multi(*keys) click to toggle source

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. You can pass in :raw => true to bypass value marshalling.

  cache.get_multi('a', 'b', ..., :raw => true)
     # File lib/memcache.rb, line 297
297:   def get_multi(*keys)
298:     raise MemCacheError, 'No active servers' unless active?
299: 
300:     opts = keys.last.is_a?(Hash) ? keys.pop : {}
301: 
302:     keys.flatten!
303:     key_count = keys.length
304:     cache_keys = {}
305:     server_keys = Hash.new { |h,k| h[k] = [] }
306: 
307:     # map keys to servers
308:     keys.each do |key|
309:       server, cache_key = request_setup key
310:       cache_keys[cache_key] = key
311:       server_keys[server] << cache_key
312:     end
313: 
314:     results = {}
315:     raw = opts[:raw] || false
316:     server_keys.each do |server, keys_for_server|
317:       keys_for_server_str = keys_for_server.join ' '
318:       begin
319:         values = cache_get_multi server, keys_for_server_str
320:         values.each do |key, value|
321:           results[cache_keys[key]] = raw ? value : Marshal.load(value)
322:         end
323:       rescue IndexError => e
324:         # Ignore this server and try the others
325:         logger.warn { "Unable to retrieve #{keys_for_server.size} elements from #{server.inspect}: #{e.message}"} if logger
326:       end
327:     end
328: 
329:     return results
330:   rescue TypeError => err
331:     handle_error nil, err
332:   end
incr(key, amount = 1) click to toggle source

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.

     # File lib/memcache.rb, line 339
339:   def incr(key, amount = 1)
340:     raise MemCacheError, "Update of readonly cache" if @readonly
341:     with_server(key) do |server, cache_key|
342:       cache_incr server, cache_key, amount
343:     end
344:   rescue TypeError => err
345:     handle_error nil, err
346:   end
inspect() click to toggle source

Returns a string representation of the cache object.

     # File lib/memcache.rb, line 181
181:   def inspect
182:     "<MemCache: %d servers, ns: %p, ro: %p>" %
183:       [@servers.length, @namespace, @readonly]
184:   end
prepend(key, value) click to toggle source

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.

     # File lib/memcache.rb, line 500
500:   def prepend(key, value)
501:     raise MemCacheError, "Update of readonly cache" if @readonly
502:     with_server(key) do |server, cache_key|
503:       logger.debug { "prepend #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
504:       command = "prepend #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
505: 
506:       with_socket_management(server) do |socket|
507:         socket.write command
508:         break nil if @no_reply
509:         result = socket.gets
510:         raise_on_error_response! result
511:         result
512:       end
513:     end
514:   end
readonly?() click to toggle source

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

     # File lib/memcache.rb, line 196
196:   def readonly?
197:     @readonly
198:   end
replace(key, value, expiry = 0, raw = false) click to toggle source

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.

     # File lib/memcache.rb, line 459
459:   def replace(key, value, expiry = 0, raw = false)
460:     raise MemCacheError, "Update of readonly cache" if @readonly
461:     value = Marshal.dump value unless raw
462:     with_server(key) do |server, cache_key|
463:       logger.debug { "replace #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger
464:       command = "replace #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
465: 
466:       with_socket_management(server) do |socket|
467:         socket.write command
468:         break nil if @no_reply
469:         result = socket.gets
470:         raise_on_error_response! result
471:         result
472:       end
473:     end
474:   end
reset() click to toggle source

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.

     # File lib/memcache.rb, line 574
574:   def reset
575:     @servers.each { |server| server.close }
576:   end
servers=(servers) click to toggle source

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.

     # File lib/memcache.rb, line 205
205:   def servers=(servers)
206:     # Create the server objects.
207:     @servers = Array(servers).collect do |server|
208:       case server
209:       when String
210:         host, port, weight = server.split ':', 3
211:         port ||= DEFAULT_PORT
212:         weight ||= DEFAULT_WEIGHT
213:         Server.new self, host, port, weight
214:       else
215:         server
216:       end
217:     end
218: 
219:     logger.debug { "Servers now: #{@servers.inspect}" } if logger
220: 
221:     # There's no point in doing this if there's only one server
222:     @continuum = create_continuum_for(@servers) if @servers.size > 1
223: 
224:     @servers
225:   end
set(key, value, expiry = 0, raw = false) click to toggle source
     # File lib/memcache.rb, line 357
357:   def set(key, value, expiry = 0, raw = false)
358:     raise MemCacheError, "Update of readonly cache" if @readonly
359: 
360:     value = Marshal.dump value unless raw
361:     with_server(key) do |server, cache_key|
362:       logger.debug { "set #{key} to #{server.inspect}: #{value.to_s.size}" } if logger
363: 
364:       if @check_size && value.to_s.size > ONE_MB
365:         raise MemCacheError, "Value too large, memcached can only store 1MB of data per key"
366:       end
367: 
368:       command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n"
369: 
370:       with_socket_management(server) do |socket|
371:         socket.write command
372:         break nil if @no_reply
373:         result = socket.gets
374:         raise_on_error_response! result
375: 
376:         if result.nil?
377:           server.close
378:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
379:         end
380: 
381:         result
382:       end
383:     end
384:   end
stats() click to toggle source

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
     # File lib/memcache.rb, line 610
610:   def stats
611:     raise MemCacheError, "No active servers" unless active?
612:     server_stats = {}
613: 
614:     @servers.each do |server|
615:       next unless server.alive?
616: 
617:       with_socket_management(server) do |socket|
618:         value = nil
619:         socket.write "stats\r\n"
620:         stats = {}
621:         while line = socket.gets do
622:           raise_on_error_response! line
623:           break if line == "END\r\n"
624:           if line =~ /\ASTAT ([\S]+) ([\w\.\:]+)/ then
625:             name, value = $1, $2
626:             stats[name] = case name
627:                           when 'version'
628:                             value
629:                           when 'rusage_user', 'rusage_system' then
630:                             seconds, microseconds = value.split(/:/, 2)
631:                             microseconds ||= 0
632:                             Float(seconds) + (Float(microseconds) / 1_000_000)
633:                           else
634:                             if value =~ /\A\d+\Z/ then
635:                               value.to_i
636:                             else
637:                               value
638:                             end
639:                           end
640:           end
641:         end
642:         server_stats["#{server.host}:#{server.port}"] = stats
643:       end
644:     end
645: 
646:     raise MemCacheError, "No active servers" if server_stats.empty?
647:     server_stats
648:   end

Protected Instance Methods

cache_decr(server, cache_key, amount) click to toggle source

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

     # File lib/memcache.rb, line 724
724:   def cache_decr(server, cache_key, amount)
725:     with_socket_management(server) do |socket|
726:       socket.write "decr #{cache_key} #{amount}#{noreply}\r\n"
727:       break nil if @no_reply
728:       text = socket.gets
729:       raise_on_error_response! text
730:       return nil if text == "NOT_FOUND\r\n"
731:       return text.to_i
732:     end
733:   end
cache_get(server, cache_key) click to toggle source

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

     # File lib/memcache.rb, line 739
739:   def cache_get(server, cache_key)
740:     with_socket_management(server) do |socket|
741:       socket.write "get #{cache_key}\r\n"
742:       keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
743: 
744:       if keyline.nil? then
745:         server.close
746:         raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
747:       end
748: 
749:       raise_on_error_response! keyline
750:       return nil if keyline == "END\r\n"
751: 
752:       unless keyline =~ /(\d+)\r/ then
753:         server.close
754:         raise MemCacheError, "unexpected response #{keyline.inspect}"
755:       end
756:       value = socket.read $1.to_i
757:       socket.read 2 # "\r\n"
758:       socket.gets   # "END\r\n"
759:       return value
760:     end
761:   end
cache_get_multi(server, cache_keys) click to toggle source

Fetches cache_keys from server using a multi-get.

     # File lib/memcache.rb, line 798
798:   def cache_get_multi(server, cache_keys)
799:     with_socket_management(server) do |socket|
800:       values = {}
801:       socket.write "get #{cache_keys}\r\n"
802: 
803:       while keyline = socket.gets do
804:         return values if keyline == "END\r\n"
805:         raise_on_error_response! keyline
806: 
807:         unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
808:           server.close
809:           raise MemCacheError, "unexpected response #{keyline.inspect}"
810:         end
811: 
812:         key, data_length = $1, $3
813:         values[$1] = socket.read data_length.to_i
814:         socket.read(2) # "\r\n"
815:       end
816: 
817:       server.close
818:       raise MemCacheError, "lost connection to #{server.host}:#{server.port}" # TODO: retry here too
819:     end
820:   end
cache_incr(server, cache_key, amount) click to toggle source

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

     # File lib/memcache.rb, line 826
826:   def cache_incr(server, cache_key, amount)
827:     with_socket_management(server) do |socket|
828:       socket.write "incr #{cache_key} #{amount}#{noreply}\r\n"
829:       break nil if @no_reply
830:       text = socket.gets
831:       raise_on_error_response! text
832:       return nil if text == "NOT_FOUND\r\n"
833:       return text.to_i
834:     end
835:   end
check_multithread_status!() click to toggle source
     # File lib/memcache.rb, line 949
949:   def check_multithread_status!
950:     return if @multithread
951:     return if @evented
952: 
953:     if Thread.current[:memcache_client] != self.object_id
954:       raise MemCacheError,         You are accessing this memcache-client instance from multiple threads but have not enabled multithread support.        Normally:  MemCache.new(['localhost:11211'], :multithread => true)        In Rails:  config.cache_store = [:mem_cache_store, 'localhost:11211', { :multithread => true }]
955:     end
956:   end
create_continuum_for(servers) click to toggle source
     # File lib/memcache.rb, line 930
930:   def create_continuum_for(servers)
931:     total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
932:     continuum = []
933: 
934:     servers.each do |server|
935:       entry_count_for(server, servers.size, total_weight).times do |idx|
936:         hash = Digest::SHA1.hexdigest("#{server.host}:#{server.port}:#{idx}")
937:         value = Integer("0x#{hash[0..7]}")
938:         continuum << Continuum::Entry.new(value, server)
939:       end
940:     end
941: 
942:     continuum.sort { |a, b| a.value <=> b.value }
943:   end
entry_count_for(server, total_servers, total_weight) click to toggle source
     # File lib/memcache.rb, line 945
945:   def entry_count_for(server, total_servers, total_weight)
946:     ((total_servers * Continuum::POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
947:   end
get_server_for_key(key, options = {}) click to toggle source

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

     # File lib/memcache.rb, line 699
699:   def get_server_for_key(key, options = {})
700:     raise ArgumentError, "illegal character in key #{key.inspect}" if
701:       key =~ /\s/
702:     raise ArgumentError, "key cannot be blank" if key.nil? || key.strip.size == 0
703:     raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
704:     raise MemCacheError, "No servers available" if @servers.empty?
705:     return @servers.first if @servers.length == 1
706: 
707:     hkey = hash_for(key)
708: 
709:     20.times do |try|
710:       entryidx = Continuum.binary_search(@continuum, hkey)
711:       server = @continuum[entryidx].server
712:       return server if server.alive?
713:       break unless failover
714:       hkey = hash_for "#{try}#{key}"
715:     end
716: 
717:     raise MemCacheError, "No servers available"
718:   end
gets(key, raw = false) click to toggle source
     # File lib/memcache.rb, line 763
763:   def gets(key, raw = false)
764:     with_server(key) do |server, cache_key|
765:       logger.debug { "gets #{key} from #{server.inspect}" } if logger
766:       result = with_socket_management(server) do |socket|
767:         socket.write "gets #{cache_key}\r\n"
768:         keyline = socket.gets # "VALUE <key> <flags> <bytes> <cas token>\r\n"
769: 
770:         if keyline.nil? then
771:           server.close
772:           raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
773:         end
774: 
775:         raise_on_error_response! keyline
776:         return nil if keyline == "END\r\n"
777: 
778:         unless keyline =~ /(\d+) (\w+)\r/ then
779:           server.close
780:           raise MemCacheError, "unexpected response #{keyline.inspect}"
781:         end
782:         value = socket.read $1.to_i
783:         socket.read 2 # "\r\n"
784:         socket.gets   # "END\r\n"
785:         [value, $2]
786:       end
787:       result[0] = Marshal.load result[0] unless raw
788:       result
789:     end
790:   rescue TypeError => err
791:     handle_error nil, err
792:   end
handle_error(server, error) click to toggle source

Handles error from server.

     # File lib/memcache.rb, line 901
901:   def handle_error(server, error)
902:     raise error if error.is_a?(MemCacheError)
903:     server.close if server && server.status == "CONNECTED"
904:     new_error = MemCacheError.new error.message
905:     new_error.set_backtrace error.backtrace
906:     raise new_error
907:   end
hash_for(key) click to toggle source

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

     # File lib/memcache.rb, line 692
692:   def hash_for(key)
693:     Zlib.crc32(key)
694:   end
key_length(key) click to toggle source

Calculate length of the key, including the namespace and namespace-separator.

     # File lib/memcache.rb, line 684
684:   def key_length(key)
685:     key.length + (namespace.nil? ? 0 : ( namespace.length + (@namespace_separator.nil? ? 0 : @namespace_separator.length) ) )
686:   end
make_cache_key(key) click to toggle source

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

     # File lib/memcache.rb, line 669
669:   def make_cache_key(key)
670:     if @autofix_keys && (key =~ /\s/ || key_length(key) > 250)
671:       key = "#{Digest::SHA1.hexdigest(key)}-autofixed"
672:     end
673: 
674:     if namespace.nil?
675:       key
676:     else
677:       "#{@namespace}#{@namespace_separator}#{key}"
678:     end
679:   end
noreply() click to toggle source
     # File lib/memcache.rb, line 909
909:   def noreply
910:     @no_reply ? ' noreply' : ''
911:   end
raise_on_error_response!(response) click to toggle source
     # File lib/memcache.rb, line 924
924:   def raise_on_error_response!(response)
925:     if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/
926:       raise MemCacheError, $1.strip
927:     end
928:   end
request_setup(key) click to toggle source

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

     # File lib/memcache.rb, line 917
917:   def request_setup(key)
918:     raise MemCacheError, 'No active servers' unless active?
919:     cache_key = make_cache_key key
920:     server = get_server_for_key cache_key
921:     return server, cache_key
922:   end
with_server(key) click to toggle source
     # File lib/memcache.rb, line 882
882:   def with_server(key)
883:     retried = false
884:     begin
885:       server, cache_key = request_setup(key)
886:       yield server, cache_key
887:     rescue IndexError => e
888:       logger.warn { "Server failed: #{e.class.name}: #{e.message}" } if logger
889:       if !retried && @servers.size > 1
890:         logger.info { "Connection to server #{server.inspect} DIED! Retrying operation..." } if logger
891:         retried = true
892:         retry
893:       end
894:       handle_error(nil, e)
895:     end
896:   end
with_socket_management(server, &block) click to toggle source

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.).

     # File lib/memcache.rb, line 851
851:   def with_socket_management(server, &block)
852:     check_multithread_status!
853: 
854:     @mutex.lock if @multithread
855:     retried = false
856: 
857:     begin
858:       socket = server.socket
859: 
860:       # Raise an IndexError to show this server is out of whack. If were inside
861:       # a with_server block, we'll catch it and attempt to restart the operation.
862: 
863:       raise IndexError, "No connection to server (#{server.status})" if socket.nil?
864: 
865:       block.call(socket)
866: 
867:     rescue SocketError, Errno::EAGAIN, Timeout::Error => err
868:       logger.warn { "Socket failure: #{err.message}" } if logger
869:       server.mark_dead(err)
870:       handle_error(server, err)
871: 
872:     rescue MemCacheError, SystemCallError, IOError => err
873:       logger.warn { "Generic failure: #{err.class.name}: #{err.message}" } if logger
874:       handle_error(server, err) if retried || socket.nil?
875:       retried = true
876:       retry
877:     end
878:   ensure
879:     @mutex.unlock if @multithread
880:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.