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

A Ruby client library for memcached.

This is intended to provide access to basic memcached functionality. It does not attempt to be complete implementation of the entire API, but it is approaching a complete implementation.

Methods

Classes and Modules

Class MemCache::MemCacheError
Class MemCache::Server

Constants

VERSION = '1.5.0'   The version of MemCache you are using.
DEFAULT_OPTIONS = { :namespace => nil, :readonly => false, :multithread => false, }   Default options for the cache object.
DEFAULT_PORT = 11211   Default memcached port.
DEFAULT_WEIGHT = 1   Default memcached server weight.

Attributes

multithread  [R]  The multithread setting for this instance
namespace  [R]  The namespace for this instance
request_timeout  [RW]  The amount of time to wait for a response from a memcached server. If a response is not completed within this time, the connection to the server will be closed and an error will be raised.
servers  [R]  The servers this client talks to. Play at your own peril.

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 exeception on cache writes when true.
  [:multithread] Wraps cache access in a Mutex for thread safety.

Other options are ignored.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 127
127:   def initialize(*args)
128:     servers = []
129:     opts = {}
130: 
131:     case args.length
132:     when 0 then # NOP
133:     when 1 then
134:       arg = args.shift
135:       case arg
136:       when Hash   then opts = arg
137:       when Array  then servers = arg
138:       when String then servers = [arg]
139:       else raise ArgumentError, 'first argument must be Array, Hash or String'
140:       end
141:     when 2 then
142:       servers, opts = args
143:     else
144:       raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
145:     end
146: 
147:     opts = DEFAULT_OPTIONS.merge opts
148:     @namespace   = opts[:namespace]
149:     @readonly    = opts[:readonly]
150:     @multithread = opts[:multithread]
151:     @mutex       = Mutex.new if @multithread
152:     @buckets     = []
153:     self.servers = servers
154:   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.5.0/memcache.rb, line 519
519:   def []=(key, value)
520:     set key, value
521:   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.5.0/memcache.rb, line 167
167:   def active?
168:     not @servers.empty?
169:   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 or MemCache#[]=.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 352
352:   def add(key, value, expiry = 0, raw = false)
353:     raise MemCacheError, "Update of readonly cache" if @readonly
354:     server, cache_key = request_setup key
355:     socket = server.socket
356: 
357:     value = Marshal.dump value unless raw
358:     command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
359: 
360:     begin
361:       @mutex.lock if @multithread
362:       socket.write command
363:       result = socket.gets
364:       raise_on_error_response! result
365:       result
366:     rescue SocketError, SystemCallError, IOError => err
367:       server.close
368:       raise MemCacheError, err.message
369:     ensure
370:       @mutex.unlock if @multithread
371:     end
372:   end

Deceremets 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.5.0/memcache.rb, line 214
214:   def decr(key, amount = 1)
215:     server, cache_key = request_setup key
216: 
217:     if @multithread then
218:       threadsafe_cache_decr server, cache_key, amount
219:     else
220:       cache_decr server, cache_key, amount
221:     end
222:   rescue TypeError, SocketError, SystemCallError, IOError => err
223:     handle_error server, err
224:   end

Removes key from the cache in expiry seconds.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 377
377:   def delete(key, expiry = 0)
378:     @mutex.lock if @multithread
379: 
380:     raise MemCacheError, "No active servers" unless active?
381:     cache_key = make_cache_key key
382:     server = get_server_for_key cache_key
383: 
384:     sock = server.socket
385:     raise MemCacheError, "No connection to server" if sock.nil?
386: 
387:     begin
388:       sock.write "delete #{cache_key} #{expiry}\r\n"
389:       result = sock.gets
390:       raise_on_error_response! result
391:       result
392:     rescue SocketError, SystemCallError, IOError => err
393:       server.close
394:       raise MemCacheError, err.message
395:     end
396:   ensure
397:     @mutex.unlock if @multithread
398:   end

Flush the cache from all memcache servers.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 403
403:   def flush_all
404:     raise MemCacheError, 'No active servers' unless active?
405:     raise MemCacheError, "Update of readonly cache" if @readonly
406:     begin
407:       @mutex.lock if @multithread
408:       @servers.each do |server|
409:         begin
410:           sock = server.socket
411:           raise MemCacheError, "No connection to server" if sock.nil?
412:           sock.write "flush_all\r\n"
413:           result = sock.gets
414:           raise_on_error_response! result
415:           result
416:         rescue SocketError, SystemCallError, IOError => err
417:           server.close
418:           raise MemCacheError, err.message
419:         end
420:       end
421:     ensure
422:       @mutex.unlock if @multithread
423:     end
424:   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.5.0/memcache.rb, line 230
230:   def get(key, raw = false)
231:     server, cache_key = request_setup key
232: 
233:     value = if @multithread then
234:               threadsafe_cache_get server, cache_key
235:             else
236:               cache_get server, cache_key
237:             end
238: 
239:     return nil if value.nil?
240: 
241:     value = Marshal.load value unless raw
242: 
243:     return value
244:   rescue TypeError, SocketError, SystemCallError, IOError => err
245:     handle_error server, err
246:   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 }

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 264
264:   def get_multi(*keys)
265:     raise MemCacheError, 'No active servers' unless active?
266: 
267:     keys.flatten!
268:     key_count = keys.length
269:     cache_keys = {}
270:     server_keys = Hash.new { |h,k| h[k] = [] }
271: 
272:     # map keys to servers
273:     keys.each do |key|
274:       server, cache_key = request_setup key
275:       cache_keys[cache_key] = key
276:       server_keys[server] << cache_key
277:     end
278: 
279:     results = {}
280: 
281:     server_keys.each do |server, keys_for_server|
282:       keys_for_server = keys_for_server.join ' '
283:       values = if @multithread then
284:                  threadsafe_cache_get_multi server, keys_for_server
285:                else
286:                  cache_get_multi server, keys_for_server
287:                end
288:       values.each do |key, value|
289:         results[cache_keys[key]] = Marshal.load value
290:       end
291:     end
292: 
293:     return results
294:   rescue TypeError, SocketError, SystemCallError, IOError => err
295:     handle_error server, err
296:   end

Increments the value for key by amount and retruns 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.5.0/memcache.rb, line 303
303:   def incr(key, amount = 1)
304:     server, cache_key = request_setup key
305: 
306:     if @multithread then
307:       threadsafe_cache_incr server, cache_key, amount
308:     else
309:       cache_incr server, cache_key, amount
310:     end
311:   rescue TypeError, SocketError, SystemCallError, IOError => err
312:     handle_error server, err
313:   end

Returns a string representation of the cache object.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 159
159:   def inspect
160:     "<MemCache: %d servers, %d buckets, ns: %p, ro: %p>" %
161:       [@servers.length, @buckets.length, @namespace, @readonly]
162:   end

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

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 174
174:   def readonly?
175:     @readonly
176:   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.5.0/memcache.rb, line 431
431:   def reset
432:     @servers.each { |server| server.close }
433:   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.5.0/memcache.rb, line 183
183:   def servers=(servers)
184:     # Create the server objects.
185:     @servers = servers.collect do |server|
186:       case server
187:       when String
188:         host, port, weight = server.split ':', 3
189:         port ||= DEFAULT_PORT
190:         weight ||= DEFAULT_WEIGHT
191:         Server.new self, host, port, weight
192:       when Server
193:         if server.memcache.multithread != @multithread then
194:           raise ArgumentError, "can't mix threaded and non-threaded servers"
195:         end
196:         server
197:       else
198:         raise TypeError, "cannot convert #{server.class} into MemCache::Server"
199:       end
200:     end
201: 
202:     # Create an array of server buckets for weight selection of servers.
203:     @buckets = []
204:     @servers.each do |server|
205:       server.weight.times { @buckets.push(server) }
206:     end
207:   end

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.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 322
322:   def set(key, value, expiry = 0, raw = false)
323:     raise MemCacheError, "Update of readonly cache" if @readonly
324:     server, cache_key = request_setup key
325:     socket = server.socket
326: 
327:     value = Marshal.dump value unless raw
328:     command = "set #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
329: 
330:     begin
331:       @mutex.lock if @multithread
332:       socket.write command
333:       result = socket.gets
334:       raise_on_error_response! result
335:       result
336:     rescue SocketError, SystemCallError, IOError => err
337:       server.close
338:       raise MemCacheError, err.message
339:     ensure
340:       @mutex.unlock if @multithread
341:     end
342:   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.5.0/memcache.rb, line 467
467:   def stats
468:     raise MemCacheError, "No active servers" unless active?
469:     server_stats = {}
470: 
471:     @servers.each do |server|
472:       sock = server.socket
473:       raise MemCacheError, "No connection to server" if sock.nil?
474: 
475:       value = nil
476:       begin
477:         sock.write "stats\r\n"
478:         stats = {}
479:         while line = sock.gets do
480:           raise_on_error_response! line
481:           break if line == "END\r\n"
482:           if line =~ /\ASTAT ([\w]+) ([\w\.\:]+)/ then
483:             name, value = $1, $2
484:             stats[name] = case name
485:                           when 'version'
486:                             value
487:                           when 'rusage_user', 'rusage_system' then
488:                             seconds, microseconds = value.split(/:/, 2)
489:                             microseconds ||= 0
490:                             Float(seconds) + (Float(microseconds) / 1_000_000)
491:                           else
492:                             if value =~ /\A\d+\Z/ then
493:                               value.to_i
494:                             else
495:                               value
496:                             end
497:                           end
498:           end
499:         end
500:         server_stats["#{server.host}:#{server.port}"] = stats
501:       rescue SocketError, SystemCallError, IOError => err
502:         server.close
503:         raise MemCacheError, err.message
504:       end
505:     end
506: 
507:     server_stats
508:   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.5.0/memcache.rb, line 570
570:   def cache_decr(server, cache_key, amount)
571:     socket = server.socket
572:     socket.write "decr #{cache_key} #{amount}\r\n"
573:     text = socket.gets
574:     raise_on_error_response! text
575:     return nil if text == "NOT_FOUND\r\n"
576:     return text.to_i
577:   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.5.0/memcache.rb, line 583
583:   def cache_get(server, cache_key)
584:     socket = server.socket
585:     socket.write "get #{cache_key}\r\n"
586:     keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
587: 
588:     if keyline.nil? then
589:       server.close
590:       raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
591:     end
592: 
593:     raise_on_error_response! keyline
594:     return nil if keyline == "END\r\n"
595: 
596:     unless keyline =~ /(\d+)\r/ then
597:       server.close
598:       raise MemCacheError, "unexpected response #{keyline.inspect}"
599:     end
600:     value = socket.read $1.to_i
601:     socket.read 2 # "\r\n"
602:     socket.gets   # "END\r\n"
603:     return value
604:   end

Fetches cache_keys from server using a multi-get.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 609
609:   def cache_get_multi(server, cache_keys)
610:     values = {}
611:     socket = server.socket
612:     socket.write "get #{cache_keys}\r\n"
613: 
614:     while keyline = socket.gets do
615:       return values if keyline == "END\r\n"
616:       raise_on_error_response! keyline
617: 
618:       unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
619:         server.close
620:         raise MemCacheError, "unexpected response #{keyline.inspect}"
621:       end
622: 
623:       key, data_length = $1, $3
624:       values[$1] = socket.read data_length.to_i
625:       socket.read(2) # "\r\n"
626:     end
627: 
628:     server.close
629:     raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
630:   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.5.0/memcache.rb, line 636
636:   def cache_incr(server, cache_key, amount)
637:     socket = server.socket
638:     socket.write "incr #{cache_key} #{amount}\r\n"
639:     text = socket.gets
640:     raise_on_error_response! text
641:     return nil if text == "NOT_FOUND\r\n"
642:     return text.to_i
643:   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.5.0/memcache.rb, line 540
540:   def get_server_for_key(key)
541:     raise ArgumentError, "illegal character in key #{key.inspect}" if
542:       key =~ /\s/
543:     raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
544:     raise MemCacheError, "No servers available" if @servers.empty?
545:     return @servers.first if @servers.length == 1
546: 
547:     hkey = hash_for key
548: 
549:     20.times do |try|
550:       server = @buckets[hkey % @buckets.nitems]
551:       return server if server.alive?
552:       hkey += hash_for "#{try}#{key}"
553:     end
554: 
555:     raise MemCacheError, "No servers available"
556:   end

Handles error from server.

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 648
648:   def handle_error(server, error)
649:     server.close if server
650:     new_error = MemCacheError.new error.message
651:     new_error.set_backtrace error.backtrace
652:     raise new_error
653:   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.5.0/memcache.rb, line 562
562:   def hash_for(key)
563:     (key.crc32_ITU_T >> 16) & 0x7fff
564:   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.5.0/memcache.rb, line 529
529:   def make_cache_key(key)
530:     if namespace.nil? then
531:       key
532:     else
533:       "#{@namespace}:#{key}"
534:     end
535:   end

[Source]

     # File vendor/rails/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb, line 695
695:   def raise_on_error_response!(response)
696:     if response =~ /\A(?:CLIENT_|SERVER_)?ERROR (.*)/
697:       raise MemCacheError, $1.strip
698:     end
699:   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.5.0/memcache.rb, line 659
659:   def request_setup(key)
660:     raise MemCacheError, 'No active servers' unless active?
661:     cache_key = make_cache_key key
662:     server = get_server_for_key cache_key
663:     raise MemCacheError, 'No connection to server' if server.socket.nil?
664:     return server, cache_key
665:   end

[Validate]