src/main/java/com/googlecode/objectify/cache/EntityMemcache.java
public Map<Key, Bucket> getAll(final Iterable<Key> keys) {
		final Map<Key, Bucket> result = new HashMap<>();

		// Sort out the ones that are uncacheable
		final Set<Key> potentials = new HashSet<>();

		for (final Key key: keys) {
			if (!cacheControl.isCacheable(key))
				result.put(key, new Bucket(key));
			else
				potentials.add(key);
		}

		Map<Key, IdentifiableValue> casValues;
		try {
			casValues = this.memcache.getIdentifiables(potentials);
		} catch (Exception ex) {
			// This should really only be a problem if the serialization format for an Entity changes,
			// or someone put a badly-serializing object in the cache underneath us.
			log.warn("Error obtaining cache for " + potentials, ex);
			casValues = new HashMap<>();
		}

		// Now create the remaining buckets
		for (final Key key: keys) {
			final IdentifiableValue casValue = casValues.get(key);	// Might be null, which means uncacheable
			final Bucket buck = new Bucket(key, casValue);
			result.put(key, buck);

			if (buck.isEmpty())
				this.stats.recordMiss(buck.getKey());
			else
				this.stats.recordHit(buck.getKey());
		}

		return result;
	}
public Map<Key, Bucket> getAll(final Iterable<Key> keys) {
		final Map<Key, Bucket> result = new HashMap<>();

		// Sort out the ones that are uncacheable
		final Set<Key> potentials = new HashSet<>();

		for (final Key key: keys) {
			if (!cacheControl.isCacheable(key))
				result.put(key, new Bucket(key));
			else
				potentials.add(key);
		}

		Map<Key, IdentifiableValue> casValues;
		try {
			casValues = this.memcache.getIdentifiables(potentials);
		} catch (Exception ex) {
			// This should really only be a problem if the serialization format for an Entity changes,
			// or someone put a badly-serializing object in the cache underneath us.
			log.warn("Error obtaining cache for " + potentials, ex);
			casValues = new HashMap<>();
		}

		// Now create the remaining buckets
		for (final Key key: keys) {
			final IdentifiableValue casValue = casValues.get(key);	// Might be null, which means uncacheable
			final Bucket buck = new Bucket(key, casValue);
			result.put(key, buck);

			if (buck.isEmpty())
				this.stats.recordMiss(buck.getKey());
			else
				this.stats.recordHit(buck.getKey());
		}

		return result;
	}
<p>Gets the Buckets for the specified keys.  A bucket is built around an IdentifiableValue so you can
putAll() them without the risk of overwriting other threads' changes.  Buckets also hide the
underlying details of storage for negative, empty, and uncacheable results.</p>

<p>Note that worst case (a cold cache), obtaining each bucket might require three memcache requests:
a getIdentifiable() which returns null, a put(null), and another getIdentifiable().  Since
there is no batch getIdentifiable(), this is *per key*.</p>

<p>When keys are uncacheable (per CacheControl) or the memcache is down, you will still get an empty
bucket back.  The bucket will have null IdentifiableValue so we can identify it as uncacheable.</p>

@return the buckets requested.  Buckets will never be null.  You will always get a bucket for every key.