Google Cloud Platform

Memcache Java API Overview

Python |Java |PHP |Go

High performance scalable web applications often use a distributed in-memory data cache in front of or in place of robust persistent storage for some tasks. App Engine includes a memory cache service for this purpose.

  1. When to use a memory cache
  2. Limits
  3. Caching data with the Low-Level API
  4. Caching data with JCache
  5. How cached data expires
  6. Configuring memcache
  7. Monitoring memcache
  8. Safely handling concurrent memcache updates
  9. Best practices

When to use a memory cache

One use of a memory cache is to speed up common datastore queries. If many requests make the same query with the same parameters, and changes to the results do not need to appear on the web site right away, the app can cache the results in the memcache. Subsequent requests can check the memcache, and only perform the datastore query if the results are absent or expired. Session data, user preferences, and any other queries performed on most pages of a site are good candidates for caching.

Memcache may be useful for other temporary values. However, when considering whether to store a value solely in the memcache and not backed by other persistent storage, be sure that your application behaves acceptably when the value is suddenly not available. Values can expire from the memcache at any time, and may be expired prior to the expiration deadline set for the value. For example, if the sudden absence of a user's session data would cause the session to malfunction, that data should probably be stored in the datastore in addition to the memcache.

The memcache service provides best-effort cache space by default. Apps with billing enabled may opt to use dedicated memcache, which provides a fixed cache size assigned exclusively to your app.


The following limits apply to the use of the memcache service:

  • The maximum size of a cached data value is 1 MiB (2^20 bytes) minus the size of the key minus an implementation-dependent overhead, which is approximately 73 bytes.
  • A key cannot be larger than 250 bytes. In the Java runtime, keys that are objects or strings longer than 250 bytes will be hashed. (Other runtimes behave differently.)
  • The "multi" batch operations can have any number of elements. The total size of the call and the total size of the data fetched must not exceed 32 megabytes.
  • A memcache key cannot contain a null byte.

Caching data with the Low-Level API

The Low-Level API provides MemcacheService and AsyncMemcacheService for accessing memcache service. This API is richer than the one provided by JCache. For more details see Low-Level API.

  String key = ..
  byte[] value;

  // Using the synchronous cache.
  MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
  value = (byte[]) syncCache.get(key); // Read from cache.
  if (value == null) {
    // Get value from another source.
    // ........

    syncCache.put(key, value); // Populate cache.

  // Using the asynchronous cache.
  AsyncMemcacheService asyncCache = MemcacheServiceFactory.getAsyncMemcacheService();
  Future<Object> futureValue = asyncCache.get(key); // Read from cache.
  // ... Do other work in parallel to cache retrieval.
  value = (byte[]) futureValue.get();
  if (value == null) {
    // Get value from another source.
    // ........

    // Asynchronously populate the cache.
    // Returns a Future<Void> which can be used to block until completion.
    asyncCache.put(key, value);

Caching data with JCache

The App Engine Java SDK supports the JCache API. JCache provides a map-like interface to cached data. You store and retrieve values in the cache using keys. Keys and values can be of any Serializable type or class. For more details, see Using JCache.

How cached data expires

By default, values stored in memcache are retained as long as possible. Values may be evicted from the cache when a new value is added to the cache if the cache is low on memory. When values are evicted due to memory pressure, the least recently used values are evicted first.

The app can provide an expiration time when a value is stored, as either a number of seconds relative to when the value is added, or as an absolute Unix epoch time in the future (a number of seconds from midnight January 1, 1970). The value will be evicted no later than this time, though it may be evicted for other reasons.

Under rare circumstances, values may also disappear from the cache prior to expiration for reasons other than memory pressure. While memcache is resilient to server failures, memcache values are not saved to disk, so a service failure may cause values to become unavailable.

In general, an application should not expect a cached value to always be available.

You can erase an application's entire cache via the API or via the Google Developers Console's memcache page.

Configuring memcache

App Engine supports two classes of the memcache service:

  • Shared memcache is the free default for App Engine applications. It provides cache capacity on a best-effort basis and is subject to the overall demand of all the App Engine applications using the shared memcache service.

  • Dedicated memcache provides a fixed cache capacity assigned exclusively to your application. It's billed by the GB-hour of cache size. Having control over cache size means your app can perform more predictably and with fewer accesses to more costly durable storage.

Both memcache service classes use the same API. Use the Google Developers Console's memcache page to select the memcache service class for an app.

Whether shared or dedicated, memcache is not durable storage. Keys may be evicted when the cache fills up, according to the cache's LRU policy. Changes in the cache configuration or datacenter maintenance events may also flush some or all of the cache.

The following table summarizes the differences between the two classes of memcache service:

Feature Dedicated Memcache Shared Memcache
Price $0.06 per GB per hour Free
Capacity 1 to 100GB No guaranteed capacity
Performance Up to 10k operations per second per GB (items < 1KB) Not guaranteed
Durable store No No
SLA None None

Dedicated memcache billing is charged in 15 minute increments. When charging in local currency, Google will convert the prices listed into applicable local currency pursuant to the conversion rates published by leading financial institutions.

If your app needs more than 100GB of cache, please contact us at

Monitoring memcache

For information about memcache performance and the relative cost of each operation type, refer to the Google Developers Console's memcache page.

Safely handling concurrent memcache updates

If you're updating the value of a memcache key that might receive other concurrent write requests, you must use the memcache methods putIfUntouched and getIdentifiable instead of put and get. The methods putIfUntouched and getIdentifiable allows multiple requests that are being handled concurrently to update the value of the same memcache key atomically, avoiding race conditions.

The code snippet below shows one way to safely update the value of a key that might have concurrent update requests from other clients:

  String key = ..

  // Using the synchronous cache.
  MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();

 // Write this value to cache using getIdentifiable and putIfUntouched.
  for (long delayMs = 1; ; delayMs *= 2)  {
    IdentifiableValue oldValue = syncCache.getIdentifiable(key);
    byte[] newValue = foo(oldValue.getValue());  // newValue depends on oldValue
    if (oldValue == null) {
      // Key doesn't exist. We can safely put it in cache.
      syncCache.put(key, newValue);
    } else if (syncCache.putIfUntouched(key, oldValue, newValue)) {
      // newValue has been successfully put into cache.
    } else {
      // Some other client changed the value since oldValue was retrieved.
      // Wait a while before trying again, waiting longer on successive loops.

A refinement you could add to this sample code is to set a limit on the number of retries, to avoid blocking for so long that your App Engine request times out.

Best practices

Following are some best practices for using memcache:

  • Handle memcache API failures gracefully. Memcache operations can fail for various reasons. Applications should be designed to catch failed operations without exposing these errors to end users. This applies especially to Set operations.

  • Use the batching capability of the API when possible, especially for small items. This will increase the performance and efficiency of your app.

  • Distribute load across your memcache keyspace. Having a single or small set of memcache items represent a disproportionate amount of traffic will hinder your app from scaling. This applies to both operations/sec and bandwidth. The problem can often be alleviated by explicit sharding of your data. For example, a frequently updated counter can be split among several keys, reading them back and summing only when a total is needed. Likewise, a 500K piece of data that must be read on every HTTP request can be split across multiple keys and read back using a single batch API call. (Even better would be to cache the value in instance memory.) For dedicated memcache, the peak access rate on a single key should be 1-2 orders of magnitude less than the per-GB rating.

For more details and more best practices for concurrency, performance, and migration, including sharing memcache between different programming languages, read the article Best Practices for App Engine Memcache.