Class: MemCache

Inherits:
Object show all
Defined in:
lib/active_support/vendor/memcache-client-1.5.0/memcache.rb

Overview

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.

Defined Under Namespace

Classes: MemCacheError, Server

Constant Summary collapse

VERSION =

The version of MemCache you are using.

'1.5.0'
DEFAULT_OPTIONS =

Default options for the cache object.

{
  :namespace   => nil,
  :readonly    => false,
  :multithread => false,
}
DEFAULT_PORT =

Default memcached port.

11211
DEFAULT_WEIGHT =

Default memcached server weight.

1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ MemCache

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.


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 127

def initialize(*args)
  servers = []
  opts = {}

  case args.length
  when 0 then # NOP
  when 1 then
    arg = args.shift
    case arg
    when Hash   then opts = arg
    when Array  then servers = arg
    when String then servers = [arg]
    else raise ArgumentError, 'first argument must be Array, Hash or String'
    end
  when 2 then
    servers, opts = args
  else
    raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
  end

  opts = DEFAULT_OPTIONS.merge opts
  @namespace   = opts[:namespace]
  @readonly    = opts[:readonly]
  @multithread = opts[:multithread]
  @mutex       = Mutex.new if @multithread
  @buckets     = []
  self.servers = servers
end

Instance Attribute Details

#multithreadObject (readonly)

The multithread setting for this instance


108
109
110
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 108

def multithread
  @multithread
end

#namespaceObject (readonly)

The namespace for this instance


103
104
105
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 103

def namespace
  @namespace
end

#request_timeoutObject

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.


98
99
100
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 98

def request_timeout
  @request_timeout
end

#serversObject

The servers this client talks to. Play at your own peril.


113
114
115
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 113

def servers
  @servers
end

Instance Method Details

#[]=(key, value) ⇒ Object

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.


519
520
521
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 519

def []=(key, value)
  set key, value
end

#active?Boolean

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

Returns:

  • (Boolean)

167
168
169
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 167

def active?
  not @servers.empty?
end

#add(key, value, expiry = 0, raw = false) ⇒ Object

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#[]=.

Raises:


352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 352

def add(key, value, expiry = 0, raw = false)
  raise MemCacheError, "Update of readonly cache" if @readonly
  server, cache_key = request_setup key
  socket = server.socket

  value = Marshal.dump value unless raw
  command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"

  begin
    @mutex.lock if @multithread
    socket.write command
    result = socket.gets
    raise_on_error_response! result
    result
  rescue SocketError, SystemCallError, IOError => err
    server.close
    raise MemCacheError, err.message
  ensure
    @mutex.unlock if @multithread
  end
end

#decr(key, amount = 1) ⇒ Object

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.


214
215
216
217
218
219
220
221
222
223
224
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 214

def decr(key, amount = 1)
  server, cache_key = request_setup key

  if @multithread then
    threadsafe_cache_decr server, cache_key, amount
  else
    cache_decr server, cache_key, amount
  end
rescue TypeError, SocketError, SystemCallError, IOError => err
  handle_error server, err
end

#delete(key, expiry = 0) ⇒ Object

Removes key from the cache in expiry seconds.


377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 377

def delete(key, expiry = 0)
  @mutex.lock if @multithread

  raise MemCacheError, "No active servers" unless active?
  cache_key = make_cache_key key
  server = get_server_for_key cache_key

  sock = server.socket
  raise MemCacheError, "No connection to server" if sock.nil?

  begin
    sock.write "delete #{cache_key} #{expiry}\r\n"
    result = sock.gets
    raise_on_error_response! result
    result
  rescue SocketError, SystemCallError, IOError => err
    server.close
    raise MemCacheError, err.message
  end
ensure
  @mutex.unlock if @multithread
end

#flush_allObject

Flush the cache from all memcache servers.

Raises:


403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 403

def flush_all
  raise MemCacheError, 'No active servers' unless active?
  raise MemCacheError, "Update of readonly cache" if @readonly
  begin
    @mutex.lock if @multithread
    @servers.each do |server|
      begin
        sock = server.socket
        raise MemCacheError, "No connection to server" if sock.nil?
        sock.write "flush_all\r\n"
        result = sock.gets
        raise_on_error_response! result
        result
      rescue SocketError, SystemCallError, IOError => err
        server.close
        raise MemCacheError, err.message
      end
    end
  ensure
    @mutex.unlock if @multithread
  end
end

#get(key, raw = false) ⇒ Object Also known as: []

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


230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 230

def get(key, raw = false)
  server, cache_key = request_setup key

  value = if @multithread then
            threadsafe_cache_get server, cache_key
          else
            cache_get server, cache_key
          end

  return nil if value.nil?

  value = Marshal.load value unless raw

  return value
rescue TypeError, SocketError, SystemCallError, IOError => err
  handle_error server, err
end

#get_multi(*keys) ⇒ Object

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 }

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 264

def get_multi(*keys)
  raise MemCacheError, 'No active servers' unless active?

  keys.flatten!
  key_count = keys.length
  cache_keys = {}
  server_keys = Hash.new { |h,k| h[k] = [] }

  # map keys to servers
  keys.each do |key|
    server, cache_key = request_setup key
    cache_keys[cache_key] = key
    server_keys[server] << cache_key
  end

  results = {}

  server_keys.each do |server, keys_for_server|
    keys_for_server = keys_for_server.join ' '
    values = if @multithread then
               threadsafe_cache_get_multi server, keys_for_server
             else
               cache_get_multi server, keys_for_server
             end
    values.each do |key, value|
      results[cache_keys[key]] = Marshal.load value
    end
  end

  return results
rescue TypeError, SocketError, SystemCallError, IOError => err
  handle_error server, err
end

#incr(key, amount = 1) ⇒ Object

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.


303
304
305
306
307
308
309
310
311
312
313
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 303

def incr(key, amount = 1)
  server, cache_key = request_setup key

  if @multithread then
    threadsafe_cache_incr server, cache_key, amount
  else
    cache_incr server, cache_key, amount
  end
rescue TypeError, SocketError, SystemCallError, IOError => err
  handle_error server, err
end

#inspectObject

Returns a string representation of the cache object.


159
160
161
162
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 159

def inspect
  "<MemCache: %d servers, %d buckets, ns: %p, ro: %p>" %
    [@servers.length, @buckets.length, @namespace, @readonly]
end

#readonly?Boolean

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

Returns:

  • (Boolean)

174
175
176
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 174

def readonly?
  @readonly
end

#resetObject

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.


431
432
433
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 431

def reset
  @servers.each { |server| server.close }
end

#set(key, value, expiry = 0, raw = false) ⇒ Object

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.

Raises:


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 322

def set(key, value, expiry = 0, raw = false)
  raise MemCacheError, "Update of readonly cache" if @readonly
  server, cache_key = request_setup key
  socket = server.socket

  value = Marshal.dump value unless raw
  command = "set #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"

  begin
    @mutex.lock if @multithread
    socket.write command
    result = socket.gets
    raise_on_error_response! result
    result
  rescue SocketError, SystemCallError, IOError => err
    server.close
    raise MemCacheError, err.message
  ensure
    @mutex.unlock if @multithread
  end
end

#statsObject

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

Raises:


467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/active_support/vendor/memcache-client-1.5.0/memcache.rb', line 467

def stats
  raise MemCacheError, "No active servers" unless active?
  server_stats = {}

  @servers.each do |server|
    sock = server.socket
    raise MemCacheError, "No connection to server" if sock.nil?

    value = nil
    begin
      sock.write "stats\r\n"
      stats = {}
      while line = sock.gets do
        raise_on_error_response! line
        break if line == "END\r\n"
        if line =~ /\ASTAT ([\w]+) ([\w\.\:]+)/ then
          name, value = $1, $2
          stats[name] = case name
                        when 'version'
                          value
                        when 'rusage_user', 'rusage_system' then
                          seconds, microseconds = value.split(/:/, 2)
                          microseconds ||= 0
                          Float(seconds) + (Float(microseconds) / 1_000_000)
                        else
                          if value =~ /\A\d+\Z/ then
                            value.to_i
                          else
                            value
                          end
                        end
        end
      end
      server_stats["#{server.host}:#{server.port}"] = stats
    rescue SocketError, SystemCallError, IOError => err
      server.close
      raise MemCacheError, err.message
    end
  end

  server_stats
end