Class: AppEngine::Memcache

Inherits:
Object
  • Object
show all
Defined in:
lib/appengine-apis/memcache.rb

Overview

The Ruby API for the App Engine Memcache service. This offers a fast distributed cache for commonly-used data. The cache is limited both in duration and also in total space, so objects stored in it may be discarded at any time.

Note that null is a legal value to store in the cache, or to use as a cache key. Strings are stored encoded as utf-8. To store binary data use AppEngine::Datastore::Blob or str.to_java_bytes.

The values returned from this API are mutable copies from the cache; altering them has no effect upon the cached value itself until assigned with one of the put methods. Likewise, the methods returning collections return mutable collections, but changes do not affect the cache.

Except for the #incr and #decr methods, this service does not offer atomicity guarantees. In particular, operations accessing multiple keys are non-atomic.

Increment has a number of caveats to its use; please consult the method documentation.

Defined Under Namespace

Classes: InvalidValueError, KeyMap, MemcacheError, ServerError

Constant Summary collapse

ADD =
MemcacheService::SetPolicy::ADD_ONLY_IF_NOT_PRESENT
REPLACE =
MemcacheService::SetPolicy::REPLACE_ONLY_IF_PRESENT
SET =
MemcacheService::SetPolicy::SET_ALWAYS
MemCacheError =
MemcacheError
ClientError =
MemcacheError
InternalError =
MemcacheError
MARSHAL_MARKER =
'--JRuby Marshal Data--'

Instance Method Summary collapse

Constructor Details

#initialize(*servers) ⇒ Memcache

Returns a new instance of Memcache.



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/appengine-apis/memcache.rb', line 76

def initialize(*servers)
  options = if servers[-1].kind_of? Hash
    servers[-1]
  else
    {}
  end
  if options.include?(:namespace)
    service.namespace = options[:namespace]
  end
  @readonly = options[:readonly]
end

Instance Method Details

#[]=(*args) ⇒ Object

call-seq:

cache[:foo, :bar] = 1, 2

Syntactic sugar for calling set_many.



290
291
292
293
294
295
296
297
# File 'lib/appengine-apis/memcache.rb', line 290

def []=(*args)
  values = args.pop
  if values.kind_of? Array
    set_many(args.zip(values))
  else
    set(args, values)
  end
end

#active?Boolean

Returns:

  • (Boolean)


93
94
95
96
# File 'lib/appengine-apis/memcache.rb', line 93

def active?
  # TODO use the capability api to see if Memcache is disabled.
  true
end

#add(key, value, expiration = 0) ⇒ Object

Sets a key’s value, iff item is not already in memcache.

Args:

  • key: Key to set.

  • value: Value to set. Any type. If complex, will be marshaled.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns true if added, false on error.



233
234
235
# File 'lib/appengine-apis/memcache.rb', line 233

def add(key, value, expiration=0)
  put(key, value, expiration, ADD)
end

#add_many(pairs, expiration = 0) ⇒ Object

Set multiple keys’ values iff items are not already in memcache.

Args:

  • pairs: Hash of keys to values, or Array of [key, value] pairs.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns a list of keys whose values were NOT set. On total success this list should be empty.



248
249
250
# File 'lib/appengine-apis/memcache.rb', line 248

def add_many(pairs, expiration=0)
  put_many(pairs, expiration, ADD)
end

#decr(key, delta = 1) ⇒ Object

Atomically fetches, deccrements, and stores a given integral value. “Integral” types are Fixnum and in some cases String (if the string is parseable as a number. The entry must already exist.

Internally, the value is a unsigned 64-bit integer. Memcache caps decrementing below zero to zero.

Args:

  • key: the key of the entry to manipulate

  • delta: the size of the decrement

Returns the post-decrement value.

Throws InvalidValueError if the object decremented is not of an integral type.



371
372
373
374
375
376
# File 'lib/appengine-apis/memcache.rb', line 371

def decr(key, delta=1)
  check_write
  convert_exceptions do
    service.increment(memcache_key(key), -delta)
  end
end

#delete(key, time = nil) ⇒ Object

Removes the given key from the cache, and prevents it from being added using #add for time seconds thereafter. Calls to #set are not blocked.

Returns true if an entry existed to delete.



200
201
202
203
204
205
206
# File 'lib/appengine-apis/memcache.rb', line 200

def delete(key, time=nil)
  time ||= 0
  check_write
  convert_exceptions do
    service.delete(memcache_key(key), time * 1000)
  end
end

#delete_many(keys, time = 0) ⇒ Object

Removes the given keys from the cache, and prevents them from being added using #add for time seconds thereafter. Calls to #set are not blocked.

Returns the set of keys deleted. Any keys in keys but not in the returned set were not found in the cache.



213
214
215
216
217
218
219
220
# File 'lib/appengine-apis/memcache.rb', line 213

def delete_many(keys, time=0)
  check_write
  key_map = KeyMap.new(keys)
  convert_exceptions do
    java_keys = service.delete_all(key_map.java_keys, time * 1000)
    key_map.ruby_keys(java_keys)
  end      
end

#do_nothing(*args) ⇒ Object Also known as: server_item_stats, server_malloc_stats, server_map_stats, server_reset_stats, server_size_stats, server_slab_stats, server_stats, servers=

For backwards compatibility. Simply returns nil



421
422
# File 'lib/appengine-apis/memcache.rb', line 421

def do_nothing(*args)
end

#flush_allObject Also known as: clear

Empties the cache of all values. Statistics are not affected. Note that #clear does not respect namespaces - this flushes the cache for every namespace.

Returns true on success, false on RPC or server error.



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/appengine-apis/memcache.rb', line 103

def flush_all
  check_write
  with_errors do
    begin
      service.clear_all
      return true
    rescue MemcacheError
      return false
    end
  end
end

#get(*keys) ⇒ Object Also known as: []

Fetch and return the values associated with the given keys from the cache. Returns nil for any value that wasn’t in the cache.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/appengine-apis/memcache.rb', line 158

def get(*keys)
  multiple = (keys.size != 1)
  if !multiple && keys[0].kind_of?(Array)
    keys = keys[0]
    multiple = true
  end
  hash = get_hash(*keys)
  values = keys.collect {|key| hash[key]}
  if multiple
    values
  else
    values[0]
  end
end

#get_hash(*keys) ⇒ Object

Looks up multiple keys from memcache in one operation. This is more efficient than multiple separate calls to #get.

Args:

  • keys: List of keys to look up.

Returns a hash of the keys and values that were present in memcache.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/appengine-apis/memcache.rb', line 181

def get_hash(*keys)
  key_map = KeyMap.new(keys)
  convert_exceptions do
    map = service.getAll(key_map.java_keys)
    key_map.map_to_hash(map) do |value|
      if value.java_kind_of?(java.util.ArrayList) && value.size == 2 &&
          value[0] == MARSHAL_MARKER
        Marshal.load(String.from_java_bytes(value[1]))
      else
        value
      end
    end
  end
end

#incr(key, delta = 1) ⇒ Object

Atomically fetches, increments, and stores a given integral value. “Integral” types are Fixnum and in some cases String (if the string is parseable as a number. The entry must already exist.

Internally, the value is a unsigned 64-bit integer. Memcache doesn’t check 64-bit overflows. The value, if too large, will wrap around.

Args:

  • key: the key of the entry to manipulate

  • delta: the size of the increment.

Returns the post-increment value.

Throws InvalidValueError if the object incremented is not of an integral type.



349
350
351
352
353
354
# File 'lib/appengine-apis/memcache.rb', line 349

def incr(key, delta=1)
  check_write
  convert_exceptions do
    service.increment(memcache_key(key), delta)
  end
end

#inspectObject



393
394
395
# File 'lib/appengine-apis/memcache.rb', line 393

def inspect
  "<Memcache ns:#{namespace.inspect}, ro:#{readonly?.inspect}>"
end

#namespaceObject

Get the name of the namespace that will be used in API calls.



379
380
381
# File 'lib/appengine-apis/memcache.rb', line 379

def namespace
  service.namespace
end

#namespace=(value) ⇒ Object

Change the namespace used in API calls.



384
385
386
# File 'lib/appengine-apis/memcache.rb', line 384

def namespace=(value)
  service.namespace = value
end

#raise_errors=(should_raise) ⇒ Object

Set whether this client raises an exception if there’s an error contacting the server.

If should_raise is true, a ServerError is raised whenever there is an error contacting the server.

If should_raise is false (the default), a cache miss is simulated instead of raising an error.



412
413
414
415
416
417
418
# File 'lib/appengine-apis/memcache.rb', line 412

def raise_errors=(should_raise)
  if should_raise
    service.error_handler = StrictErrorHandler.new
  else
    service.error_handler = LogAndContinueErrorHandler.new
  end
end

#raise_errors?Boolean

Returns whether the client raises an exception if there’s an error contacting the server. By default it will simulate a cache miss instead of raising an error.

Returns:

  • (Boolean)


400
401
402
# File 'lib/appengine-apis/memcache.rb', line 400

def raise_errors?
  service.error_handler.kind_of? StrictErrorHandler
end

#readonly?Boolean

Returns true if the cache was created read-only.

Returns:

  • (Boolean)


389
390
391
# File 'lib/appengine-apis/memcache.rb', line 389

def readonly?
  @readonly
end

#replace(key, value, expiration = 0) ⇒ Object

Replaces a key’s value, failing if item isn’t already in memcache.

Unlike #add and #replace, this method always sets (or overwrites) the value in memcache, regardless of previous contents.

Args:

  • key: Key to set.

  • value: Value to set. Any type. If complex, will be marshaled.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns true if replaced, false on cache miss.



314
315
316
# File 'lib/appengine-apis/memcache.rb', line 314

def replace(key, value, expiration=0)
  put(key, value, expiration, REPLACE)
end

#replace_many(pairs, expiration = 0) ⇒ Object

Replace multiple keys’ values, failing if the items aren’t in memcache.

Args:

  • pairs: Hash of keys to values, or Array of [key, value] pairs.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns a list of keys whose values were NOT set. On total success this list should be empty.



329
330
331
# File 'lib/appengine-apis/memcache.rb', line 329

def replace_many(pairs, expiration=0)
  put_many(pairs, expiration, REPLACE)
end

#serviceObject

Returns the Java MemcacheService object used by this Memcache client.



89
90
91
# File 'lib/appengine-apis/memcache.rb', line 89

def service
  @service ||= MemcacheServiceFactory.memcache_service
end

#set(key, value, expiration = 0) ⇒ Object

Sets a key’s value, regardless of previous contents in cache.

Unlike #add and #replace, this method always sets (or overwrites) the value in memcache, regardless of previous contents.

Args:

  • key: Key to set.

  • value: Value to set. Any type. If complex, will be marshaled.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns true if set, false on error.



267
268
269
# File 'lib/appengine-apis/memcache.rb', line 267

def set(key, value, expiration=0)
  put(key, value, expiration, SET)
end

#set_many(pairs, expiration = 0) ⇒ Object

Set multiple keys’ values at once, regardless of previous contents.

Args:

  • pairs: Hash of keys to values, or Array of [key, value] pairs.

  • expiration: Optional expiration time, either relative number of seconds from current time (up to 1 month), an absolute Unix epoch time, or a Time. By default, items never expire, though items may be evicted due to memory pressure.

Returns a list of keys whose values were NOT set. On total success this list should be empty.



282
283
284
# File 'lib/appengine-apis/memcache.rb', line 282

def set_many(pairs, expiration=0)
  put_many(pairs, expiration, SET)
end

#statsObject

Gets memcache statistics for this application.

All of these statistics may reset due to various transient conditions. They provide the best information available at the time of being called.

Returns a Hash mapping statistic names to associated values:

:hits

Number of cache get requests resulting in a cache hit.

:misses

Number of cache get requests resulting in a cache miss.

:byte_hits

Sum of bytes transferred on get requests. Rolls over to zero on overflow.

:items

Number of key/value pairs in the cache.

:bytes

Total size of all items in the cache.

:oldest_item_age

How long in seconds since the oldest item in the cache was accessed. Effectively, this indicates how long a new item will survive in the cache without being accessed. This is not the amount of time that has elapsed since the item was created.

On error, returns nil.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/appengine-apis/memcache.rb', line 136

def stats
  with_errors do
    begin
      stats = service.statistics
      if stats
        {
          :hits => stats.hit_count,
          :misses => stats.miss_count,
          :byte_hits => stats.bytes_returned_for_hits,
          :items => stats.item_count,
          :bytes => stats.total_item_bytes,
          :oldest_item_age => stats.max_time_without_access / 1000.0
        }
      end
    rescue ServerError
      nil
    end
  end
end